Перейти к основному содержимому

Социальные функции

Социальная система обеспечивает чат в реальном времени, гильдии, друзей с отслеживанием присутствия и пиринговый обмен монстрами/предметами. Все сервисы используют встроенные социальные API Nakama (Groups, Friends, Chat channels) и матчи реального времени (для обмена).

Обзор архитектуры​

КлассФайлОтветственность
ChatServiceSocial/SocialServices.csЧат реального времени с 7 типами каналов
GuildServiceSocial/SocialServices.csCRUD гильдий, роли, управление участниками через Nakama Groups
FriendServiceSocial/SocialServices.csСписок друзей, отслеживание присутствия, блокировка
TradeServiceSocial/SocialServices.csP2P обмен с проверяемой сервером машиной состояний

Доступ через 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 определяет иерархию разрешений:

РольЗначениеРазрешения
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 использует 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КонстантаЗначение
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 для сессии обмена и уведомляет цель через 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) => { };

Справочник по конфигурации​

Поле конфигурацииПо умолчаниюОписание
MaxFriends100Максимум друзей на игрока
MaxGuildMembers50Максимум участников гильдии
ChatHistoryCount50Сообщений чата для хранения в памяти