| name | netease-custom-container-ui |
| description | Use when: 制作、复刻或排查网易我的世界 Add-on 自定义容器、netease:block_container、container UI、inventory_screen_common、回声箱子 UI、容器格子、进度条、空槽提示图显隐、Java GUI 贴图迁移、ScreenProxy、RegisterScreenProxy、容器事件。重点指导资源包 JSON UI 与行为包 Python 双端联动。 |
| argument-hint | 容器方块名、槽位数量、screen_name、UI 需求 |
网易自定义容器 UI 制作工作流
本技能把 echo_chest 模组的自定义容器制作流程抽象成可复用步骤,重点覆盖资源包 UI、客户端 ScreenProxy、服务端容器事件和常见对齐问题。
适用场景
- 新做一个基于
netease:block_container 的自定义方块容器。
- 复刻“自定义箱子 + 原版背包 + 自定义槽位/进度条”的 UI。
- 排查容器打不开、UI 不加载、格子错位、服务端容器事件不触发、进度条不同步。
- 调整容器槽位布局、保留特殊槽位、给槽位叠加图标或动态控件。
- 从 Java 版整张 GUI 贴图迁移到网易/基岩 JSON UI,处理背景拼接、箭头进度条裁剪、空槽提示图显隐。
可用资源
先检查的项目文件
参考当前项目时,优先看这些文件:
- 方块容器声明:
behavior_pack_*/netease_blocks/<block>.json
- UI 注册表:
resource_pack_*/ui/_ui_defs.json
- 容器 UI:
resource_pack_*/ui/<container>.json
- 客户端注册与打开状态:
behavior_pack_*/**/client/*Listen.py
- ScreenProxy:
behavior_pack_*/**/client/ui/*Screen.py
- 服务端容器逻辑:
behavior_pack_*/**/server/*Listen.py
- 常量路径:
behavior_pack_*/**/modConfig.py
总体流程
- 声明容器方块:在行为包方块 JSON 中设置
description.base_block 为 netease_container,并添加 netease:block_container。
- 绑定容器界面:
netease:block_container.screen_name 必须等于 UI 主屏幕名,例如 echo_chest.EchoChestMain。
- 编写资源包 UI:用
common.inventory_screen_common 承接原版容器能力,并在 $screen_content 中放自定义面板。
- 注册 UI 文件:把
ui/<name>.json 加进资源包 ui/_ui_defs.json。
- 注册 ScreenProxy:客户端系统初始化时用
NativeScreenManager.instance().RegisterScreenProxy(screen_name, proxy_path)。
- 在 ScreenProxy 中接管界面:
OnCreate 里拿 ScreenNode,通知客户端系统记录打开状态;OnDestroy 里清理并通知服务端关闭。
- 服务端处理容器事件:监听
ItemPushInCustomContainerServerEvent、ItemPullOutCustomContainerServerEvent、PlayerTryPutCustomContainerItemServerEvent 等事件校验槽位和物品。
- 双端同步动态状态:服务端保存方块实体数据,按需
CallClient 同步 UI 数据;客户端在 OnTick 或回调中更新进度条、动画和 Molang。
行为包方块 JSON 要点
在 minecraft:block.components 中配置:
netease:block_container.container_size:容器总槽位数,必须覆盖 UI 中要展示的槽位数。
netease:block_container.custom_description:服务端容器事件里的 collectionName 常用这个值判断容器来源。
netease:block_container.screen_name:必须精确匹配资源包 UI 的 namespace.screen。
netease:block_entity.tick:需要自动吸物品、经验、更新数据时设为 true。
完成检查:container_size、UI maximum_grid_items、服务端可访问槽位范围三者一致;特殊槽位要在服务端单独限制。
UI JSON 制作重点
1. 主屏幕入口
推荐从原版容器模板继承:
- 定义
<ScreenName>@common.inventory_screen_common。
- 在
variables 中给桌面端/移动端设置 $screen_content。
$screen_content 指向自己的主面板,例如 <namespace>.<main_panel>。
- 顶层要有
namespace。
关键完成条件:screen_name 写成 namespace + "." + 主屏幕控件名。
1.1 Java GUI 贴图迁移到基岩 UI
Java 版容器常把完整背景、槽位、箭头、提示图画在同一张 GUI PNG 上;基岩/网易 JSON UI 不应直接把这张整图作为背景。迁移时按功能拆分:
- 背景面板:用原版/网易 UI 背景拼接,例如
textures/ui/dialog_background_opaque、common.inventory_panel_bottom_half_with_label、common.hotbar_grid_template。
- 槽位:用
common.container_item 或静态 collection_panel/grid 生成真实容器槽位,不要依赖 Java 背景图里的假槽位。
- 进度条/箭头:从 Java GUI 中裁出空箭头底图和填充箭头图,分别作为
empty_progress_bar 与 filled_progress_bar 的贴图;去掉整图背景后,空箭头也必须单独绘制。
- 提示图:从物品图或 Java GUI 裁成小图,放到
$cell_overlay_ref 或脚本控制的 image 中,不能把提示图烙在背景上。
如果只是想还原 Java 截图布局,先记录 Java 像素坐标,再换算成 JSON UI 中的 offset/size。不要把 Java 整张 GUI PNG 当作基岩背景,否则会和原版背包面板、关闭按钮、槽位高亮重复叠加。
2. 容器格子 grid
常用结构:
type: "grid"
collection_name: "netease_container"
grid_item_template: "<namespace>.<grid_item>"
grid_rescaling_type: "horizontal"
maximum_grid_items: <container_size>
<grid_item>@common.container_item,并设置 $item_collection_name: "netease_container"
注意:UI 里的 collection_name 通常保持 netease_container;服务端事件里的 collectionName 不一定是这个值,常按 custom_description 判断。
3. 布局结构
推荐结构:
common.root_panel 承接安全区和输入逻辑。
common.common_panel 承接对话框背景。
common.inventory_panel_bottom_half_with_label + common.hotbar_grid_template 显示玩家背包和快捷栏。
- 自定义容器区域用独立
image 或 panel 包住:背景、容器 grid、标题、特殊进度条。
- 关闭按钮沿用
common.light_close_button / common.compact_close_button,避免重做关闭逻辑。
完成检查:自定义容器区域和玩家背包不要互相遮挡;layer 从背景到物品、按钮逐层递增。
布局排错经验:
- 如果最里层露出一块多余空白背景,检查是否用了
bg_image@$dialog_background。只需要承载控件时改成普通 panel;需要背景时才继承 $dialog_background。
- 自定义上方面板贴住玩家背包时,用面板高度计算偏移:例如玩家背包
offset=[0,48]、高度 96,其顶边约在父面板中心;上方面板高度 44 时常用 offset=[0,-22] 让底边贴齐。
- 关闭按钮放到当前容器面板内,设置
anchor_from/top_right、anchor_to/top_right 和小偏移,避免继承默认外层位置后跑偏。
4. 进度条与特殊控件
可用做法:
- 用
panel 包一组 netease_editor_template_namespace.empty_progress_bar 和 filled_progress_bar。
- 通过变量配置空槽贴图、填充贴图、裁剪方向、九宫格等。
- 在 ScreenProxy
OnTick 中通过 GetBaseUIControl(path).asProgressBar().SetValue(value) 更新。
路径很长时,不要猜;先用 UI 结构确认路径,再集中写成常量或局部变量。动态生成的 grid 子项可能要等 UI 初始化完成后再操作。
进度条常见坑:
- 去掉 Java 整张背景图后,箭头底图会一起消失;必须单独提供空箭头贴图,必要时再加一个静态 image 兜底显示底图。
filled_progress_bar 只负责填充裁剪,不等于会绘制空底图。
- 动态进度控件要放在背景和槽位之上,
layer 通常高于槽位背景,但低于关闭按钮。
4.1 空槽提示图显隐
优先参考原版盔甲/鞘翅槽和织布机槽的做法:
- 在槽位 item 上设置
$cell_overlay_ref,指向一个提示 image,例如 namespace.fly_empty_image。
- 提示 image 自身绑定一个布尔值控制
#visible。
- 如果是原版 collection,可尝试
#empty_image_visible;但网易 netease_container 自定义容器不一定会给这个 binding 正确更新。
- 自定义容器更稳的方案是使用
ViewBinder.BF_BindBool:
- 服务端读取真实容器槽位是否为空。
- 服务端通过
CallClient 同步 inputEmpty 这类布尔状态。
- 客户端系统缓存状态。
- ScreenProxy 中用
@ViewBinder.binding(ViewBinder.BF_BindBool, "#xxx_visible") 返回缓存值。
- UI image 绑定同一个
#xxx_visible。
不要把提示图放到 $background_images 背景层来“靠物品盖住”。这在半透明物品、同形状贴图或缩放差异下会露边,且不是真正隐藏。
需要可复制的 JSON/Python 片段时,读取 ./references/java-gui-porting-and-empty-slot.md。
5. 动态调整槽位或叠加图标
只有 grid 自动生成项难以静态控制时才用 ScreenProxy 延迟修正:
OnCreate 后用短定时器延迟执行,等待 grid 子控件生成。
- 通过完整控件路径拿到目标槽位。
- 调用
SetPosition 调整位置。
- 用
CreateChildControl(template, name, parent, True) 叠加原版模板图标。
客户端 Python 联动
客户端系统职责:
- 在初始化中注册 ScreenProxy。
- 监听
ClientBlockUseEvent,记录玩家打开的是哪个容器方块坐标。
- ScreenProxy
OnCreate 回调到客户端系统,设置打开状态、播放开箱效果、通知服务端 NotifyOpenChest。
- ScreenProxy
OnDestroy 清理 UI 节点、播放关箱效果、通知服务端 NotifyCloseChest。
- 接收服务端同步数据,例如进度值,再由 ScreenProxy 更新 UI。
- 需要动态显隐提示图时,在 ScreenProxy 中使用
ViewBinder 绑定布尔值,不要只依赖 #empty_image_visible。
边界要求:客户端只做 UI、声音、粒子、Molang 表现和请求;容器真实物品、方块实体数据、校验逻辑放服务端。
服务端容器逻辑
服务端系统职责:
- 记录每个玩家正在打开的容器坐标。
- 在容器事件中按
collectionName 和 collectionIndex 做校验。
- 对特殊槽位做白名单/黑名单。
- 在
ServerBlockEntityTickEvent 中读取/写入方块实体数据,处理自动加工、吸取、合堆、生成物品。
- 对正在查看该容器的玩家同步 UI 动态数据。
- 特殊槽位提示图的显隐状态应以服务端真实容器槽位为准,例如同步
{ "progress": 0.5, "inputEmpty": false }。
完成检查:客户端关闭 UI 时服务端记录必须清理,否则会持续推送旧数据。
验收清单
- 方块 JSON:
base_block、netease:block_container、screen_name、container_size 正确。
- 资源包:UI JSON 存在且已写入
_ui_defs.json。
- UI:
namespace.screen、$screen_content、grid.collection_name、grid_item_template、maximum_grid_items 一致。
- 客户端:ScreenProxy 已注册,
OnCreate/OnDestroy 可追踪打开与关闭状态。
- 服务端:容器事件能收到,槽位校验只影响目标容器。
- 动态 UI:进度条、特殊槽位、图标叠加只在 UI 节点存在后操作。
- 空槽提示图:空槽时显示,放入物品后真正隐藏;如果是
netease_container,优先用服务端同步 + ViewBinder 验证。
- Java 贴图迁移:UI 背景由基岩原版面板拼接,Java 整张 GUI 只作为裁剪小贴图来源。
- 双端:客户端表现和服务端数据职责分离,无跨端 API 混用。
- 运行前:用 JSON 解析检查资源包/行为包 JSON;Python 校验优先使用编辑器诊断,避免生成
.pyc。
使用方式
示例请求:
- “用
netease-custom-container-ui 按 9 格容器做一个自定义箱子 UI。”
- “用
netease-custom-container-ui 检查这个容器为什么打开后不是我的 UI。”
- “用
netease-custom-container-ui 给第 24 槽加燃料槽限制和 UI 图标。”
- “用
netease-custom-container-ui 给自定义容器增加服务端同步进度条。”