通道系统(Channel)
基本信息
| 属性 | 值 |
|---|---|
| 存取类 | com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor |
| 描述注册 | com.gtnewhorizon.structurelib.ChannelDescription |
| GUI | com.gtnewhorizon.structurelib.gui.GuiScreenConfigureChannels |
| 承载物品 | Constructable Trigger |
通道用来告诉多方块"这次要用哪个变体"。主通道由物品堆叠数承载(堆几个就是几号档位),子通道则是 NBT 里的额外键值对,用来选择同一档位下的不同分支。
NBT 字段
所有子通道数据存放在物品 NBT 根下的一个复合标签中:
| 字段 | 类型 | 说明 |
|---|---|---|
channels |
复合标签(TAG_COMPOUND) |
键为通道标识(全小写),值为正整数 |
键的约束由源码强制:必须非空且全小写。在 debug 模式下违反会直接抛 IllegalArgumentException(Channel name can be lower case ONLY),正常模式下不校验大小写。
unsetChannelData 在移除后会自动做逐层清理:若 channels 复合标签变空则移除它,若整个根标签变空则把 stackTagCompound 置为 null。因此一个完全没有通道的触发器不会残留任何 NBT。
wipeChannelData 同样会做这层清理。
主通道与子通道的关系
getChannelData(stack, channel) 的行为是取不到就回落到主通道:
- 若
channels中存在该键 → 返回其整数值 - 否则 → 返回
stackSize(物品堆叠数)
因此子通道是"覆盖默认值"而非"附加维度"。withChannel(stack, channel) 则返回一个把子通道值提升为主通道的临时物品栈,若该子通道不存在则原样返回。
存取 API
| 方法 | 作用 |
|---|---|
setChannelData |
写入子通道,值必须 > 0,否则抛异常 |
getChannelData |
读取,不存在时回落到堆叠数 |
hasSubChannel() |
是否存在任何子通道 |
hasSubChannel(channel) |
是否存在指定子通道 |
unsetChannelData |
移除指定子通道 |
wipeChannelData |
清除全部子通道 |
countChannelData |
子通道数量(不含主通道) |
iterateChannelData |
返回 Stream<键值对>(不含主通道) |
withChannel |
将指定子通道提升为主通道 |
iterateChannelData 返回的流不支持修改,底层数据在迭代中被改动仍可能抛出 ConcurrentModificationException。
mod 间注册通道描述
其他 mod 可以通过 IMC(mod 间通信)向 StructureLib 注册通道描述,指令侧也能据此在 GUI 中提供自动补全和 tooltip。StructureLib 处理两个 IMC 键:
| IMC 键 | 载荷 | 处理 |
|---|---|---|
register_channel |
Channels 标签列表,每项含 Channel 与 Description |
调用 StructureLibAPI.registerChannelDescription |
register_channel_item |
Items 标签列表 |
把物品登记为某个通道值的指示物 |
被登记的物品在 GUI 中会以"Indicates value X for sub channel Y"的 tooltip 呈现,并可直接拖放到 GUI 中来设置对应子通道值。
通道配置 GUI
玩家对空气右键(服务端、不潜行)触发器可打开通道配置界面。
| 元素 | 语言键 | 说明 |
|---|---|---|
| 标题 | item.structurelib.constructableTrigger.gui.title |
Sub Channels |
| 键输入框 | ...gui.key |
Key: |
| 值输入框 | ...gui.value |
Value: |
| Add 按钮 | ...gui.add |
新增子通道 |
| Update 按钮 | ...gui.set |
更新已有子通道 |
| Remove 按钮 | ...gui.unset |
移除子通道 |
| Clear 按钮 | ...gui.wipe |
清除全部(红色,需二次确认) |
| 来源标注 | ...gui.channels.from |
显示通道由哪个 mod 注册 |
GUI 实现了 NEI 集成接口(INEIGuiHandler),可从 NEI 物品面板拖放物品进来直接赋值。
清空操作有 3 秒超时的二次确认:WIPE_CONFIRM_TIMEOUT_MS = 3000,期间按钮变为 Sure?,超时后恢复。
拾取通道
客户端支持中键拾取与重置通道,涉及两处实现:
MixinGuiContainer_PickChannel(客户端 mixin,注入GuiContainer.mouseClicked)——在物品栏/容器界面中用中键拾取通道ChannelPickHandler——在世界中用中键对位置拾取通道
拾取到 ItemConstructableTrigger 时中键点击会被直接吞掉(cancel),避免把物品丢出。ChannelWipeConfirmation 负责清除确认。
中键拾取所依赖的 GUI 补丁
ChannelPickHandler 需要访问原版 GuiSlot 的 overlayBackground 方法,仓库内附带 AT 文件 structurelib_at.cfg:
public net.minecraft.client.gui.GuiSlot func_148136_c(IIII)V # overlayBackground
但需要注意的是,gradle.properties 中 accessTransformersFile = 留空,且 StructureLibCore.getAccessTransformerClass() 返回 null。AT 文件虽然被打进 jar 根目录,但并未通过 IFMLLoadingPlugin 声明挂载。