Skip to content

Commit baf4114

Browse files
committed
- Update documents.
1 parent 0ea74a8 commit baf4114

File tree

9 files changed

+814
-34
lines changed

9 files changed

+814
-34
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ PrototypeMachinery 是一个面向 **Minecraft 1.12.2 (Forge)** 的模组/框架
3333
- 机器注册:[`docs/MachineRegistration.md`](./docs/MachineRegistration.md)
3434
- CraftTweaker 集成:[`docs/CraftTweaker.md`](./docs/CraftTweaker.md)
3535
- UI:[`docs/UI.md`](./docs/UI.md)
36+
- Machine UI Editor(Web Editor 侧):
37+
- 功能现状/代码导航:[`web-editor/src/machine-ui/README.md`](./web-editor/src/machine-ui/README.md)
38+
- 运行时对接草案:[`docs/MachineUiEditorRuntime.md`](./docs/MachineUiEditorRuntime.md)
3639
- API 入口:[`docs/API.md`](./docs/API.md)
3740
- JEI / HEI 集成:[`docs/JEI.md`](./docs/JEI.md)
3841

@@ -112,6 +115,9 @@ Quick links:
112115
- Machine registration: [`docs/MachineRegistration.md`](./docs/MachineRegistration.md)
113116
- CraftTweaker integration: [`docs/CraftTweaker.md`](./docs/CraftTweaker.md)
114117
- UI (ModularUI + script overrides): [`docs/UI.md`](./docs/UI.md)
118+
- Machine UI Editor (Web Editor):
119+
- Feature/status & code map: [`web-editor/src/machine-ui/README.md`](./web-editor/src/machine-ui/README.md)
120+
- Runtime integration draft: [`docs/MachineUiEditorRuntime.md`](./docs/MachineUiEditorRuntime.md)
115121
- Public API entry point: [`docs/API.md`](./docs/API.md)
116122

117123
### Source entry points

docs/CraftTweaker.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,24 @@ builder 支持通过 structureId 延迟解析结构:
4848
- `UIRegistry.register(machineId, panel, priority)`:以优先级覆盖/叠加 UI 定义
4949
- `UIBindings.*`:把 UI 组件与 `ZSDataComponent` 的键值绑定,实现脚本侧数据读写
5050

51+
### Runtime JSON(Machine UI Editor 导出)
52+
53+
除了 builder 方式(`PMUI`)之外,`UIRegistry` 也支持直接注册 **runtime JSON**(通常由 Machine UI Editor 导出):
54+
55+
- `UIRegistry.registerRuntimeJson(machineId, runtimeJson)`
56+
- `UIRegistry.registerRuntimeJsonWithPriority(machineId, runtimeJson, priority)`
57+
58+
其中 `runtimeJson` 是一个 JSON 字符串;Mod 侧会在加载 UI 时解析 JSON,并按当前实现构建 ModularUI。
59+
60+
建议:
61+
62+
- 把 runtime JSON 当作“工具链产物”,脚本里只负责注册(以及注册必要的 `UIBindings`)。
63+
- 如果需要条件/tabs/表达式绑定等能力,优先参考对接契约文档,避免依赖未落地语义。
64+
65+
对接细节(字段契约、兼容策略、支持的 widget type、visibleIf/enabledIf 与 tabs 的当前语义等)见:
66+
67+
- [Machine UI Editor:Runtime JSON 对接(现状 + 契约 + 限制)](./MachineUiEditorRuntime.md)
68+
5169
## See also
5270

5371
- [机器类型注册(MachineType)与脚本注册](./MachineRegistration.md)

docs/MachineUiEditorRuntime.md

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
# Machine UI Editor:Runtime JSON 对接(现状 + 契约 + 限制)
2+
3+
本文件描述 **Web Editor(Machine UI Editor)导出的 Runtime JSON** 在 PrototypeMachinery 模组侧的解释与构建方式。
4+
5+
这份文档的目标是:
6+
7+
- 给出一个“可长期维护”的 **稳定契约**(字段名、默认值、兼容策略)
8+
- 解释当前实现已经支持什么,以及“看起来像支持但其实不支持”的坑
9+
- 把 WebEditor 的 IR 与 Mod 侧的 Kotlin 实现对齐(避免文档飘在实现之上)
10+
11+
## 已落地的实现(代码现状)
12+
13+
### Runtime JSON 注册入口(ZenScript)
14+
15+
- `mods.prototypemachinery.ui.UIRegistry.registerRuntimeJson(machineId, runtimeJson)`
16+
- `mods.prototypemachinery.ui.UIRegistry.registerRuntimeJsonWithPriority(machineId, runtimeJson, priority)`
17+
18+
代码:`src/main/kotlin/integration/crafttweaker/zenclass/ui/UIRegistry.kt`
19+
20+
> 这意味着:Web Editor 生成的 ZenScript(runtime-json 形式)已经可以直接在游戏里注册 UI。
21+
22+
### Runtime JSON 解析器(Mod 侧)
23+
24+
代码:`src/main/kotlin/impl/ui/runtime/MachineUiRuntimeJson.kt`
25+
26+
当前解析器特性:
27+
28+
- **容错**:未知 widget type 会被忽略(不会崩 UI)
29+
- **字段兼容**:多个同义字段名会被识别(例如 `w/width``h/height`
30+
- **Tabs**:支持 `options.tabs[]`(新)与 legacy `backgroundA/backgroundB`(旧)
31+
- **条件字段**:支持 `visibleIf/enabledIf`(生成 `ConditionalDefinition` 包装)
32+
- **容器递归**:支持 `panel/row/column/grid/scroll_container` 以及嵌套 children
33+
34+
### 条件包装器(visibleIf / enabledIf)
35+
36+
定义:`ConditionalDefinition``src/main/kotlin/api/ui/definition/WidgetDefinitions.kt`
37+
38+
构建:`ConditionalWidgetFactory``src/main/kotlin/client/gui/builder/factory/ConditionalWidgetFactory.kt`
39+
40+
当前语义(重要):
41+
42+
- `visibleIf``enabledIf` 都会被解析为 **bool 绑定/表达式**
43+
- 最终效果是:`wrapper.isEnabled = (visibleIf && enabledIf)`
44+
45+
也就是说:
46+
47+
- 目前 **没有“真正不渲染”的隐藏语义**;而是通过 enable/disable 走一条轻量路径
48+
- `visibleIf``enabledIf` 在当前实现里不会产生“不同的 UI 效果”(都等价于 enable gate)
49+
50+
### Tabs(多 Tab / 分页)
51+
52+
定义:`TabDefinition` / `TabContainerDefinition``WidgetDefinitions.kt`
53+
54+
构建:`TabWidgetFactory``src/main/kotlin/client/gui/builder/factory/TabWidgetFactory.kt`
55+
56+
实现要点:
57+
58+
- **切换是纯客户端行为**:不需要服务端同步
59+
-`tabPosition == "LEFT"` 时使用 `PagedWidget + PageButton`
60+
- 非激活页不会渲染(性能与“所见即所得”更接近)
61+
- 前两个 tab 使用 DefaultMachineUI 风格图标贴图
62+
- 其他位置(TOP/BOTTOM/RIGHT)目前走简单 fallback:按钮 + enable/disable 内容
63+
64+
### 变量绑定与表达式(Binding Expr)
65+
66+
绑定系统入口:
67+
68+
- ZenScript 注册:`src/main/kotlin/integration/crafttweaker/zenclass/ui/UIBindings.kt`
69+
- 运行时创建 SyncValue:`src/main/kotlin/client/gui/builder/UIBindings.kt`
70+
71+
表达式实现:`src/main/kotlin/client/gui/builder/bindingexpr/UiBindingExpr.kt`
72+
73+
当前已经支持:
74+
75+
- **bool**`not(...)``and(...)``or(...)`(支持嵌套)
76+
- **double**`clamp(...)``norm(...)`(支持嵌套),并支持数值字面量(如 `0.5`
77+
78+
并且:表达式是 **只读**(不会产生 setter)。
79+
80+
## Runtime JSON 稳定契约(建议按此长期维护)
81+
82+
### 顶层结构
83+
84+
Web Editor 的 runtime 导出(`web-editor/src/machine-ui/exporters/jsonExporter.ts`)输出结构:
85+
86+
- `schemaVersion: 1`
87+
- `name: string`
88+
- `canvas: { width, height }`
89+
- `options?: { ... }`
90+
- `widgets?: Widget[]`
91+
92+
其中 `guides/gridSize/showGuides` 等 editor-only 字段会被剥离。
93+
94+
### Tabs:options 字段
95+
96+
推荐使用新结构:
97+
98+
- `options.tabs[]: { id: string, label?: string, texturePath?: string }`
99+
- `options.activeTabId?: string`
100+
101+
兼容旧结构(仍会被识别):
102+
103+
- `options.backgroundA.texturePath` / `options.backgroundB.texturePath`
104+
- `options.activeBackground: "A" | "B"`
105+
106+
运行时选择初始 tab 的顺序(实现一致):
107+
108+
1) `activeTabId`
109+
2) `activeBackground`
110+
3) `tabs[0]?.id`
111+
4) fallback:`"A"`
112+
113+
### Widget 通用字段
114+
115+
绝大多数 widgets 支持以下通用字段:
116+
117+
- `type: string`
118+
- `x, y: int`
119+
- `w/h``width/height: int`
120+
- `tabId?: string`**仅在顶层 widgets 生效**
121+
- `visibleIf?: string`
122+
- `enabledIf?: string`
123+
124+
关于 `tabId`
125+
126+
- `tabId` 为空/缺失:视为 **全局 widget**(所有 tab 可见)
127+
- `tabId` 非空:视为归属到某个 tab,仅在该 tab 页里渲染
128+
- **嵌套 children 不参与 tab 分区**
129+
- Mod 侧解析器对嵌套 children 关闭 tabId 读取(`allowTabTag=false`
130+
- WebEditor runtime 导出会删除 nested child 的 `tabId`(防止“跨 tab 嵌套”造成语义歧义)
131+
132+
### 条件字段:visibleIf / enabledIf
133+
134+
两者都是 **bool 绑定 key 或表达式**(详见下节语法)。
135+
136+
当前实现的“实际效果”是:
137+
138+
$$
139+
\mathrm{widgetEnabled} = (\text{visibleIf} \lor \text{true}) \land (\text{enabledIf} \lor \text{true})
140+
$$
141+
142+
(实现里把缺失视为 `true`。)
143+
144+
## 绑定表达式语法(实现即文档)
145+
146+
表达式字符串形如:`func(arg0;arg1;...)`
147+
148+
- 参数分隔符是 `;`(避免与逗号/JSON 冲突)
149+
- 支持嵌套:`clamp(norm(foo;0;100);0;1)`
150+
- 未识别的 `func(...)` 会被当作“普通 key”处理(不会报错,但也不会按函数求值)
151+
152+
### Bool 表达式
153+
154+
支持:
155+
156+
- `not(x)`
157+
- `and(a;b;...)`
158+
- `or(a;b;...)`
159+
160+
示例:
161+
162+
- `visibleIf: "not(formed)"`
163+
- `enabledIf: "and(has_power;not(redstone_lock))"`
164+
165+
### Double 表达式
166+
167+
支持:
168+
169+
- 数值字面量:`0` / `0.5` / `-1`
170+
- `clamp(x;min;max)`
171+
- `norm(x;min;max)`
172+
173+
示例:
174+
175+
- `progressKey: "norm(energy;0;100000)"`
176+
- `progressKey: "clamp(raw_progress;0;1)"`
177+
178+
### Sync key 规则(避免冲突)
179+
180+
ModularUI 要求 `(key,id)` 对应唯一 SyncHandler 类型。
181+
182+
因此 `client/gui/builder/UIBindings.kt` 把同一个逻辑 key 按类型做了命名空间隔离:
183+
184+
- `prototypemachinery:ui_binding:bool:<raw>`
185+
- `prototypemachinery:ui_binding:double:<raw>`
186+
- `prototypemachinery:ui_binding:string:<raw>`
187+
188+
这允许你在不同组件里用同一个“业务 key 名”,不会因为类型不同而冲突。
189+
190+
## 支持的 widget 类型(runtime JSON → Kotlin 定义)
191+
192+
以下是当前 `MachineUiRuntimeJson` 明确支持的 type(大小写不敏感,内部会 `lowercase()`):
193+
194+
### 容器/布局(可递归)
195+
196+
- `panel`
197+
- `row`
198+
- `column`
199+
- `grid`
200+
- `scroll_container`(以及同义:`scrollcontainer/scroll/scrollarea`
201+
202+
### 叶子组件
203+
204+
- `text`
205+
- `progress`
206+
- `slotGrid`(JSON 中常见写法;lowercase 后等价于 `slotgrid`
207+
- `image`
208+
- `button`
209+
- `toggle`(以及同义:`togglebutton/toggle_button`
210+
- `slider`
211+
- `textField`(以及同义:`text_field/textfield/input_box/inputbox`
212+
- `playerInventory`(以及同义:`player_inventory`
213+
214+
> 注意:这里的“支持”指 runtime JSON 解析器能产出对应 `WidgetDefinition`;最终是否能正确显示,还取决于对应 WidgetFactory 是否实现。
215+
216+
## 已知限制与待办(建议写在这里,避免散落)
217+
218+
### 1) visibleIf/enabledIf 目前等价
219+
220+
如果你需要“不可见但仍占位/可见但不可交互”等更细的语义,需要在 `ConditionalWidgetFactory` 做更精细的区分(例如只控制渲染/只控制交互)。
221+
222+
### 2) 表达式功能刻意保持最小
223+
224+
目前没有:
225+
226+
- `eq(...)` / 比较运算
227+
- string 表达式(拼接、format)
228+
229+
建议策略:先用脚本端注册基础 binding key,把复杂逻辑放在脚本端;表达式只用于 UI 内的轻量“归一化/开关组合”。
230+
231+
### 3) TabContainer 目前只对 LEFT 做了精细实现
232+
233+
TOP/BOTTOM/RIGHT 仍是 fallback(按钮 + enable/disable)。如果 WebEditor 将来支持不同 tab bar 布局,建议在 `TabWidgetFactory` 里补齐。
234+
235+
### 4) 资源化管线(长期)
236+
237+
当前推荐的“落地姿势”仍是:WebEditor 导出 ZenScript(runtime-json),脚本里注册。
238+
239+
如果要做真正的“资源/数据包驱动”加载(不复制粘贴 JSON 字符串),需要新增:
240+
241+
- runtime JSON loader(从资源路径读取)
242+
- schema 版本迁移
243+
- 更友好的 parse 错误提示

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
- **脚本与 UI**
2727
- [CraftTweaker(ZenScript)集成](./CraftTweaker.md)
2828
- [UI:默认 ModularUI + 脚本 UIRegistry](./UI.md)
29+
- [Machine UI Editor:Runtime JSON 对接(现状 + 契约 + 限制)](./MachineUiEditorRuntime.md)
2930
- (相关)[结构预览 GUI(/pm_preview_ui)](./StructurePreview.md)
3031

3132
- **客户端集成(Client Integrations)**

0 commit comments

Comments
 (0)