mirror of
https://github.com/motajs/template.git
synced 2026-10-29 12:20:17 +08:00
docs(10-39): 新增高级 III 导览并补齐互链
- 新建 III 导览:读者画像、与 I/II 的区别、前置、八篇地图、阅读路径、边界与自检 - 七篇正文补返回导览,并把已有的兄弟页提及改为真实相对链接(含 event-system 收集章节锚点)
This commit is contained in:
parent
44c14c68e0
commit
93ac3efec8
@ -163,3 +163,5 @@ console.log(await executable.exec({ value: 21 }, {})); // 42
|
||||
- **解释器不负责派发**:触发器过滤与坐标收集在事件系统,不在解释器。
|
||||
|
||||
相关背景可看[高级 I 的 data-common Store](../i/data-stores.md)与[中级教程的自定义事件](../../intermediate/events.md)。
|
||||
|
||||
返回[高级 III 导览](./index.md)。
|
||||
|
||||
@ -191,3 +191,5 @@ L0 内部也有先后:道具仓库与怪物仓库依赖图块仓库,所以
|
||||
- **读档期间录像被临时禁用**:每个对象的 `loadState` 都被禁用与恢复包裹,执行器不在包裹内。
|
||||
- **不要重排装配顺序**:层与层之间的依赖是固定的,重排会让需要回指主对象的子系统拿不到已装配的下层。
|
||||
- **不要手动构造内部子系统**:需要新能力时新增插件,或通过公开入口扩展;自己创建内部对象会绕过存档、录像与生命周期管理。
|
||||
|
||||
返回[高级 III 导览](./index.md)。
|
||||
|
||||
@ -266,3 +266,5 @@ export function createEventRegistrations(): BuiltInFunction[] {
|
||||
- **多条事件是异步顺序执行**:不要假设它们并发,也不要依赖未声明的执行顺序。
|
||||
- **执行器状态是共享的**:`mode` 与 `reduce` 会影响后续执行,改完记得复位。
|
||||
- **事件注册属于内容配置**:序列化事件注册与地图事件 id 绑定由编辑器负责,本篇不展开。
|
||||
|
||||
返回[高级 III 导览](./index.md)。
|
||||
|
||||
62
docs/advanced/iii/index.md
Normal file
62
docs/advanced/iii/index.md
Normal file
@ -0,0 +1,62 @@
|
||||
# 高级 III 导览
|
||||
|
||||
高级 III 是高级教程的最高难度层,讲数据端系统的原理与架构:这些系统为什么这样拆分、数据在它们之间怎么流动、哪些约束不能打破。它面向已经能独立改动数据端、现在需要判断改动该落在哪一层的作者。
|
||||
|
||||
这一页说明这一层是给谁的、与高级 I 和高级 II 的区别、需要哪些前置、收录了哪八篇,以及两条阅读路径。本层只写数据端;渲染端原理不在当前范围。
|
||||
|
||||
## 高级 III 是给谁的
|
||||
|
||||
- 你能读懂类、接口、泛型、装饰器与 TypedArray,不需要逐行解释也能跟上类型签名;
|
||||
- 你需要判断一个改动该落在状态、系统还是加载层,而不是只把效果调出来;
|
||||
- 你要排查存档与回放不一致的问题,需要知道状态从哪里来、由谁装配、什么时候写入;
|
||||
- 你愿意先理解机制再动手,接受「顺序固定」与「不要重排装配」这类约束。
|
||||
|
||||
## 与高级 I、高级 II 的区别
|
||||
|
||||
高级 I 讲扩展机制怎么用:插件、存档、录像一致性与钩子各自怎么接。高级 II 讲具体系统怎么改到自己的需求:范围、图块、装备、寻路、属性与战斗上下文。高级 III 讲这些系统为什么这样设计、数据怎么流、失败会在哪里出现。
|
||||
|
||||
三层是难度标签,不是领域分类:同一主题可能在不同层以不同深度出现。读高级 III 时默认你已经会使用相关接口,因此这里不重复调用步骤,只讲结构、顺序与边界。
|
||||
|
||||
## 前置
|
||||
|
||||
建议先读完[高级 I 导览](../i/)与[高级 II 导览](../ii/)。其中四篇会反复被本层引用:
|
||||
|
||||
- [插件系统](../i/plugin-system.md):插件顺序与生命周期是装配页与插件内部页的直接前置;
|
||||
- [可存档内容](../i/saveable-content.md):存档 id、对象与执行器的约定贯穿装配页;
|
||||
- [录像一致性](../i/replay-consistency.md):录像页默认你知道哪些方法要登记;
|
||||
- [自动寻路](../ii/pathfinding.md):寻路页只讲原理与契约,用法与损失函数在那篇。
|
||||
|
||||
## 八篇地图
|
||||
|
||||
| 序 | 篇目 | 它能让你做什么 |
|
||||
| --- | -------------------------------------------------------- | ------------------------------------------------ |
|
||||
| 1 | [加载系统](./loading-system.md) | 看清三层加载模型、阶段顺序与三类扩展点 |
|
||||
| 2 | [录像系统底层](./replay-internals.md) | 理解录像的二进制形状、沙箱播放与存档字段 |
|
||||
| 3 | [事件系统](./event-system.md) | 分清仓库、收集、执行与解释器各自负责什么 |
|
||||
| 4 | [Anon-Tokyo 解释器](./anon-tokyo.md) | 看懂语句如何编译成程序、执行上下文如何推进 |
|
||||
| 5 | [通行性谓词与寻路原理](./passability-and-pathfinding.md) | 理解通行契约、两个谓词实例与构图搜索的分工 |
|
||||
| 6 | [CoreState 组装与初始化](./core-state.md) | 看清四层装配、加载管线与存档挂载 |
|
||||
| 7 | [插件内部机制](./plugin-internals.md) | 知道插件顺序怎么算、状态怎么推进、依赖写错会怎样 |
|
||||
|
||||
本页是第八篇,负责导览;其余七篇各自独立成文。
|
||||
|
||||
## 阅读路径
|
||||
|
||||
- **原理线**:CoreState 组装与初始化 → 插件内部机制 → 事件系统 → Anon-Tokyo 解释器 → 通行性谓词与寻路原理 → 录像系统底层 → 加载系统。适合想一次把数据端的结构与数据流打通。
|
||||
- **按需线**:先读 CoreState 组装与初始化,建立分层与加载管线的心智模型,再跳到与当前问题最相关的一篇。每篇都单独成立,不需要按顺序读。
|
||||
|
||||
## 本层不覆盖什么
|
||||
|
||||
- 渲染端的结构性内容:渲染场景、绘制流程与客户端主对象属于渲染端,本层只写数据端;
|
||||
- 编辑器与内容配置:编辑器属性项、地图事件 id 绑定与序列化事件注册由编辑器负责;
|
||||
- API 签名清单:本层只讲机制与取舍,不罗列接口参数;
|
||||
- legacy 包:旧引擎实现只作为对比基线出现,不作为扩展入口。
|
||||
|
||||
## 怎么验证你读懂了
|
||||
|
||||
- 能说出 CoreState 的四层装配顺序,并解释为什么不能重排;
|
||||
- 能说明插件依赖成环或缺失时,为什么下游也会被阻断;
|
||||
- 能解释事件为什么按「点事件整组在前、图块事件整组在后」的顺序执行;
|
||||
- 能说出通行性谓词为什么会有两个实例,以及怎样让两侧保持一致;
|
||||
- 能说明录像为什么能按步号读取,以及读取流为什么会过期;
|
||||
- 能画出加载的三段阶段,并说清额外配置与自定义任务各自该在什么时候追加。
|
||||
@ -48,7 +48,7 @@ MotaDataLoader 按阶段把任务交给 LoadManager
|
||||
|
||||
这个顺序决定了两条最常用的规则:`addExtraConfig` 必须在第 3 步开始前调用,`addCustomTask` 必须在第 5 步开始前调用。放在 `onCoreConfigLoaded` 或 `onExtraConfigLoaded` 里,或者在加载开始前调用,都来得及;等加载已经开始再追加,就不会再被本次加载读取。
|
||||
|
||||
`CoreState.load()` 在加载器外面又包了两层插件时机:插件的 `initialize()` 在 `loader.start()` 之前执行,`afterLoad()` 在加载完成之后执行。因此 `initialize` 适合追加配置或自定义任务,`afterLoad` 适合读取已经加载好的配置。插件三个时机的完整语义见[插件系统](../i/plugin-system.md#生命周期)。
|
||||
[`CoreState.load()`](./core-state.md#加载管线) 在加载器外面又包了两层插件时机:插件的 `initialize()` 在 `loader.start()` 之前执行,`afterLoad()` 在加载完成之后执行。因此 `initialize` 适合追加配置或自定义任务,`afterLoad` 适合读取已经加载好的配置。插件三个时机的完整语义见[插件系统](../i/plugin-system.md#生命周期)。
|
||||
|
||||
## 配置文件与处理器
|
||||
|
||||
@ -244,3 +244,5 @@ state.loader.start()
|
||||
- <strong>自定义任务要设置 starter</strong>:任务不会自动继承加载器的 starter,漏设时任务不会开始加载。
|
||||
- <strong>字节进度可能不准</strong>:所有任务的总大小要等响应头都到达后才能确定,总量未知时字节比例只是近似值。
|
||||
- <strong>start 必须被迭代</strong>:调用 `loader.start()` 只是拿到异步迭代器,真正推进加载的是迭代过程。`CoreState.load()` 已经替你完成迭代;手动驱动时要自己消费完。
|
||||
|
||||
返回[高级 III 导览](./index.md)。
|
||||
|
||||
@ -107,7 +107,7 @@ export function judgeStep(
|
||||
| 勇士移动 | `DefaultHeroMoveTopImpl` 构造时自己新建一个默认谓词 | 顶层实现的 `predicate()` 返回它 |
|
||||
| 寻路构图 | 数据端初始化时另建一个默认谓词 | `finder.usePassPredicate(...)` 注入 |
|
||||
|
||||
`DefaultHeroMoveTopImpl` 把谓词保存在字段里,`predicate()` 每次返回同一个实例。数据端初始化时又为寻路求解器新建了一个默认谓词,通过 `state.pathfinding.finder.usePassPredicate(...)` 注入。两者行为一致,因为它们由同一个类实现、读同一份地图数据;但它们没有共享状态,也没有自动同步。
|
||||
`DefaultHeroMoveTopImpl` 把谓词保存在字段里,`predicate()` 每次返回同一个实例。[数据端初始化时](./core-state.md)又为寻路求解器新建了一个默认谓词,通过 `state.pathfinding.finder.usePassPredicate(...)` 注入。两者行为一致,因为它们由同一个类实现、读同一份地图数据;但它们没有共享状态,也没有自动同步。
|
||||
|
||||
因此,改勇士侧的谓词不会改变寻路侧,改寻路侧的谓词也不会改变勇士侧。要让两边保持一致,必须两边都换:要么把同一个谓词实例同时交给顶层实现和求解器,要么在两侧分别构造同一个自定义类。
|
||||
|
||||
@ -247,3 +247,5 @@ state.pathfinding.finder.usePassPredicate(
|
||||
- 目标在脚下不移动:起点与终点相同时直接返回 `TargetUnder`。
|
||||
- 换楼层要重新绑定图层:绑定的是具体图层对象,换楼层后要换成新楼层的事件层。
|
||||
- 瞬移会跳过途经格:瞬移分支只落到终点,途经格的进入、离开与撞击都不会发生;需要补触发时用钩子,或用回退策略退回逐步移动。
|
||||
|
||||
返回[高级 III 导览](./index.md)。
|
||||
|
||||
@ -119,3 +119,5 @@ export function useMyPlugin(state: ICoreState): void {
|
||||
- **数据端依赖与渲染端依赖是两套**:`dataDeps` 只写数据端插件 id,渲染端另有 `clientDeps`;两者不要混用。
|
||||
- **取回结果要判空并检查状态**:`getDataPlugin` 可能返回 `null`,也可能拿到尚未到达目标状态的插件。
|
||||
- **`orderPlugins` 不供读者调用**:它是构造期的内部机制;要控制顺序,声明依赖即可。
|
||||
|
||||
返回[高级 III 导览](./index.md)。
|
||||
|
||||
@ -151,6 +151,8 @@ export class ReplayChoiceConsumer {
|
||||
|
||||
录像的指令码与参数类型码遵循只增不改:新语义必须分配新值,不能复用旧码。格式只增不改,也不承诺旧文本格式能被新版本读回。
|
||||
|
||||
录像作为可存档对象由主插件注册,装配与挂载顺序见[CoreState 组装与初始化](./core-state.md#存档挂载)。
|
||||
|
||||
## 内部实现:三块缓冲区
|
||||
|
||||
此章节讲解一下录像二进制格式的内部实现。一条录像步在存储里由三块缓冲区配合表示:
|
||||
@ -217,3 +219,5 @@ export class ReplayChoiceConsumer {
|
||||
- <strong>缩窄指令宽度会截断</strong>:从 16 位缩到 8 位时,超过 255 的指令码会被截断并告警。已有指令码应保持在 255 以内,或者继续使用 16 位宽度。
|
||||
- <strong>存档返回的是内部引用</strong>:不要就地修改 `commandArray` 与 `paramArray`;长期保存要先复制。读档会重建索引并让旧读取流失效,正在播放的沙箱要重建。
|
||||
- <strong>旧格式不保证可读回</strong>:旧引擎的文本录像不是当前二进制格式;格式只增不改,也不承诺跨版本读回。
|
||||
|
||||
返回[高级 III 导览](./index.md)。
|
||||
|
||||
Loading…
Reference in New Issue
Block a user