sapdon 3.2.2 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +255 -121
  2. package/doc/dev/architecture.md +418 -0
  3. package/doc/dev/cli.md +467 -0
  4. package/doc/dev/core.md +751 -0
  5. package/doc/dev/lr-paradigm.md +85 -0
  6. package/doc/dev/oc.md +582 -0
  7. package/doc/dev/workflow.md +257 -0
  8. package/doc/hello_sapdon/hello_sapdon.md +3 -3
  9. package/doc/user/api/biome.md +558 -0
  10. package/doc/user/api/block.md +1530 -0
  11. package/doc/user/api/entity.md +685 -0
  12. package/doc/user/api/extra.md +231 -0
  13. package/doc/user/api/item.md +1125 -0
  14. package/doc/user/api/neo-guidebook.md +409 -0
  15. package/doc/user/api/recipe.md +427 -0
  16. package/doc/user/api/sapdon-ui.md +185 -0
  17. package/doc/user/api/texture.md +181 -0
  18. package/doc/user/config/build-config.md +83 -0
  19. package/doc/user/config/mod-info.md +33 -0
  20. package/doc/user/faq.md +160 -0
  21. package/doc/user/quick-start.md +122 -0
  22. package/doc/user/tutorials/block.md +474 -0
  23. package/doc/user/tutorials/entity.md +356 -0
  24. package/doc/user/tutorials/item.md +449 -0
  25. package/doc/user/tutorials/neo-guidebook-experience.md +381 -0
  26. package/doc/user/tutorials/neo-guidebook.md +640 -0
  27. package/doc/user/tutorials/recipe.md +278 -0
  28. package/doc/user/tutorials/sapdon-ui.md +207 -0
  29. package/package.json +8 -3
  30. package/prod/cli/index.js +1 -1
  31. package/prod/cli/start.js +1 -1458
  32. package/prod/core/index.d.ts +4564 -2166
  33. package/prod/core/index.js +1 -59015
  34. package/prod/core/package.json +7 -0
  35. package/prod/oc/index.d.ts +350 -108
  36. package/prod/oc/index.js +1 -1
  37. package/prod/oc/package.json +7 -0
  38. package/prod/utils/index.d.ts +20 -9
  39. package/prod/utils/index.js +1 -1
  40. package/prod/utils/package.json +7 -0
  41. package/doc/BlockAPI.md +0 -145
  42. package/doc/api.md +0 -128
  43. package/doc/oc/index.md +0 -0
  44. package/doc/sapdon-ts.md +0 -64
  45. package/src/templates/js_sapdon/build.config +0 -23
  46. package/src/templates/js_sapdon/main.mjs +0 -4
  47. package/src/templates/js_sapdon/mod.info +0 -7
  48. package/src/templates/js_sapdon/pack_icon.png +0 -0
  49. package/src/templates/js_sapdon/package.json +0 -20
  50. package/src/templates/js_sapdon/res/animations/animation_item.animation.json +0 -34
  51. package/src/templates/js_sapdon/res/animations/large_item.animation.json +0 -27
  52. package/src/templates/js_sapdon/res/models/blocks/crop.geo.json +0 -48
  53. package/src/templates/js_sapdon/res/models/entity/animation/animation_item.geo.json +0 -26
  54. package/src/templates/js_sapdon/res/models/entity/animation/large_item.geo.json +0 -28
  55. package/src/templates/js_sapdon/res/textures/blocks/none.png +0 -0
  56. package/src/templates/js_sapdon/res/textures/blocks/test_log_oak.png +0 -0
  57. package/src/templates/js_sapdon/res/textures/blocks/test_log_top.png +0 -0
  58. package/src/templates/js_sapdon/res/textures/items/masterball.png +0 -0
  59. package/src/templates/js_sapdon/scripts/custom_components/cropComponent.js +0 -50
  60. package/src/templates/js_sapdon/scripts/custom_components/items/gui_book.js +0 -37
  61. package/src/templates/js_sapdon/scripts/custom_components/registry.js +0 -25
  62. package/src/templates/js_sapdon/scripts/index.js +0 -0
  63. package/src/templates/ts_sapdon/build.config +0 -23
  64. package/src/templates/ts_sapdon/main.ts +0 -11
  65. package/src/templates/ts_sapdon/mod.info +0 -7
  66. package/src/templates/ts_sapdon/pack_icon.png +0 -0
  67. package/src/templates/ts_sapdon/package.json +0 -20
  68. package/src/templates/ts_sapdon/res/models/blocks/crop.geo.json +0 -48
  69. package/src/templates/ts_sapdon/res/textures/blocks/test_log_oak.png +0 -0
  70. package/src/templates/ts_sapdon/res/textures/blocks/test_log_top.png +0 -0
  71. package/src/templates/ts_sapdon/res/textures/items/masterball.png +0 -0
  72. package/src/templates/ts_sapdon/scripts/components/cropComponent.ts +0 -44
  73. package/src/templates/ts_sapdon/scripts/components/items/guiBook.ts +0 -36
  74. package/src/templates/ts_sapdon/scripts/components/registry.ts +0 -24
  75. package/src/templates/ts_sapdon/scripts/index.ts +0 -7
  76. package/src/templates/ts_sapdon/tsconfig.json +0 -117
@@ -0,0 +1,85 @@
1
+ # L-R 编程范式(sapdon 存量类 Addon 通用骨架)
2
+
3
+ > 服务对象:`examples/digitCircuit`(数电)、`examples/fluid_pipe`(流体)、`examples/power_grid`(电力)——一个可复用的「装备类/生态类」Addon 编程范式。
4
+
5
+ 这三类系统在玩法与数据形态上完全不同,但骨架高度一致:**都走一条"逻辑求解 → 渲染同步"的 L/R 分离流水线**,且都由 **连接(C₁)、源(S)、功能(F)、消费(Cc)、储量(R)** 五元组构成一个可更新的"资源网络"。
6
+
7
+ ---
8
+
9
+ ## 1. 五元组映射
10
+
11
+ | 元组 | digitCircuit 数电 | fluid_pipe 流体 | power_grid 电力 |
12
+ |---|---|---|---|
13
+ | **连接** C₁ | 导线 Net(逐面 `wire_connect:*` 手臂) | 段 Segment(逐面 `pipe_connect:*`) | 段(正交自动连通) |
14
+ | **源** S | on/off/switch 信号源 | 泵输出(+△) | 燃煤发电机 / 太阳能 |
15
+ | **功能** F | 门 / 分线器 / 合并器 | 阀门 / 三通 | 继电器(可控桥) |
16
+ | **消费** Cc | 显示灯(仅显示) | 罐纯吸收 / 空气汇 | 电力熔炉(真实熔炼) |
17
+ | **储量** R | 寄存器 store / 芯片 | 罐(32 格液位) | 电池(0..MAX 电量) |
18
+
19
+ > 关键洞察:F(功能块)本质是**受控的连接/变换器**——数电的门变换信号、流体的阀透传/断流、电力的继电器合并/分隔电网。绝大多数新生态系统只需把 F 当"可通断的耦合节点"即可起步。
20
+
21
+ ## 2. 分层约束(铁的边界)
22
+
23
+ ```
24
+ ┌───────────────────────────────────────────────────────────┐
25
+ │ L 层 scripts/core/(纯逻辑,禁止 import @minecraft/server)│
26
+ │ graph.ts 建立"连接块图":洪水填充成段/网,收设备端点 │
27
+ │ resource.ts/ settle.ts 资源在场上的传播/归并 + 逐 tick 结算│
28
+ │ → 把结果写进 seg.xxx(powered / front / covered / value) │
29
+ ├───────────────────────────────────────────────────────────┤
30
+ │ R 层 scripts/engine/(MC 引擎,读世界方块) │
31
+ │ const/world/state/log/diag │
32
+ │ graph.ts 实现 L 层 FloodGraph 接口(世界实现) │
33
+ │ rebuild.ts 放置/破坏/开关 → 重建段 + 加载后渐进重建 │
34
+ │ render.ts 只读 seg.xxx 同步写方块状态(发光/液位/带电) │
35
+ │ persist.ts 只存小状态(设备表/连接位置),图=重建不落盘 │
36
+ │ tick.ts 主循环:L 结算 → R 渲染 → 设备存活检查 │
37
+ │ index.ts 注册:命令 / 自定义组件 / 事件 / 主循环 │
38
+ └───────────────────────────────────────────────────────────┘
39
+ ```
40
+
41
+ 铁律:
42
+ 1. **L 层零 MC 依赖** → 可在 Node 直接镜像测试。
43
+ 2. **L 写 `seg.*`,R 只读** → 状态单一来源,渲染层可缓存去重。
44
+ 3. **连接块手臂/朝向 = 方块状态**(世界自动持久化),**图与传播场不落盘**,靠事件 + 加载后 `rebuildPending` 逐批(每 tick 64 个)渐进洪水重建。
45
+ 4. **持久化只存小状态**:设备表(储量 level、源燃料、消费进度)+ 连接位置;液位/流动态只进内存 + 方块状态视觉兜底。
46
+ 5. **写动态属性不可吞异常**(吞掉 = 重进世界静默丢存档);用 `loaded` 门闩防启动早期空表覆盖存档。
47
+
48
+ ## 3. 资源网络结算模板
49
+
50
+ ```
51
+ 每 tick(如 20t≈1s):
52
+ 1. 连接图(L) : 沿连接块洪水 → 段/网
53
+ 2. 场/结算(L) : 若含共享设备的耦合(F/S/R 把邻段并网,F 用受控 open/closed 决定是否并)
54
+ → 网格级(或势场级)结算:源供给侧 vs 消费需求侧 + 储量缓冲
55
+ → 写 seg.powered / seg.covered 等
56
+ 3. 渲染(R) : 读 seg.* → 写方块状态(电线发光、设备液位/燃烧/带电)
57
+ 4. 存量(R) : 充电/放电/喂料扣除 → save(仅结构事件)
58
+ ```
59
+
60
+ 三种结算粒度的取舍:
61
+ - **网格级全局**(power_grid):供/需对账 + 全有/全无。最简、稳压直觉,丢物理空间感。
62
+ - **势/距离场**(fluid_pipe):源势沿图传播、衰减,决定"能不能到、到多高"。物理感强但复杂度高(需构思成本函数)。
63
+ - **布尔/位宽**(digitCircuit):信号值 + 位宽固定点,天然适合布尔逻辑合成。
64
+
65
+ ## 4. 镜像测试约束(母子同步)
66
+
67
+ L 层用 TS 写、Node 不能直接跑,因此采用**镜像副本**:`test/<name>.test.mjs` 复制 L 层纯逻辑(JS 版)。**改 L 层逻辑必须同步测试副本并跑绿**。这同时约束了 L 层必须保持"纯函数、可独立 import、无副作用",否则镜像就同步不动。
68
+
69
+ ## 5. 可复用工具箱(从三项目沉淀)
70
+
71
+ - `world.ts`:`blockKey / keyParts / getBlockByKey / getAdjacent` 一致性 key(`dim:x,y,z`)。
72
+ - `rebuildAround / rebuildPending / rebuildStale`:结构重建三件套(事件重建 + 加载渐进重建 + 失效重建)。
73
+ - `persist.ts`:动态属性分块读写通用骨架(单块/多块 `_chunks`)。
74
+ - 旋转设备:局部参考系语义面 + `ROT_FACE(facing, local)` 映射(见 fluid_pipe AGENTS),杜绝写死世界面。
75
+ - 诊断:`console.warn` ContentLog + 运行日志开关 + dump 就近转储。
76
+
77
+ ## 6. 已知坑(三项目通用)
78
+
79
+ - 方块状态 **≤16 有效值**(整数范围 max-min ≤15)。
80
+ - 单元用普通立方体或命名材质 geo;同一方块所有 material instance 必须同一 `render_method`。
81
+ - Molang 变量名统一小写;粒子 `basic_*` 需传 `variable.direction`。
82
+ - 调试证据优先 ContentLog(`console.warn`),`world.sendMessage` 不进日志。
83
+
84
+ ---
85
+ > 新生态示例(如"供水系统""机械传动""雨洪管网")建议直接套本范式:先定 L 层 graph+settle 并写镜像测试(绿后进 R),再补 R 层引擎与贴图。
package/doc/dev/oc.md ADDED
@@ -0,0 +1,582 @@
1
+ # OC 运行时模块文档
2
+
3
+ `src/oc/` 是 Sapdon 的 **OC (Object-Component) 运行时**,是一个轻量级 ECS 风格游戏框架,专门用于 Minecraft Bedrock 版 Script API 环境。作为 `@sapdon/runtime` 发布。
4
+
5
+ ---
6
+
7
+ ## 1. 架构总览
8
+
9
+ OC 模块实现了一个简化的 **Entity-Component 架构**:
10
+
11
+ ```
12
+ ┌──────────────────────────────────────────────────┐
13
+ │ Minecraft 世界 │
14
+ │ │
15
+ │ ┌──────────────┐ ┌──────────────┐ │
16
+ │ │ Entity A │ │ Entity B │ │
17
+ │ │ │ │ │ │
18
+ │ │ ComponentManager │ │ ComponentManager │ │
19
+ │ │ ├─ HealthComp │ │ ├─ MoveComp │ │
20
+ │ │ ├─ InputComp │ │ ├─ RenderComp │ │
21
+ │ │ └─ HudComp │ │ └─ HealthComp │ │
22
+ │ └──────────────┘ └──────────────┘ │
23
+ │ │
24
+ │ ┌────────────────────────────────────────────────┐ │
25
+ │ │ Level (实体集合) │ │
26
+ │ │ └─ Scheduler (驱动 tick 循环) │ │
27
+ │ └────────────────────────────────────────────────┘ │
28
+ └──────────────────────────────────────────────────┘
29
+ ```
30
+
31
+ **核心概念:**
32
+
33
+ | 概念 | 说明 |
34
+ |------|------|
35
+ | **Component** | 附着在实体上的数据+行为单元,每个 Component 有自己的 `onTick()` |
36
+ | **ComponentManager** | 每个实体一个,管理其所有 Component 的生命周期 |
37
+ | **Level** | 实体集合,以 string ID 索引,每个 ID 对应一个 ComponentManager |
38
+ | **Scheduler** | 驱动游戏循环,每 tick 遍历所有实体的所有 Component 调用 `onTick()` |
39
+ | **Optional\<T\>** | Monadic 空值包装器 |
40
+ | **EventEmitter** | 自定义发布/订阅事件系统 |
41
+
42
+ ### 目录结构
43
+
44
+ ```
45
+ src/oc/
46
+ ├── index.ts # 包入口,聚合导出
47
+ ├── core.ts # 核心 ECS:Component, ComponentManager, lazyGet, RequireComponents
48
+ ├── arch.ts # 引导启动:GameInstance, initialize/finalize
49
+ ├── optional.ts # Optional Monad
50
+ ├── level.ts # Level + Scheduler 接口
51
+ ├── events/
52
+ │ └── index.ts # EventEmitter (自定义事件系统)
53
+ ├── input/
54
+ │ └── base.ts # PlayerInputComponent (按键/轴输入)
55
+ ├── math/
56
+ │ ├── index.ts # 聚合导出
57
+ │ ├── array.ts # ArrayEx (数组基类)
58
+ │ ├── spline.ts # SplineInterpolator (样条插值)
59
+ │ ├── vec/vec3.ts # Vec3 (3D 向量)
60
+ │ ├── vec/vec4.ts # Vec4 (4D 向量)
61
+ │ └── mat/mat4.ts # Matrix (4x4 矩阵)
62
+ ├── action/
63
+ │ ├── interface.ts # ActionValueType, IAction, IActionTrigger, IActionModifier
64
+ │ ├── triggers.ts # ActionTriggers (pressed/released/hold 等)
65
+ │ └── modifiers.ts # ActionModifiers (negate/scale)
66
+ ├── ui/hud.ts # HudComponent (基类)
67
+ └── minecraft/ # Minecraft 集成层
68
+ ├── index.ts # 聚合导出
69
+ ├── core.ts # MinecraftTickingScheduler, MinecraftLevel, MinecraftGameInstance
70
+ ├── decorator.ts # @Minecraft, @MinecraftMain, @PlayerSpawned, @EntitySpawned, @SpawnFilter
71
+ ├── utils.ts # MinecraftMethod 装饰器, utils 单例
72
+ ├── scriptEvent.ts # @ScriptEvent 装饰器 + 事件桥接
73
+ ├── input/
74
+ │ └── inputComponent.ts # MinecraftPlayerInputComponent
75
+ ├── component/
76
+ │ ├── kinematics.ts # EasyKinematicsComponent (力驱动运动)
77
+ │ └── vehicleComponent.ts # EasyVehicleComponent (载具物理)
78
+ └── ui/
79
+ └── actionbar.ts # PlayerHudComponent (actionbar HUD)
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 2. Component 系统 (`core.ts`)
85
+
86
+ ### 2.1 接口层级
87
+
88
+ ```
89
+ Component<Actor> (接口)
90
+ ├── onTick(dt: number): void
91
+ ├── detach(): void
92
+ ├── getManager(): ComponentManager<Actor>
93
+ └── getEntity(): Actor
94
+
95
+ BasicComponent<Actor> (接口,extends Component)
96
+ ├── onAttach(manager: ComponentManager<Actor>): void
97
+ └── onDetach(manager: ComponentManager<Actor>): void
98
+
99
+ RequiredComponent<Actor> (接口,extends BasicComponent)
100
+ └── getComponent<T>(ctor): T
101
+ ```
102
+
103
+ ### 2.2 实现层级
104
+
105
+ ```
106
+ CustomComponent<Actor> (抽象类,实现 Component)
107
+ └── BaseComponent<Actor> (类,实现 BasicComponent)
108
+ ```
109
+
110
+ **CustomComponent** 使用 `Symbol` 存储 manager 和 entity 引用:
111
+
112
+ ```typescript
113
+ const REFLECT_MANAGER = Symbol('manager')
114
+ const REFLECT_ENTITY = Symbol('entity')
115
+
116
+ class CustomComponent<Actor> {
117
+ onTick(dt: number) {} // 默认空实现
118
+ detach() { this.getManager()?.update() }
119
+ getManager() { return this[REFLECT_MANAGER] }
120
+ getEntity() { return this[REFLECT_ENTITY] }
121
+ }
122
+ ```
123
+
124
+ **BaseComponent** 添加空的 `onAttach` / `onDetach` 钩子。
125
+
126
+ ### 2.3 Component 生命周期
127
+
128
+ ```
129
+ 构造 (new Component())
130
+
131
+
132
+ onAttach(manager) ← 被 ComponentManager.attachComponent() 调用
133
+
134
+
135
+ onTick(dt) ← 每帧由 ComponentManager.handleTicks() 调用
136
+ │ (可被多次调用)
137
+
138
+ detach() / onDetach(manager) ← 被 ComponentManager.detachComponent() 调用
139
+ ```
140
+
141
+ ### 2.4 ComponentManager
142
+
143
+ 每个实体拥有一个 `ComponentManager`,管理 `Map<ComponentCtor, Component>`:
144
+
145
+ | 方法 | 说明 |
146
+ |------|------|
147
+ | `attachComponent(component)` | 附加组件,调用 `onAttach`,处理 `RequiredComponent` 依赖 |
148
+ | `detachComponent(ctor)` | 分离组件,调用 `onDetach` |
149
+ | `getComponent<T>(ctor)` | 获取组件,不存在则报错 |
150
+ | `getComponentUnsafe<T>(ctor)` | 安全获取,返回 `Optional<T>` |
151
+ | `getOrCreate<T>(ctor, ...args)` | 获取或创建 |
152
+ | `has(ctor)` | 检查组件是否存在 |
153
+ | `clear()` | 清除所有组件 |
154
+ | `handleTicks(en, dt)` | 遍历所有组件调用 `onTick`,支持 before/after 队列 |
155
+ | `update()` | 触发组件重附加(用于依赖变更后刷新) |
156
+
157
+ **全局单例**:`ComponentManager.global` 用于存放全局(非实体相关)组件。
158
+
159
+ **性能分析**:`ComponentManager.profilerEnable = true` 可启用 tick 耗时日志。
160
+
161
+ ### 2.5 RequiredComponent (依赖注入)
162
+
163
+ `RequireComponents(...params)` 是一个 mixin 工厂,创建一个带依赖的 Component 类:
164
+
165
+ ```typescript
166
+ // 用法示例
167
+ const MyComponent = RequireComponents(
168
+ HealthComponent,
169
+ [MovementComponent, 1.0], // 带构造参数的依赖
170
+ TransformComponent
171
+ )
172
+
173
+ class MyComponent extends BaseComponent<Entity> {
174
+ // 通过 this.getComponent(HealthComponent) 访问依赖
175
+ }
176
+ ```
177
+
178
+ 每次 `attachComponent` 时,如果组件有 `REQUIRED_COMPONENTS` 符号属性,这些依赖会被自动构造并附加。
179
+
180
+ ### 2.6 lazyGet
181
+
182
+ `lazyGet(component, ctor)` 返回一个 **Proxy**,在首次访问属性时才通过 manager 查找目标组件。用于组件间交叉引用,避免循环依赖问题。
183
+
184
+ ---
185
+
186
+ ## 3. Level 与 Scheduler (`level.ts`)
187
+
188
+ ### 3.1 Level
189
+
190
+ ```typescript
191
+ abstract class Level<T> {
192
+ protected table: Map<string, ComponentManager<T>>
193
+
194
+ addEntity(id: string): ComponentManager<T>
195
+ removeEntity(id: string): void
196
+ getManager(id: string): ComponentManager<T>
197
+
198
+ abstract getScheduler(): Scheduler<T>
199
+
200
+ start() // → scheduler.start(table)
201
+ stop() // → scheduler.stop()
202
+ }
203
+ ```
204
+
205
+ - 实体用 string ID 标识
206
+ - `getManager(id)` 在不存在时自动创建新 manager
207
+ - `start()` / `stop()` 委托给 scheduler
208
+
209
+ ### 3.2 Scheduler
210
+
211
+ ```typescript
212
+ interface Scheduler<Actor> {
213
+ currentTick: number
214
+ timeDilation: number
215
+
216
+ start(table: Map<string, ComponentManager<Actor>>): void
217
+ stop(): void
218
+ }
219
+ ```
220
+
221
+ `timeDilation` 属性可加速/减速游戏时间(默认 1.0)。
222
+
223
+ ---
224
+
225
+ ## 4. Minecraft 集成
226
+
227
+ ### 4.1 整体启动流程
228
+
229
+ ```
230
+ Minecraft 启动
231
+
232
+
233
+ @MinecraftMain 装饰器
234
+ → GameInstance(target) 注册游戏类
235
+ → utils.start() 挂载生命周期钩子
236
+
237
+
238
+ system.beforeEvents.startup 触发
239
+
240
+
241
+ initialize(GameInstanceClass)
242
+ → new GameInstanceClass()
243
+ → instance.onStart()
244
+
245
+
246
+ MinecraftGameInstance.onStart()
247
+ → 创建 MinecraftLevel
248
+ → 创建 MinecraftTickingScheduler
249
+ → level.start() → scheduler.start(table)
250
+ → 安排 afterStart() 下一 tick 执行
251
+
252
+
253
+ 每游戏 tick:
254
+ scheduler.executeTick(table)
255
+ → 遍历每个实体的 ComponentManager
256
+ → manager.handleTicks(entity, dt)
257
+ → 每个 Component.onTick(dt)
258
+ ```
259
+
260
+ ### 4.2 MinecraftTickingScheduler
261
+
262
+ ```typescript
263
+ @Minecraft
264
+ class MinecraftTickingScheduler implements Scheduler<string> {
265
+ currentTick = 0
266
+ timeDilation = 1
267
+
268
+ start(table: Map<string, ComponentManager<Entity>>) {
269
+ system.runInterval(() => {
270
+ this.executeTick(table)
271
+ })
272
+ }
273
+
274
+ executeTick(table) {
275
+ this.currentTick++
276
+ const dt = this._timeStamp * this.timeDilation
277
+ for (const [id, manager] of table) {
278
+ // 使用 world.getEntity(id) 获取 Minecraft Entity 对象
279
+ manager.handleTicks(entity, dt)
280
+ }
281
+ }
282
+ }
283
+ ```
284
+
285
+ ### 4.3 MinecraftGameInstance
286
+
287
+ ```typescript
288
+ abstract class MinecraftGameInstance implements GameInstance {
289
+ onStart() {
290
+ const level = new MinecraftLevel()
291
+ this.setLevel(level)
292
+ level.start()
293
+ system.run(() => this.afterStart?.()) // 下一 tick 执行 afterStart
294
+ }
295
+ }
296
+ ```
297
+
298
+ 用户自定义游戏类 extends `MinecraftGameInstance`,实现 `onStart()` 和可选的 `afterStart()`。
299
+
300
+ ### 4.4 装饰器
301
+
302
+ #### 类装饰器
303
+
304
+ | 装饰器 | 说明 |
305
+ |--------|------|
306
+ | `@Minecraft` | 代理构造,确保在 Minecraft 环境中运行 |
307
+ | `@MinecraftMain` | 标记为主游戏类,注册 GameInstance + 挂载生命周期 + 设置生成监听 |
308
+ | `@PlayerSpawned(...deps)` | 标记在玩家生成时自动附加的 Component |
309
+ | `@EntitySpawned(...deps)` | 标记在实体生成时自动附加的 Component |
310
+ | `@ActorSpawned(...deps)` | 同时监听玩家和实体生成 |
311
+ | `@SpawnFilter(filter)` | 条件过滤:`(entity) => boolean`,返回 false 则不附加 |
312
+
313
+ #### 方法装饰器
314
+
315
+ | 装饰器 | 说明 |
316
+ |--------|------|
317
+ | `@MinecraftMethod` | 包装方法,执行前断言 Minecraft 环境 |
318
+ | `@ScriptEvent(eventId)` | 绑定方法到 Minecraft script event |
319
+
320
+ ### 4.5 生成时自动附加
321
+
322
+ `@MinecraftMain` 内部逻辑:
323
+
324
+ ```
325
+ world.afterEvents.playerSpawn
326
+ → 查找所有 @PlayerSpawned 装饰的类
327
+ → 构造实例 + attachComponent(playerManager)
328
+ → @SpawnFilter 可过滤
329
+
330
+ world.afterEvents.entitySpawn (排除 player)
331
+ → 查找所有 @EntitySpawned 装饰的类
332
+ → 构造实例 + attachComponent(entityManager)
333
+ ```
334
+
335
+ ### 4.6 ScriptEvent 桥接
336
+
337
+ ```typescript
338
+ // 方法装饰器用法
339
+ class MySystem {
340
+ @ScriptEvent('myAddon:event')
341
+ handleEvent(ev: ScriptEventCommandMessageAfterEvent) {
342
+ console.log('event received', ev)
343
+ }
344
+ }
345
+
346
+ // 编程式用法
347
+ ScriptEvent.on('myAddon:event', handler)
348
+ ScriptEvent.off('myAddon:event', handler)
349
+ ```
350
+
351
+ 内部通过 `EventEmitter` + `system.afterEvents.scriptEventReceive` 实现。
352
+
353
+ ---
354
+
355
+ ## 5. 输入系统 (`input/`)
356
+
357
+ ### 5.1 PlayerInputComponent
358
+
359
+ ```typescript
360
+ class PlayerInputComponent<Actor> extends BaseComponent<Actor> {
361
+ // 按键状态管理
362
+ inputKey(key: string, pressing: boolean): void
363
+ getKeyState(key: string): IKeyState
364
+ getKeyPressing(key: string): boolean
365
+ getKeyPressTimes(key: string): number
366
+ exhaust(key: string): void // 重置按键次数
367
+
368
+ // 轴输入
369
+ inputAxis(key: string, value: AxisValue): void
370
+ getAxis(key: string): AxisValue
371
+
372
+ // 轴工具
373
+ combineAxis(axis1, axis2): AxisValue
374
+ splitAxis(axis, nDim1, nDim2?): [AxisValue, AxisValue]
375
+ }
376
+ ```
377
+
378
+ **IKeyState**:`{ pressing: boolean, times: number, consume(): void }`
379
+
380
+ **AxisValue**:`number | [number, number] | [number, number, number]`
381
+
382
+ ### 5.2 MinecraftPlayerInputComponent
383
+
384
+ extends `PlayerInputComponent<Entity>`。集成 Minecraft 玩家的按钮输入和摇杆轴:
385
+
386
+ - 通过 `world.afterEvents.playerButtonInput` 监听按钮事件
387
+ - 每 tick 通过 `player.inputInfo.getMovementVector()` 获取移动轴
388
+
389
+ ---
390
+
391
+ ## 6. 动作系统 (`action/`)
392
+
393
+ | 导出 | 说明 |
394
+ |------|------|
395
+ | `ActionValueType` | 枚举:`BOOL(0)`, `VEC(1)`, `VEC2(2)`, `VEC3(3)` |
396
+ | `IActionModifier<E, V>` | 变换函数:`(type: E, value: V) => V` |
397
+ | `IActionTrigger` | 触发判定:`(type, value, ticks) => boolean` |
398
+ | `IAction<E>` | 组合:`valueType + modifiers[] + triggers[]` |
399
+ | `ActionTriggers` | 工厂:`pressed()`, `released()`, `hold(act, hold)`, `holdThenRelease(act)` |
400
+ | `ActionModifiers` | 工厂:`negate(mask)`, `scale(scalar)` |
401
+
402
+ ---
403
+
404
+ ## 7. 事件系统 (`events/`)
405
+
406
+ ### EventEmitter
407
+
408
+ 自定义实现,使用**双向链表**实现 O(1) 的添加/移除:
409
+
410
+ | 方法 | 说明 |
411
+ |------|------|
412
+ | `on(type, handler)` | 订阅事件 |
413
+ | `once(type, handler)` | 一次性订阅 |
414
+ | `prependListener(type, handler)` | 插到监听器列表头部 |
415
+ | `prependOnceListener(type, handler)` | 一次性插到头部 |
416
+ | `off(type, handler)` / `removeListener(type, handler)` | 取消订阅 |
417
+ | `removeAllListeners(type)` | 移除所有 |
418
+ | `emit(type, ...args)` | 触发事件(使用 bind thisArg) |
419
+ | `emitNone(type, ...args)` | 触发事件(不使用 thisArg) |
420
+ | `listenerCount(type)` | 监听器数量 |
421
+ | `listeners(type)` / `rawListeners(type)` | 获取监听器列表 |
422
+ | `eventNames()` | 获取所有事件名 |
423
+ | `setMaxListeners(n)` | 设置最大监听器数 |
424
+
425
+ 特点:
426
+ - 双向链表实现,O(1) add/remove
427
+ - 支持 `captureRejections`(捕获 Promise 返回的 handler 的 rejection)
428
+ - emit 过程中的错误会被捕获并重定向到 `'error'` 事件
429
+
430
+ ---
431
+
432
+ ## 8. 数学库 (`math/`)
433
+
434
+ ### Vec3 / Vec4 / Matrix
435
+
436
+ 向量和矩阵继承自 `ArrayEx`(extends `Array`):
437
+
438
+ **Vec3**:`fromXYZ()`、`normalize()`、`dot()`、`cross()`、`add()`、`sub()`、`mul()`、`div()`
439
+
440
+ **Vec4**:同上 + `multiply(Matrix)`(矩阵乘法)
441
+
442
+ **Matrix**(4x4):
443
+ - 静态:`identity()`、`translate()`、`perspective()`、`orthographic()`、`lookAt()`
444
+ - 实例:`setIdentity()`、`setTranslation()`、`setRotation()`、`setRotationX/Y/Z()`、`setScale()`、`transpose()`、`multiply()`
445
+
446
+ ### SplineInterpolator
447
+
448
+ 五点平滑样条插值(quincunx 算法):
449
+
450
+ ```typescript
451
+ const spline = SplineInterpolator.fromPoints(points, lambda)
452
+ spline.interpolate(x) // 插值
453
+ spline.max(step) // 找最大值
454
+ spline.min(step) // 找最小值
455
+ spline.curve(nInterval) // 获取曲线点集
456
+ ```
457
+
458
+ ---
459
+
460
+ ## 9. HUD 系统 (`ui/`)
461
+
462
+ ### HudComponent
463
+
464
+ ```typescript
465
+ abstract class HudComponent<Actor, Hud> extends CustomComponent<Actor> {
466
+ hud: Hud
467
+ actor: Optional<Actor>
468
+
469
+ abstract render(): Hud
470
+ abstract draw(): void
471
+
472
+ onTick(dt: number) {
473
+ this.hud = this.render()
474
+ this.draw()
475
+ }
476
+ }
477
+ ```
478
+
479
+ 每 tick 调用 `render()` 生成 HUD 数据,然后调用 `draw()` 渲染。
480
+
481
+ ### PlayerHudComponent
482
+
483
+ extends `HudComponent<Player, string>`。使用 `/title @s actionbar` 命令将字符串渲染到玩家动作栏。
484
+
485
+ ---
486
+
487
+ ## 10. 物理组件
488
+
489
+ ### EasyKinematicsComponent
490
+
491
+ ```typescript
492
+ class EasyKinematicsComponent extends CustomComponent<Entity> {
493
+ mass = 1
494
+ allowRotation = false
495
+ simulated = false // false=applyImpulse, true=tryTeleport
496
+ force: Vec3
497
+ angularVelocity: Vec3
498
+ }
499
+ ```
500
+
501
+ 每 tick 应用力和角速度。`simulated = false` 时使用 `applyImpulse`,`true` 时使用 `tryTeleport` + 牛顿运动学公式。
502
+
503
+ ### EasyVehicleComponent
504
+
505
+ 载具物理数据:
506
+
507
+ ```typescript
508
+ class EasyVehicleComponent extends CustomComponent<Entity> {
509
+ width = 2, height = 1.5, length = 4.5
510
+ mass = 1500
511
+ inertia: Vec3 // 由 _calcInertia() 自动计算(矩形棱柱公式)
512
+ }
513
+ ```
514
+
515
+ ---
516
+
517
+ ## 11. Optional Monad (`optional.ts`)
518
+
519
+ ```typescript
520
+ class Optional<T> {
521
+ static none<T>(): Optional<T> // 空值
522
+ static some<T>(value?: T): Optional<T> // 包装值
523
+
524
+ unwrap(): T // 取值,空则抛错
525
+ isEmpty(): boolean // 是否为 null/undefined
526
+ orElse(other: T): T // 空则返回默认值
527
+ use<R>(fn, self?): R | Optional<R> // monadic bind (flatMap)
528
+ }
529
+ ```
530
+
531
+ `use()` 是 flatMap 操作:非空时调用 `fn(value)`,如果返回值是 Optional 则直接返回,否则用 `Optional.some()` 包装;空时返回 `Optional.none()`。
532
+
533
+ ---
534
+
535
+ ## 12. 类继承关系总图
536
+
537
+ ```
538
+ Array (原生)
539
+ └── ArrayEx
540
+ ├── Vec3
541
+ ├── Vec4
542
+ └── Matrix
543
+
544
+ CustomComponent<Actor> (抽象)
545
+ ├── BaseComponent<Actor>
546
+ │ ├── PlayerInputComponent<Actor>
547
+ │ │ └── MinecraftPlayerInputComponent
548
+ │ ├── HudComponent<Actor, Hud> (抽象)
549
+ │ │ └── PlayerHudComponent (抽象)
550
+ │ └── RequireComponents() 生成的类
551
+ ├── EasyKinematicsComponent
552
+ └── EasyVehicleComponent
553
+
554
+ Level<T> (抽象)
555
+ └── MinecraftLevel
556
+
557
+ Scheduler<Actor> (接口)
558
+ └── MinecraftTickingScheduler
559
+
560
+ GameInstance<A> (接口)
561
+ └── MinecraftGameInstance (抽象)
562
+
563
+ EventEmitter (独立)
564
+ Optional<T> (独立)
565
+ SplineInterpolator (独立)
566
+ KeyState (独立)
567
+ ```
568
+
569
+ ---
570
+
571
+ ## 13. 包入口 (`index.ts`)
572
+
573
+ ```typescript
574
+ // src/oc/index.ts
575
+ export * from './core.js' // Component 体系 + ComponentManager
576
+ export * from './optional.js' // Optional<T>
577
+ export * from './input/base.js' // PlayerInputComponent
578
+ export * from './arch.js' // GameInstance, initialize
579
+ export * from './math/index.js' // Vec3, Vec4, Matrix, Spline, MathExt
580
+ export * from './minecraft/index.js' // Minecraft 集成全部
581
+ export * from './ui/hud.js' // HudComponent
582
+ ```