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) 动态生成结果容器。