docs: sync event/replay contracts with current design

- 03-EVENT-CONTRACT.md: short built-in names, class-based BuiltInFunction,
  single createEventRegistrations(), event/utils.ts (not barrel-exported)
- 03-REPLAY-CONTRACT.md: BaseReplayCommand shape, Teleport code, numeric fixed
  params, move notExecuted batching, CoreState registerReplayCommands, 2001-2008
- scope quick 260913-qtq record to AI changes only
This commit is contained in:
unanmed 2026-09-13 19:24:54 +08:00
parent e5f6d492b2
commit d7837fcc24
4 changed files with 194 additions and 214 deletions

View File

@ -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<P, E>`(来自 `@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
落点均保持不变。

View File

@ -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<boolean>` — 参数校验后的实际逻辑。
- `execute(step): Promise<boolean>` — 先校验参数,再委托 `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<boolean>` 之前等待控制器完成;道具与装备沿用
既有同步边界,其结果被适配为同一布尔边界。
## 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<boolean>` 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<boolean>`
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 当作最终注册表。

View File

@ -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<P, E>``name` 使用短名 + `func` 方法
- 删除落单函数;共享工具迁至 `event/utils.ts`(不经 index 导出)
- 非公共功能性函数改为类私有方法
- 移动序列改用 `IObjectMover.push`,删除 `appendMoveSteps`
- `registrations.ts` 仅保留 `createEventRegistrations()`,按三类顺序写入八条
- 删除 `EventBuiltinName`
2. **事件内建函数类式重写**`map.ts` / `hero.ts` / `event.ts` 每个内建函数改为
`EventXxx implements BuiltInFunction<P, E>``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 诊断

View File

@ -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`。
---