Operaciones en Vivo
Las Operaciones en Vivo cubren eventos con límite de tiempo, tablas de clasificación, logros, guardado en la nube, copias de seguridad automáticas y soporte para mods. Estos sistemas permiten la entrega continua de contenido y la participación de los jugadores después del lanzamiento.

Eventos, clasificaciones y logros en una sola pantalla. La cuenta atrás del evento activo es hora del servidor, no el reloj del cliente, y eso es lo que impide que un jugador termine un evento antes de tiempo cambiando la fecha de su sistema.
Descripción General de la Arquitectura
| Clase | Archivo | Responsabilidad |
|---|---|---|
EventService | Persistence/PersistenceAndSystems.cs | Gestión de eventos estacionales/con tiempo limitado |
LeaderboardService | Persistence/PersistenceAndSystems.cs | Tablas de clasificación con envío de puntuaciones |
AchievementService | Persistence/PersistenceAndSystems.cs | Sistema de seguimiento y desbloqueo de logros |
CloudSaveService | Persistence/PersistenceAndSystems.cs | Subida/descarga de guardado en la nube con detección de conflictos |
BackupScheduler | AntiCheat/AntiCheatManager.cs | Copias de seguridad periódicas automatizadas en la nube |
ModLoader | Persistence/PersistenceAndSystems.cs | Carga de mods de la comunidad en tiempo de ejecución |
Acceso via NakamaManager.Instance:
var events = NakamaManager.Instance.Events;
var leaderboards = NakamaManager.Instance.Leaderboards;
var achievements = NakamaManager.Instance.Achievements;
var cloudSave = NakamaManager.Instance.CloudSave;
var mods = NakamaManager.Instance.Mods;
Servicio de Eventos
EventService gestiona eventos en vivo con tiempo limitado (días comunitarios, contenido estacional, encuentros especiales).
Obtener Eventos Activos
List<LiveEvent> activeEvents = await events.GetActiveEventsAsync();
foreach (var evt in activeEvents)
{
// evt.Id -- ID único del evento
// evt.Name -- nombre de visualización
// evt.Description -- detalles del evento
// evt.StartTime -- timestamp Unix (ms) de inicio
// evt.EndTime -- timestamp Unix (ms) de fin
// evt.RewardData -- configuración de recompensa codificada en JSON
}
Ciclo de Vida del Evento
events.OnEventStarted += (LiveEvent evt) =>
{
Debug.Log($"Event started: {evt.Name}");
// Mostrar banner del evento, habilitar encuentros especiales, etc.
};
events.OnEventEnded += (string eventId) =>
{
Debug.Log($"Event ended: {eventId}");
// Eliminar contenido del evento, distribuir recompensas de participación
};
Modelo de Datos de LiveEvent
| Campo | Tipo | Descripción |
|---|---|---|
Id | string | Identificador único del evento |
Name | string | Nombre de visualización |
Description | string | Texto de descripción del evento |
StartTime | long | Timestamp de inicio (Unix ms) |
EndTime | long | Timestamp de fin (Unix ms) |
RewardData | string | Configuración de recompensa codificada en JSON |
Los eventos se configuran del lado del servidor a través de la consola de Nakama. El cliente consulta eventos activos y reacciona a las notificaciones de inicio/fin.
Servicio de Tabla de Clasificación
LeaderboardService proporciona tablas de clasificación con envío de puntuaciones y consultas de clasificación.
Enviar Puntuaciones
await leaderboards.SubmitScoreAsync(
boardId: "pvp_season_1",
score: 1850);
await leaderboards.SubmitScoreAsync(
boardId: "dex_completion",
score: 151);
Consultar Clasificaciones
// Obtener mejores jugadores
List<LeaderboardEntry> top100 = await leaderboards.GetTopPlayersAsync(
boardId: "pvp_season_1",
limit: 100);
foreach (var entry in top100)
{
// entry.Rank, entry.DisplayName, entry.Score, entry.UserId
}
// Obtener mi clasificación
LeaderboardEntry myRank = await leaderboards.GetMyRankAsync("pvp_season_1");
Refrescar Datos
await leaderboards.RefreshAsync();
Modelo de Datos de LeaderboardEntry
| Campo | Tipo | Descripción |
|---|---|---|
UserId | string | ID de usuario de Nakama del jugador |
DisplayName | string | Nombre de visualización del jugador |
Score | long | Valor de la puntuación |
Rank | int | Posición en la tabla de clasificación |
Las tablas de clasificación soportan reinicios estacionales -- crea nuevos IDs de tabla por temporada (ej., pvp_season_2) y archiva los antiguos.
Servicio de Logros
AchievementService rastrea los logros de los jugadores con soporte de progreso.
Listar Logros
List<Achievement> all = await achievements.GetAchievementsAsync();
foreach (var ach in all)
{
// ach.Id, ach.Name, ach.Description
// ach.CurrentProgress, ach.MaxProgress
// ach.IsUnlocked, ach.RewardData
}
Desbloquear Logros
// Desbloqueo instantáneo
await achievements.UnlockAsync("first_capture");
// Basado en progreso (ej., "Atrapa 100 monstruos")
await achievements.UpdateProgressAsync("centurion_catcher", progress: 47);
Eventos de Logros
achievements.OnAchievementUnlocked += (Achievement ach) =>
{
Debug.Log($"Achievement unlocked: {ach.Name}!");
// Mostrar popup de desbloqueo, otorgar recompensa desde ach.RewardData
};
Refrescar Datos
await achievements.RefreshAsync();
Modelo de Datos de Achievement
| Campo | Tipo | Descripción |
|---|---|---|
Id | string | ID único del logro |
Name | string | Nombre de visualización |
Description | string | Descripción del logro |
CurrentProgress | int | Valor de progreso actual |
MaxProgress | int | Progreso objetivo para completar |
IsUnlocked | bool | Si el logro está completado |
RewardData | string | Configuración de recompensa codificada en JSON |
Servicio de Guardado en la Nube
CloudSaveService conecta el sistema de guardado JSON existente de MCE con el almacenamiento del servidor Nakama, permitiendo el juego entre dispositivos.
Subir Guardados
string saveJson = GetLocalSaveJson(); // Tu serialización de guardado
bool success = await cloudSave.UploadSaveAsync(saveJson);
// cloudSave.HasCloudSave -- true después de una subida exitosa
// cloudSave.LastSyncTimestamp -- timestamp Unix de la última sincronización
Descargar Guardados
string saveJson = await cloudSave.DownloadSaveAsync();
// Deserializar y aplicar al estado local del juego
Detección de Conflictos
Al guardar, el sistema puede detectar conflictos entre datos locales y en la nube:
SaveConflictInfo conflict = await cloudSave.CheckSaveConflictAsync(localTimestamp);
if (conflict.HasConflict)
{
// conflict.CloudTimestamp vs conflict.LocalTimestamp
// Presentar UI para que el jugador elija qué guardado conservar
}
Sincronización Especializada
// Sincronizar cajas del PC (almacenamiento de monstruos)
await cloudSave.SyncPCStorageAsync(pcStorageJson);
// Sincronizar datos de progreso (medallas, Pokédex, misiones)
await cloudSave.SyncProgressionAsync(progressionJson);
Programador de Copias de Seguridad
BackupScheduler es un MonoBehaviour que crea copias de seguridad automáticas periódicas en la nube.
Configuración
| Campo | Default | Descripción |
|---|---|---|
autoSaveIntervalMinutes | 5 | Minutos entre copias de seguridad automáticas |
maxBackupSlots | 10 | Tamaño máximo del historial de copias (rotación FIFO) |
Qué respalda
Una copia sube lo que devuelve el SaveDataProvider del planificador (un Func<string>). Cuando no estableces uno, el planificador usa LocalSaveDataProvider, que lee el guardado más reciente de SavegameManager que haya en disco. Si no hay proveedor, ni guardado en disco, ni conexión, la copia se omite y se dispara OnBackupFailed: no se sube nada y no se escribe ninguna entrada en el historial.
Copia de Seguridad Manual
backupScheduler.CreateBackupAsync();
Restaurar desde Copia de Seguridad
string saveJson = await backupScheduler.RestoreBackupAsync(backupId);
Eventos de Copia de Seguridad
backupScheduler.OnBackupCreated += (BackupEntry entry) =>
{
// entry.Id, entry.Timestamp, entry.Type, entry.SizeBytes
};
backupScheduler.OnBackupFailed += (string reason) =>
{
Debug.LogWarning($"Backup failed: {reason}");
};
Tipos de Copia de Seguridad
| Tipo | Descripción |
|---|---|
Auto | Copia de seguridad periódica automática |
Manual | Copia de seguridad iniciada por el jugador |
PreBattle | Instantánea antes de entrar en batalla |
PreTrade | Instantánea antes de ejecutar un intercambio |
Soporte de Mods
ModLoader permite cargar paquetes de contenido creados por la comunidad en tiempo de ejecución. Esto está controlado por OnlineConfig.EnableModSupport (default: false).
Cargar Mods
Los mods instalados se cargan automáticamente durante la conexión cuando el soporte de mods está habilitado:
await mods.LoadInstalledModsAsync();
Instalar y Eliminar Mods
bool installed = await mods.InstallModAsync("/path/to/mod.zip");
bool removed = await mods.UninstallModAsync(modId);
Listar Mods Cargados
IReadOnlyList<ModManifest> loadedMods = mods.LoadedMods;
foreach (var mod in loadedMods)
{
// mod.Id, mod.Name, mod.Author, mod.Version
// mod.Description, mod.IsEnabled
// mod.ContentTypes -- ej., ["monsters", "maps", "items", "moves"]
}
Eventos de Mods
mods.OnModLoaded += (ModManifest manifest) =>
{
Debug.Log($"Mod loaded: {manifest.Name} v{manifest.Version}");
};
mods.OnModError += (string modId, string error) =>
{
Debug.LogError($"Mod error ({modId}): {error}");
};
Modelo de Datos de ModManifest
| Campo | Tipo | Descripción |
|---|---|---|
Id | string | Identificador único del mod |
Name | string | Nombre de visualización |
Author | string | Autor del mod |
Version | string | Versión semántica |
Description | string | Descripción del mod |
ContentTypes | string[] | Tipos de contenido: "monsters", "maps", "items", "moves" |
IsEnabled | bool | Si el mod está actualmente activo |
Sistemas Anti-Trampas
La capa anti-trampas funciona junto con las operaciones en vivo para mantener la integridad del juego. Consulta las clases dedicadas anti-trampas en AntiCheat/AntiCheatManager.cs:
- SpeedValidator: Detecta hacks de velocidad comparando la velocidad de movimiento contra un máximo configurable (default: 8 tiles/segundo).
- ActionRateLimiter: Previene el spam de acciones con un intervalo mínimo configurable (default: 0.3 segundos).
- InventoryAuditor: Detecta duplicación de objetos y objetos ilegales comparando el inventario reportado por el cliente contra las instantáneas del servidor.
- AntiCheatManager: Coordina todos los validadores con umbrales de violación -- 10 violaciones activan una expulsión, 25 activan una prohibición.