Galacticraft GUI 层:2 个 IGuiHandler + 14 个 GUI ID

事实基准:/Users/evlos/a/mirror/Galacticraft(master,minecraftVersion = 1.7.10)逐行读源码。

两套分派机制(不要混为一谈)

Galacticraft 的 GUI 分成两套完全不同的路由:

机制 载体 分派依据 覆盖范围
A. ID 路由 GuiIdsCore(8 个)+ GuiIdsPlanets(6 个) 整型 ID 玩家随身界面、火箭总装界面
B. 类型路由 core/client/gui/GuiHandler.java TileEntity 的 instanceof 机器界面

⚠ 机制 B 完全不用 GuiIds* 的 ID—— getServerGuiElement(int ID, ...) 拿到 ID 后先判两个特例, 然后直接看 world.getTileEntity(x, y, z) 是什么类型(GuiHandler.java:112)。 所以「ID 是 0」在两套机制里指向完全不同的界面。

实现类

类 接口 源码
micdoodle8.mods.galacticraft.core.client.gui.GuiHandler cpw.mods.fml.common.network.IGuiHandler core/client/gui/GuiHandler.java:86
micdoodle8.mods.galacticraft.planets.PlanetsProxy IGuiHandler planets/PlanetsProxy.java:17

IGuiHandler 由 FML(BuildCraftCore 同理)提供,不在 Galacticraft 源码树内, 所以 check_class_claims.py 报 NOT FOUND 属预期。

GuiHandler 的三个方法:

方法 行 作用
getServerGuiElement(int ID, EntityPlayer, World, int, int, int) :89 服务端返回 Container
getClientGuiElement(int, EntityPlayer, World, int, int, int) :164 转调私有重载
getClientGuiElement(int, EntityPlayer, World, Vector3) :173 客户端返回 GuiScreen

机制 A:ID 路由(共 14 个 ID)

GuiIdsCore(core/client/gui/GuiIdsCore.java,8 个)

ID 常量 行
0 NASA_WORKBENCH_ROCKET :5
1 NASA_WORKBENCH_BUGGY :6
2 NASA_WORKBENCH_NEW_SCHEMATIC :7
3 GALAXY_MAP :8
4 ROCKET_INVENTORY :9
5 EXTENDED_INVENTORY :10
6 SPACE_RACE_START :11
7 SPACE_RACE_JOIN :12

GuiIdsPlanets(planets/GuiIdsPlanets.java,6 个)

ID 常量 行
0 NASA_WORKBENCH_CARGO_ROCKET :6
1 NASA_WORKBENCH_TIER_2_ROCKET :7
2 MACHINE_MARS :8
3 MACHINE_ASTEROIDS :11
4 NASA_WORKBENCH_TIER_3_ROCKET :12
5 NASA_WORKBENCH_ASTRO_MINER :13

⚠ 两张 ID 常量表都从 0 开始,且 ID 0 在两处指向不同界面 (NASA_WORKBENCH_ROCKET vs NASA_WORKBENCH_CARGO_ROCKET)—— 分派必须靠「哪个 handler 收到了」区分,不能只凭 ID 判含义。

📌 GuiIdsCore / GuiIdsPlanets 都不是 enum,而是 public class + 一串 public static final int 常量 (GuiIdsCore.java:3、GuiIdsPlanets.java:3)——Java 侧没有类型约束, 写错常量名只能靠编译期报「找不到符号」发现。

机制 A 的两个特例(GuiHandler.java:100-108)

条件 返回 行
ID == GuiIdsCore.ROCKET_INVENTORY 且 player.ridingEntity instanceof EntityTieredRocket ContainerRocketInventory(player.inventory, (EntityTieredRocket) player.ridingEntity, ...getType()) :100-105
ID == GuiIdsCore.EXTENDED_INVENTORY ContainerExtendedInventory(player, stats.extendedInventory) :106-108

⚠ 火箭背包只在「玩家正骑在火箭上」时才开(:100 的双重条件)—— 下火箭后按同样的键不会打开。

⚠ 这里再次印证 1.7.10 的火箭是 Entity: 判定写的是 player.ridingEntity instanceof EntityTieredRocket。 源码树中不存在 BlockRocket / TileEntityRocket。

ContainerRocketInventory 的第三个参数是 getType()—— 即按火箭类型(T1 / T2 / T3 / 货船)给不同的货舱布局。

机制 B:机器界面(instanceof 路由,共 18 个分支)

GuiHandler.getServerGuiElement(:112 起)按 TileEntity 类型分派, 本篇只列 Container 一侧(Gui 侧见下表同行):

Container 类 GuiHandler.java 行 对应 TileEntity
core.inventory.ContainerRefinery :114 TileEntityRefinery
core.inventory.ContainerOxygenCollector :117 TileEntityOxygenCollector
core.inventory.ContainerOxygenDistributor :120 TileEntityOxygenDistributor
core.inventory.ContainerFuelLoader :122 TileEntityFuelLoader
core.inventory.ContainerOxygenSealer :124 TileEntityOxygenSealer
core.inventory.ContainerCargoLoader :126 TileEntityCargoLoader
core.inventory.ContainerCargoLoader :128 TileEntityCargoUnloader(复用同一个)
core.inventory.ContainerParaChest :130 TileEntityParaChest
core.inventory.ContainerSolar :132 TileEntitySolar
core.inventory.ContainerEnergyStorageModule :134 TileEntityEnergyStorageModule
core.inventory.ContainerCoalGenerator :136 TileEntityCoalGenerator
core.inventory.ContainerElectricFurnace :138 TileEntityElectricFurnace
core.inventory.ContainerIngotCompressor :140 TileEntityIngotCompressor
core.inventory.ContainerElectricIngotCompressor :142 TileEntityElectricIngotCompressor
core.inventory.ContainerCircuitFabricator :144 TileEntityCircuitFabricator
core.inventory.ContainerOxygenStorageModule :146 TileEntityOxygenStorageModule
core.inventory.ContainerOxygenCompressor :148 TileEntityOxygenCompressor
core.inventory.ContainerOxygenDecompressor :150 TileEntityOxygenDecompressor

客户端对应返回(GuiHandler.java 私有重载 :173 起): GuiSolar :220、GuiCoalGenerator :226 等。

特例:Cargo Unloader 复用 Loader 的 Container

GuiHandler.java:127-129:

} else if (tile instanceof TileEntityCargoUnloader) {
    return new ContainerCargoLoader(player.inventory, (TileEntityCargoUnloader) tile);
}

⚠ TileEntityCargoUnloader 分支返回的是 ContainerCargoLoader, 只是把实体强转成 TileEntityCargoUnloader 传进去。 find 全树结果:ContainerCargoUnloader 类根本不存在, 只有 core/inventory/ContainerCargoLoader.java 一个文件。 所以这不是「两个 Container 各配一个 TE」,而是装载机与卸载机共用同一个 Container 类。

不会崩:ContainerCargoLoader 的构造签名是 public ContainerCargoLoader(InventoryPlayer, IInventory cargoLoader)(core/inventory/ContainerCargoLoader.java:17)—— 第二参是 IInventory 接口而非具体 TE 类, 所以 TileEntityCargoUnloader(实现了 IInventory)能正常传入。

⚠ 即:共用 Container 是有意为之的接口编程,不是复制粘贴 bug。 真正的代价是装载机与卸载机的界面无法区分方向语义。

分派链末端

GuiHandler.java:68(偏移后约 :156-160)还有一个兜底分支 return page.getResultContainer(playerBase, x, y, z);—— 供 NASA 工作台的图纸(schematic) 动态生成结果容器。

相关条目