План реализации World Interaction System

Дата анализа: 18 июля 2026 года
Целевой проект: Last Chaos Unchained
Референс для изучения: D:\Servers\AA\AAEmu-develop_last и D:\Servers\AA\sqlite database info

1. Цель

Создать для Last Chaos единую серверно-авторитетную систему взаимодействий, в которой NPC, трупы монстров, ремесленные устройства, ресурсы, двери, строения и другие объекты предоставляют игроку контекстные действия. Действие отображается отдельной иконкой, исполняется через общий механизм умений/операций и может иметь время применения, стоимость, требования, эффекты и переходы состояния.

Система должна включать:

Это новая система Last Chaos. Из AA используются концепции и связи данных, но не исходный код, протокол или иерархия классов.

2. Результат исследования

2.1. Что полезно в AA

  1. Действие связано с умением. Умение задаёт цель, дальность, время применения, стоимость, откат и эффекты.
  2. WorldInteraction является семантикой действия. Крафт, сбор, строительство, лут и использование объекта проходят через общий pipeline.
  3. Skill plots — граф исполнения, а не земельные участки. Таблицы plots, plot_events, plot_conditions, plot_effects, plot_next_events описывают ветвящиеся действия.
  4. Doodad-граф задаёт фазы объекта. Объект имеет текущую фазу, доступные действия, автоматические функции, таймеры и переходы.
  5. Крафт использует skill ID, требуемый объект, материалы, продукты и категорию взаимодействия.
  6. Труп остаётся объектом мира, а лут хранится в контейнере трупа и открывается отдельным запросом.
  7. Дом является постоянным объектом, имеет стадии строительства, права, декорации и состояние после рестарта.

2.2. Что нельзя переносить из AA без исправления

В изученном AAEmu обнаружены неполные проверки дальности, возможность обхода владения умением, разрыв проверки cooldown, неатомарный крафт, гонки лута, недостаточная авторизация interaction, слабая проверка размещения домов и незавершённая модель строительных территорий. Эти решения не являются образцом безопасности.

2.3. Что уже есть в Last Chaos

Можно переиспользовать:

Отсутствуют:

Текущий custom craft нуждается в замене/адаптере: не проверяет станцию и дальность, не применяет delay и rate, не обеспечивает атомарность материалов и результата.

3. Архитектурные правила

  1. Сервер является единственным источником доступности и результата действия.
  2. Клиент передаёт намерение, но не итоговую цель, награду, стоимость, фазу, права или завершённость.
  3. Discovery и Execute являются разными операциями. Execute повторяет все проверки.
  4. Любое изменяющее состояние действие имеет idempotency key.
  5. Любая изменяемая цель имеет generation и revision.
  6. Длительная операция не хранит необработанные указатели на CPC, CNPC, CItem или CArea.
  7. Операция после ожидания повторно разрешает actor/target по стабильным идентификаторам.
  8. SQLite хранит только статические шаблоны.
  9. PostgreSQL хранит владение, прогресс, jobs, claims, постоянные объекты и аудит.
  10. Все изменения предметов, денег и persistent state выполняются в одной транзакционной границе либо через reserve/commit/outbox.
  11. Неизвестный type/kind приводит к fail-closed, а не к default success.
  12. Новые packet IDs имеют явные числовые значения и версию контракта.
  13. Новые изменения EntitiesMP выполняются в *.es; сгенерированные файлы не являются первичным исходником.

4. Целевая модель

4.1. Interaction target

InteractionTargetRef
  kind
  runtimeId
  generation
  revision
  zoneId
  areaId

Клиент отправляет kind + runtimeId. Остальные поля сервер получает из world state.

4.2. Action descriptor

ActionDescriptor
  actionToken
  actionDefinitionId
  skillId
  iconId
  nameTextId
  descriptionTextId
  castTimeMs
  cooldownRemainingMs
  priority
  flags
  availability
  reasonCode

actionToken — короткоживущая capability, привязанная к сессии, персонажу, цели, action ID, generation/revision и поколению каталога. Изменяющие действия используют одноразовый token.

4.3. Interaction operation

Состояния:

Created -> Validating -> Casting/Waiting -> Committing -> Completed
                                  \-> Cancelled
                                  \-> Failed

Операция содержит request ID, idempotency key, actor ID, target reference, pinned catalog snapshot, deadline и результат.

4.4. Action graph

Граф является собственной реализацией идеи skill plots:

В графах запрещены произвольный SQL, shell-команды, бесконечные циклы и неизвестные типы. Loader проверяет достижимость, ссылки, лимиты, циклы и typed parameters до публикации каталога.

4.5. Interaction catalog

SQLite загружается во временный immutable snapshot. После полной проверки snapshot атомарно публикуется. Ошибка reload не заменяет текущую рабочую версию. Запущенная операция завершает работу на своём snapshot.

4.6. Очки работы

Вводятся два независимых server-authoritative пула:

На клиенте фиксируется однозначное соответствие:

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

SQLite world_labor_profiles задаёт:

world_labor_profile_tiers задаёт premium/tier modifiers, а world_labor_modifier_rules — модификаторы от account buffs, предметов или событий. Enum-таблицы для их kind не создаются: значения фиксируются C++ enums и валидируются loader-ом.

Порядок расчёта фиксирован:

  1. Берётся базовый profile.
  2. Перед изменением tier/buff выполняется regeneration checkpoint со старым modifier snapshot.
  3. Tier replacement заменяет соответствующее базовое значение recovery; без replacement tier value складывается с базовым.
  4. Modifier rules группируются по stacking_group: внутри ненулевой группы применяется запись с наибольшим priority, группа 0 складывается полностью.
  5. Cap additions складываются и effective maximum обязан быть больше нуля.
  6. Recovery после всех additions ограничивается диапазоном 0..effectiveMax.
  7. Cost multipliers применяются в порядке tier -> buff/event -> actability, каждый шаг округляется один раз по утверждённому integer правилу; положительная базовая стоимость после скидок не может стать меньше minimum_cost.

Account и character pool имеют отдельные regen_cursor_at, поэтому snapshot передаёт отдельное время следующего tick для каждой полосы. Смена premium/buff не может ретроактивно применить новый коэффициент к старому offline-интервалу.

world_labor_cost_profiles связывает действие с единственным источником стоимости:

Явный cost_source исключает двойное списание из action, skill effect и завершения craft. Расход проверяется и резервируется до применения item/world/building effects. Commit labor, inventory, craft result или building progress является одной операцией.

Catalog validator обязан обеспечивать single-charge:

При успешном расходе могут начисляться character actability EXP, vocation points и character EXP. Коэффициенты уровней actability хранятся в world_actability_levels; существующий CharacterActability требуется подключить к lifecycle CPC.

PostgreSQL хранит account_labor, character_labor, labor_reservations и immutable labor_ledger. Account balance не копируется в каждый character object. Для одновременного расхода блокировки всегда берутся в порядке account row -> character row. Regeneration использует atomic update/revision и сохраняет остаток времени через regen_cursor_at.

Wallet хранит balance и reserved_balance; доступно к расходу только balance-reserved_balance. Reserve под row locks атомарно увеличивает reserved_balance, commit уменьшает одновременно balance и reserved_balance, release уменьшает только reserved_balance. Все переходы reservation выполняются conditional update по ожидаемому state/revision, поэтому timeout-release и commit не могут завершиться одновременно.

character_labor связан составным FK с канонической парой t_characters(a_index,a_user_index). Каждая ledger-запись расхода ссылается на reservation_id, а unique (reservation_id,pool_kind) допускает не более одной account- и одной character-проводки данного резерва. Regeneration и административные начисления без reservation используют отдельную wallet-scoped operation identity.

5. Протокол

Использовать существующий custom transport, не меняя старые opcode. Новый module selector и все sub-opcode должны иметь явные значения.

Envelope:

protocolVersion : uint16
opcode          : uint16
payloadSize     : uint32
requestId       : uint64
payload

Обязательны negotiation/capability bits. Сервер не отправляет новые пакеты клиенту без подтверждённой версии.

5.1. Базовые сообщения

5.2. Loot

TakeAllLoot не содержит присланный клиентом список наград. Сервер сам обходит доступные entries и возвращает частичный результат.

5.3. Craft

5.4. Housing

Preview не является разрешением на commit. Финальный запрос повторно проверяется.

6. Клиент

6.1. Панель действий

Для interactActions.xml создать controller/window class и зарегистрировать XML в UI lifecycle.

Требования:

Предлагаемые классы:

Client/Sources/Engine/WorldInteraction/WorldInteractionProtocol.*
Client/Sources/Engine/WorldInteraction/WorldInteractionController.*
Client/Sources/Engine/WorldInteraction/ActionListModel.*
Client/Sources/Engine/Interface/UIInteractActions.*

6.2. Loot window

Для lootWindow.xml создать UILootWindow и model:

6.3. EntitiesMP

Новые entity-источники по необходимости:

EntitiesMP/WorldInteractable.es
EntitiesMP/BuildingInstance.es
EntitiesMP/HousingDecoration.es

Труп на первом этапе может использовать существующую dead-модель NPC. Отдельная entity нужна только если текущий lifecycle не позволяет сохранить NPC до corpse expiry.

6.4. PlayerInfo и labor packets

Client\Sources\Engine\Interface\CUIPlayerInfo::UpdateLabor() сейчас пуст и должен обновлять pb_labor1, pb_labor2, txt_labor через SetProgressRange, SetProgressPos, SetText и tooltips.

Клиентская model получает initial snapshot после выбора персонажа и delta/snapshot после каждого commit или regeneration tick. Snapshot содержит current/max для обоих пулов, revisions, server time и next tick. Клиент не прогнозирует и не списывает labor самостоятельно.

7. Серверные модули

Рекомендуемое разделение:

GameServer/App/Features/WorldInteraction/
  WorldInteractionService
  ActionDiscoveryService
  InteractionSessionManager
  InteractionTargetResolver
  InteractionOperation
  InteractionRateLimiter

GameServer/App/Features/ActionGraph/
  ActionGraphCatalog
  ActionGraphValidator
  ActionGraphExecutor
  ActionNodeRegistry

GameServer/App/Features/WorldObjects/
  WorldObject
  WorldObjectManager
  WorldObjectScheduler
  WorldObjectReplication

GameServer/App/Features/CorpseLoot/
  CorpseLootContainer
  CorpseLootService
  LootPolicyResolver

GameServer/App/Features/TimedCrafting/
  TimedCraftService
  CraftCatalog
  CraftJobRepository
  CraftEscrow

GameServer/App/Features/Housing/
  TerritoryManager
  BuildingManager
  BuildingPlacementValidator
  HousingPermissionService
  DecorationService

Новые модули не должны разрастаться в Player.es, doFuncItem.cpp, Craft.cpp или NPC flags.

8. Loot: целевой поток

  1. ProcDead(CNPC*) завершает combat rewards и вызывает loot generator.
  2. Для включённого NPC создаётся CorpseLootContainer с уникальным corpse ID и spawn generation.
  3. Генерация лута выполняется один раз сервером.
  4. В контейнер сохраняются stable entry IDs, rights snapshot, position, revision и expiry.
  5. NPC остаётся мёртвым в мире до expiry либо заменяется corpse runtime object.
  6. Discovery выдаёт «Посмотреть лут» и «Забрать всё» только при наличии прав и допустимой дальности.
  7. Open возвращает snapshot для lootWindow.xml.
  8. Take переводит entry Available -> Reserved -> Awarded; при ошибке inventory возвращает Available.
  9. Take-all выполняет bounded batch и возвращает partial result.
  10. После опустошения corpse удаляется с небольшой задержкой.

Права хранят character IDs, а не указатели. Каждая операция повторно проверяет area/layer/distance/expiry/rights. Одинаковый entry не может быть выдан двум игрокам.

Обычный массовый лут допустимо держать в памяти. PostgreSQL включается для boss/event loot, активного claim и политик persist_across_restart.

9. Крафт: целевой поток

  1. Игрок взаимодействует с NPC/workbench.
  2. Сервер выдаёт action token и service session.
  3. Каталог рецептов приходит от сервера.
  4. StartCraft проверяет recipe, станцию, дальность, права, skill/proficiency, оба labor pool, материалы, деньги и количество повторов.
  5. Материалы, деньги и labor резервируются в durable escrow.
  6. Создаётся timed job и отображается cast/progress.
  7. На completion сервер повторно проверяет job state и атомарно создаёт результат.
  8. При заполненном inventory результат остаётся claimable или доставляется безопасным существующим способом.
  9. Cancel возвращает escrow ровно один раз.
  10. Restart восстанавливает незавершённые jobs.

Существующие crafts, craft_materials, craft_products дополняются связями; альтернативная копия рецептов не создаётся.

10. Строительство и дома

10.1. Статические данные

Модель каждого этапа настраивается строкой world_building_stages.visual_asset_id. Первая стадия задаётся world_building_templates.initial_stage_id, а завершённая стадия имеет is_complete=1. Для одного шаблона допускается ровно одна завершённая стадия.

Клиентская SQLite содержит world_visual_assets и world_building_stage_visuals. visual_asset_id разрешается в resource_kind, .smc/.mdl path, анимации, scale, offsets, shadow и render flags. Сервер никогда не принимает resource path от клиента и оперирует только template/stage/visual IDs. Client/server catalogs проверяются как одна exact identity set перед выпуском.

Release validator требует для каждого дома ровно одну is_complete=1, её достижимость от initial_stage_id, отсутствие next_stage_id у финальной стадии и точное совпадение client/server tuple (building_template_id, stage_id, visual_asset_id, is_complete). Partial unique index в SQLite гарантирует только отсутствие двух финальных стадий; наличие одной гарантирует validator.

Одна локация может иметь несколько территорий и несколько полигонов.

10.2. Placement

Сервер проверяет:

Commit:

lock plot/cells
revalidate
consume blueprint/currency
insert building and claim
write audit/outbox
commit
spawn and publish

10.3. Construction

Дом создаётся в состоянии строительства. Каждая стадия связана с action/skill, visual asset, labor cost и материалами. Вклад материала блокирует account labor, character labor и building row в фиксированном порядке, проверяет текущую стадию, резервирует labor, списывает предмет, записывает contribution и изменяет progress в одной транзакции. Последний вклад переводит строение в следующую фазу только один раз.

10.4. Permissions

Права задаются bit mask, отдельно для:

Поддерживаемые principals: owner, account, character, party, guild, family, public. Каждый handler проверяет право повторно.

10.5. Lifecycle

Необходимо предусмотреть repair, damage policy, tax/lease при необходимости, demolition, возврат декораций, orphan recovery и восстановление после рестарта. Удаление дома не должно оставлять claims, reserved items или decorations без владельца.

11. Базы данных

11.1. SQLite

Подготовлен файл:

WI System\world_interaction_sqlite_schema.sql

Он создаёт статическую схему без enum-таблиц и не дублирует существующие skills, effects, crafts, craft_materials, craft_products, items и t_npc.

SQLite применяется offline к staging-копии compact.db. До применения требуется проверить фактическую DDL production artifact, сделать backup, выполнить quick_check и foreign_key_check. Автоматического SQLite migration runner в текущем сервере нет.

Клиентский visual catalog подготовлен отдельно:

WI System\world_interaction_client_sqlite_schema.sql

Его нельзя применять к server compact.db. Он предназначен только для client Data\compact.db и настраивает модели стадий дома, объектов и декораций.

11.2. PostgreSQL

Подготовлена миграция:

Server\Sources\db\migrations\lc_game\0024_world_interaction_runtime.sql

Она содержит runtime state, jobs, durable loot, housing, idempotency и audit.

До добавления текущего draft последним физическим файлом был 0023. Перед включением нового 0024 в deployment нужно убедиться, что в другой ветке или окружении не существуют миграции с этим номером. Уже применённый файл нельзя редактировать.

12. Этапы реализации

Этап 0. Зафиксировать контракты и содержимое БД

Критерий: схемы и packet contract reviewed; старый клиент продолжает подключаться.

Этап 1. Безопасный фундамент

Критерий: повтор packet не повторяет effect; malformed payload не изменяет state.

Этап 2. NPC context actions

Критерий: удалённый или подменённый NPC не открывает сервис; иконки и hotkeys приходят из server action list.

Этап 3. Action graph и world objects

Критерий: два игрока не получают exclusive reward; restart восстанавливает persistent object.

Этап 4. Timed crafting

Критерий: материалы и результат не теряются и не дублируются при disconnect/restart/full inventory.

Этап 5. Corpse loot

Критерий: corpse остаётся в мире; один entry выдаётся один раз; partial take-all корректен.

Этап 6. Территории и housing

Критерий: строить можно только в разрешённых территориях; concurrent overlap невозможен; состояние восстанавливается после restart.

Этап 7. Production hardening

13. Тестовая матрица

Без отдельного разрешения команды ниже не запускаются. При реализации потребуются:

14. Наблюдаемость

Structured events:

Обязательные поля: request/operation ID, character ID, target kind/ID/template, action/graph ID, catalog generation, zone/area, result code, duration, old/new revision. Discovery не логировать на INFO при каждом наведении.

15. Rollback

16. Основные риски

Риск Мера
Старый и новый клиент используют разные контракты capability negotiation и отсутствие unsolicited packets
Повторная выдача item idempotency, row lock, entry state machine
Target исчез после cast stable IDs и повторный resolve перед commit
Reload изменил активное действие pinned immutable snapshot
Потеря материалов крафта durable escrow и transaction
Пересечение домов server geometry + claim locks/cells
Большие/циклические графы static limits и execution budget
UI содержит жёстко заданный язык localization IDs и client resources
Экономика меняется сразу rollout по NPC/recipe/zone и fallback
Документация расходится с исходниками статус подтверждается только существующими файлами и review

17. Definition of Done

Система считается готовой, когда:

  1. Клиент только отображает server action list.
  2. Все action requests повторно валидируются сервером.
  3. Крафт, loot и строительство идемпотентны.
  4. Инвентарь и persistent state изменяются атомарно.
  5. Трупы поддерживают просмотр и take-all через подготовленное окно.
  6. Дома размещаются только внутри настроенных SQLite территорий.
  7. Stages, permissions и decorations переживают restart.
  8. Static catalog fail-closed валидируется до публикации.
  9. Протокол имеет negotiation, limits и packet guards.
  10. Есть аудит, метрики, recovery и управляемый rollback.