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

Операционная деятельность

Операционная деятельность охватывает временные события, таблицы лидеров, достижения, облачные сохранения, автоматическое резервное копирование и поддержку модов. Эти системы обеспечивают непрерывную доставку контента и вовлечение игроков после запуска.

центр LiveOps

События, таблицы лидеров и достижения на одном экране. Обратный отсчёт активного события идёт по времени сервера, а не по часам клиента, — именно это не даёт игроку закончить событие досрочно, изменив системную дату.

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

КлассФайлОтветственность
EventServicePersistence/PersistenceAndSystems.csУправление сезонными/временными событиями
LeaderboardServicePersistence/PersistenceAndSystems.csРанжированные таблицы лидеров с отправкой очков
AchievementServicePersistence/PersistenceAndSystems.csОтслеживание достижений и система разблокировки
CloudSaveServicePersistence/PersistenceAndSystems.csЗагрузка/скачивание облачных сохранений с обнаружением конфликтов
BackupSchedulerAntiCheat/AntiCheatManager.csАвтоматическое периодическое облачное резервное копирование
ModLoaderPersistence/PersistenceAndSystems.csЗагрузка модов сообщества во время выполнения

Доступ через 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;

Сервис событий​

EventService управляет временными живыми событиями (дни сообщества, сезонный контент, особые встречи).

Получение активных событий​

List<LiveEvent> activeEvents = await events.GetActiveEventsAsync();

foreach (var evt in activeEvents)
{
// evt.Id -- уникальный ID события
// evt.Name -- отображаемое имя
// evt.Description -- детали события
// evt.StartTime -- временная метка Unix (мс) начала
// evt.EndTime -- временная метка Unix (мс) окончания
// evt.RewardData -- JSON-кодированная конфигурация награды
}

Жизненный цикл события​

events.OnEventStarted += (LiveEvent evt) =>
{
Debug.Log($"Event started: {evt.Name}");
// Показать баннер события, включить особые встречи и т.д.
};

events.OnEventEnded += (string eventId) =>
{
Debug.Log($"Event ended: {eventId}");
// Удалить контент события, раздать награды за участие
};

Модель данных LiveEvent​

ПолеТипОписание
IdstringУникальный идентификатор события
NamestringОтображаемое имя
DescriptionstringТекст описания события
StartTimelongВременная метка начала (Unix мс)
EndTimelongВременная метка окончания (Unix мс)
RewardDatastringJSON-кодированная конфигурация награды

События настраиваются на стороне сервера через консоль Nakama. Клиент опрашивает активные события и реагирует на уведомления о начале/окончании.

Сервис таблиц лидеров​

LeaderboardService предоставляет ранжированные таблицы лидеров с отправкой очков и запросами рейтинга.

Отправка очков​

await leaderboards.SubmitScoreAsync(
boardId: "pvp_season_1",
score: 1850);

await leaderboards.SubmitScoreAsync(
boardId: "dex_completion",
score: 151);

Запрос рейтингов​

// Получить лучших игроков
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
}

// Получить мой рейтинг
LeaderboardEntry myRank = await leaderboards.GetMyRankAsync("pvp_season_1");

Обновление данных​

await leaderboards.RefreshAsync();

Модель данных LeaderboardEntry​

ПолеТипОписание
UserIdstringID пользователя Nakama игрока
DisplayNamestringОтображаемое имя игрока
ScorelongЗначение очков
RankintПозиция в таблице лидеров

Таблицы лидеров поддерживают сезонные сбросы — создавайте новые ID таблиц для каждого сезона (например, pvp_season_2) и архивируйте старые.

Сервис достижений​

AchievementService отслеживает достижения игроков с поддержкой прогресса.

Список достижений​

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
}

Разблокировка достижений​

// Мгновенная разблокировка
await achievements.UnlockAsync("first_capture");

// На основе прогресса (например, "Поймать 100 монстров")
await achievements.UpdateProgressAsync("centurion_catcher", progress: 47);

События достижений​

achievements.OnAchievementUnlocked += (Achievement ach) =>
{
Debug.Log($"Achievement unlocked: {ach.Name}!");
// Показать всплывающее окно, выдать награду из ach.RewardData
};

Обновление данных​

await achievements.RefreshAsync();

Модель данных Achievement​

ПолеТипОписание
IdstringУникальный ID достижения
NamestringОтображаемое имя
DescriptionstringОписание достижения
CurrentProgressintТекущее значение прогресса
MaxProgressintЦелевой прогресс для завершения
IsUnlockedboolЗавершено ли достижение
RewardDatastringJSON-кодированная конфигурация награды

Сервис облачных сохранений​

CloudSaveService соединяет существующую JSON-систему сохранений MCE с хранилищем сервера Nakama, обеспечивая кроссплатформенную игру.

Загрузка сохранений​

string saveJson = GetLocalSaveJson(); // Ваша сериализация сохранения
bool success = await cloudSave.UploadSaveAsync(saveJson);

// cloudSave.HasCloudSave -- true после успешной загрузки
// cloudSave.LastSyncTimestamp -- временная метка Unix последней синхронизации

Скачивание сохранений​

string saveJson = await cloudSave.DownloadSaveAsync();
// Десериализовать и применить к локальному состоянию игры

Обнаружение конфликтов​

При сохранении система может обнаруживать конфликты между локальными и облачными данными:

SaveConflictInfo conflict = await cloudSave.CheckSaveConflictAsync(localTimestamp);

if (conflict.HasConflict)
{
// conflict.CloudTimestamp vs conflict.LocalTimestamp
// Показать UI, чтобы игрок выбрал, какое сохранение оставить
}

Специализированная синхронизация​

// Синхронизация ячеек ПК (хранилище монстров)
await cloudSave.SyncPCStorageAsync(pcStorageJson);

// Синхронизация данных прогресса (значки, Покедекс, квесты)
await cloudSave.SyncProgressionAsync(progressionJson);

Планировщик резервного копирования​

BackupScheduler — это MonoBehaviour, который создаёт периодические автоматические облачные резервные копии.

Конфигурация​

ПолеПо умолчаниюОписание
autoSaveIntervalMinutes5Минут между автоматическими резервными копиями
maxBackupSlots10Максимальный размер истории резервирования (ротация FIFO)

Что резервируется​

Резервная копия выгружает то, что возвращает SaveDataProvider планировщика (Func<string>). Если вы его не задали, планировщик использует LocalSaveDataProvider, который читает самое свежее сохранение SavegameManager на диске. Если нет провайдера, нет сохранения на диске или нет соединения, резервное копирование пропускается и срабатывает OnBackupFailed: ничего не выгружается, и запись в историю не создаётся.

Ручное резервирование​

backupScheduler.CreateBackupAsync();

Восстановление из резервной копии​

string saveJson = await backupScheduler.RestoreBackupAsync(backupId);

События резервирования​

backupScheduler.OnBackupCreated += (BackupEntry entry) =>
{
// entry.Id, entry.Timestamp, entry.Type, entry.SizeBytes
};

backupScheduler.OnBackupFailed += (string reason) =>
{
Debug.LogWarning($"Backup failed: {reason}");
};

Типы резервных копий​

ТипОписание
AutoПериодическая автоматическая резервная копия
ManualРезервная копия, инициированная игроком
PreBattleСнимок перед входом в бой
PreTradeСнимок перед выполнением обмена

Поддержка модов​

ModLoader позволяет загружать созданные сообществом пакеты контента во время выполнения. Это контролируется OnlineConfig.EnableModSupport (по умолчанию: false).

Загрузка модов​

Установленные моды загружаются автоматически во время подключения, когда поддержка модов включена:

await mods.LoadInstalledModsAsync();

Установка и удаление модов​

bool installed = await mods.InstallModAsync("/path/to/mod.zip");
bool removed = await mods.UninstallModAsync(modId);

Список загруженных модов​

IReadOnlyList<ModManifest> loadedMods = mods.LoadedMods;

foreach (var mod in loadedMods)
{
// mod.Id, mod.Name, mod.Author, mod.Version
// mod.Description, mod.IsEnabled
// mod.ContentTypes -- например, ["monsters", "maps", "items", "moves"]
}

События модов​

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}");
};

Модель данных ModManifest​

ПолеТипОписание
IdstringУникальный идентификатор мода
NamestringОтображаемое имя
AuthorstringАвтор мода
VersionstringСемантическая версия
DescriptionstringОписание мода
ContentTypesstring[]Типы контента: "monsters", "maps", "items", "moves"
IsEnabledboolАктивен ли мод в данный момент

Античит-системы​

Античит-слой работает вместе с операционной деятельностью для поддержания целостности игры. См. специальные античит-классы в AntiCheat/AntiCheatManager.cs:

  • SpeedValidator: Обнаруживает читы на скорость, сравнивая скорость передвижения с настраиваемым максимумом (по умолчанию: 8 тайлов/секунду).
  • ActionRateLimiter: Предотвращает спам действий с настраиваемым минимальным интервалом (по умолчанию: 0,3 секунды).
  • InventoryAuditor: Обнаруживает дублирование предметов и нелегальные предметы, сравнивая инвентарь, сообщённый клиентом, со снимками сервера.
  • AntiCheatManager: Координирует все валидаторы с порогами нарушений — 10 нарушений вызывают кик, 25 — бан.