Galacticraft 封包层:3 个枚举式包表,共 80 个包类型
事实基准:
/Users/evlos/a/mirror/Galacticraft(master,minecraftVersion = 1.7.10)逐行读源码。 方向与载荷类型都写死在枚举构造参数里,不是靠 ID 猜的。
封包架构:枚举即「方向 + 载荷类型签名」
core/network/PacketSimple.java:105 起:
public enum EnumSimplePacket {
// spotless:off
// SERVER
S_RESPAWN_PLAYER(Side.SERVER, String.class), // :109
S_TELEPORT_ENTITY(Side.SERVER, String.class, Boolean.class), // :110
S_IGNITE_ROCKET(Side.SERVER), // :111
S_OPEN_SCHEMATIC_PAGE(Side.SERVER, Integer.class), // :112
...
每个常量的构造参数就是它的完整签名:
| 参数 | 含义 |
|---|---|
| 第 1 个 | Side.SERVER 或 Side.CLIENT —— 这个包发往哪一端 |
| 第 2…n 个 | Xxx.class —— 载荷各字段的类型 |
⚠ 方向不是推断的,是枚举里写死的。 (这与 BuildCraft 的「按包类注册侧掩码」不同,见 BuildCraft 封包层。)
3 张包表(集合运算:逐条匹配 NAME(Side.X, ...))
| 包表 | 枚举 | 源码 | 常量数 | SERVER | CLIENT |
|---|---|---|---|---|---|
| 核心 | PacketSimple.EnumSimplePacket |
core/network/PacketSimple.java:105 |
69 | 30 | 39 |
| 小行星带 | PacketSimpleAsteroids.EnumSimplePacketAsteroids |
planets/asteroids/network/PacketSimpleAsteroids.java:29 |
5 | 1 | 4 |
| 火星 | PacketSimpleMars.EnumSimplePacketMars |
planets/mars/network/PacketSimpleMars.java:34 |
6 | 4 | 2 |
| 合计 | — | — | 80 | 35 | 45 |
计数方法:正则 \b([A-Z][A-Z0-9_]*)\s*\(\s*Side\.(SERVER|CLIENT)([^)]*)\) 逐条匹配,
三张表内均无重名。
⚠ 80 是「枚举里声明的包类型数」,不是「switch 分支数」,也不是「实际会发出的包数」。 直接数
case标签会得到 128(含嵌套 switch 里的重复标签), 直接数标识符会得到 140(把SERVER/CLIENT枚举名也数进去)。 三个数字都不等于 80。
载荷长度分布(核心 69 个,按构造参数个数统计)
| 载荷字段数 | 包类型数 |
|---|---|
| 0 | 17 |
| 1 | 19 |
| 2 | 12 |
| 3 | 8 |
| 4 | 7 |
| 5 | 3 |
| 6 | 1 |
| 10 | 1 |
| 11 | 1 |
⚠ 载荷长度极不均匀——从 0 字段到 11 字段。
11 字段的那个包是本 mod 里最重的单包。
小行星带 5 个:字段数 2、2、3、5、8。 火星 6 个:字段数 0、2、3、3、3、5。
关键包(按用途分类,不逐条列举 80 条)
火箭(1.7.10 火箭是 Entity,所以这些包发给实体)
| 包 | 方向 | 说明 |
|---|---|---|
S_IGNITE_ROCKET |
SERVER | 点火 |
S_UPDATE_SHIP_YAW / S_UPDATE_SHIP_PITCH / S_UPDATE_SHIP_MOTION_Y |
SERVER | 姿态 / 高度 |
C_DISPLAY_ROCKET_CONTROLS |
CLIENT | 火箭控制面板 |
C_RESPAWN_PLAYER / S_RESPAWN_PLAYER |
CLIENT / SERVER | 玩家重生 |
另有独立封包类 core/network/PacketRotateRocket.java(旋转火箭),
它不在枚举里,是单独的 IPacket 实现。
空间站
| 包 | 方向 |
|---|---|
S_BIND_SPACE_STATION_ID |
SERVER |
C_UPDATE_SPACESTATION_LIST / C_UPDATE_SPACESTATION_DATA / C_UPDATE_SPACESTATION_CLIENT_ID |
CLIENT |
C_UPDATE_STATION_SPIN / C_UPDATE_STATION_DATA / C_UPDATE_STATION_BOX |
CLIENT |
S_RENAME_SPACE_STATION |
SERVER |
C_UPDATE_WIRE_BOUNDS |
CLIENT |
氧气 / 环境
| 包 | 方向 |
|---|---|
C_AIR_REMAINING |
CLIENT |
C_UPDATE_OXYGEN_VALIDITY |
CLIENT |
C_UPDATE_THERMAL_LEVEL |
CLIENT |
C_UPDATE_ARCLAMP_FACING / S_REQUEST_ARCLAMP_FACING |
CLIENT / SERVER |
GUI
| 包 | 方向 |
|---|---|
C_CLOSE_GUI |
CLIENT |
C_OPEN_PARACHEST_GUI |
CLIENT |
C_OPEN_SPACE_RACE_GUI / C_OPEN_JOIN_RACE_GUI |
CLIENT |
S_OPEN_SCHEMATIC_PAGE |
SERVER |
S_OPEN_FUEL_GUI |
SERVER |
S_OPEN_EXTENDED_INVENTORY |
SERVER |
S_ON_ADVANCED_GUI_CLICKED_INT / S_ON_ADVANCED_GUI_CLICKED_STRING |
SERVER |
装备 / 随身界面:48 个,但在另一张表里
装备包不在 EnumSimplePacket 里,而是独立的
GCPlayerHandler.EnumModelPacket(core/entities/player/GCPlayerHandler.java:1085),
由 PacketSimple 通过 fromOrdinal((Integer) this.data.get(1))(:398)在载荷里再套一层分派。
| 前缀 | 数量 | 覆盖 |
|---|---|---|
ADD* |
20 | 面罩、齿轮、左右各 6 个罐位、降落伞、频率模块、4 件热能服 |
REMOVE* |
10 | 降落伞、频率模块、4 件热能服、左右罐(合并为 REMOVE_LEFT_TANK / REMOVE_RIGHT_TANK) |
SHOW* |
9 | 面罩、齿轮、左右罐、频率模块、4 件热能服 |
HIDE* |
9 | 同 SHOW* |
| 合计 | 48 |
⚠ 命名不统一:多数是驼峰(ADDMASK / HIDEGEAR / SHOWLEFTTANK),
少数带下划线(ADD_PARACHUTE / REMOVE_THERMAL_BOOTS / REMOVE_LEFT_TANK)——
按「驼峰前缀」分组会漏掉下划线那批。
⚠ ADD 有 20 个而 REMOVE 只有 10 个——因为罐位是成对的
(ADDLEFTGRAYTANK…ADDRIGHTGREENTANK 各 6 个 = 12 个 ADD),
而移除只需一个 REMOVE_LEFT_TANK / REMOVE_RIGHT_TANK 即可。
两者数量不对称,不是漏写。
双重分派(易漏)
装备包走两层:
- 外层
EnumSimplePacket(如C_UPDATE_GEAR_SLOT,Side.CLIENT)——方向与封装; - 内层
EnumModelPacket(48 个之一)——具体动作。
⚠ 只统计 EnumSimplePacket 会漏掉这 48 个;
只统计 ADDMASK 之类的标识符又会漏掉外层 69 个。
两个枚举是嵌套关系,不是并列。
其它封包类(不在枚举里)
| 类 | 用途 | 源码 |
|---|---|---|
PacketRotateRocket |
火箭旋转 | core/network/PacketRotateRocket.java |
PacketDynamic |
动态载荷(实体 / TileEntity 通用) | core/network/PacketDynamic.java:20 |
PacketDynamicInventory |
动态背包 | core/network/PacketDynamicInventory.java |
PacketEntityUpdate |
实体更新 | core/network/PacketEntityUpdate.java |
PacketControllableEntity |
可控实体 | core/network/PacketControllableEntity.java |
PacketDynamic 的判别式是 int 0 / 1(core/network/PacketDynamic.java:54、:57、:87、:100):
| 判别值 | 目标 | 处理 |
|---|---|---|
| 0 | Entity | entity instanceof IPacketReceiver(:94) |
| 1 | TileEntity | tile instanceof IPacketReceiver(:110) |
服务端侧还有 if (side == Side.SERVER && player instanceof EntityPlayerMP) 守卫(:140、:156)。
接收端
core/network/GalacticraftPacketHandler.java:14:
public class GalacticraftPacketHandler extends SimpleChannelInboundHandler<IPacket> {
IPacket 的两个方向方法是 handleClientSide / handleServerSide,
由 PacketDynamic.handleClientSide(:251 附近)等实现。
独立频道(与包表无关)
core/GalacticraftCore.java:242:
ConnectionPacket.bus = NetworkRegistry.INSTANCE.newEventDrivenChannel(ConnectionPacket.CHANNEL);
⚠ GC 另有一个 FML handshake 频道(ConnectionPacket.CHANNEL),
用于 mod 列表握手,与 80 个游戏包是两条独立通道。