Funciones Sociales
El sistema social proporciona chat en tiempo real, gremios, amigos con seguimiento de presencia e intercambio de monstruos/objetos entre pares. Todos los servicios usan las APIs sociales integradas de Nakama (Groups, Friends, Chat channels) y partidas en tiempo real (para intercambios).
Descripción General de la Arquitectura
| Clase | Archivo | Responsabilidad |
|---|---|---|
ChatService | Social/SocialServices.cs | Chat en tiempo real con 7 tipos de canal |
GuildService | Social/SocialServices.cs | CRUD de gremios, roles, gestión de miembros via Grupos de Nakama |
FriendService | Social/SocialServices.cs | Lista de amigos, seguimiento de presencia, bloqueo |
TradeService | Social/SocialServices.cs | Intercambio P2P con máquina de estados validada por servidor |
Acceso via NakamaManager.Instance:
var chat = NakamaManager.Instance.Chat;
var guilds = NakamaManager.Instance.Guilds;
var friends = NakamaManager.Instance.Friends;
var trade = NakamaManager.Instance.Trade;
Sistema de Chat
ChatService implementa un sistema de chat en tiempo real multicanal usando el sistema de mensajería de canales de Nakama.
Tipos de Canal
El enum ChatChannel define 7 tipos de canal:
| Canal | Descripción |
|---|---|
Global | Chat público de todo el servidor |
Local | Visible para jugadores cercanos (mismo mapa/área) |
Trade | Discusiones de economía e intercambios |
Guild | Mensajes solo del gremio |
Whisper | Mensajes directos privados 1-a-1 |
System | Anuncios y notificaciones del servidor |
Battle | Comunicación relacionada con batallas |
Unirse a Canales
El canal global se une automáticamente durante la conexión via JoinGlobalChannelAsync(). Usa el tipo de canal Room de Nakama con persistencia habilitada:
// Automático al conectar -- pero se puede llamar manualmente
await chat.JoinGlobalChannelAsync();
Enviar Mensajes
// Enviar a un canal público
await chat.SendMessageAsync(ChatChannel.Global, "Hello everyone!");
await chat.SendMessageAsync(ChatChannel.Trade, "WTB: Rare Candy x10");
// Whisper (mensaje directo)
await chat.SendMessageAsync(ChatChannel.Whisper, "Hey!", targetId: recipientUserId);
Los whispers usan el tipo de canal DirectMessage de Nakama. El servicio se une automáticamente a un canal DM con el usuario objetivo antes de enviar.
Recibir Mensajes
chat.OnMessageReceived += (ChatMessage msg) =>
{
// msg.SenderId, msg.SenderName, msg.Content
// msg.Channel (ChatChannel enum), msg.Timestamp
};
// Acceder al historial de mensajes (limitado a ChatHistoryCount)
IReadOnlyList<ChatMessage> history = chat.History;
El historial de mensajes está limitado a OnlineConfig.ChatHistoryCount (default: 50). Los mensajes más antiguos se eliminan cuando se supera el límite.
Eventos de Canal
chat.OnChannelJoined += (ChatChannel channel) =>
{
Debug.Log($"Joined {channel} channel");
};
Limpieza
Todos los canales se abandonan durante la desconexión:
await chat.LeaveAllChannelsAsync();
Sistema de Gremios
GuildService gestiona gremios (equipos/clubes) usando la API de Groups de Nakama.
Crear un Gremio
GuildInfo guild = await guilds.CreateGuildAsync(
name: "Team Rocket",
tag: "TR",
description: "Prepare for trouble!");
// guild.Id, guild.Name, guild.Tag, guild.MemberCount, guild.MaxMembers
Máximo de miembros por gremio: OnlineConfig.MaxGuildMembers (default: 50).
Unirse y Abandonar
bool joined = await guilds.JoinGuildAsync(guildId);
bool left = await guilds.LeaveGuildAsync();
Roles del Gremio
El enum GuildRole define una jerarquía de permisos:
| Rol | Valor | Permisos |
|---|---|---|
Leader | 0 | Control total (expulsar, ascender, disolver) |
Officer | 1 | Expulsar miembros, gestionar ajustes |
Member | 2 | Participación estándar |
Recruit | 3 | Acceso limitado (período de prueba) |
Gestión de Miembros
// Ascender a un miembro
await guilds.PromoteMemberAsync(memberId, GuildRole.Officer);
// Expulsar a un miembro
await guilds.KickMemberAsync(memberId);
Buscar Gremios
List<GuildInfo> results = await guilds.SearchGuildsAsync("Rocket");
foreach (var g in results)
{
// g.Id, g.Name, g.Description
// g.MemberCount, g.MaxMembers
}
Estado del Gremio
bool isInGuild = guilds.IsInGuild;
GuildInfo current = guilds.CurrentGuild;
guilds.OnGuildUpdated += (GuildInfo info) => { };
guilds.OnMemberJoined += (string userId) => { };
guilds.OnMemberLeft += (string userId) => { };
Sistema de Amigos
FriendService usa la API de Friends de Nakama con seguimiento de presencia en tiempo real via eventos de estado.
Cargar Amigos
Los amigos se cargan automáticamente durante la conexión:
await friends.RefreshFriendsListAsync(userId);
Esto llama a Client.ListFriendsAsync (limitado a OnlineConfig.MaxFriends, default: 100) y luego sigue los estados de todos los amigos via Socket.FollowUsersAsync() para actualizaciones de presencia en tiempo real.
Solicitudes de Amistad
// Enviar una solicitud de amistad
bool sent = await friends.SendFriendRequestAsync(targetUserId);
// Aceptar una solicitud de amistad
bool accepted = await friends.AcceptFriendRequestAsync(fromUserId);
// Eliminar un amigo
bool removed = await friends.RemoveFriendAsync(friendId);
Seguimiento de Presencia
Las actualizaciones de presencia en tiempo real llegan via ReceivedStatusPresence en el socket:
friends.OnFriendOnline += (FriendInfo info) =>
{
Debug.Log($"{info.DisplayName} is online!");
};
friends.OnFriendOffline += (string userId) =>
{
Debug.Log($"Friend {userId} went offline");
};
friends.OnFriendRequestReceived += (FriendInfo info) =>
{
// Mostrar notificación de solicitud de amistad
};
Datos de Amigos
IReadOnlyList<FriendInfo> list = friends.FriendsList;
foreach (var f in list)
{
// f.UserId, f.DisplayName, f.IsOnline
// f.CurrentMap, f.Level
}
Bloqueo
bool blocked = await friends.BlockUserAsync(userId);
Usa BlockFriendsAsync de Nakama que previene toda interacción.
Intercambio entre Jugadores
TradeService implementa un sistema de intercambio P2P completo usando partidas en tiempo real de Nakama con un protocolo de máquina de estados.
Códigos de Operación del Protocolo de Intercambio
| OpCode | Constante | Significado |
|---|---|---|
100 | OP_TRADE_REQUEST | Solicitud de intercambio inicial |
101 | OP_TRADE_OFFER | El jugador ofrece un monstruo u objeto |
102 | OP_TRADE_CONFIRM | El jugador confirma el intercambio |
103 | OP_TRADE_CANCEL | El jugador cancela el intercambio |
104 | OP_TRADE_COMPLETE | El servidor confirma intercambio completado |
Estados del Intercambio
El enum TradeState sigue el ciclo de vida del intercambio:
Pending -> Offering -> Confirming -> Completed (o Cancelled en cualquier punto)
Iniciar un Intercambio
string tradeId = await trade.RequestTradeAsync(targetUserId);
Esto crea una partida Nakama para la sesión de intercambio y notifica al objetivo via el RPC trade_request.
Ofrecer Objetos
// Ofrecer un monstruo
await trade.OfferMonsterAsync(tradeId, monsterId);
// Ofrecer un objeto
await trade.OfferItemAsync(tradeId, itemId: "rare_candy", quantity: 5);
Las ofertas se envían como mensajes de estado de partida con OP_TRADE_OFFER.
Confirmar y Completar
// Ambos jugadores deben confirmar
await trade.ConfirmTradeAsync(tradeId);
Cuando ambos jugadores confirman, el servidor ejecuta el intercambio atómicamente y envía OP_TRADE_COMPLETE.
Cancelar
await trade.CancelTradeAsync(tradeId);
Envía OP_TRADE_CANCEL, abandona la partida y limpia el intercambio activo.
Eventos de Intercambio
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.");
};
Propiedades del Estado de Intercambio
bool isTrading = trade.IsTrading;
string activeTradeId = trade.ActiveTradeId;
string activeMatchId = trade.ActiveMatchId;
El estado de la partida de intercambio también se enruta a través del manejador ReceivedMatchState de NakamaManager, que reenvía a Trade.HandleMatchState().
Notificaciones Sociales
NakamaManager muestra todas las notificaciones de Nakama a través de un evento unificado:
NakamaManager.Instance.OnNotification += (string subject, string content) =>
{
Debug.Log($"Notification: {subject} - {content}");
};
Adicionalmente, existen eventos dedicados para acciones sociales comunes:
NakamaManager.Instance.OnBattleInvite += (string fromUserId) => { };
NakamaManager.Instance.OnTradeRequest += (string fromUserId) => { };
NakamaManager.Instance.OnFriendRequest += (string fromUserId) => { };
Referencia de Configuración
| Campo de Config | Default | Descripción |
|---|---|---|
MaxFriends | 100 | Máximo de amigos por jugador |
MaxGuildMembers | 50 | Máximo de miembros del gremio |
ChatHistoryCount | 50 | Mensajes de chat a retener en memoria |