Links
Tags
Creators
Details
1.2.5
Compatibility
Required content
Changes
🧮 公式语法升级 / Formula Syntax Upgrade
| Feature / 功能 | Description / 说明 |
|---|---|
| 🔁 Control Flow / 控制流 | repeat / while / if / else / break / continue — loop-heavy formulas spread across ticks / 循环公式跨 tick 分摊 |
| 🧊 vec3 + Vector Functions / 向量 | vec3(x,y,z), swizzle v.x/y/z, length/normalize/dot/cross/dist/yaw/pitch (yaw/pitch aligned with the DIRECTION node, degrees, yaw ∈ [0,360)) |
| ⚖️ Comparison & Logic / 比较逻辑 | < > <= >= exact; ==/!= 1e-6 tolerance; && || ! with !=0 truthiness |
| 📐 Scalar Functions / 标量函数 | sin cos tan asin acos atan2 sinh cosh sqrt ln log exp sec csc cot (degrees convention) |
| 🎯 @output vec3 Expansion / 输出展开 | @output v (vec3) auto-expands into 3 scalar pins v.x/v.y/v.z with stable pinIds |
| 🔗 Warm Restart Toggle / 温启动开关 | Pinless eval-policy setting in the formula edit panel (segmented toggle like the signal generator's mode switch): warm keeps iterating toward new inputs without resetting progress vs strict freeze (default) / 编辑区无引脚两段式按钮:温启动保留进度继续迭代 vs 严格冻结(默认) |
| 🌐 CJK Input / 中文输入 | Chinese/full-width symbols, letters, digits and spaces convert to half-width ASCII as you type (()→(), ×→*, ≥→>=, full-width x→x …) / 中文/全角符号输入即转半角 |
| ✍️ Editor Support / 编辑器支持 | Syntax highlighting, autocomplete and validation for all new tokens; -- line comments / 新语法高亮补全校验;-- 行注释 |
- 统一求值引擎 / Unified eval engine:single
Valuestack machine for legacy RPN and new AST scripts — old formulas are byte-identical, no migration, no dual-engine drift. / 单一栈机统一求值,旧脚本逐位不变。 - 详见
docs/formula-syntax-manual.md。
⏱️ 公式预算池 / Formula Budget Pool
| Mechanism / 机制 | Behavior / 行为 |
|---|---|
| 🕒 Per-Node Slice / 节点配额 | slice = formulaBudgetMs / N_heavy_prev (default 3.0ms, configurable 0.5–20); every node admitted every tick — zero starvation / 保底零饿死 |
| 🤝 Cooperative Suspend / 协作挂起 | Wall-clock check every 16 iterations at loop boundaries; carrier (loop stack + Env snapshot) saved, resumed next tick via seek execution — no lost iterations / 循环边界挂起续算不丢迭代 |
| 📤 Emit-on-Done / 收敛输出 | Outputs frozen during spread, fresh value only on done — half-converged values never leak / 半收敛解永不流出 |
| 🧊 Freeze / Warm / 冻结与温启动 | Input change mid-spread: strict freeze completes the old snapshot (default); warm keeps iterating toward the new inputs without resetting progress (opt-in) / 温启动保留进度继续迭代 |
| 🛡️ MAX_ITER Backstop / 兜底 | 1M iterations spread-wide → shed to lastGood + one-shot warning, unfrozen on formula edit / 超限冻结直到编辑 |
| ⚡ Dedup / 去重 | Same script + same inputs deduplicated per tick (pure functions, array-isolated) / tick 级去重 |
| 📊 Progress Bar / 进度条 | Thin render-state bar on FORMULA nodes (no values): 0..1 progress, breathing fill for while (indeterminate) / 渲染态进度条 |
- 架构 / Architecture:inline gating — FORMULA evaluates in place at its topological position; no central queue, no added tick latency. / 内联门控,无中央队列无延迟。
🎯 火控弹道解算示例 / Fire-Control Ballistic Solver Example
docs/examples/ballistic_solver.formula — Newton-iteration aim solver ported to the FORMULA language (CreateBigCannons ballistic model: semi-implicit Euler dt=1/20, linear/quadratic drag). 361-point pitch scan + damped Newton refinement; ~600k interpreter iterations per solve, spread across ticks by the budget pool with a progress bar. 11 input pins (muzzle/target positions, v0, gravity, drag, density) → 6 outputs (yaw/pitch/reachable + velocity vector). Validated against a Python reference implementation across four scenarios.
/ 牛顿迭代弹道反解:361 点俯仰扫描 + 阻尼牛顿精化,单次约 60 万次解释器迭代、由预算池跨 tick 分摊(带进度条)。11 输入(炮口/目标坐标、初速、重力、阻力、密度)→ 6 输出(射向角/射角/可达 + 初速向量)。四组场景与 Python 参考实现对拍通过。
🖥️ GUI 架构迁移 / GUI Architecture Migration
- 7 个编辑界面脱离容器体系 / All 7 editors leave the container system:全部 7 个编辑界面(蓝图 / 转速代理 / 编程计算机 / 传感器 / 控制座椅 / 显示器 / 雷达)从
AbstractContainerScreen+Menu体系迁移为继承新基类AbstractGraphScreen(纯Screen),由方块在客户端直接setScreen打开——无 Menu 注册、无网络 round-trip、即时打开。/ All 7 editors (blueprint / speed proxy / program computer / sensor / control seat / monitor / radar) now extend the newAbstractGraphScreenbase (plainScreen) and open client-side viasetScreen— no menu registrations, no network round-trip, instant open. - 第三方模组注入规避 / Third-party mod injection avoided:界面不再被 FTB Quests、Quark 等模组识别为容器界面,节点编辑器画布上不再出现无关按钮、任务覆盖层或装饰边框。/ Screens are no longer recognized as container GUIs, so unrelated buttons, quest overlays and decorative frames no longer appear on the node canvas.
- 容器设施全部删除 / Container plumbing removed:删除 7 个
XxxMenu类、SchematicCompute.MENUS注册(7 个 MenuType)、ClientSetup.registerScreens、SyncedGraphBlockEntity的MenuProvider以及 7 个 BE 的getDisplayName/createMenu。/ Deleted the 7 menu classes, the MENUS DeferredRegister, registerScreens, MenuProvider on the base BE, and getDisplayName/createMenu on all 7 BEs. - 便携终端路径 / Portable terminal path:终端打开设备编辑界面改为直接构造
XxxScreen(editingPos),不再构造虚拟 Menu。/ The terminal now constructs editor screens directly from the edit position — no virtual menus. - 守卫保持 / Guards preserved:
pendingLocalOps回弹保护(5892caa)、BE 失效自动关界面、GraphJoin/LeavePacket协作生命周期全部迁入基类,行为与迁移前一致;雷达设置面板 EditBox 写回经preClose()钩子保留。/ The pendingLocalOps bounce-back guard, BE-invalidation auto-close and join/leave collaboration lifecycle all moved into the base class with unchanged behavior; the radar settings EditBox write-back survives via thepreClose()hook.
🖼️ 显示器图像系统修复 / Monitor Image System Fixes
| Fix / 修复 | Description / 说明 |
|---|---|
| ⚓️ 图像锚点对齐 / Image anchor clamp | 编辑器与世界渲染器统一为左上角锚点语义;修正世界渲染器 clamp 上界缺因子 2(1-bbHalfW → 1-2*bbHalfW),缩放图像不再在右/下边框伸出半个图像宽度、与编辑器错位 / Editor and world renderer now share the top-left anchor; the world clamp bound missing the factor-2 was fixed, so scaled images no longer overhang the right/bottom border by half their size |
| 📏 边框与辅助线 / Bezel + placement grid | 死区减半(0.08→0.04 屏宽,编辑器与世界渲染器同源常量);摆放辅助线绑定内容区 16 等分 + 中心十字,整格对齐 / Bezel margin halved (shared constant across editor and world renderer); the placement grid is bound to the content area in 16 exact divisions with a bold center cross |
| 🖱️ 陈旧选择拖拽 / Stale-selection drag | 空白处按下不再抓取图层面板的旧选择——仅当按下点落在已选元素(裁剪后的旋转 AABB)内才开拖;"先点图1、再拖图2"不再误移图1 / Pressing empty canvas no longer grabs the layer-panel selection; the press must fall inside the selected element's clamped rotated AABB, fixing the "select image 1, drag image 2 → image 1 moves" bug |
| 🧩 图像画布尺寸 / IMAGE canvas size | IMAGE/IMAGE_SEQUENCE 支持自定义 W×H(1..32,默认 16×16):EditPanel 宽高输入、像素编辑器/缩略图/世界渲染/命中/拖拽全链路按节点尺寸、改尺寸左上角保留内容且全帧同步、旧档迁移保护;像素撤销计数标记改用并行元数据(1×1 画布不再冲突),并补全帧操作缺失的重做路径 / Custom W×H canvas (1..32, default 16×16) with edit-panel inputs, size-aware pixel editor/thumbnails/renderer/hit-tests, top-left-preserving resize across all frames, and legacy-save migration guard; pixel-undo count markers moved to parallel metadata (no more 1×1 collision) and the missing frame-redo path was completed |
| 🖌️ 像素同步与拖拽稳定性 / Pixel sync + drag stability | 像素编辑器所有关闭路径统一走定向同步(SET_IMAGE_PIXELS op),颜色确认(Enter/OK)不再静默关闭丢画;不再全量上传整图(避免冲掉其他玩家并发编辑);显示区拖拽改节流流式同步(100ms,松手终发)并支持触屏(mouseDragged 同样更新);整图同步替换后 selectedDisplayNode 按 id 重映射,消除"首次拖正常、之后本地冻结"的孤儿引用 / Every pixel-editor close path now runs the targeted SET_IMAGE_PIXELS sync — color confirm (Enter/OK) no longer closes silently and loses the painting; no more full-graph upload on close (which clobbered other players' concurrent edits); display drags throttle-stream their layout (100ms, final op on release), work on touch (mouseDragged updates too), and selectedDisplayNode is remapped by id after full-graph syncs, fixing the orphaned-reference freeze after the first drag |
| 🤝 显示布局实时协作 / Display-layout collaboration | 拖拽位置实时同步给队友;显示模式下渲染队友光标+名字+拖拽组件彩色描边(组件软锁:队友拖拽中的组件不可抓取);存在光标严格按模式隔离(节点图模式与显示模式互不串场)/ Drag positions sync live to teammates; the display editor renders teammates' cursors with name tags and colored outlines around components being dragged (soft lock: those components cannot be grabbed); presence cursors are strictly per-mode (node-graph and display modes no longer bleed into each other) |
| 🚪 关屏定向提交 / Close-time targeted commit | 显示器关屏不再全量上传整图(旧行为会用本客户端快照覆盖服务端、冲掉其他玩家并发的编辑);改为只把尚未同步的局部编辑定向提交:EditBox 输入(含 TAB 切焦点遗留文本)、busBox 频道名与频段改名、像素编辑器当前帧、进行中的显示区拖拽终态——全部走定向 op/包,图数据早已由各 op 实时同步,服务端即最新真相;设置面板保持显式 Apply 契约 / Closing the monitor screen no longer uploads the whole graph (which overwrote the server graph with this client's snapshot and clobbered other players' concurrent edits); only unsynced in-progress edits are committed via targeted ops/packets: EditBox text (incl. text left by TAB focus moves), busBox channel names + band renames, the pixel editor's current frame, and the final state of an in-flight display drag — the graph is already kept in sync live by ops, so the server holds the truth; the settings panel keeps its explicit-Apply contract |
- 详见
docs/monitor-image-fixes-audit.md(评审结论、grilling 决策与提交记录)。
🎨 像素编辑器 / Pixel Editor
| Feature / 功能 | Description / 说明 |
|---|---|
| 🖥️ Standalone Screen / 独立界面 | Double-click an IMAGE/IMAGE_SEQUENCE node opens a dedicated PixelEditorScreen (painting-app layout) instead of an in-editor overlay; ESC/✕ returns to the graph editor (portable-terminal wrapper preserved) / 双击 IMAGE/IMAGE_SEQUENCE 节点打开独立 PixelEditorScreen(绘画软件布局),替代编辑器内浮层;ESC/✕ 回到图编辑器(便携终端包装保持) |
| 🧰 Toolbar / 工具栏 | Responsive tile layout that never overlaps: left panel (single-column compact tool rail: Brush, Eraser, Paint-bucket fill, Eyedropper, Line, Rectangle — brush size/opacity/current colour now live in the top bar), slim top bar (brush state + Fit + Guide + Undo/Redo + Canvas + Close, right-aligned) — the color palette is an embedded always-on right panel (see below), so the canvas fills the whole central column / 响应式瓦片布局,任何窗口尺寸都不重叠:左面板(单列窄工具列:画笔、橡皮、油漆桶、取色器、直线、矩形——笔刷大小/透明度/当前色已移入顶栏)、瘦身顶栏(画笔状态 + 适配 + 指南 + 撤销/重做 + 画布 + 关闭,右对齐)——取色器为内嵌式常驻右面板(见下),画布占满整条中央列 |
| 📐 Layout / 布局 | Every panel keeps its content inside its own strip (top bar y<30, left panel x<30, palette in the reserved right band, canvas/frame strip in the central column), so components can never overlap at any window size; internal controls are all positioned relative to their own panel anchor (no hard-coded absolute screen coords) / 每个面板内容都约束在自身条带内(顶栏 y<30、左面板 x<30、取色器在右缘预留带、画布/帧条在中央列),因此任何窗口尺寸都不重叠;内部控件全部相对各自面板锚点定位(不再使用硬编码绝对屏幕坐标) |
| 🖌️ PS Tool Rail / PS 式工具列 | The left tool rail is now PS-style: it hugs the left edge and the right divider (no empty gap), drops the per-button border outline, and highlights the whole cell rectangle only on hover/selected; the rail is slimmed to ~30px so the canvas gets more room / 左侧工具列改为 PS 式:紧贴左缘与右分隔线(不留空隙),去掉每个按钮的单独边框,仅悬停/选中时高亮整块矩形;工具列收窄到约 30px,把更多空间让给画布 |
| ✨ Beautify / 美化 | PS-style polish: cleaner two-tone tool icons, a context status bar (current tool / brush size / zoom (cell px) / grid state / hovered cell / sequence frame), a Fit zoom button, a grid toggle (G), and keyboard shortcuts (B/E/F/I/L/R/H tools, 1..7 rail order, [/] brush size, Ctrl+Z/Y undo/redo); the brush size also has a draggable slider on the right of the top-bar status row (1–32, same range as [/]) / PS 风格打磨:双色调更清晰的工具图标、上下文状态栏(当前工具/笔刷大小/缩放(单元格 px)/网格状态/光标格/序列帧号)、Fit 缩放按钮、网格开关(G)、键盘快捷键(B/E/F/I/L/R/H 工具、1..7 按顺序、[/] 笔刷大小、Ctrl+Z/Y 撤销重做);笔刷大小另有顶栏状态行右侧可拖动滑块(1–32,与 [/] 同范围) |
| ✋ Hand Tool / 抓手工具 | A Hand tool at the end of the tool rail (shortcut H): with it selected, left-click-drag pans the canvas — no need for middle-mouse or Space / 工具列末尾新增抓手工具(快捷键 H):选中后左键拖拽即可平移画布,无需中键或空格 |
| 🎨 Embedded Palette / 内嵌调色板 | The color picker is an embedded always-on panel on the right (scaled ~0.8x): the panel fills the right band down to the screen bottom, with a solid background + left divider like the other panels (not a floating popup), no toggle, no outside-click close, and the canvas stops at its left edge. The Favorites/Recent titles are drawn at full size (readable) and each shows more rows; the OK and Eraser buttons are hidden (in the embedded palette) and the eraser logic was removed from ColorPickerWidget entirely / 取色器改为内嵌式常驻右侧面板(约 0.8x):面板铺满右缘到屏幕底部,实心底 + 左侧分隔线(非浮空弹窗)、无开关、点外部不关闭,画布止于其左缘;「常用/最近使用」标题字放大、各显示更多行;不再显示「确定」和「橡皮擦」按钮,且从 ColorPickerWidget 里彻底移除了橡皮擦逻辑 |
| 🖱️ RMB Erase / 右键擦除 | Right-click erases to transparent under any tool / 任何工具下右键直接擦为透明 |
| 🩸 Eyedropper Sync / 取色器同步 | The eyedropper now syncs the embedded palette: picking a colour on the canvas updates the palette's SV plane / hue / alpha bars and hex field in real time / 吸管取色后内嵌调色板实时同步:SV 平面、色相/透明度滑条与 hex 输入框都切到吸取的颜色 |
| 🔍 Zoom & Pan / 缩放平移 | Mouse-wheel zoom (anchored at cursor), middle-drag or Space+LMB to pan / 滚轮缩放(光标锚定),中键或空格+左键平移 |
| ↩️ Undo/Redo Buttons / 撤销重做按钮 | Top-bar Undo/Redo buttons + Ctrl+Z/Y; resize undo restores size and all frames / 顶栏撤销/重做按钮 + Ctrl+Z/Y;尺寸撤销恢复旧尺寸与全部帧 |
| 🎞️ Frame Strip / 帧条 | Bottom thumbnail strip with click-to-switch, ◀/▶ nav, +New (blank / from current), Delete frame, and drag-to-reorder — new REMOVE_IMAGE_FRAME / MOVE_IMAGE_FRAME ops keep frame edits server-authoritative / 底部缩略图条:点击切换、◀/▶ 导航、+New(空白/复制当前)、删除帧、拖拽重排 — 新增 REMOVE_IMAGE_FRAME / MOVE_IMAGE_FRAME op 保持帧编辑服务端权威 |
| 🎞️ Sequence Area Layout / 序列区布局 | The sequence area is now stacked: a button row (◀/▶ nav + frame count, +New, Delete) sits directly above the thumbnail strip, and the thumbnail strip is flush against the bottom of the screen — the +/- buttons no longer sit inside the strip, and thumbnails fill it from the left margin / 序列区改为上下两段:±/导航 + 帧号、+New、删除 的按钮行紧挨在缩略图条上方,缩略图条紧贴屏幕底部(按钮不再嵌在条内),缩略图从左边缘起填满 |
| 🖼️ Dynamic Thumbnails / 缩略图动态缩放 | Sequence-frame thumbnails now scale dynamically: the height stays fixed (36px — bigger, compact) while the width follows the image's aspect ratio (a wide frame gets a wide thumb, a tall frame a narrow one), rendered to fill by per-pixel integer rects; the per-thumbnail cell borders are removed, the current frame is highlighted with a coloured backdrop instead, and the strip is tightened so the images sit flush at the bottom with no gap / 序列帧缩略图改为动态缩放:高度固定(36px,更大更紧凑)、宽度随图像宽高比(宽图宽缩略图、高图窄缩略图),用逐像素整数矩形缩放填满;去掉了每帧的边框,当前帧改用底色高亮替代,并收紧条高让图像紧贴底部无间隙 |
| 📐 Canvas Size / 画布尺寸 | A "Canvas" button opens a small W/H popup (1..32) with separate Apply/Cancel — Apply resizes every frame / 「画布」按钮弹出 W/H 小窗(1..32)+ 应用/取消,应用时对所有帧生效 |
🪟 全息显示器 HUD 模式(Phase 1)/ Monitor HUD Mode (Phase 1)
- 选项卡切换:显示器设置面板新增
[3D 模式] [HUD 模式]tab——点击立即切换(服务端权威、MonitorSettingsPacket广播、所见即所得);HUD tab 提供面板宽/高/横纵偏移/距离 5 参数,沿用显式 Apply/Enter 契约。设计文档docs/monitor-hud-mode-design.md/ Settings panel gains[3D Mode] [HUD Mode]tabs: clicking switches immediately (server-authoritative, broadcast via MonitorSettingsPacket, WYSIWYG); the HUD tab exposes panel width/height/lateral/vertical offsets and standoff distance under the explicit Apply/Enter contract. - 世界内玻璃面板:中心锚点玻璃 quad 按
FACING对齐、距方块表面可调(≥0.05 防 z-fighting)、尺寸 0.1–10 方块、世界固定尺寸远小近大;内容复用现有TEXT/DATA/IMAGE/IMAGE_SEQUENCE节点,在 BER 主 pass 直接绘制(纯官方接口:PoseStack+MultiBufferSource+MonitorRenderTypes.SCREEN_PIXEL+Font.drawInBatch,与 3D 模式同一套 Sodium/Iris 已验证管线;弃用离屏 FBO——Sodium/Veil 世界管线在 flush 时重置视口/裁剪,读回实证内容只落进纹理一角),玻璃自发光全亮(0xF000F0),任意视角不消失(裁剪包围盒覆盖玻璃体积) / In-world glass panel: center-anchored quad aligned toFACINGwith adjustable standoff (≥0.05 anti-z-fighting), 0.1–10 blocks, fixed world size (perspective-correct near/far); content reusesTEXT/DATA/IMAGE/IMAGE_SEQUENCEnodes drawn directly in the BER main pass (official interfaces only:PoseStack+MultiBufferSource+MonitorRenderTypes.SCREEN_PIXEL+Font.drawInBatch— the same Sodium/Iris-proven pipeline as 3D mode; the offscreen FBO was dropped because the Sodium/Veil world pipeline resets viewport/scissor at flush time, with readback proving content only landed in one corner of the texture); the glass is fully self-lit and never vanishes at odd angles (cull box covers the glass volume). - AR HUD Phase 2(共形俯仰梯 + 地平线,2026-08-19 首批):新增
HUD_PITCH_LADDER节点(输入 pitch/roll + 透传输出,玩家在图中自接ATTITUDE/VIEW_ANGLE/公式/总线等任意源;刻度参数 range/interval,默认 ±90° 全姿态——Sable 物理引擎四元数旋转、无万向锁)。刻度族贴世界水平面共形投影(§9.1:玩家相机 → 世界方向族 → 玻璃平面求交,t≤0/出界自动裁剪),地平线加粗高亮,pitch/roll 输入驱动绿色姿态标记。普通组件(TEXT/IMAGE 等)可选「贴玻璃 / 贴世界」锚定模式(世界绝对方向 yaw/pitch,创建时默认取玩家当前视线,GraphNode独立字段 + NBT v5 迁移);编辑器用固定相机模拟预览。数据 20Hz 随ClientboundGraphEvalPacket刷新。 / AR HUD Phase 2 (conformal pitch ladder + horizon, first batch 2026-08-19): newHUD_PITCH_LADDERnode (inputs pitch/roll + passthrough outputs, wired by the player to any source —ATTITUDE/VIEW_ANGLE/formula/bus; ladder params range/interval, default ±90° full attitude — Sable's quaternion physics has no gimbal lock). Ladder lines are world-horizontal direction fans projected conformally (§9.1: player camera → world direction fan → glass-plane intersection, t≤0/off-panel auto-clipped), the horizon is bold, and the pitch/roll inputs drive a green attitude marker. Ordinary components (TEXT/IMAGE etc.) can choose on-glass / on-world anchoring (world-absolute yaw/pitch, defaulting to the player's current view at creation;GraphNodefields + NBT v5 migration); the editor shows a fixed-camera mock preview. Data refreshes at 20Hz viaClientboundGraphEvalPacket. - Phase 2 后续(航向带/速度矢量/其余
HUD_*组件)随排期,见设计文档 §十三 / Remaining Phase 2 (heading tape, velocity vector, other HUD_* components) stays on the roadmap, see design doc §13. - 近处屏幕 + 远处虚像画布(2026-08-21 定稿:顶点级深度锚定 + 玩家屏幕 4 边形遮罩):HUD 按真实原理实现——近处玻璃屏幕只画边框,内容画在沿 -FACING(玩家面前)100 格的虚像画布上(尺寸 ×100 保持角尺寸,内容恒定大小浮在远处 = 无限远聚焦);画布世界固定(poseStack 局部坐标)→ 天然共形(贴世界)且不依赖玩家相机投影,Sable 结构天然支持;俯仰梯 = 画布内姿态仪(tan 透视刻度 + pitch 平移地平线 + roll 旋转,
ladderCanvasY纯函数);深度锚定(顶点级几何实现,2026-08-21):顶点构造为V'=(fx·gz/fz, fy·gz/fz, gz)——屏幕位置保持远处画布投影、深度 = 玻璃平面 gz(官方接口 + vanillaposition_colorshader,无自定义 shader——Veil 4.0 拦截自定义 ShaderInstance 绑定)→ 前方物体遮挡虚像、后方物体不遮挡(真实 HUD 遮挡关系);玩家屏幕定位遮罩(4 边形):玻璃面板 4 角点从玩家眼睛投影到画布平面(projectGlassCornersToCanvas),内容(IMAGE 像素 / 俯仰梯刻度)经 Sutherland-Hodgman 凸裁剪(clipPolyToQuad)只在玩家透过玻璃看到的 4 边形区域内显示——「hud 只在玻璃上显示」,视线离开玻璃(画布不在玻璃投影内)内容消失;组件级剔除(逐像素/逐字符画布矩形裁剪、view-ray)已移除(GPU 省不了多少、CPU 开销大);姿态数据 20Hz 客户端指数插值到 60fps。ConformalProjectionTest覆盖 4 边形投影与裁剪纯函数 / Near screen + far virtual-image canvas (2026-08-21 final: vertex-level depth anchor + player-screen 4-gon mask): HUD follows the real principle — a near glass screen drawing only a border, with content on a virtual-image canvas pushed 100 blocks along -FACING (in front of the player) (size ×100 preserves angular size; content floats at constant size far away = infinite focus); the canvas is world-fixed (poseStack local frame) → natively conformal (world-anchored) with no player-camera projection, Sable structures work natively; the pitch ladder is an in-canvas attitude indicator (tan-perspective ticks, pitch-shifted horizon, roll rotation, pureladderCanvasY); depth anchoring (vertex-level geometric, 2026-08-21): vertices are built asV'=(fx·gz/fz, fy·gz/fz, gz)— screen position keeps the far-canvas projection while depth lands on the glass plane gz (official interfaces + the vanillaposition_colorshader; no custom shader — Veil 4.0 skips custom ShaderInstance binding) → near objects occlude the image, far ones do not (real-HUD occlusion); player-screen-positioned 4-gon mask: the glass panel's 4 corners project from the player's eye onto the canvas plane (projectGlassCornersToCanvas), content (IMAGE pixels / pitch-ladder ticks) is Sutherland-Hodgman-clipped (clipPolyToQuad) to show only inside the 4-gon region seen through the glass — "HUD shows only on the glass", looking away (canvas outside the glass projection) hides it; component-level culling (per-pixel/per-glyph canvas-rect clipping, view-ray) was removed (negligible GPU savings, heavy CPU cost); 20Hz attitude data is exponentially interpolated client-side to smooth 60fps.ConformalProjectionTestcovers the 4-gon projection and clipping pure functions.
🖱️ 拖拽不再刷新全部节点状态 / Drag No Longer Refreshes All Node States
| Fix / 修复 | Description / 说明 |
|---|---|
| 🎨 视觉 op 不 bump 代际 / Visual ops no longer bump | MOVE_NODE / SET_ZORDER / SET_COMMENT_TEXT / SET_COMMENT_COLORS / SET_COMMENT_SIZE 是纯视觉 op(不进求值器、非 Monitor 显示内容),不再 bumpGeneration()——此前拖拽节流(50ms)下每个 op 都触发服务端 recompileEvaluatorFull → runtimeState.clear() 清零全部时序状态,并让客户端 renderBg 重建所有展开节点的编辑区 / These visual-only ops (never read by the evaluator, not monitor display content) no longer bump the generation — previously every throttled drag op (50ms) triggered a server full recompile → runtimeState.clear() wiped all sequential state and rebuilt every expanded EditState on clients. |
| 🧩 移除 applyOp 无条件父图 bump / No unconditional parent bump | EditSessionRegistry.applyOp 不再无条件 bump 父图代际——结构 op 由 NodeGraph 内部 bump,子图 op 靠子图代际陈旧检测 + rebuildInputCache() 兜底(仅引脚映射变化才 bump)/ No more unconditional parent-graph bump in applyOp — structural ops bump inside NodeGraph, sub-graph ops rely on per-subgraph staleness + rebuildInputCache() (bumps only when pin indices actually change). |
| 📡 Monitor 全量同步限流 / Coalesced monitor full sync | 显示拖拽的 flagFullSync 改为 requestFullSync + tick 内 flushPendingFullSync 合并冲刷——20Hz 的显示 op 只向非编辑者推 ~0.5Hz 全图 NBT(FULL_SYNC_GRACE_TICKS 节流字段此前是死代码);编辑者仍经 op 广播实时同步 / Display-drag full syncs now coalesce via requestFullSync + tick flushPendingFullSync — 20Hz display ops push the full graph NBT at ~0.5Hz to non-editors (the FULL_SYNC_GRACE_TICKS throttle was dead code); editors still sync in real time via op broadcast. |
| 🧪 回归测试 / Regression tests | OpGenerationTest(10 例):MOVE/视觉 op 不 bump、求值 op 仍 bump、结构变更仍 bump / 10 cases: move/visual ops don't bump, eval ops still bump, structural changes still bump. |
🔄 编辑不再清空时序/积分状态 / Edits No Longer Reset Sequential & Integral State
| Fix / 修复 | Description / 说明 |
|---|---|
| 🧬 重编译保留主图状态 / Recompiles preserve main-graph state | recompileEvaluatorFull 改为像子图状态一样保存→剪除→恢复主图五类状态(pidState 含 PID/ACCUMULATOR/INTEGRATOR、delayQueues、flipflopStates、pulseTimers、debugTime)——连线、添加节点、公式输入、参数编辑(如时序节点参数引脚动态调参)不再清空时序与积分;删除节点仅剪除该节点状态(含辅助槽位 -(id+1)/id+100000/id+200000)/ recompileEvaluatorFull now saves→prunes→restores the five main-graph state maps (like sub-graph state): wiring, adding nodes, formula input and param edits (e.g. dynamic tuning via sequential-node param pins) no longer wipe timing or integrals; removing a node prunes only its own state incl. auxiliary slots. |
| ⚙️ base/light 重编译同步修复 / base & light recompiles fixed too | recompileEvaluator / recompileEvaluatorLight(Sensor/ControlSeat/Radar/Monitor/SpeedProxy)不再 pidState.clear()——其他方块编辑时 PID 积分同样保留 / These no longer clear pidState — PID integrals survive edits on the other five block types too. |
| 🗑️ 语义保持 / Semantics kept | 整图替换(loadGraphFromBytes/关屏上传)仍显式清空(旧节点 ID 无意义);编译按钮的 Latch/GATE/T_FLIPFLOP 当前状态回归初始(params[1])不变 / Whole-graph replacement still clears explicitly (old IDs meaningless); the compile button's latch-state reset stays unchanged. |
| 🧪 测试 / Tests | RuntimeState.pruneToAliveIds + 3 例单测(保留含辅助槽/剪除死节点/空集合),全量测试通过 / prune logic + 3 unit tests; full suite green. |
🗂️ 添加节点菜单双列切换 / Add-Node Menu Two-Column Toggle
| Feature / 功能 | Description / 说明 |
|---|---|
| 🖱️ 手动双列开关 / Manual two-column toggle | 菜单标题行右侧的状态文字按钮(单列/双列,双语),点击切换,开启时金色文字+金框高亮:开启后所有展开分类均按双列渲染;关闭时全部单列(三角函数不再自动双列)/ A state-label button on the menu title row (1 Col/2 Col, localized): click to toggle, gold text+border while active. When on, every expanded category renders in two columns; when off, all are single-column (Trig no longer auto-expands to two). |
| 📏 宽度常驻 / Persistent width | 双列开启时面板宽度常驻双列——即使没有任何展开分类也保持,切换不跳动 / While two-column is on, the panel width stays two-column even with nothing expanded — no resizing jump. |
| 🔍 搜索列数同步 / Search columns follow | 搜索模式扁平列表列数跟随开关(开启=2 列,关闭=1 列),不再硬编码 2 / The search flat list follows the toggle (on=2, off=1) instead of a hardcoded 2. |
| 🖱️ 搜索可滚动 / Search scrollable | 搜索模式 totalH 改为扁平列表真实高度——匹配多时出现滚动条,滚轮/拖拽可滚动(此前恒短、无法滚动)/ Search-mode totalH now reflects the flat-list height — the scrollbar appears for many matches and wheel/drag scrolling works (previously the list was always shorter than the panel and could not scroll). |
| 📐 布局联动 / Layout consistency | 面板宽度 / 高度封顶 / 滚动条 / 点击命中全部按当前生效列数计算,切换瞬间重排无错位 / Panel width, height cap, scrollbar and click hit-testing all follow the effective column count — no misalignment on toggle. |
📐 姿态传感器 ATTITUDE 修复 / Attitude Sensor ATTITUDE Fix
- ATTITUDE 节点随方块朝向变换 / ATTITUDE now follows the block facing:同一 Sable 结构上朝向不同的姿态传感器,ATTITUDE(pitch/roll)输出此前完全相同——旧实现只取结构级俯仰/横滚(
cachedSubPitch/cachedSubRoll),与方块朝向无关。该结构级语义自 v1.0.1 首次 Sable 集成(ca514a1) 起遗留至今:当时及 1.1.x 只实测了 VELOCITY/FORWARD 等朝向类节点,ATTITUDE 从未对照方块朝向验证。修复后由「方块朝向 × 子世界旋转」的局部基向量推导方块自身世界姿态:前向向量决定俯仰(与 FORWARD 节点同公式、同符号约定),上向量绕前向轴的倾斜决定横滚。同一结构上侧向安装的传感器,其结构俯仰表现为自身横滚,输出不再相同。/ ATTITUDE (pitch/roll) outputs were identical across differently-faced sensors on the same Sable structure — the old implementation used structure-level pitch/roll only, ignoring the block facing. That structure-level semantics had been inherited since the first Sable integration at v1.0.1 (ca514a1): only VELOCITY/FORWARD heading-type nodes were verified in 1.0.1–1.1.x, and ATTITUDE was never checked against block facings. Now the block's world-space attitude is derived from its local basis rotated by facing × sub-world pose: the forward vector yields pitch (same formula and sign convention as the FORWARD node) and the up vector's tilt around the forward axis yields roll. A sideways-mounted sensor reports the structure pitch as its own roll. - 输出引脚不变 / Output pins unchanged:仍为 2 引脚(pitch、roll),无图迁移。/ Still 2 pins (pitch, roll) — no graph migration.
- 纯函数可单测 / Pure-function testable:新增
SensorAttitudeMath.blockAttitude()(零 Minecraft/Sable 依赖)+SensorAttitudeMathTest(7 例,含实测数值锁定:结构 yaw=6.37°/pitch=19.03°/roll=-0.28° 时,面 WEST→(0.27,-19.03)、面 NORTH→(19.03,0.28)、面 SOUTH→(-19.03,-0.28))。/ NewSensorAttitudeMath.blockAttitude()(zero Minecraft/Sable deps) +SensorAttitudeMathTest(7 cases, locking measured values). - 注意 / Note:pitch 符号约定由旧的欧拉角约定改为前向仰角约定(与 FORWARD 一致)——面朝与结构相反方向的传感器 pitch/roll 符号可能翻转;依赖旧数值的现有图需复查。/ The pitch sign convention switched from the legacy Euler-angle convention to the forward-elevation convention (matching FORWARD) — sensors facing opposite the structure may flip sign; existing graphs relying on legacy values should be re-checked.
- 文档更正 / Docs correction:VELOCITY / ACCELERATION 描述由「结构本地 / Structure-local」更正为「方块本地 / Block-local」——代码始终按方块 FACING 将结构运动分解到方块自身坐标轴(与 FORWARD/ATTITUDE 的朝向相关语义一致),文档此前与代码不符。/ VELOCITY / ACCELERATION descriptions corrected from "Structure-local" to "Block-local" — the code always resolves structure motion into the block's own axes via FACING (consistent with the facing-dependent FORWARD/ATTITUDE semantics); the docs previously contradicted the code.
⏱️ 保险节点长信号支持 / FUSE Long-Signal Support
- 长信号 = 脉冲发生器 / Held-high input = pulse generator:FUSE(保险)此前只在输入上升沿触发一次(2 tick 脉冲 → 冷却),持续高电平期间冷却结束后不会再次触发,无法当脉冲发生器。现在输入持续高电平时,冷却结束后自动再触发(周期 ≈ 2 + cooldown),输入变低即停止;上升沿仍立即触发、冷却期间的新上升沿仍被忽略、输入在脉冲/冷却中途变低时当前一轮完整走完——旧行为完全兼容。/ FUSE previously fired only once on a rising edge (2-tick pulse → cooldown) and never re-fired while the input stayed high, so it could not act as a pulse generator. A held-high input now re-fires after each cooldown (period ≈ 2 + cooldown) and stops when the input drops; rising edges still fire immediately, new rising edges during cooldown are still ignored, and a drop mid-cycle lets the current cycle finish — fully backward compatible.
- 脉冲宽度修正 / Pulse-width fix:文档与注释均写「2 tick 脉冲」,旧代码因计数器 off-by-one 实际只输出 1 tick——已修正为真正的 2 tick(触发 tick + 1)。/ The docs and comments claimed a "2-tick pulse" but an off-by-one in the pulse counter emitted only 1 tick — now a true 2-tick pulse (fire tick + 1).
- 回归测试 / Regression tests:新增
FuseLongSignalTest(4 例):上升沿 2 tick 脉冲+冷却、持续高电平循环脉冲(t0/t6/t12 触发)、中途变低当前轮走完+重武装、冷却期上升沿忽略+长信号再触发。/ NewFuseLongSignalTest(4 cases): rising-edge 2-tick pulse + cooldown, held-high repeating pulses (fires at t0/t6/t12), mid-cycle drop completes then re-arms, cooldown-period rising edge ignored + held-high re-fire.
🔌 时序节点参数引脚修复 / Sequential-Node Param-Pin Fix
- 可连线编辑区参数引脚生效 / Wireable edit-area (param) pins now work:DELAY/PULSE_EXTEND/LOOP/FUSE 等时序节点的参数引脚(可连线编辑区)此前连上信号没有任何效果——通用参数覆盖机制(连线值临时覆盖
node.params)只应用在eval()默认路径,时序节点走evalExt()直接读node.params,从未应用覆盖。修复后evalExt顶部统一应用、尾部恢复(连线值只在该 tick 生效,不污染 EditBox/NBT,断开连线恢复默认)。/ The wireable edit-area (param) pins of sequential nodes (DELAY/PULSE_EXTEND/LOOP/FUSE) previously had no effect when wired — the generic override mechanism (wired values temporarily replacenode.params) only ran in theeval()default path, while sequential nodes go throughevalExt()and readnode.paramsdirectly.evalExtnow applies the override up front and restores afterwards (wired values last one tick only — the EditBox/NBT stay clean and un-wiring restores the default). - DELAY 入队移入求值器 / DELAY enqueue moved into the evaluator:DELAY 的入队此前在方块实体(求值器外)读取
params[0]——参数恢复后读不到连线值。现入队并入 DELAY 求值分支(与子图一致),连线的 duration 即刻生效;BE 侧入队代码移除。/ The DELAY enqueue previously lived in the block entities (outside the evaluator), readingparams[0]after the override was restored — so a wired duration never applied. The enqueue now lives inside the DELAY evaluation branch (matching sub-graphs); the BE-side enqueue was removed. - 边界行为(维持现状)/ Edge behavior (unchanged):
<0/0一律钳制为 1(最小时长),非整数向零截断(2.9→2);INTEGRATOR limit<0 输出恒 0、负 step 反向计数由玩家自行负责。/ Values<0/0clamp to 1 (minimum duration); non-integers truncate toward zero (2.9→2); INTEGRATOR limit<0 forces output 0 and negative step counts backwards — left to the player. - 回归测试 / Regression tests:新增
SequentialParamPinTest(3 例):FUSE cooldown 连线(cd=5→周期 6)、PULSE_EXTEND duration 连线(3 tick)、LOOP count+interval 连线(count=2/interval=3)。/ NewSequentialParamPinTest(3 cases): FUSE cooldown wired (cd=5 → period 6), PULSE_EXTEND duration wired (3 ticks), LOOP count+interval wired (count=2/interval=3).
🖥️ 显示器设置面板合并 + 统一细边框 / Monitor Settings-Panel Merge + Unified Thin Border
| Feature / 功能 | Description / 说明 |
|---|---|
| 🔀 设置面板合并 / Merged settings panel | 3D 与 HUD 两组设置不再用 tab 切换——面板顶部一个 HUD 模式复选框(hudMode 布尔开关),下方 3D 8 项 + HUD 6 项字段常显,非激活组置灰禁用(保存时两组都写入,切回模式数值不丢)/ 3D and HUD settings no longer switch via tabs — a single HUD-mode checkbox (hudMode boolean) sits at the top with both field groups (3D 8 + HUD 6) always visible; the inactive group is greyed out (both groups are still saved, so values survive mode switches) |
| 📏 统一细边框 / Unified thin border | 3D 模式丢弃 0.04 方块粗边框(drawBorderFace 已删除),改用与 HUD 一致的 4 条 addThickLine 细线(≈1 像素)——两模式视觉一致 / 3D mode drops the 0.04-block border (drawBorderFace removed) for the same 4 addThickLine fine lines as HUD (≈1 px) — both modes now look identical |
| 🔍 虚像屏幕大小 / Virtual-image scale | 新增设置 virtualImageScale(虚像缩放系数,默认 1.0,范围 0.25–4.0):只缩放 HUD 虚像内容画布,与物理玻璃面板(panelSizeX/Y)解耦——调大虚像时玻璃不变,超出玻璃视口的内容被 4 边形遮罩裁剪(透过玻璃看 HUD,物理正确)/ New setting virtualImageScale (default 1.0, range 0.25–4.0): scales only the HUD virtual-image content canvas, decoupled from the physical glass (panelSizeX/Y) — enlarging the image leaves the glass unchanged and content beyond the viewport is clipped by the 4-gon mask (physically correct through-glass view) |
| 💾 数据契约扩展 / Data-contract extension | MonitorSettingsPacket 增至 15 字段(virtualImageScale 追加在 HUD 组末尾);BE 新增 NBT key vis——旧档缺省回落 1.0,无迁移 / MonitorSettingsPacket grows to 15 fields (virtualImageScale appended after the HUD group); new BE NBT key vis — legacy saves default to 1.0, no migration |
| 🧪 编解码回归测试 / Codec regression test | 新增 MonitorSettingsPacketCodecTest:15 字段编解码往返逐字段一致 + 字段流顺序断言(vis 位于 HUD 组之后)/ New MonitorSettingsPacketCodecTest: 15-field codec round-trip + byte-stream order assertion (vis sits after the HUD group) |
🎯 可编程变速箱 + 数控齿轮箱 / Programmable Transmission + CNC Gearbox
| Feature / 功能 | Description / 说明 |
|---|---|
| ⚙️ 可编程变速器 / Programmable Transmission | 新方块 programmable_transmission:由节点图编程目标转速(TX_OUT 节点),经 RotationPropagatorMixin 以绝对速度传导到机械网络 / New block: programs an absolute target RPM from the node graph (TX_OUT node), conveyed to the kinetic network via RotationPropagatorMixin |
| 🎛️ 数控齿轮箱 / CNC Gearbox | 新方块 cnc_gearbox:离合 + 运动配额(quota)——速度由上游变速器决定,运动方块只做接合/脱开 / New block: clutch + motion quota — speed comes from the upstream transmission; the motion block only clutches |
| 📜 指令栈节点 / Command-stack nodes | MOVE(米)/ ROTATE(度)/ WAIT(tick)——触点上升沿入队;CLUTCH 保持常接合意图;ENCODER 报告位置/速度,带电平触发复位引脚 / Rising edge enqueues; CLUTCH keeps standing engagement; ENCODER reports position/velocity with a level-triggered reset pin |
| 🧩 参数引脚 / Param pins | 运动类节点输入成为可编辑参数(可选连线覆盖),与 CLAMP min/max 同机制 / Motion-category node inputs become editable params with optional wire override |
| 🧠 触发级内存 / Trigger-level memory | nodeEdge 状态跨重编译、BE 重建与存档重载存活 / survives recompiles, BE recreation and reloads |
| 🐛 修复 / Fixes | 负方向网络不再卡死运动配额(quota 永续楔死)/ negative-direction networks no longer wedge the motion quota forever |
| 📚 文档 / Docs | docs/programmable-gearbox-plan.md · docs/programmable-gearbox-eval.md · docs/programmable-gearbox-handoff.md · docs/graph-host-convergence-plan.md |
💾 运行时状态跨存档重载存活 / Runtime State Survives World Reloads
| Fix / 修复 | Description / 说明 |
|---|---|
| 🗃️ 全量恢复 / Full restore | 存档写入的始终是完整运行时状态,但恢复侧长期只读回 pidState 一项——delayQueues、flipflopStates、pulseTimers、debugTime、nodeEdge、subStates 六类在每次重载后静默归零。现由 RuntimeState.putAllFrom() 单点全量恢复,继承线与 Kinetic 线共用同一入口 / Saves always wrote the full runtime state, but the restore side read back only pidState — six more categories silently reset on every world reload. RuntimeState.putAllFrom() now restores all seven in one place, shared by the inheritance and the kinetic line. |
| ⚠️ 触发误重触发 / Spurious re-trigger | 触发电平(nodeEdge)丢失会把"常高"信号误判为新的上升沿 → 存档重载或区块重载后 MOVE/ROTATE/WAIT 指令被反复重新入队("输入一次指令后一直转")。已修复并由回归测试守着 / Losing the trigger level re-fired held-high signals as fresh rising edges, re-enqueuing motion commands after every reload ("one command, spins forever"). Fixed and covered by regression tests. |
| 🧩 分叉抹平 / Divergence removed | Blueprint / ProgramComputer / Radar 原先各自在子类里补互不相同的恢复子集,其余四种 BE 只有 pid;现统一上提到基类,子类不再打补丁 / those three used to patch in different subsets while four other BEs got pid only; it now happens once in the base class. |
| ⚙️ Kinetic 线子图状态 / Kinetic-line sub-graph state (行为变更) | 可编程变速器 / 数控齿轮箱此前不恢复 subStates——封装(ENCAPSULATION)内的 DELAY / LATCH / T_FLIPFLOP / PID 在存档重载或离合翻转导致的 BE 重建后归零。现与原生线一致恢复。注意三线的性质不同:原生线 Blueprint / ProgramComputer / Radar 原本就在恢复(保持行为),ControlSeat / Sensor / Monitor / SpeedProxy 为新增恢复,Kinetic 线为新增恢复(行为变更)/ The transmission and CNC gearbox used to skip subStates, so every timing node inside an encapsulation reset on world reload or on the BE recreation caused by a clutch flip. Now restored like the native line. The three groups differ in kind: Blueprint / ProgramComputer / Radar already restored it (behaviour kept), ControlSeat / Sensor / Monitor / SpeedProxy gain it, and the kinetic line gains it (a behaviour change). |
| ⏱️ 信号发生器相位 / Generator phase (行为变更) | ControlSeat / Radar / Sensor 改用 recompileEvaluatorFull(),debugTime(信号发生器相位)跨重编译保留,与 Monitor / SpeedProxy 的 light 路径对齐;老路径 recompileEvaluator() 已删除 / these three now keep the generator phase across recompiles, matching the light path used by Monitor / SpeedProxy; the legacy recompileEvaluator() path is gone. |
| 🧪 测试 / Tests | RuntimeStateRestoreTest 新增 7 例,含"只恢复 pid 必然重触发"的负例对照;全量 332 例通过 / 7 new cases including a negative guard that proves the old behaviour re-fires; full suite of 332 green. |
🔀 图宿主收敛 · 阶段 1(合并逻辑与回弹保护)/ Graph Host Convergence · Phase 1
| Change / 变更 | Description / 说明 |
|---|---|
| 🧩 合并逻辑上提 / Merge hoisted | 7 个 BE 各自覆写的 IMergeableBE.accept() 上提到 SyncedGraphBlockEntity,类型特定字段改由新增的 acceptTypeSpecific() 钩子承载(与 loadTypeSpecific 同构)。七个近乎逐字相同的实现删去六份半,纯去重(唯一例外见下条)/ Six and a half near-verbatim copies removed; pure de-duplication except for the item below. |
| 📡 SpeedProxy 补发方块更新 (行为变更) | SpeedProxy 合并时原本不发送 sendBlockUpdated,客户端拿不到新的 getUpdateTag;现与其余六个 BE 对齐 / SpeedProxy used to skip it, so tracking clients never received a fresh update tag after a merge. |
| 🛡️ Radar 回弹保护恢复 (行为变更,bug 修复) | Radar 的 loadAdditional 原先重复执行图加载,且不检查 pendingLocalOps / 像素绘制 / 显示拖拽 —— 等于关掉基类三道回弹保护:编辑器打开、正在绘画或拖拽元素时,服务端同步包会砸掉本地图(孤儿化 pixelEdit.node / draggedDisplayNode,表现为"图像变透明""拖拽不跟手")。现已恢复三道护栏 / Radar used to re-load the graph with none of the three guards, so a server sync could clobber in-progress edits (wiped pixels, drags that stop following the cursor). |
| 🧭 回弹判定收敛 / Guard converged | 三道护栏的判定收敛到 GraphHostOwner.isGraphReplaceBlocked(pendingLocalOps) 一处,继承线与组合线(GraphHost)共用,不再各写一份导致判定漂移 / One shared implementation instead of two copies that could drift apart. |
| 🤝 跨变体合并保持 / Cross-variant merge kept | 上提后的类型判定允许基类 ↔ Sable 兼容变体(compat/*BlockEntitySable 四个子类不覆写 accept)双向合并,与旧 instanceof 语义一致 —— 整合包中途加装/移除 Sable 时两种 BE 会在同一世界共存 / Base ↔ Sable variant merges still work, matching the old instanceof semantics; both kinds coexist when Sable is installed or removed mid-game. |
🔀 图宿主收敛 · 阶段 3(薄壳化清理与归档)/ Graph Host Convergence · Phase 3
| Change / 变更 | Description / 说明 |
|---|---|
| 🧹 编辑器保存路径统一 / Editor-save path unified (行为变更,bug 修复) | Blueprint / Radar / Monitor 三个各自为政的 loadGraphFromBytes 覆写删除/收缩,统一走引擎 loadGraphFromBytes → loadEditorTag。顺带修复三处分叉缺陷:Radar 缺 BUS 注销——编辑保存后旧图通道在 SignalBus 泄漏;Blueprint / Radar 缺子图/触发器状态清理——封装内时序跨编辑保存残留;Monitor 缺全量同步推送——保存后其他客户端的显示器图保持陈旧 / The three divergent loadGraphFromBytes overrides are gone in favor of the engine path (loadEditorTag). Three latent divergences fixed: Radar skipped BUS unregistration (SignalBus leak across editor saves), Blueprint/Radar skipped the sub-graph/flipflop clear (stale encapsulation timing), Monitor skipped the full-sync push (tracking clients kept a stale monitor graph). |
| 📻 flipflop 差分广播上提 / Flipflop diff hoisted | Blueprint / ProgramComputer 逐字一致的 30 行触发器差分广播块上提为引擎 broadcastFlipflopDiff()(基线随引擎),两个子类各删 30 行与两个基线字段 / The byte-identical 30-line flipflop diff-sync twins moved into GraphHost.broadcastFlipflopDiff() with the baselines; 30 lines and two fields gone from each subclass. |
| 🏛️ 契约单点 / One contract | GraphHostOwner 并入 GraphBlockEntity(唯一契约,引擎构造参数随之统一),GraphHost 沿用现名;17 个过渡委托桥已标 @Deprecated 并附迁移指南,按 BE 逐个直连后再删(日常维护,不再以计划文档跟踪)/ GraphHostOwner merged into GraphBlockEntity (the single contract, engine takes it as constructor parameter); GraphHost keeps its name; the 17 transitional bridges carry @Deprecated plus a migration guide. |
🐛 联测修复:配额换算与雷达同步 / Fixes from playtesting: quota conversion & radar sync
| Fix / 修复 | Description / 说明 |
|---|---|
| ⏱️ MOVE 配额换算 (行为变更,bug 修复) | MotionQuota 的米制换算多乘了 dt(0.05):官方换算是 speed/512 每 tick(Create 字节码核实:机械活塞以 convertToLinear 为每 tick 位移)。多出的因子让 MOVE 慢 20 倍——90 米 @64RPM 记账 0.00625 米/tick 要 12 分钟,体感即"触发指令后一直旋转"。现 90 米 @64RPM = 720 tick = 36 秒;编码器积分与配额共用同一常量,换算修复后两者保持一致 / The meters conversion carried an extra dt(0.05): the official rate is speed/512 per tick (verified against Create bytecode). MOVE was 20x slower - 90 m at 64 RPM took 12 minutes, reading as "spins forever". Now 36 s. Encoder and quota share the constant, so they stay consistent. |
| 📡 雷达目标推送门控 (行为变更,性能修复) | 雷达 tick 末尾无条件推送完整 BE NBT(含整张图)——自雷达诞生起如此。所有追踪客户端每 tick 重载图:编辑雷达时编辑器"图每次重建"的根因。现仅目标列表有变化时推送;blip 的实时数值本就走 EvalSnapshot 广播,空闲时零推送 / The radar's tick ended with an unconditional full-BE-NBT push (whole graph included) every tick since the radar was born - every tracking client reloaded the graph per tick, which read as "the radar graph rebuilds all the time" in the editor. Now gated on an actual target-list change; live blip values already flow via EvalSnapshot. |
| 🏗️ 字段委托引擎 / Fields delegated to the engine | 阶段 1 改写完成:基类只剩 private final GraphHost host + 同名访问器桥(graph() 等)+ 契约委托,7 个子类与全部外部类(屏幕 / 渲染器 / 包类)改走访问器与契约;引擎补 recompileEvaluatorLight() / invalidateEvaluator() / adoptFrom() / getFlipflopStates(),GraphHost 成为两线唯一引擎实现(净 −370 行)。三处次可见对齐已披露(服务端 NBT 加载立即强制重编译组合拳、快照广播补求值器空守卫、屏幕侧 be.running = start 改走 setRunning()),均可观察行为不变 / Phase-1 rewrite done: the base class keeps only a private final GraphHost host plus same-name accessor bridges and contract delegation; all external pokes moved onto the contract. The engine gains recompileEvaluatorLight() / invalidateEvaluator() / adoptFrom() / getFlipflopStates(), making GraphHost the single engine implementation for both lines (−370 lines net). Three sub-visible alignments disclosed, none observable. |
| 🏷️ 总线/频段命名实时同步 (行为变更) | 总线名与频段名的输入框此前既无 responder 也不走 enterActions —— 只在点编译或关屏时才批量上传,协作者看不到正在改的名字;而 PRIVATE / TEXT 等同类命名框一直是逐字符同步的,行为不一致。现停止输入约 0.5 秒(10 tick)即自动提交,仍走原本的定向 op 与 BusBandUploadPacket(不做整图上传)。用防抖而非逐字符:总线名提交要清旧频道全局数据、重评估冲突、按 BUS_IN/BUS_OUT 分别处理频段,逐字符会把 "abc" 打成 a→ab→abc 三个频道,对端 BUS_IN 还会反复跟着换频段定义;防抖一次只发最终值。编译/关屏的兜底提交保留 / Bus and band name boxes had neither responder nor enterActions — they were only uploaded on compile or screen close, so collaborators never saw a rename in progress, unlike the PRIVATE/TEXT name boxes which synced per keystroke. Now auto-committed ~0.5 s (10 ticks) after typing stops, still via targeted op / BusBandUploadPacket (no whole-graph upload). Debounced, not per-keystroke: committing a bus name clears the old channel's global data, re-evaluates conflicts and handles bands per direction, so per-keystroke would create a→ab→abc as three channels and keep swapping the peer's BUS_IN band definition; debouncing sends only the final value. The compile/close fallback stays. |
| 👻 AR HUD 鬼影溢出 (bug 修复) | 画布与字形顶点跑到相机后方(fz>0)时,锚定比例 s=zAnchor/fz 变负 → 顶点被镜像到屏幕对侧,quad 的一条边横扫整个屏幕。实测 θ=60°(普通斜视,远未到掠射)即触发:fz=+34、s=−0.08,而 s 钳制阈值是 ±1e4 —— 量级差五个数量级,完全拦不住(历史测试也注明"s 钳制不覆盖镜像剔除")。现渲染前把几何裁到相机平面,留 1 格边距:裁到 fz=0 会让边界顶点正好落在相机平面上 → s=zAnchor/0=−Infinity → 又被镜像。全部顶点在相机前方时短路,逐像素零额外开销 / When canvas or glyph vertices fall behind the camera (fz>0), the anchor ratio s=zAnchor/fz goes negative and the vertex is mirrored across the screen, stretching one quad edge over the whole view. Measured at θ=60° — an ordinary oblique view, nowhere near grazing: fz=+34, s=−0.08, while the s clamp sits at ±1e4, five orders of magnitude away (the older test even noted "s clamp does not cover mirror rejection"). Geometry is now clipped to the camera plane before emission, with a 1-block margin: clipping to fz=0 puts the boundary vertex exactly on the camera plane → s=zAnchor/0=−Infinity → mirrored again. Short-circuits when everything is in front, so per-pixel cost stays zero. |
🖥️ 编辑器顶栏与设置 / Editor Top Bar & Settings
| Change / 变更 | Description / 说明 |
|---|---|
| 🏷️ 图名称与编辑器顶栏 (新增) | 编辑器顶部新增固定顶栏:左侧是图名称输入框(逐字符同步,走新图级 op SET_BLOCK_NAME——名称挂在 NodeGraph 上随图序列化与同步,纯视觉 op 不触发重编译),右侧是设置按钮。此前同类方块在终端里无法区分 / A fixed top bar: the graph name box on the left (synced per keystroke via the new graph-level op SET_BLOCK_NAME — the name lives on the NodeGraph, serialized and synced with it, visual-only so no recompile) and a settings button on the right. |
| 🔎 便携终端搜索与自定义名 (行为变更) | 便携终端设备名自定义名优先、类型名回退,两条取名路径(本地区块扫描与 Sable 无线回包)均已对齐;并新增搜索框(按名称/坐标过滤,不区分大小写)——渲染、点击命中、滚动条几何共用同一过滤列表,否则行号错位会打开错误的设备 / Custom names win over type names in BOTH scan paths (local chunk scan and the Sable packet — the two name sources had to be aligned), plus a search box filtering by name/coordinates. Rendering, click hit-testing and scrollbar geometry all consume the same filtered list. |
| ⚙️ 独立全屏设置界面 (重构) | 顶栏设置按钮打开独立全屏设置界面(左侧竖排 tab 列:界面颜色 / 键位绑定 / 节点指南 + 返回项):切走时编辑会话保持、返回幂等重新加入;界面颜色内嵌调整——16 项色板 + 默认/应用 + 停靠取色器,点「调整」滑出全宽形态 / The top-bar settings button opens a standalone full-screen settings screen (vertical tab column: colors / key bindings / node guide + back): switching away keeps the edit session alive and returning re-joins idempotently; colors are adjusted in place — 16 swatch entries + defaults/apply + a docked palette, expanding to a full-width form via the adjust button. |
| 🗑️ 工具栏样式按钮移除 (行为变更) | 编辑器工具栏的「样式」按钮与 16 色配置面板已删除——独立设置界面内嵌了同样的颜色调整(默认/应用/取色器齐备),界面颜色只保留设置界面一处入口;蓝图计算机的导入/导出按钮左移补位;注释节点的颜色编辑弹窗与显示器/像素编辑器的取色器不受影响 / The editor toolbar's Style button and the 16-color config panel are removed — the standalone settings screen embeds the same color adjustment (defaults/apply/picker included), making settings the single entry for UI colors; the Blueprint import/export button shifts left to fill the gap; the comment-node color popup and the Monitor/pixel-editor pickers are unaffected. |
| 🖱️ 颜色列表滚动条 (新增) | 设置界面「界面颜色」tab 的颜色列表新增可拖动滚动条:thumb 按住拖拽、轨道点击上下翻 3 行(书签面板同款交互),样式与节点指南 tab 的滚动条一致;行区右移出 10px 条带给滚动条,调整按钮随之左移;滚轮滚动行为不变 / The colors list in the settings screen's colors tab gains a draggable scrollbar: press-drag the thumb or click the track to page by 3 rows (bookmark-panel interaction), styled like the guide tab's scrollbar; the row strip moves 10px left to make room (adjust buttons follow); wheel scrolling is unchanged. |
| ⌨️ 屏幕虚拟键盘绑键 (行为变更) | 键位绑定 tab 点击动作行后整个界面左滑,右侧展开无 F 行紧凑配列的屏幕虚拟键盘(Esc+数字行+三行字母+底部修饰行+Home/方向键)与鼠标左/右/中三键;选中动作的现值键帽描边显示;旧的「监听下一个物理按键」流程退役——键位绑定改为纯屏幕操作,物理键盘不再参与 / Clicking an action row in the key-bindings tab slides the UI left into an on-screen virtual keyboard (no-F-row compact layout) with the three mouse buttons; the action's current binding is outlined on the caps; the old "listen for the next physical key" flow is retired — rebinding is purely on-screen. |
| ⌨️ 删除选中/框选可绑定 (行为变更) | 「删除选中节点」与「框选多选」纳入绑定表(现共 10 个动作):删除选中默认 Delete——原为 Backspace/Delete 双硬编码,现 Backspace 不再删除选中节点(可自行把 Backspace 绑上去),框选默认 Tab(补全弹层优先消费的行为保留:弹层可见时框选键先接受补全;改绑后按键与释放都跟随绑定键);虚拟键盘新增 Del 键帽(Home 旁);键位列表改为滚动窗口并附可拖动滚动条(thumb 拖拽 / 轨道 ±3 行翻页 / 滚轮,与颜色列表同款),「收起」按钮固定在列表下方不再被顶出屏幕 / "Delete selected" and "Box-select" join the binding table (10 actions now): delete-selected defaults to Delete — previously hardcoded to both Backspace and Delete, Backspace no longer deletes the selection (bind it yourself if wanted); box-select defaults to Tab with the suggestion-popup-first behaviour preserved (a visible popup consumes the box-select key; both press and release follow the bound key); the virtual keyboard gains a Del cap next to Home; the key-list becomes a scrolling window with a draggable scrollbar (thumb drag / ±3-row track paging / wheel, colors-list style) so the Collapse button always stays visible below the list. |
| ⌨️ 键序连招绑定 (行为变更) | 键位绑定升级为序列模式:每个键盘动作是一条 1~4 步的有序序列(每步 = 主键+修饰位),虚拟键盘上点键帽即追加步骤(修饰键帽挂起、随步骤入列自动复位),操作条 = 删一步 / 默认 / 清除 / 确定绑定,预览行实时显示「绑定: Ctrl+K → D」;确定时替换该动作整条序列。匹配引擎逐键推进:完整命中触发、前缀缓冲等待(1.5s 超时作废)、无匹配清缓冲并以当前键为新首步重开(vim 式);输入框聚焦不推进。冲突规则:等长键序全同必拒;不等长时短者键前缀且短者修饰 ⊆ 长者才拒——Ctrl+D 单步与 D→K 连招可共存。单步序列与旧单键行为逐项一致,旧配置(key/mods)读取时无损迁移为单步序列(新格式 seq);鼠标动作(平移/菜单)不参与序列保持单键 / Key bindings are now sequence-based: each keyboard action holds one ordered 1–4 step sequence (each step = key + modifier mask). On the virtual keyboard a cap click appends a step (modifier caps latch and clear with the step); the bar is Del-step / Default / Clear / Bind; the preview line shows "Bind: Ctrl+K → D" live; Bind replaces the action's whole sequence. The matcher advances per keystroke: full match fires, a prefix buffers (1.5 s timeout), a miss clears and re-evaluates the key as a new first step (vim-style restart); focused inputs never advance it. Conflicts: equal-length identical key sequences are always refused; for different lengths only a key-prefix with the shorter's mods contained in the longer's is refused — a Ctrl+D single step and a D→K combo can coexist. Single-step sequences replicate the old single-key behaviour exactly, and legacy configs (key/mods) migrate losslessly into single-step sequences on load (new seq format); mouse actions (pan/menu) stay single-button. |
| ⌨️ 画布交互可重绑 (行为变更,默认不变) | 平移画布、上下文菜单、删除/撤销/重做/复制/重置视角/保存书签的按键此前全部硬编码(左键拖图、右键菜单、X 删除、Ctrl+Z…)。现由 EditorKeys 绑定表统一管理,持久化到客户端配置;默认值与旧行为逐项一致。设置弹窗内点击动作行即可重绑(Esc 取消),同类冲突拒绝;每行附"默认"恢复按钮 / Pan, context menu, delete, undo, redo, duplicate, reset-view and bookmark keys were all hardcoded. They now live in an EditorKeys binding table persisted into the client config; defaults replicate the old behaviour exactly. Click an action row in settings to rebind (Esc cancels); clashes are refused; each row has a reset button. |
| 📖 节点指南 (新增) | 设置弹窗的指南 tab 从 NodeType 元数据自动生成全部 92 个节点类型(名称走既有 lang 键、引脚数与参数名来自枚举字段)——零手写文案,新增节点自动跟上;说明文案可随后按需补 lang 键 / The guide tab is generated from NodeType metadata (names from existing lang keys, pins and params from the enum fields) — all 92 types with zero hand-written copy, and new nodes appear automatically. Descriptions can be added later as lang keys. |
| 📖 节点指南详情面板 (新增) | 指南 tab 点击任意节点行 → 界面左滑展开(与颜色/键位 tab 同款形态):左侧节点栏带可拖拽滚动条,右侧详情面板显示该节点元信息(入/出/参数)与逐行换行的详细说明;ESC 或列表下方「收起」按钮退回全宽列表;收起态悬停行仍在底部预览一行截断说明 / Clicking any row in the guide tab slides the UI left (same expansion as the Colors/Keys tabs): the node bar on the left gains a draggable scrollbar, and a detail pane on the right shows the node's metadata (in/out/params) plus its wrapped description. ESC or the Collapse button below the list returns to the full-width list; hovering a row in the collapsed list still previews a truncated line at the bottom. |
| 📖 全量双语节点文案 (新增) | 93 个节点类型全部补齐中英双语详细说明,写入 gui…guide.<TYPE> lang 键(设置 → 节点指南即读);审阅稿见 docs/node-guide.md(由流水线从 lang 键生成,符号已清洗,统一约定:布尔判定 >0.5(BOOL 例外 >0)、三角按度等)。说明覆盖功能定位、引脚语义、参数含义与常见陷阱 / All 93 node types now ship full bilingual (zh/en) descriptions in the gui…guide.<TYPE> lang keys consumed by Settings → Node Guide. The review copy lives in docs/node-guide.md (generated from the lang keys; symbol-sanitised; shared conventions such as boolean truth >0.5 — BOOL is >0 — and degrees). Copy covers purpose, pin semantics, parameters and common pitfalls. |
🦾 数控齿轮箱两端轴动画与输入端跟随 / CNC Gearbox Two-Shaft Animation & Input-Face Following
| Feature / 功能 | Description / 说明 |
|---|---|
| 🌀 两端独立轴动画 / Independent two-shaft animation | 两端各一根独立旋转的轴端帽(官方 SplitShaft 语义 + 官方角度公式与棋盘相位):输入端恒随网络转速旋转;输出端受离合控制——接合随网络、分离静止。vanilla 回退渲染器与 Flywheel 双实例双路径同语义(Sable 等模组把 Flywheel 后端压到 off 时自动走回退渲染器)/ Each end carries an independently rotating shaft cap (official SplitShaft semantics + the official angle formula and checkerboard phase): the input end always spins at network speed; the output end is clutch-controlled — network speed while engaged, static while disengaged. The vanilla fallback renderer and the Flywheel dual-instance path share the semantics (the fallback takes over when mods like Sable push Flywheel's backend to off). |
| 📐 端帽对齐与机壳朝向 / Cap alignment + housing rotation | axis=x 时端帽此前缺少 +Z→+X 对齐(绕轴心扫圈如风扇叶)、机壳 y=90 与输入面语义镜像;现按官方 rotateToFace(SOUTH, dir) 组合对齐、机壳改 y=270,端帽与机壳在两轴向上完全一致 / On axis=x the end caps previously missed the +Z→+X alignment (sweeping the axis like fan blades) and the housing y=90 mirrored the input-face semantics; now aligned via the official rotateToFace(SOUTH, dir) composition with the housing at y=270 — caps and housing agree on both axes. |
| 🔁 扳手翻面重排动力源 / Wrench flip re-sources | 扳手翻面此前只切换状态不重排动力——旧 source 仍指向(翻面后已无轴面的)输出侧,方块保持被倒挂驱动,两端动画语义与实际链条脱节。翻面后现走官方拆建序列(network.remove → detachKinetics → removeSource → attachKinetics)从新输入面重新认源 / The wrench flip only cycled the state without re-sourcing — the old source kept pointing at the (now shaft-less) output side, leaving the block driven backwards with its input/output animation disconnected from the real chain. Flipping now runs the official teardown sequence (network.remove → detachKinetics → removeSource → attachKinetics) and re-sources from the NEW input face. |
| 🧲 放置双侧感知 / Placement senses both sides | 放进完整链条缺口(两侧都是动力方块)时旧逻辑恒定输入=西,与电机在哪侧无关。现比较两侧邻居的实时转速,强转侧为输入面(打平仍默认西,扳手可调)/ Placing into a running chain gap (kinetic neighbours on both sides) always bound input=west regardless of where the motor was. The two neighbours' live speeds are now compared and the stronger-spinning side becomes the input face (a tie still defaults west — the wrench overrides). |
| 🧭 输入面自动跟随 (行为变更) / Input-face auto-follow | 把动力换到另一侧(挪电机/重接链条)后台块不再死锁旧端:空闲(分离 + 无源 + 停转)时若对侧邻轴在转而输入侧邻轴不动,输入面自动翻向驱动侧并主动重探连接。有驱动、在转、接合中或两侧都在转时不翻(歧义交给扳手)——运行中的方块永不自动换向 / Moving the power to the other end (relocating the motor / re-routing the chain) no longer dead-ends the block on its old side: while idle (disengaged + sourceless + stopped), if the neighbour on the OPPOSITE side spins while the input-side neighbour doesn't, the input face flips toward the drive and re-probes the connection. Never flips while sourced, spinning, or engaged, nor when both sides spin (genuine ambiguity — the wrench decides). |
💥 变速器拆建不再炸方块 / Transmission Teardown No Longer Destroys the Block
- 炸方块链条 / The destroy chain:官方
validateKinetics的清理顺序是先removeSource(自身速度清 0)再detachKinetics,而handleRemoved在速度为 0 时短路早退——以变速器为源的下游树不会被清洗(残留旧源+旧转速)。之后任何一次 attach 传播里,下游轴经无源自驱引导把变速器反向收编(source 指到输出侧),TX→上游边再把绝对目标推向异号的上游速度 → 命中官方incompatible守卫destroyBlock;堵住收编后同网的残留树又会命中 epsilon-cycle 守卫。官方 SpeedController 靠「驱动轮恒在 UP 方向、邻居迭代序靠前」避开,直通串联的变速器没有这层保护 / OfficialvalidateKineticsremoves the source (zeroing speed) BEFORE detaching, sohandleRemovedearly-returns on speed==0 and the downstream tree stays sourced to the transmission and spinning. On the next attach pass the downstream shaft claims the transmission backwards through the sourceless bootstrap; the TX→upstream edge then pushes the absolute target into an opposite-sign upstream speed, hitting the officialincompatibleguard (destroyBlock); once the claim is blocked, the same stale tree trips the same-network epsilon-cycle guard instead. The official SpeedController dodges this only because its drive cog is always UP, early in the neighbour iteration order — an inline transmission has no such luck. - 三层修复 / Three-layer fix:① 依赖方边完美 no-op——source 指向本体的邻居在
getDesiredOutputSpeed返回本体自身转速(newSpeed==邻速走官方|差|≤1e-4 → continue;返回 0 会被官方兜底重挂载块反向改爹);② 孤儿动力态预检——源失速/幻影带速时趁自身速度非 0 先 detach,让官方清洗正常运行;③forceCleanDependentTree——无源停转的终态下自走子树逐节点官方removeSource/ ① dependent-edge perfect no-op ingetDesiredOutputSpeed: a neighbour whose source points at us gets our own current speed back (official|diff|<=1e-4 → continue; returning 0 lets the official fall-through re-parent block flip us under the dependent); ② orphaned-kinetic-state pre-tick hook: with a dead source or phantom speed, detach while our own speed is still non-zero so the official cleanup can run; ③forceCleanDependentTree: in the terminal sourceless-and-stopped state, walk the subtree and give each node the officialremoveSource(). - 验证 / Verified:RCON 台架实测——0→64→128→64→±64 跨号全部安全、60 轮高频改目标 0 销毁(修复前第 3 轮必炸)、完整拓扑(电机→轴→变速器→轴→CNC→轴)同时改目标+发指令 20/20 全活;单元测试全绿 / RCON bench: the full transition matrix incl. sign crosses holds, 60 rounds of high-frequency target flips with zero destruction (deterministic destroy at round 3 before), and 20/20 rounds of the full topology with simultaneous target flips and CNC commands; unit tests green.
🔧 ENCODER 注入与迁移清理 / Encoder Wiring & Migration Cleanup
- ENCODER 实机恒 0 修复:数控齿轮箱构造器此前只注入指令栈 sink、漏注编码器视图——
GraphEvaluator.encoderView恒为 null,游戏内 ENCODER 三输出恒 0、复位无效(单元测试手动注入视图故全绿)。现setEvaluatorCustomizer同时注入setCommandSink+setEncoderView;GraphHost 每次重建求值器都会重放该回调 / The CNC gearbox constructor injected only the command sink, never the encoder view —GraphEvaluator.encoderViewstayed null, so ENCODER always read 0 in game (unit tests injected the view manually and stayed green). The customizer now wires bothsetCommandSinkandsetEncoderView; GraphHost replays it on every evaluator rebuild. - V4→V5 迁移移除死锚定盖章 / Dead per-node anchor stamping removed:早期草稿给每个节点盖章
am/ay/ap(逐节点贴玻璃/贴世界锚定),该功能从未落地——GraphNode从不读写这些键、渲染只做显示器级锚定,迁移注释承诺的GraphNode.load默认处理也不存在。现移除盖章(保留子图递归与版本号),注释留档原因与「不要无实现地重新加回」的警示 / An earlier draft stamped per-node AR-HUD anchor tags (am/ay/ap) for a feature that never landed —GraphNodenever reads or writes them and rendering anchors at the monitor level. The stamping is removed (sub-graph recursion and the version stamp remain), with a comment recording why and warning not to re-add it without implementing the fields and renderer consumption. - 封装时序清单更正 / Encapsulation timing checklist corrected:
docs/encapsulation-timing-state-reset.md第 14 项「BUS 信号快照SignalBus.snapshot()」从未实现,标记为「未采用」并注明现状(BUS/PRIVATE 频道为即时读写共享表) / Checklist item 14 referenced aSignalBus.snapshot()that never existed in the codebase — marked "not adopted", noting the current immediate read/write shared-table behaviour.
🎨 界面颜色主题扩容 16→23 并收编各屏 / Theme Extended to 23 Colors Across Screens
- 新键 7 个 / 7 new theme keys:
panel_bg / panel_header / panel_border / inset_bg / accent / error / hover(默认 = 各屏现值,首屏零视觉变化),设置 → 界面颜色现共 23 项色板(旧配置缺失项自动回落默认,向后兼容) / Seven keys added with defaults matching current visuals; the Settings → Colors list now holds 23 swatches (legacy configs fall back per-key to defaults — backward compatible). - 编辑器漏网色接入 / Editor stragglers wired:强调金(主选/次选、框选、焦点/开关开态、★激活)、悬停行高亮、警示红(公式错误徽章/报告框/警示条)、滚动条 thumb 与部分边框改走
ACC()/HOV()/ERR()/CB()/CSB()——换主题不再失配 / Selection gold, hover highlights, error reds and scrollbar thumbs now read from the theme — no more mismatch when switching themes. - 其它屏收编 / Peripheral screens absorbed:显示器 / 便携终端 / 像素编辑器 / 取色器 的暖深棕面板 chrome(面板底/标题带/边框/内凹井底/金边)与强调色统一走主题访问器(
PBG/PHT/PBR/PINS/ACC/CSB,跨包 public);各屏画布/内容层、文字灰阶、success/danger 动作色与半透明变体保持本地 / The Monitor / Portable Terminal / Pixel editor / Color Picker warm-brown panel chrome now follows the theme via cross-package public accessors; canvas content layers, text greys, success/danger action colours and alpha variants stay local. - MLE 聚焦错误 / MultiLineEditBox error state:聚焦+错误边框与 UNKNOWN 下划线改走
ERR()/ Focused-error border and the unknown-token underline useERR().
Optional dependencies
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:1bUvJALH:rGAuZkoO"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:1bUvJALH:rGAuZkoO"
}

