# Netease Custom Container UI

> Use when: 制作、复刻或排查网易我的世界 Add-on 自定义容器、netease:block_container、container UI、inventory_screen_common、回声箱子 UI、容器格子、进度条、空槽提示图显隐、Java GUI 贴图迁移、ScreenProxy、RegisterScreenProxy、容器事件。重点指导资源包 JSON UI 与行为包 Python 双端联动。

- Skill: `xiaobo121388/netease-custom-container-ui` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add xiaobo121388/netease-custom-container-ui`
- Raw SKILL.md: https://api.skillmd.com/api/skills/xiaobo121388/netease-custom-container-ui/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: xiaobo121388 (https://skillmd.com/u/xiaobo121388)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/xiaobo121388/netease-custom-container-ui

---


# 网易自定义容器 UI 制作工作流

本技能把 `echo_chest` 模组的自定义容器制作流程抽象成可复用步骤，重点覆盖资源包 UI、客户端 ScreenProxy、服务端容器事件和常见对齐问题。

## 适用场景

- 新做一个基于 `netease:block_container` 的自定义方块容器。
- 复刻“自定义箱子 + 原版背包 + 自定义槽位/进度条”的 UI。
- 排查容器打不开、UI 不加载、格子错位、服务端容器事件不触发、进度条不同步。
- 调整容器槽位布局、保留特殊槽位、给槽位叠加图标或动态控件。
- 从 Java 版整张 GUI 贴图迁移到网易/基岩 JSON UI，处理背景拼接、箭头进度条裁剪、空槽提示图显隐。

## 可用资源

- UI JSON 模板：[`./assets/container-ui.template.json`](./assets/container-ui.template.json)
- 客户端 ScreenProxy 模板：[`./assets/client-screen-proxy.template.py`](./assets/client-screen-proxy.template.py)
- 客户端注册模板：[`./assets/client-listen.template.py`](./assets/client-listen.template.py)
- 服务端容器事件模板：[`./assets/server-container.template.py`](./assets/server-container.template.py)
- 排错清单：[`./references/troubleshooting.md`](./references/troubleshooting.md)
- Java GUI 迁移与空槽提示图显隐参考：[`./references/java-gui-porting-and-empty-slot.md`](./references/java-gui-porting-and-empty-slot.md)

## 先检查的项目文件

参考当前项目时，优先看这些文件：

- 方块容器声明：`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`

## 总体流程

1. **声明容器方块**：在行为包方块 JSON 中设置 `description.base_block` 为 `netease_container`，并添加 `netease:block_container`。
2. **绑定容器界面**：`netease:block_container.screen_name` 必须等于 UI 主屏幕名，例如 `echo_chest.EchoChestMain`。
3. **编写资源包 UI**：用 `common.inventory_screen_common` 承接原版容器能力，并在 `$screen_content` 中放自定义面板。
4. **注册 UI 文件**：把 `ui/<name>.json` 加进资源包 `ui/_ui_defs.json`。
5. **注册 ScreenProxy**：客户端系统初始化时用 `NativeScreenManager.instance().RegisterScreenProxy(screen_name, proxy_path)`。
6. **在 ScreenProxy 中接管界面**：`OnCreate` 里拿 `ScreenNode`，通知客户端系统记录打开状态；`OnDestroy` 里清理并通知服务端关闭。
7. **服务端处理容器事件**：监听 `ItemPushInCustomContainerServerEvent`、`ItemPullOutCustomContainerServerEvent`、`PlayerTryPutCustomContainerItemServerEvent` 等事件校验槽位和物品。
8. **双端同步动态状态**：服务端保存方块实体数据，按需 `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 空槽提示图显隐

优先参考原版盔甲/鞘翅槽和织布机槽的做法：

1. 在槽位 item 上设置 `$cell_overlay_ref`，指向一个提示 image，例如 `namespace.fly_empty_image`。
2. 提示 image 自身绑定一个布尔值控制 `#visible`。
3. 如果是原版 collection，可尝试 `#empty_image_visible`；但网易 `netease_container` 自定义容器不一定会给这个 binding 正确更新。
4. 自定义容器更稳的方案是使用 `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`](./references/java-gui-porting-and-empty-slot.md)。

### 5. 动态调整槽位或叠加图标

只有 grid 自动生成项难以静态控制时才用 ScreenProxy 延迟修正：

1. `OnCreate` 后用短定时器延迟执行，等待 grid 子控件生成。
2. 通过完整控件路径拿到目标槽位。
3. 调用 `SetPosition` 调整位置。
4. 用 `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` 给自定义容器增加服务端同步进度条。”

