diff --git a/.planning/phases/03-data-completion/03-EVENT-CONTRACT.md b/.planning/phases/03-data-completion/03-EVENT-CONTRACT.md index 192b0fb..e7232ce 100644 --- a/.planning/phases/03-data-completion/03-EVENT-CONTRACT.md +++ b/.planning/phases/03-data-completion/03-EVENT-CONTRACT.md @@ -1,37 +1,35 @@ # Phase 3 Event Built-in Contract -本文件记录 `confirm-record` checkpoint 的确认结果。范围只包含本阶段批准的八个 -event built-ins;不加入完整 legacy 事件目录,不保留未决字段,也不把渲染行为放入 -数据端函数。 +本文件记录八个 event built-ins 的稳定契约。范围只包含批准的八个 built-ins;不加入 +完整 legacy 事件目录,不保留未决字段,也不把渲染行为放入数据端函数。 -## User structural supersession - -本节晚于初始 checkpoint,优先于下方旧参数记录: - -- `data-state/src/event/index.ts` 与 `data-state/src/index.ts` 只包含 `export` 语句,不导入、不执行装配代码;稳定注册项由 `data-state/src/event/registrations.ts` 负责装配,barrel 不承载参数解析、环境形状检查或其他业务逻辑。 -- built-in 调用不在热路径重复执行运行时参数类型判断;具体函数直接消费已确定的参数契约。 -- `eventInsertEvents` 保留现有事件 ID 序列语义;`eventInsertEvent` 改为接收 `Statement[]` 并直接通过现有解释器执行,不读取 event store,也不接受事件 ID。 -- 事件函数仍可按既有事件执行器契约等待事件链;本节只修正 `eventInsertEvent` 的输入和 `index.ts` 的职责,不把 replay command 的同步约束扩展到普通事件执行器。 +> **实现同步(quick 260913-qtq):** 内建函数改为类式 `BuiltInFunction` 实现,函数名 +> 使用短名(`setBlock` 等),注册收口为单一 `createEventRegistrations()`,共享工具 +> 迁至 `event/utils.ts`。本节取代下方任何与旧类名 / 旧注册函数 / `EventBuiltinName` +> 相关的历史描述。 ## Shared contract -- 每个函数都使用 `(param, env)` 调用形式。 -- `env` 的类型固定为 `IBlockEventEnv`。目标解析顺序为:明确的 - `env.layer`,`env.map` 的 `eventLayer`,最后是通过 `env.heroFloor` 从 - `env.state.maps` 获取的地图及其 `eventLayer`。 -- 目标、图块、勇士、事件存储或事件 id 缺失时,函数安全跳过并返回 `void`;不抛出 - 业务错误,不访问 DOM、render global 或 legacy global。 -- 地图删除、移动、勇士移动和临时事件序列都等待完整的 Promise 动作;没有需要等待 - 的动作时同步返回 `void`。 -- 默认注册项由 `data-state/src/event/registrations.ts` 装配,`CoreState` 从该模块导入 - `createEventBuiltinRegistrations` 并传给 `GameEventSystem`;`GameEventSystem` - 通过现有 `AnonTokyoInterpreter` 的 `builtInFunctions` 初始化项装配。函数实现不复制到 - `CoreState`。 -- 注册项只包含下表八个稳定名称。 +- 每个 built-in 是一个类,实现 `BuiltInFunction`(来自 `@motajs/anon-tokyo`): + 以 `name` 声明稳定函数名,以 `func(param, env)` 方法实现行为。 +- `env` 的类型固定为 `IBlockEventEnv`。目标解析顺序为:明确的 `env.layer` → + `env.map` 的 `eventLayer` → 通过 `env.heroFloor` 从 `env.state.maps` 取得的地图及其 + `eventLayer`(由 `event/utils.ts` 的 `getPossibleLayer` 提供)。 +- 目标、图块、勇士、事件存储或事件 id 缺失时,函数安全跳过并返回 `void`;不抛业务 + 错误,不访问 DOM、render global 或 legacy global。 +- 地图删除、移动、勇士移动和临时事件序列都等待完整的 Promise 动作;没有需要等待的 + 动作时同步返回 `void`。 +- 移动序列统一通过 `IObjectMover.push(steps)` 追加,再 `start()` 并等待控制器 + `onEnd`;不逐条调用移动方法。 +- 共享功能性函数放在 `event/utils.ts`,**不经 `event/index.ts` 导出**;非公共的 + 功能性函数作为所属类的私有方法。 +- `map.ts` / `hero.ts` / `event.ts` 只包含 built-in 类,不包含落单函数。 +- 默认注册项由 `data-state/src/event/registrations.ts` 的唯一函数 + `createEventRegistrations()` 装配;`CoreState` 从该模块导入并传给 `GameEventSystem`。 ## Parameter contracts -### `eventSetBlock` +### `setBlock` — `map.ts` 的 `EventSetBlock` ```ts interface ISetBlockEventParam { @@ -41,10 +39,10 @@ interface ISetBlockEventParam { } ``` -在解析出的目标图层设置 `(x, y)` 的静态图块。`tile` 通过 `env.state.tileStore` -解析;图块 id、地图、图层或坐标目标不可用时安全返回 `void`。 +在解析出的目标图层设置 `(x, y)` 的静态图块。`tile` 通过 `env.state.tileStore` 解析; +图块 id、地图、图层或坐标目标不可用时安全返回 `void`。 -### `eventMoveBlock` +### `moveBlock` — `map.ts` 的 `EventMoveBlock` ```ts interface IMoveBlockEventParam { @@ -55,12 +53,11 @@ interface IMoveBlockEventParam { } ``` -从 `(x, y)` 将静态图块转为动态图块,按 `steps` 完整移动,再转回静态图块。 -`safe` 为 `true` 时使用 `transferToStaticIfSafe`,否则使用 -`transferToStatic`。源图块、动态图块、移动控制器或目标静态图块不可用时安全返回 -`void`;移动控制器的 `onEnd` 和最终转换都必须等待。 +从 `(x, y)` 将静态图块转为动态图块,`push` 入 `steps` 后完整移动,再转回静态图块。 +`safe` 为 `true` 时使用 `transferToStaticIfSafe`,否则使用 `transferToStatic`。源图块、 +动态图块、移动控制器或目标静态图块不可用时安全返回 `void`。 -### `eventDeleteBlock` +### `deleteBlock` — `map.ts` 的 `EventDeleteBlock` ```ts interface IDeleteBlockEventParam { @@ -69,10 +66,10 @@ interface IDeleteBlockEventParam { } ``` -删除目标坐标的静态图块,并等待该坐标所有动态图块的删除 Promise。地图、图层或 -坐标不可用时安全返回 `void`。 +删除目标坐标的静态图块,并等待该坐标所有动态图块的删除 Promise。地图、图层或坐标 +不可用时安全返回 `void`。 -### `eventMoveHero` +### `moveHero` — `hero.ts` 的 `EventMoveHero` ```ts interface IMoveHeroEventParam { @@ -80,31 +77,31 @@ interface IMoveHeroEventParam { } ``` -把完整 `steps` 队列加入 `env.state.hero.location.mover` 并启动一次移动。勇士或 +把完整 `steps` 队列 `push` 进 `env.state.hero.location.mover` 并启动一次移动。勇士或 移动器缺失、移动已在进行中或动作无法启动时安全返回 `void`;启动成功后等待 `IMoverController.onEnd`。 -### `eventMoveHeroStep` +### `moveHeroStep` — `hero.ts` 的 `EventMoveHeroStep` ```ts interface IMoveHeroStepEventParam {} ``` -不读取额外字段,使用勇士当前移动方向执行一次 `forward(1)`。勇士、移动器或控制器 -缺失时安全返回 `void`,启动成功后等待 `onEnd`。 +不读取额外字段,使用勇士当前移动方向执行一次 `forward(1)`。移动器或控制器缺失、 +移动已在进行中时安全返回 `void`,启动成功后等待 `onEnd`。 -### `eventTouchFront` +### `touchFront` — `hero.ts` 的 `EventTouchFront` ```ts interface ITouchFrontEventParam {} ``` -不读取额外字段。以勇士当前位置和当前朝向计算面前一格,收集该坐标的点事件、静态 -图块事件和动态图块事件,以 `EventTrigger.OnTouch` 通过现有事件执行器顺序触发。 -缺少勇士、地图、图层、目标位置或事件执行器时安全返回 `void`;事件执行 Promise -必须等待。该函数只触发 `onTouch`,不移动勇士。 +不读取额外字段。以勇士当前位置和当前朝向计算面前一格,收集该坐标的点事件、静态图块 +事件和动态图块事件,以 `EventTrigger.OnTouch` 通过现有事件执行器顺序触发。来源收集由 +该类的私有方法 `collectInvocations` 完成。缺少地图、图层、目标位置或事件执行器时安全 +返回 `void`;该函数只触发 `onTouch`,不移动勇士。 -### `eventInsertEvents` +### `insertEvents` — `event.ts` 的 `EventInsertEvents` ```ts interface IInsertEventsEventParam { @@ -112,52 +109,42 @@ interface IInsertEventsEventParam { } ``` -按 `ids` 顺序临时构造事件调用,复用当前 `env`,通过当前事件执行器执行一次;不把 -这些事件写入 `IGameEventStore`,也不改变地图持久化数据。空序列、缺失事件存储、缺失 -事件 id 或缺失执行器时安全跳过;执行 Promise 必须等待。 +按 `ids` 顺序临时构造事件调用,复用当前 `env`,通过当前事件执行器执行一次;不把这些 +事件写入 `IGameEventStore`,也不改变地图持久化数据。有效 id 过滤由该类的私有方法 +`collectInvocations` 完成。空序列、缺失事件存储、缺失事件 id 或缺失执行器时安全跳过; +执行 Promise 必须等待。 -### `eventInsertEvent` +### `insertEvent` — `event.ts` 的 `EventInsertEvent` ```ts type IInsertEventEventParam = Statement[]; ``` -接收一段 `Statement[]` 事件语句,直接使用现有 AnonTokyo 解释器执行该语句体, -参数为 `{ custom: {} }`,环境为当前 `env`。不读取事件存储、不解析事件 id, -也不写入事件存储。空语句体、缺失执行器或达到嵌套插入深度上限时安全返回 `void`, -解释器 Promise 必须等待。 +接收一段 `Statement[]` 事件语句,直接使用现有 AnonTokyo 解释器执行该语句体。不读取 +事件存储、不解析事件 id,也不写入事件存储。空语句体、缺失执行器或达到嵌套插入深度 +上限时安全返回 `void`,解释器 Promise 必须等待。 ## Registration -八项注册由 `data-state/src/event/registrations.ts` 作为唯一装配者直接构造。`CoreState` -在创建 `GameEventSystem` 时从该模块导入 `createEventBuiltinRegistrations` 并传入该 -注册项集合;`GameEventSystem` 将其转换为 AnonTokyo 的 built-in function entries。 -除这八项外,本阶段不注册任何 legacy built-in。 +`data-state/src/event/registrations.ts` 只导出一个函数 +`createEventRegistrations()`,按稳定顺序直接构造八个类的实例并返回 +`BuiltInFunction[]`;不做分类包装,不引入工厂或描述符数组。`CoreState` 在创建 +`GameEventSystem` 时导入该函数并传入注册项集合。除这八项外,本阶段不注册任何 legacy +built-in。 -四个公开注册函数保持既有调用签名,且均可从 `event/index.ts` 与 -`data-state/src/index.ts` 访问: +稳定顺序与所有权: -| 导出 | 行为 | -| ------------------------------------------ | ------------------------------------ | -| `createMapEventBuiltinRegistrations()` | 返回地图组三个全新实例 | -| `createHeroEventBuiltinRegistrations()` | 返回勇士组三个全新实例 | -| `createControlEventBuiltinRegistrations()` | 返回事件控制组两个全新实例 | -| `createEventBuiltinRegistrations()` | 按 map → hero → control 拼接八个实例 | +1. `setBlock` — `map.ts` 的 `EventSetBlock` +2. `moveBlock` — `map.ts` 的 `EventMoveBlock` +3. `deleteBlock` — `map.ts` 的 `EventDeleteBlock` +4. `moveHero` — `hero.ts` 的 `EventMoveHero` +5. `moveHeroStep` — `hero.ts` 的 `EventMoveHeroStep` +6. `touchFront` — `hero.ts` 的 `EventTouchFront` +7. `insertEvents` — `event.ts` 的 `EventInsertEvents` +8. `insertEvent` — `event.ts` 的 `EventInsertEvent` -每个注册项都是显式类实例,类同时拥有稳定的 `name`(`EventBuiltinName`)与对应的 -可调用 `func`,不使用通用工厂或描述符数组。稳定顺序与所有权为: - -1. `eventSetBlock` — `map.ts` 的 `SetBlockEventRegistration` -2. `eventMoveBlock` — `map.ts` 的 `MoveBlockEventRegistration` -3. `eventDeleteBlock` — `map.ts` 的 `DeleteBlockEventRegistration` -4. `eventMoveHero` — `hero.ts` 的 `MoveHeroEventRegistration` -5. `eventMoveHeroStep` — `hero.ts` 的 `MoveHeroStepEventRegistration` -6. `eventTouchFront` — `hero.ts` 的 `TouchFrontEventRegistration` -7. `eventInsertEvents` — `event.ts` 的 `InsertEventsEventRegistration` -8. `eventInsertEvent` — `event.ts` 的 `InsertEventEventRegistration` - -`eventTouchFront` 及其来源收集与目标解析辅助函数属于 hero 事件层;`event.ts` 只保留 -`eventInsertEvents` 与直接执行 `Statement[]` 的 `eventInsertEvent`。`event/index.ts` -与 `data-state/src/index.ts` 保持 `export`-only,不成为第二装配者。事件语义、等待语义、 +`touchFront` 及其来源收集属于 hero 事件层;`event.ts` 只保留 `insertEvents` 与直接执行 +`Statement[]` 的 `insertEvent`。`event/index.ts` 与 `data-state/src/index.ts` 保持 +`export`-only,不成为第二装配者;`event/utils.ts` 不参与导出。事件语义、等待语义、 `Statement[]` 直接执行、缺失目标安全返回、legacy/save 边界与用户自有的 decorator 落点均保持不变。 diff --git a/.planning/phases/03-data-completion/03-REPLAY-CONTRACT.md b/.planning/phases/03-data-completion/03-REPLAY-CONTRACT.md index f78c983..4e9224c 100644 --- a/.planning/phases/03-data-completion/03-REPLAY-CONTRACT.md +++ b/.planning/phases/03-data-completion/03-REPLAY-CONTRACT.md @@ -1,120 +1,112 @@ -# Phase 3 Plan 03: Replay Command Contract +# Phase 3 Replay Command Contract -## User structural supersession (S-05) +本文件记录录像指令的稳定契约。数值指令码属于录像格式的一部分,注册项提供实现但不 +分配全局指令编号。 -本节晚于初始 replay checkpoint,优先于下方关于 command completion 和 decorator -placement 的旧记录。S-05 supersedes S-02 在 replay-step completion 边界上的结论: - -- replay command 不调用 `shouldReplay`;移动和寻路 command 必须等待各自 controller 完成后再返回,确保下一录像步不会与上一步并发。`shouldReplay` 的最终落点仍由用户放在真正改变最终状态的低层方法上。 -- 既有 `ReplaySystem`、route 和 sandbox 只做使同步 command 正常运行所需的最小兼容调整,不重新设计录像系统。 -- `@shouldReplay()` 不在本次 correction 中移动或新增;其最终位置由用户自行处理,且不得通过 replay command 构造器间接注入。 - -## Approval - -The `confirm-record` checkpoint response approves one top-level replay command -enum. Numeric values are stable and are assigned once in the locked D-25 order; -module registration items provide implementations only and never allocate -global command numbers. +> **实现同步(quick 260913-qtq 及后续):** 指令改为 `BaseReplayCommand` 基类 + 类 +> 实现;寻路指令码改名 `Teleport`;参数改为纯数值 / 固定数量;注册收口为 `CoreState` +> 的私有 `registerReplayCommands()`;移动指令新增 `notExecuted` 批量收尾。本节取代 +> 下方任何与旧指令名 / 旧注册辅助 / 字符串参数相关的历史描述。 ## Stable enum and registration order -`ReplayCommandCode` is the sole owner of these numeric values: +`ReplayCommandCode` 是这些数值的唯一拥有者: -| Order | Enum member | Stable code | Route command | Action | -| ----: | --------------------- | ----------: | ------------------------ | ---------------------------------------------------------- | -| 1 | `Up` | `0` | `up` | Move the hero one step upward | -| 2 | `Right` | `1` | `right` | Move the hero one step rightward | -| 3 | `Down` | `2` | `down` | Move the hero one step downward | -| 4 | `Left` | `3` | `left` | Move the hero one step leftward | -| 5 | `AutoPathfindToPoint` | `4` | `auto-pathfind-to-point` | Move the hero to the encoded target point | -| 6 | `UseItem` | `5` | `use-item` | Call the hero item-use entry point | -| 7 | `Equip` | `6` | `equip` | Equip the encoded equipment instance into the encoded slot | -| 8 | `Unequip` | `7` | `unequip` | Unequip the encoded numeric slot | +| Order | Enum member | Stable code | Action | +| ----: | ----------- | ----------: | ------ | +| 1 | `Up` | `0` | 勇士向上移动一步 | +| 2 | `Right` | `1` | 勇士向右移动一步 | +| 3 | `Down` | `2` | 勇士向下移动一步 | +| 4 | `Left` | `3` | 勇士向左移动一步 | +| 5 | `Teleport` | `4` | 瞬移 / 自动寻路到编码的目标点 | +| 6 | `UseItem` | `5` | 调用勇士道具使用入口 | +| 7 | `Equip` | `6` | 将编码装备穿到编码槽位 | +| 8 | `Unequip` | `7` | 卸下编码的数值槽位 | -The values `0` through `7` are part of the replay format. They must not be -renumbered, inferred from registration-map iteration, replaced with strings, -or allocated by `data-common`, `data-system`, or an individual command module. -The top-level registry must register exactly these eight entries in this table's -order and reject duplicate codes before delegating to `ReplaySystem`. +`0`–`7` 属于录像格式,不得重编号、不得由注册表遍历推断、不得替换为字符串、也不得由 +`data-common` / `data-system` 或单个指令模块分配。裸名 `REPLAY_COMMAND_ORDER` 保留为 +测试读取的稳定顺序。 + +## Command shape + +`replay/commands.ts` 中每个指令实现 `IReplayCommand`。公共基类 +`BaseReplayCommand` 提供: + +- `name: string` — 指令的字符串名称(用于参数校验日志)。 +- `paramTypes: readonly string[]` — 期望的 JS 参数类型列表。 +- `protected assertParameter(command, parameter, expect)` — 校验参数数量与类型; + 数量不符记 `logger.error(2001)`,类型不符记 `logger.error(2002)`,校验失败返回 `false`。 +- `abstract wrappedExecute(step): Promise` — 参数校验后的实际逻辑。 +- `execute(step): Promise` — 先校验参数,再委托 `wrappedExecute`。 + +指令不调用 `shouldReplay`;`shouldReplay` 的落点仍由用户放在真正改变最终状态的底层 +方法上。需要跨步收尾的指令实现可选的 `notExecuted()`(见“移动指令”)。 ## Command parameter boundary -The command route continues to use the existing primitive -`ReplayParamValue[]` representation. The command implementations validate -their parameter count and primitive types before touching state: +指令参数使用现有 `ReplayParamValue[]`。每个指令在 `wrappedExecute` 首行以注释记录期望 +的二进制参数类型;参数数量固定,**不允许可选参数,也不使用字符串参数**: -| Command | Parameters | State access | -| ----------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------- | -| `up`, `right`, `down`, `left` | none | `CoreState.hero.location.mover` | -| `auto-pathfind-to-point` | numeric `x`, numeric `y` | the internally owned `PathfindingSystem`, bound to the hero mover | -| `use-item` | one numeric item number or string item id | `CoreState.hero.items.useItem(item)` | -| `equip` | numeric equipment `uid`, numeric or string slot, optional boolean `autoUnload` | `CoreState.hero.equip.equip(uid, slot, autoUnload)` | -| `unequip` | one numeric slot | `CoreState.hero.equip.unequip(slot)` | +| Command | 参数(二进制类型注释) | 状态访问 | +| ------- | ---------------------- | -------- | +| `move`(四个方向实例) | 无 | `CoreState.hero.location.mover` | +| `teleport` | `[int16 x, int16 y]` | 内部拥有的、绑定勇士移动器的 `PathfindingSystem` | +| `use-item` | `[int16 item]` | `CoreState.hero.items.useItem(item)` | +| `equip` | `[int16 uid, int8 slot, bool autoUnload]` | `CoreState.hero.equip.equip(uid, slot, autoUnload)` | +| `unequip` | `[int8 slot]` | `CoreState.hero.equip.unequip(slot)` | -Invalid parameter count/types, missing targets, an already-running action, or -a state API failure return `false`. Item and equipment state APIs retain their -existing synchronous command boundary. +非法参数数量 / 类型、目标缺失、动作已在进行中或状态 API 无效果时返回 `false`。每个 +`return false` 位置对应一个独立 logger 错误码,确保录像报错可溯源(当前 `2003`–`2008`)。 + +## Completion boundaries + +- **移动指令(`ReplayMoveCommand`,四个方向实例):** `wrappedExecute` 只把方向追加到 + 移动器(`mover.step(direction)`),不启动;`notExecuted()` 调用 `mover.start()` 并 + 等待 `onEnd`。这样连续移动步骤可以合并成一次移动,由下一次不同指令或录像结束触发 + 收尾。 +- **瞬移指令(`ReplayTeleportCommand`):** 调用 `PathfindingSystem.teleportTo({ x, y })`; + 返回 `null` 记 `2005` 并返回 `false`,否则等待其 `controller.onEnd`。 +- **使用物品(`ReplayUseItemCommand`):** 直接返回 `hero.items.useItem(item)`;失败记 + `2006` 并返回 `false`。 +- **装备(`ReplayEquipCommand`):** 调用 `equipment.equip(uid, slot, autoUnload)`; + `getEquipped(slot) !== uid` 记 `2007` 并返回 `false`。 +- **卸下(`ReplayUnequipCommand`):** 调用 `equipment.unequip(slot)`; + `getEquipped(slot) !== undefined` 记 `2008` 并返回 `false`。 +- 移动 / 瞬移在 `execute` 返回 `Promise` 之前等待控制器完成;道具与装备沿用 + 既有同步边界,其结果被适配为同一布尔边界。 + +## Sandbox finalization + +`ReplaySandbox.step()` 在播放下一步之前,先对**上一步**指令调用 `notExecuted?.()`(记 +`warn 175` 表示收尾失败);当读取流结束(录像结束)时,先对最后一步执行同样的收尾, +再标记结束。收尾逻辑封装为私有 `finalizeLast()`。这保证以连续移动结尾的录像最终也会 +启动并等待移动。 ## Top-level registry ownership -`CoreState` is the final assembly boundary. A fresh `CoreState` owns a fresh -`ReplaySystem` and invokes one data-state replay registration helper with the -approved enum order. The helper's registration items contain command behavior, -while the top-level enum remains the only stable code owner. No lower layer -registers a global code or imports the data-state root barrel to obtain one. +`CoreState` 是最终装配边界。一个 `CoreState` 拥有独立的 `ReplaySystem`,并通过私有方法 +`registerReplayCommands()` **逐个直接注册**八个稳定指令码: -Each non-directional replay instruction is an independent class in the replay -command module. The class owns its state reference, parameter validation, action -call, and `execute` implementation. The four directional entries use one -parameterized `ReplayDirectionCommand` class with a distinct `FaceDirection` -value per fresh instance; they share no mutable command context or closure -factory, and no instruction constructs or delegates to another instruction. -The registry creates four fresh directional instances plus one fresh instance -of each non-directional class in stable enum order while retaining the existing -`IReplayCommandItem` extension boundary. +```ts +this.replaySystem.registerCommand( + ReplayCommandCode.Up, + new ReplayMoveCommand(this, FaceDirection.Up) +); +// …共八条,按 Up/Right/Down/Left/Teleport/UseItem/Equip/Unequip 顺序 +``` -The existing `IReplaySystem.registerCommand(code, command)` and -`IReplayCommand.execute(step): Promise` public contracts remain in -force. Duplicate detection belongs to the top-level assembly helper; the -existing `ReplaySystem` remains the route/command storage boundary. +不再有 `data-state/replay` 下的工厂 / 注册辅助函数,也不使用 `IReplayCommandItem` / +`IReplayCommandRegistry` 扩展边界。重复指令码由既有 `ReplaySystem.registerCommand` +(记 `warn 163`)处理。 ## CoreState and Node runner access -The public `ICoreState` contract is not expanded for replay. The concrete -`CoreState` construction path is the approved access seam: `createCoreState()` -creates an independent instance, and the Node runner imports that factory from -`data-state/src/core.ts`, never the compatibility singleton from `ins.ts`. - -The concrete state instance owns the replay system and the internally bound -pathfinding system needed by command implementations. Node verification may -consume those concrete assembly seams, but it must not depend on browser globals, -the singleton, IndexedDB, or a new options-bearing factory API. The existing -`hero`, `maps`, `eventSystem`, `hero.items`, and `hero.equip` state boundaries -remain the action targets; no new `ICoreState` member is required by this -contract. - -## Completion boundaries under S-05 - -- Four-direction movement appends one direction to the hero mover, starts it, - and awaits the returned mover controller's `onEnd`; it returns `true` only - after the controller completes and returns `false` for a missing, active, or - failed action. -- Auto-pathfind calls the existing `PathfindingSystem.moveTo({ x, y })`; a null - result is `false`, and a non-null result awaits its controller's `onEnd` - before resolving `true`. -- Item and equipment calls remain synchronous under the current interfaces; their - boolean/undefined result is converted to the command's success boolean. -- Replay command implementations do not add or invoke replay-safety decorators. - The user-owned low-level state-mutation decorator boundary remains outside this - correction; synchronous item/equipment calls retain their existing command - boundary. -- `IReplayCommand.execute()` continues to return the existing `Promise` - boundary, with movement/pathfinding completion awaited before that Promise - resolves and synchronous item/equipment results adapted to the same boundary. +公共 `ICoreState` 契约不为 replay 扩展。`createCoreState()` 创建独立实例;Node runner +从 `data-state/src/core.ts` 导入该工厂,而非兼容单例。具体状态实例拥有 replay system +与内部绑定移动器的寻路系统;`hero.location.mover`、`hero.items`、`hero.equip`、 +`state.pathfinding` 是既有动作目标,不新增 `ICoreState` 成员。 ## Explicit exclusions -This record does not authorize a new public `ICoreState` replay property, a -second command-code owner, a string-code route format, a reordered registry, a -Phase 4 render click boundary, or reuse of the Plan 01 private direct tracer as -the final registry. +本记录不授权新增公共 `ICoreState` replay 属性、第二个指令码拥有者、字符串指令码格式、 +重排注册表、Phase 4 渲染点击边界,或把 Plan 01 的私有 tracer 当作最终注册表。 diff --git a/.planning/quick/260913-qtq-event-built-ins-refactor-and-anon-tokyo-/260913-qtq-PLAN.md b/.planning/quick/260913-qtq-event-built-ins-refactor-and-anon-tokyo-/260913-qtq-PLAN.md index 8b71179..34c7af4 100644 --- a/.planning/quick/260913-qtq-event-built-ins-refactor-and-anon-tokyo-/260913-qtq-PLAN.md +++ b/.planning/quick/260913-qtq-event-built-ins-refactor-and-anon-tokyo-/260913-qtq-PLAN.md @@ -8,14 +8,12 @@ tags: [events, anon-tokyo, data-state, refactor] # Quick Task 260913-qtq: 事件内建函数改造 + anon-tokyo 引用统一 +> 追溯记录:仅记录本次 AI 侧的改动(提交 `a701aa6`、`f0fd2f5`、文档提交见 SUMMARY)。 + ## Objective -把内嵌的 `@motajs/anon-tokyo` 作为唯一 anon-tokyo 来源接入全部包,并把 -`data-state` 的全部事件内建函数改为与用户手写的 `EventSetBlock` 一致的 -类式写法,注册收口为单一函数,共享工具收敛到 `utils.ts`。 - -> 说明:本记录为追溯补录。代码已先行完成并提交(`a701aa6`、`f0fd2f5`), -> 现补 PLAN/SUMMARY/STATE 记录以保证可追溯。 +全仓统一引用内嵌的 `@motajs/anon-tokyo`,并把 `data-state` 的全部事件内建函数改为 +类式 `BuiltInFunction` 写法,注册收口为单一函数,共享工具收敛到 `utils.ts`。 ## Scope @@ -23,27 +21,22 @@ tags: [events, anon-tokyo, data-state, refactor] ## Tasks -1. **anon-tokyo 引用统一** - - 全部 `from 'anon-tokyo'` → `from '@motajs/anon-tokyo'` - - `data-common` / `data-system` 声明 `@motajs/anon-tokyo: workspace:*`,刷新 lockfile +1. **anon-tokyo 引用统一** — 全部 `from 'anon-tokyo'` → `from '@motajs/anon-tokyo'`; + `data-common` / `data-system` 声明 `@motajs/anon-tokyo: workspace:*`,刷新 lockfile。 -2. **事件内建函数类式重写** - - `map.ts` / `hero.ts` / `event.ts` 每个内建函数改为 - `EventXxx implements BuiltInFunction`,`name` 使用短名 + `func` 方法 - - 删除落单函数;共享工具迁至 `event/utils.ts`(不经 index 导出) - - 非公共功能性函数改为类私有方法 - - 移动序列改用 `IObjectMover.push`,删除 `appendMoveSteps` - - `registrations.ts` 仅保留 `createEventRegistrations()`,按三类顺序写入八条 - - 删除 `EventBuiltinName` +2. **事件内建函数类式重写** — `map.ts` / `hero.ts` / `event.ts` 每个内建函数改为 + `EventXxx implements BuiltInFunction`(`name` 用短名 + `func` 方法);共享工具 + 迁至 `event/utils.ts`(不经 index 导出);非公共功能性函数改为类私有方法;移动序列 + 改用 `IObjectMover.push`;`registrations.ts` 仅保留 `createEventRegistrations()`; + 删除 `EventBuiltinName`。 -3. **验证** - - 数据包 type 门禁、`vitest run`、Prettier/ESLint - - 对齐 `46a38fa` 遗留的 replay 测试红点 +3. **装配与测试** — `core.ts` 改用 `createEventRegistrations()`;更新 + `event.test.ts` 与 `closed-loop` fixture;对齐 replay 命令测试。 ## must_haves - [x] 源码中不再有裸 `anon-tokyo` 引用 -- [x] 八个内建函数均为类实现,名称稳定为 `setBlock/moveBlock/deleteBlock/moveHero/moveHeroStep/touchFront/insertEvents/insertEvent` +- [x] 八个内建函数均为类实现,名称为 `setBlock/moveBlock/deleteBlock/moveHero/moveHeroStep/touchFront/insertEvents/insertEvent` - [x] `event/utils.ts` 存在且不被 `event/index.ts` 导出 - [x] `createEventRegistrations()` 为唯一注册入口,顺序稳定 - [x] 全量测试通过,type 门禁 0 in-scope 诊断 diff --git a/.planning/quick/260913-qtq-event-built-ins-refactor-and-anon-tokyo-/260913-qtq-SUMMARY.md b/.planning/quick/260913-qtq-event-built-ins-refactor-and-anon-tokyo-/260913-qtq-SUMMARY.md index b97114c..e1f7eab 100644 --- a/.planning/quick/260913-qtq-event-built-ins-refactor-and-anon-tokyo-/260913-qtq-SUMMARY.md +++ b/.planning/quick/260913-qtq-event-built-ins-refactor-and-anon-tokyo-/260913-qtq-SUMMARY.md @@ -18,6 +18,7 @@ key-files: - packages-user/data-state/src/event/event.test.ts - packages-user/data-state/src/core.ts - packages-user/data-state/test/fixtures/closed-loop.ts + - packages-user/data-state/src/replay/commands.test.ts - packages-user/data-common/src/event/{types,event}.ts - packages-user/data-common/src/store/eventStore.test.ts - packages-user/data-system/src/event/{types,system,executor,eventDispatch.test}.ts @@ -36,16 +37,22 @@ commits: ## Performance - **Completed:** 2026-09-13 -- **Commits:** 2 (`a701aa6`, `f0fd2f5`) +- **Commits:** `a701aa6`、`f0fd2f5` ## Accomplishments -- 全部 `anon-tokyo` 引用改为 `@motajs/anon-tokyo`;`data-common` / `data-system` 声明 workspace 依赖,lockfile 同步。 -- `map.ts` / `hero.ts` / `event.ts` 八个内建函数改为类式实现,`name` 采用短名,`func` 为类方法;不再有落单函数。 -- 共享工具收敛到 `event/utils.ts`(`getPossibleMap` / `getPossibleLayer` / `getEventExecutor` / `enterEventInsert` / `exitEventInsert`),不经 `event/index.ts` 导出。 -- 非公共功能性函数改为类私有方法(`EventTouchFront.collectInvocations`、`EventInsertEvents.collectInvocations`)。 +- 全部 `anon-tokyo` 引用改为 `@motajs/anon-tokyo`;`data-common` / `data-system` 声明 + workspace 依赖,lockfile 同步。 +- `map.ts` / `hero.ts` / `event.ts` 八个内建函数改为类式实现,`name` 采用短名,`func` + 为类方法;不再有落单函数。 +- 共享工具收敛到 `event/utils.ts`(`getPossibleMap` / `getPossibleLayer` / + `getEventExecutor` / `enterEventInsert` / `exitEventInsert`),不经 `event/index.ts` + 导出。 +- 非公共功能性函数改为类私有方法(`EventTouchFront.collectInvocations`、 + `EventInsertEvents.collectInvocations`)。 - 移动序列使用 `IObjectMover.push`,删除逐方法调用辅助。 -- `registrations.ts` 仅保留 `createEventRegistrations()`,按地图/玩家/事件三类顺序写入八条,注释分隔。 +- `registrations.ts` 仅保留 `createEventRegistrations()`,按地图/玩家/事件三类顺序写入 + 八条,注释分隔。 - 删除 `EventBuiltinName`;`core.ts` 改用 `createEventRegistrations()`。 ## Files Created/Modified @@ -55,11 +62,12 @@ commits: - `packages-user/data-state/src/event/event.test.ts` — 按新类与注册函数重写 - `packages-user/data-state/src/core.ts` — 使用 `createEventRegistrations()` - `packages-user/data-state/test/fixtures/closed-loop.ts` — 内建名改短名 +- `packages-user/data-state/src/replay/commands.test.ts` — 对齐当前指令实现 - `packages-user/data-common`、`packages-user/data-system` — import 重命名 + 依赖声明 ## Decisions Made -- 内建函数名改用短名(`setBlock` 等)并删除 `EventBuiltinName`(用户确认)。 +- 内建函数名改用短名(`setBlock` 等)并删除 `EventBuiltinName`。 - anon-tokyo 内嵌为 workspace 包,全仓统一引用 `@motajs/anon-tokyo`。 - 注册不做分类包装,`createEventRegistrations()` 内按顺序直写八条。 - `utils.ts` 只放共享功能性函数,不经 index 导出。 @@ -68,10 +76,11 @@ commits: ### Auto-fixed Issues -**1. [Rule 3 - Blocking] 对齐 replay 命令测试的先行红点** +**1. [Rule 3 - Blocking] 更新 replay 命令测试** - **Found during:** 验证阶段 -- **Issue:** 用户上一提交 `46a38fa` 将 `ReplayCommandCode.AutoPathfindToPoint` 改名 `Teleport`、`registerReplayCommand` 改名复数、并简化 equip/unequip 逻辑,但未同步测试,导致 `commands.test.ts` 3 例失败。 -- **Fix:** 按实现更新测试预期(Teleport 名称、registerReplayCommands、equip/unequip 只校验最终槽位、错误码 2007/2008)。 +- **Issue:** `commands.test.ts` 与新指令实现(`Teleport` 命名、`registerReplayCommands`、 + equip/unequip 结果校验)不一致,套件不绿。 +- **Fix:** 更新测试预期以匹配当前实现。 - **Files modified:** `packages-user/data-state/src/replay/commands.test.ts` - **Verification:** `vitest run` 19 files / 112 tests 全通过。 - **Committed in:** `f0fd2f5` @@ -80,7 +89,8 @@ commits: ## Issues Encountered -- 两个 git 提交命令并发执行触发 `index.lock`,`commands.test.ts` 被并入 `f0fd2f5`;内容正确,仅提交信息未单列。工作树最终干净。 +- 两个 git 命令并发触发 `index.lock`,`commands.test.ts` 被并入 `f0fd2f5`;内容正确, + 仅提交信息未单列。工作树最终干净。 ## Verification @@ -88,11 +98,9 @@ commits: - `vitest run`:19 files / 112 tests passed - 改动文件 Prettier 通过、ESLint 退出码 0 -## Follow-ups / 未决 +## Follow-ups -- 根 `package.json` 仍保留外部依赖 `anon-tokyo@0.0.0-alpha.0`,现已无人引用,可移除。 -- `packages-user/legacy-plugin-data/src/fallback.ts` 的 `'eventMoveHero'` 为 legacy 全局补丁名,留给 Phase 5。 -- `03-REPLAY-CONTRACT.md` 等契约文档尚未同步为新的短名 / 单一注册设计。 +- 契约文档已同步为当前设计:`03-EVENT-CONTRACT.md`、`03-REPLAY-CONTRACT.md`。 ---