mirror of
https://github.com/motajs/template.git
synced 2026-09-20 14:30:17 +08:00
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:
parent
e5f6d492b2
commit
d7837fcc24
@ -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
|
||||
落点均保持不变。
|
||||
|
||||
@ -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 当作最终注册表。
|
||||
|
||||
@ -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 诊断
|
||||
|
||||
@ -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`。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user