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

Подарки монстров (Grant ID)

Когда NPC вручает игроку монстра — стартового в лаборатории, подарок на маршруте, воскрешённое ископаемое, — одиночная игра просто добавляет его в команду. Онлайн-игра так сделать не может: клиент, который может подарить себе любого монстра, может подарить себе и легендарного. В MCE Online подарок создаёт сервер, а клиент лишь просит о нём.

Доступно в уровнях Pro и Source.

Как подарок работает онлайн​

Узел, стоящий за каждым подарком, — GiveMonsters (CommandGraph, категория Monsters). Стартовый монстр и «подарок NPC» — это один и тот же узел, поэтому механизм для обоих один.

  1. Игрок разговаривает с NPC, и граф доходит до узла GiveMonsters.
  2. Онлайн узел не добавляет монстра. Он просит об этом сервер через OnlineCommandBridge.RequestGiveMonsterAsync(grantId, ...), указывая место по его GrantId.
  3. Сервер ищет место в собственных данных (monster_grant_data) и создаёт вид, уровень и количество, которые записаны у него. Любой вид, присланный клиентом в запросе, игнорируется и записывается в лог.
  4. Сервер запоминает, что этот персонаж уже получил подарок в этом месте (monster_grant_claims, ключ — персонаж и идентификатор подарка), поэтому подарок можно получить один раз на персонажа: NPC, с которым можно заговорить снова, — не фабрика монстров.
  5. Клиент получает созданного монстра и показывает его теми же диалогами, что и офлайн-путь.

Если сервер подарок не выдаёт, узел ничего не выдаёт локально — это снова открыло бы лазейку самоподарка — и показывает диалог «grant failed». Офлайн ничего не меняется.

GrantId​

Каждому месту подарка нужен собственный GrantId: непрозрачная строка, хранящаяся в узле GiveMonsters (в Inspector она скрыта; вручную её не вводят). Сервер отклоняет пустой идентификатор с указанием причины (grantId is required), поэтому место без идентификатора не выдаёт онлайн-подарок — оно закрывается при сбое.

Идентификаторы:

  • Для места, а не для типа узла. Два узла, скопированные друг с друга, делят авторский GUID узла, но выдают разное; проставляемый идентификатор — это GUID узла плюс короткий хеш того, где узел находится (GUID ассета и путь свойства), поэтому каждое место уникально.
  • Стабильные. Хеш использует GUID ассета, а не его путь, поэтому перенос или переименование файла его не меняет.
  • Непрозрачные. В идентификаторе нет #, / и расширения файла, поэтому узел всегда может «произнести» его во время выполнения.
  • Никогда не перезаписываются. Проставление заполняет только пустые идентификаторы. Уже существующий идентификатор, а значит и серверный сид для него, никогда не меняется.

Проставление и экспорт (сторона редактора)​

MonsterGrantSeedExporter (в сборке MCE.Online.Editor) выполняет две задачи:

  1. Сканирует префабы и сцены проекта в поисках узлов GiveMonsters. Сцены сначала фильтруются по тексту (сцена открывается, только если её YAML содержит метку класса узла), поэтому сканирование не открывает каждую сцену.
  2. При включённом проставлении он заполняет недостающие GrantId и записывает сид: SeedData/monster-grant-seed.json (что выдаёт каждое место) и SeedData/monster-grant-seed.report.txt (замеченные и пропущенные места с указанием причины).

Точки входа экспортёра:

ВходНазначение
MonsterGrantSeedExporter.Export(folder)Проставить недостающие идентификаторы, затем записать сид и отчёт
MonsterGrantSeedExporter.Scan(stampMissingIds, persist)Сканирование; по желанию проставление, по желанию сохранение на диск. Один Scan() ничего не читает и не пишет
BatchExporters.ExportMonsterGrantsПакетный режим: -executeMethod OpenMon.MCE.Online.Editor.BatchExporters.ExportMonsterGrants

Отправка сида на сервер выполняется от сервера к серверу, как и остальные сиды: RPC mce_monster_grant_seed отклоняет любой вызов с пользовательской сессией и требует HTTP-ключ времени выполнения (о том, почему этот ключ никогда не попадает внутрь Unity, см. Засев видов).

Подарок с выбором

GiveMonsters выдаёт один фиксированный подарок (или случайный). Узел, предлагающий выбор, — GiveMonstersBasedOnChosenItem, как у воскрешателя ископаемых, — экспортируется с choice: true: сервер выводит предмет, который стоит выбранный вариант, из индекса, выбранного игроком, поэтому клиент не может воскресить дорогое ископаемое, заявив, что потратил дешёвое.

Импортированные игры​

Игра, импортированная из картриджа или проекта Essentials, полна узлов подарков: три стартовых монстра в лаборатории, покемон от NPC, ископаемое. Проставление за вас выполняют импортёры.

Когда импортёры NPC (HeartGold / SoulSilver, FireRed) заканчивают работу, GrantsEnLoImportado.Estampar() запускает сканирование экспортёра с включёнными проставлением и сохранением — если онлайн-слой входит в проект; без него проставлять нечего. Затем он проверяет собственную работу независимым подсчётом: читает все сцены в Assets/ImportedGames и считает пустые значения GrantId, в том числе те, что сцена хранит как переопределение экземпляра префаба (стартовый монстр лаборатории — именно такой случай). Если хоть один остался, он выбрасывает исключение, поэтому импорт никогда не завершается «успешно», оставляя подарки, которые сервер отклонил бы:

[Importador] GrantId de los regalos: estampados=3 yaEstaban=0 quedanSinId=0

(estampados: проставлено сейчас; yaEstaban: идентификатор уже был; quedanSinId: всё ещё пусты, должно быть 0.)

После этого экспортируйте сид подарков (см. выше) и отправьте его на сервер. Как и другие RPC засева, mce_monster_grant_seed отклоняет любой вызов с пользовательской сессией: записать его может только вызов от сервера к серверу с HTTP-ключом времени выполнения.

Справочник​

ТипГдеРоль
GiveMonstersOpenMon.MCE.Actors.CommandGraph.MonstersУзел; несёт GrantId и обращается к серверу, когда игра онлайн
OnlineCommandBridge.RequestGiveMonsterAsyncMCE.Online.RuntimeЗапрос (grantId, необязательный индекс выбора)
MonsterGrantSeedExporterOpenMon.MCE.Online.EditorСканирует, проставляет и экспортирует сид
GrantsEnLoImportadoOpenMon.MCE.EssentialsImporter.EditorПроставляет и проверяет идентификаторы после импорта
mce_monster_grant_seedRPC NakamaОт сервера к серверу: сохраняет места