Социальные функции
Социальная система обеспечивает чат в реальном времени, гильдии, друзей с отслеживанием присутствия и пиринговый обмен монстрами/предметами. Все сервисы используют встроенные социальные API Nakama (Groups, Friends, Chat channels) и матчи реального времени (для обмена).
Обзор архитектуры
| Класс | Файл | Ответственность |
|---|---|---|
ChatService | Social/SocialServices.cs | Чат реального времени с 7 типами каналов |
GuildService | Social/SocialServices.cs | CRUD гильдий, роли, управление участниками через Nakama Groups |
FriendService | Social/SocialServices.cs | Список друзей, отслеживание присутствия, блокировка |
TradeService | Social/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(). Он использует канал типа Room Nakama с включённой персистентностью:
// Автоматически при подключении -- но можно вызвать вручную
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);
Шёпот использует тип канала DirectMessage Nakama. Сервис автоматически присоединяется к каналу ЛС с целевым пользователем перед отправкой.
Получение сообщений
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 управляет гильдиями (командами/клубами) с помощью API Groups Nakama.
Создание гильдии
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 определяет иерархию разрешений:
| Роль | Значение | Разрешения |
|---|---|---|
Leader | 0 | Полный контроль (кик, повышение, роспуск) |
Officer | 1 | Кик участников, управление настройками |
Member | 2 | Стандартное участие |
Recruit | 3 | Ограниченный доступ (испытательный срок) |
Управление участниками
// Повысить участника
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 использует Friends API Nakama с отслеживанием присутствия в реальном времени через события статуса.
Загрузка друзей
Друзья загружаются автоматически во время подключения:
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);
Отслеживание присутствия
Обновления присутствия в реальном времени поступают через 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);
Использует BlockFriendsAsync Nakama, который предотвращает любое взаимодействие.
Обмен между игроками
TradeService реализует полную систему P2P-обмена с использованием матчей реального времени Nakama и протокола конечного автомата.
Коды операций протокола обмена
| OpCode | Константа | Значение |
|---|---|---|
100 | OP_TRADE_REQUEST | Начальный запрос обмена |
101 | OP_TRADE_OFFER | Игрок предлагает монстра или предмет |
102 | OP_TRADE_CONFIRM | Игрок подтверждает обмен |
103 | OP_TRADE_CANCEL | Игрок отменяет обмен |
104 | OP_TRADE_COMPLETE | Сервер подтверждает завершение обмена |
Состояния обмена
Перечисление TradeState отслеживает жизненный цикл обмена:
Pending -> Offering -> Confirming -> Completed (или Cancelled в любой момент)
Инициирование обмена
string tradeId = await trade.RequestTradeAsync(targetUserId);
Это создаёт матч Nakama для сессии обмена и уведомляет цель через RPC trade_request.
Предложение предметов
// Предложить м онстра
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;
Состояние матча обмена также маршрутизируется через обработчик ReceivedMatchState в NakamaManager, который перенаправляет его в 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) => { };
Справочник по конфигурации
| Поле конфигурации | По умолчанию | Описание |
|---|---|---|
MaxFriends | 100 | Максимум друзей на игрока |
MaxGuildMembers | 50 | Максимум участников гильдии |
ChatHistoryCount | 50 | Сообщений чата для хранения в памяти |