跳到主要内容

社交功能

社交系统提供实时聊天、公会、带有状态跟踪的好友以及玩家间的怪物/物品交易。所有服务均使用 Nakama 的内置社交 API(Groups、Friends、Chat channels)和实时匹配(用于交易)。

架构概览​

类文件职责
ChatServiceSocial/SocialServices.cs支持 7 种频道类型的实时聊天
GuildServiceSocial/SocialServices.cs通过 Nakama Groups 实现公会增删改查、角色、成员管理
FriendServiceSocial/SocialServices.cs好友列表、状态跟踪、屏蔽
TradeServiceSocial/SocialServices.cs带有服务器验证状态机的 P2P 交易

通过 NakamaManager.Instance 访问:

var chat = NakamaManager.Instance.Chat;
var guilds = NakamaManager.Instance.Guilds;
var friends = NakamaManager.Instance.Friends;
var trade = NakamaManager.Instance.Trade;

聊天系统​

ChatService 使用 Nakama 的频道消息系统实现了一个多频道实时聊天系统。

频道类型​

ChatChannel 枚举定义了 7 种频道类型:

频道描述
Global全服务器公开聊天
Local附近玩家可见(同一地图/区域)
Trade经济和交易讨论
Guild仅公会消息
Whisper私密 1 对 1 直接消息
System服务器公告和通知
Battle战斗相关通信

加入频道​

全局频道在连接期间通过 JoinGlobalChannelAsync() 自动加入。它使用 Nakama 的 Room 类型频道并启用持久化:

// 连接时自动加入 -- 但也可以手动调用
await chat.JoinGlobalChannelAsync();

发送消息​

// 发送到公共频道
await chat.SendMessageAsync(ChatChannel.Global, "Hello everyone!");
await chat.SendMessageAsync(ChatChannel.Trade, "WTB: Rare Candy x10");

// 悄悄话(直接消息)
await chat.SendMessageAsync(ChatChannel.Whisper, "Hey!", targetId: recipientUserId);

悄悄话使用 Nakama 的 DirectMessage 频道类型。服务在发送前会自动与目标用户加入 DM 频道。

接收消息​

chat.OnMessageReceived += (ChatMessage msg) =>
{
// msg.SenderId, msg.SenderName, msg.Content
// msg.Channel (ChatChannel enum), msg.Timestamp
};

// 访问消息历史(上限为 ChatHistoryCount)
IReadOnlyList<ChatMessage> history = chat.History;

消息历史记录上限为 OnlineConfig.ChatHistoryCount(默认:50)。超出上限时,最旧的消息会被移除。

频道事件​

chat.OnChannelJoined += (ChatChannel channel) =>
{
Debug.Log($"Joined {channel} channel");
};

清理​

所有频道在断开连接时退出:

await chat.LeaveAllChannelsAsync();

公会系统​

GuildService 使用 Nakama 的 Groups API 管理公会(团队/俱乐部)。

创建公会​

GuildInfo guild = await guilds.CreateGuildAsync(
name: "Team Rocket",
tag: "TR",
description: "Prepare for trouble!");

// guild.Id, guild.Name, guild.Tag, guild.MemberCount, guild.MaxMembers

每个公会的最大成员数:OnlineConfig.MaxGuildMembers(默认:50)。

加入和离开​

bool joined = await guilds.JoinGuildAsync(guildId);
bool left = await guilds.LeaveGuildAsync();

公会角色​

GuildRole 枚举定义了权限层级:

角色值权限
Leader0完全控制(踢出、晋升、解散)
Officer1踢出成员、管理设置
Member2标准参与
Recruit3有限访问(试用期)

成员管理​

// 晋升成员
await guilds.PromoteMemberAsync(memberId, GuildRole.Officer);

// 踢出成员
await guilds.KickMemberAsync(memberId);

搜索公会​

List<GuildInfo> results = await guilds.SearchGuildsAsync("Rocket");

foreach (var g in results)
{
// g.Id, g.Name, g.Description
// g.MemberCount, g.MaxMembers
}

公会状态​

bool isInGuild = guilds.IsInGuild;
GuildInfo current = guilds.CurrentGuild;

guilds.OnGuildUpdated += (GuildInfo info) => { };
guilds.OnMemberJoined += (string userId) => { };
guilds.OnMemberLeft += (string userId) => { };

好友系统​

FriendService 使用 Nakama 的 Friends API 并通过状态事件实现实时状态跟踪。

加载好友​

好友在连接期间自动加载:

await friends.RefreshFriendsListAsync(userId);

这会调用 Client.ListFriendsAsync(上限为 OnlineConfig.MaxFriends,默认:100),然后通过 Socket.FollowUsersAsync() 关注所有好友的状态以获取实时状态更新。

好友请求​

// 发送好友请求
bool sent = await friends.SendFriendRequestAsync(targetUserId);

// 接受好友请求
bool accepted = await friends.AcceptFriendRequestAsync(fromUserId);

// 删除好友
bool removed = await friends.RemoveFriendAsync(friendId);

状态跟踪​

实时状态更新通过 Socket 上的 ReceivedStatusPresence 到达:

friends.OnFriendOnline += (FriendInfo info) =>
{
Debug.Log($"{info.DisplayName} is online!");
};

friends.OnFriendOffline += (string userId) =>
{
Debug.Log($"Friend {userId} went offline");
};

friends.OnFriendRequestReceived += (FriendInfo info) =>
{
// 显示好友请求通知
};

好友数据​

IReadOnlyList<FriendInfo> list = friends.FriendsList;

foreach (var f in list)
{
// f.UserId, f.DisplayName, f.IsOnline
// f.CurrentMap, f.Level
}

屏蔽​

bool blocked = await friends.BlockUserAsync(userId);

使用 Nakama 的 BlockFriendsAsync,可阻止所有互动。

玩家间交易​

TradeService 使用 Nakama 实时匹配和状态机协议实现了一个完整的 P2P 交易系统。

交易协议操作码​

OpCode常量含义
100OP_TRADE_REQUEST初始交易请求
101OP_TRADE_OFFER玩家提供怪物或物品
102OP_TRADE_CONFIRM玩家确认交易
103OP_TRADE_CANCEL玩家取消交易
104OP_TRADE_COMPLETE服务器确认交易完成

交易状态​

TradeState 枚举跟踪交易生命周期:

Pending -> Offering -> Confirming -> Completed(或任何时刻 Cancelled)

发起交易​

string tradeId = await trade.RequestTradeAsync(targetUserId);

这会为交易会话创建一个 Nakama 匹配,并通过 trade_request RPC 通知目标玩家。

提供物品​

// 提供怪物
await trade.OfferMonsterAsync(tradeId, monsterId);

// 提供物品
await trade.OfferItemAsync(tradeId, itemId: "rare_candy", quantity: 5);

提供物作为匹配状态消息通过 OP_TRADE_OFFER 发送。

确认和完成​

// 双方玩家都必须确认
await trade.ConfirmTradeAsync(tradeId);

当双方玩家都确认后,服务器原子性地执行交易并发送 OP_TRADE_COMPLETE。

取消​

await trade.CancelTradeAsync(tradeId);

发送 OP_TRADE_CANCEL,离开匹配,并清除活跃交易。

交易事件​

trade.OnTradeRequestReceived += (string fromUserId, string fromName) => { };
trade.OnTradeStateChanged += (TradeState state) => { };
trade.OnTradeCompleted += (bool success) =>
{
if (success) Debug.Log("Trade completed!");
else Debug.Log("Trade was cancelled.");
};

交易状态属性​

bool isTrading = trade.IsTrading;
string activeTradeId = trade.ActiveTradeId;
string activeMatchId = trade.ActiveMatchId;

交易匹配状态也通过 NakamaManager 的 ReceivedMatchState 处理程序路由,并转发到 Trade.HandleMatchState()。

社交通知​

NakamaManager 通过统一事件呈现所有 Nakama 通知:

NakamaManager.Instance.OnNotification += (string subject, string content) =>
{
Debug.Log($"Notification: {subject} - {content}");
};

此外,常见的社交操作也有专用事件:

NakamaManager.Instance.OnBattleInvite += (string fromUserId) => { };
NakamaManager.Instance.OnTradeRequest += (string fromUserId) => { };
NakamaManager.Instance.OnFriendRequest += (string fromUserId) => { };

配置参考​

配置字段默认值描述
MaxFriends100每名玩家的最大好友数
MaxGuildMembers50最大公会成员数
ChatHistoryCount50内存中保留的聊天消息数