Дата анализа: 18 июля 2026 года
Целевой проект: Last Chaos Unchained
Референс для изучения: D:\Servers\AA\AAEmu-develop_last и D:\Servers\AA\sqlite database info
Создать для Last Chaos единую серверно-авторитетную систему взаимодействий, в которой NPC, трупы монстров, ремесленные устройства, ресурсы, двери, строения и другие объекты предоставляют игроку контекстные действия. Действие отображается отдельной иконкой, исполняется через общий механизм умений/операций и может иметь время применения, стоимость, требования, эффекты и переходы состояния.
Система должна включать:
Client\Sources\xml\interactActions.xml;Это новая система Last Chaos. Из AA используются концепции и связи данных, но не исходный код, протокол или иерархия классов.
plots, plot_events, plot_conditions, plot_effects, plot_next_events описывают ветвящиеся действия.В изученном AAEmu обнаружены неполные проверки дальности, возможность обхода владения умением, разрыв проверки cooldown, неатомарный крафт, гонки лута, недостаточная авторизация interaction, слабая проверка размещения домов и незавершённая модель строительных территорий. Эти решения не являются образцом безопасности.
Можно переиспользовать:
Server\Sources\GameServer\SkillCastService.* — scheduled casting и отмена;CArea, CCell, CZone, NPC и поиск объектов;CNPC::AddClientInteraction / RemoveInteraction как часть interaction lease;DatabaseManager;GamePacketGuard для безопасного чтения пакетов;AsyncLogDispatcher и структурированные события;Отсутствуют:
interactActions.xml и lootWindow.xml;Текущий custom craft нуждается в замене/адаптере: не проверяет станцию и дальность, не применяет delay и rate, не обеспечивает атомарность материалов и результата.
CPC, CNPC, CItem или CArea.*.es; сгенерированные файлы не являются первичным исходником.InteractionTargetRef
kind
runtimeId
generation
revision
zoneId
areaId
Клиент отправляет kind + runtimeId. Остальные поля сервер получает из world state.
ActionDescriptor
actionToken
actionDefinitionId
skillId
iconId
nameTextId
descriptionTextId
castTimeMs
cooldownRemainingMs
priority
flags
availability
reasonCode
actionToken — короткоживущая capability, привязанная к сессии, персонажу, цели, action ID, generation/revision и поколению каталога. Изменяющие действия используют одноразовый token.
Состояния:
Created -> Validating -> Casting/Waiting -> Committing -> Completed
\-> Cancelled
\-> Failed
Операция содержит request ID, idempotency key, actor ID, target reference, pinned catalog snapshot, deadline и результат.
Граф является собственной реализацией идеи skill plots:
ValidateActor;ValidateTarget;ValidateDistance;ValidateRequirementSet;BeginCast;Wait;ConsumeItems;ConsumeMoney;GrantItems;ApplySkillEffect;OpenNpcService;AdvanceObjectState;GenerateLoot;OpenLoot;StartCraft;AdvanceBuildingStage;EmitClientEvent;WriteAudit;Complete / Fail.В графах запрещены произвольный SQL, shell-команды, бесконечные циклы и неизвестные типы. Loader проверяет достижимость, ссылки, лимиты, циклы и typed parameters до публикации каталога.
SQLite загружается во временный immutable snapshot. После полной проверки snapshot атомарно публикуется. Ошибка reload не заменяет текущую рабочую версию. Запущенная операция завершает работу на своём snapshot.
Вводятся два независимых server-authoritative пула:
На клиенте фиксируется однозначное соответствие:
pb_labor1 — account labor;pb_labor2 — character labor;txt_labor — Очки работы: total/maxTotal (account/maxAccount + character/maxCharacter).Обе полосы получают собственный 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-ом.
Порядок расчёта фиксирован:
stacking_group: внутри ненулевой группы применяется запись с наибольшим priority, группа 0 складывается полностью.0..effectiveMax.minimum_cost.Account и character pool имеют отдельные regen_cursor_at, поэтому snapshot передаёт отдельное время следующего tick для каждой полосы. Смена premium/buff не может ретроактивно применить новый коэффициент к старому offline-интервалу.
world_labor_cost_profiles связывает действие с единственным источником стоимости:
crafts.labor_point;Явный cost_source исключает двойное списание из action, skill effect и завершения craft. Расход проверяется и резервируется до применения item/world/building effects. Commit labor, inventory, craft result или building progress является одной операцией.
Catalog validator обязан обеспечивать single-charge:
fixed требует fixed_cost>0 и не допускает skill/craft charging effect;skill требует ровно один world_interaction_actions.skill_id;craft требует ровно одну строку world_interaction_action_crafts;none запрещает любые labor charging nodes/effects;world_interaction_skill_actions используется только для discovery/binding и не является вторым источником стоимости;При успешном расходе могут начисляться 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.
Использовать существующий custom transport, не меняя старые opcode. Новый module selector и все sub-opcode должны иметь явные значения.
Envelope:
protocolVersion : uint16
opcode : uint16
payloadSize : uint32
requestId : uint64
payload
Обязательны negotiation/capability bits. Сервер не отправляет новые пакеты клиенту без подтверждённой версии.
ClientCapabilities / ServerCapabilities;DiscoverActions / ActionList;ExecuteAction / ActionState;CancelAction;OpenService.OpenLoot;LootContents;TakeLootEntry;TakeAllLoot;LootMutationResult;TakeAllLoot не содержит присланный клиентом список наград. Сервер сам обходит доступные entries и возвращает частичный результат.
CraftCatalogRequest / CraftCatalogPage;StartCraft;CraftJobState;CancelCraft;ClaimCraftResult при необходимости.TerritorySnapshot;PlacementPreviewRequest/Result;PlaceBuilding;BuildingState;ContributeBuildingMaterial;HousingPermissionUpdate;DecorationPlace/Move/Remove.Preview не является разрешением на commit. Финальный запрос повторно проверяется.
Для interactActions.xml создать controller/window class и зарегистрировать XML в UI lifecycle.
Требования:
str_idx/локализацию, не hardcoded строки.Предлагаемые классы:
Client/Sources/Engine/WorldInteraction/WorldInteractionProtocol.*
Client/Sources/Engine/WorldInteraction/WorldInteractionController.*
Client/Sources/Engine/WorldInteraction/ActionListModel.*
Client/Sources/Engine/Interface/UIInteractActions.*
Для lootWindow.xml создать UILootWindow и model:
Новые entity-источники по необходимости:
EntitiesMP/WorldInteractable.es
EntitiesMP/BuildingInstance.es
EntitiesMP/HousingDecoration.es
Труп на первом этапе может использовать существующую dead-модель NPC. Отдельная entity нужна только если текущий lifecycle не позволяет сохранить NPC до corpse expiry.
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 самостоятельно.
Рекомендуемое разделение:
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.
ProcDead(CNPC*) завершает combat rewards и вызывает loot generator.CorpseLootContainer с уникальным corpse ID и spawn generation.lootWindow.xml.Available -> Reserved -> Awarded; при ошибке inventory возвращает Available.Права хранят character IDs, а не указатели. Каждая операция повторно проверяет area/layer/distance/expiry/rights. Одинаковый entry не может быть выдан двум игрокам.
Обычный массовый лут допустимо держать в памяти. PostgreSQL включается для boss/event loot, активного claim и политик persist_across_restart.
StartCraft проверяет recipe, станцию, дальность, права, skill/proficiency, оба labor pool, материалы, деньги и количество повторов.Существующие crafts, craft_materials, craft_products дополняются связями; альтернативная копия рецептов не создаётся.
Модель каждого этапа настраивается строкой 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.
Одна локация может иметь несколько территорий и несколько полигонов.
Сервер проверяет:
Commit:
lock plot/cells
revalidate
consume blueprint/currency
insert building and claim
write audit/outbox
commit
spawn and publish
Дом создаётся в состоянии строительства. Каждая стадия связана с action/skill, visual asset, labor cost и материалами. Вклад материала блокирует account labor, character labor и building row в фиксированном порядке, проверяет текущую стадию, резервирует labor, списывает предмет, записывает contribution и изменяет progress в одной транзакции. Последний вклад переводит строение в следующую фазу только один раз.
Права задаются bit mask, отдельно для:
Поддерживаемые principals: owner, account, character, party, guild, family, public. Каждый handler проверяет право повторно.
Необходимо предусмотреть repair, damage policy, tax/lease при необходимости, demolition, возврат декораций, orphan recovery и восстановление после рестарта. Удаление дома не должно оставлять claims, reserved items или decorations без владельца.
Подготовлен файл:
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 и настраивает модели стадий дома, объектов и декораций.
Подготовлена миграция:
Server\Sources\db\migrations\lc_game\0024_world_interaction_runtime.sql
Она содержит runtime state, jobs, durable loot, housing, idempotency и audit.
До добавления текущего draft последним физическим файлом был 0023. Перед включением нового 0024 в deployment нужно убедиться, что в другой ветке или окружении не существуют миграции с этим номером. Уже применённый файл нельзя редактировать.
compact.db;kind полей;Критерий: схемы и packet contract reviewed; старый клиент продолжает подключаться.
CUIPlayerInfo::UpdateLabor();CharacterActability к CPC.Критерий: повтор packet не повторяет effect; malformed payload не изменяет state.
interactActions.xml;Критерий: удалённый или подменённый NPC не открывает сервис; иконки и hotkeys приходят из server action list.
Критерий: два игрока не получают exclusive reward; restart восстанавливает persistent object.
Критерий: материалы и результат не теряются и не дублируются при disconnect/restart/full inventory.
lootWindow.xml;Критерий: corpse остаётся в мире; один entry выдаётся один раз; partial take-all корректен.
Критерий: строить можно только в разрешённых территориях; concurrent overlap невозможен; состояние восстанавливается после restart.
Без отдельного разрешения команды ниже не запускаются. При реализации потребуются:
Structured events:
interaction_execute_requested/rejected/completed;action_graph_failed;craft_job_started/completed/cancelled/recovered;loot_entry_taken;building_placed/stage_changed/demolished;housing_permission_changed;interaction_catalog_reloaded.Обязательные поля: request/operation ID, character ID, target kind/ID/template, action/graph ID, catalog generation, zone/area, result code, duration, old/new revision. Discovery не логировать на INFO при каждом наведении.
| Риск | Мера |
|---|---|
| Старый и новый клиент используют разные контракты | 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 |
Система считается готовой, когда: