Saltar al contenido principal

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​

ClaseArchivoResponsabilidad
ChatServiceSocial/SocialServices.csChat en tiempo real con 7 tipos de canal
GuildServiceSocial/SocialServices.csCRUD de gremios, roles, gestión de miembros via Grupos de Nakama
FriendServiceSocial/SocialServices.csLista de amigos, seguimiento de presencia, bloqueo
TradeServiceSocial/SocialServices.csIntercambio 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:

CanalDescripción
GlobalChat público de todo el servidor
LocalVisible para jugadores cercanos (mismo mapa/área)
TradeDiscusiones de economía e intercambios
GuildMensajes solo del gremio
WhisperMensajes directos privados 1-a-1
SystemAnuncios y notificaciones del servidor
BattleComunicació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:

RolValorPermisos
Leader0Control total (expulsar, ascender, disolver)
Officer1Expulsar miembros, gestionar ajustes
Member2Participación estándar
Recruit3Acceso 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​

OpCodeConstanteSignificado
100OP_TRADE_REQUESTSolicitud de intercambio inicial
101OP_TRADE_OFFEREl jugador ofrece un monstruo u objeto
102OP_TRADE_CONFIRMEl jugador confirma el intercambio
103OP_TRADE_CANCELEl jugador cancela el intercambio
104OP_TRADE_COMPLETEEl 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 ConfigDefaultDescripción
MaxFriends100Máximo de amigos por jugador
MaxGuildMembers50Máximo de miembros del gremio
ChatHistoryCount50Mensajes de chat a retener en memoria