封包与同步
基本信息
| 属性 | 值 |
|---|---|
| 封包目录 | src/main/java/serverutils/net/ |
| 封包类总数 | 44(git ls-files 'src/main/java/serverutils/net/Message*.java' | wc -l) |
| 辅助类 | 2(ServerUtilitiesNetHandler、TransferHelper),不继承封包基类 |
| 基类 | serverutils.lib.net.MessageBase(implements IMessage) |
| 方向基类 | MessageToClient / MessageToServer |
| 网络库 | FML SimpleNetworkWrapper + FMLEmbeddedChannel |
| 通道数 | 6 |
| 编解码 | 自研 DataIn / DataOut(lib/io/) |
封包基类
MessageBase(src/main/java/serverutils/lib/net/MessageBase.java:8-27)把 Forge 的字节码接口转成模组自己的读写:
public abstract NetworkWrapper getWrapper();
@Override
public final void toBytes(ByteBuf buf) { writeData(new DataOut(buf)); }
@Override
public final void fromBytes(ByteBuf buf) { readData(new DataIn(buf)); }
public void writeData(DataOut data) {}
public void readData(DataIn data) {}
toBytes/fromBytes都是final,子类不能覆写,只能实现writeData/readData- 两个
writeData/readData都是空实现,不需要序列化的封包可不覆写 getWrapper()声明为abstract,强制每个封包声明自己属于哪条通道
6 条通道
ServerUtilitiesNetHandler(net/ServerUtilitiesNetHandler.java:6-11)以 NetworkWrapper.newWrapper(id) 建立:
| 常量 | 通道 id | S2C | C2S | 合计 |
|---|---|---|---|---|
GENERAL |
serverutilities |
12 | 5 | 17 |
FILES |
utilities_files |
5 | 4 | 9 |
CLAIMS |
utilities_claim |
3 | 4 | 7 |
MY_TEAM |
utilities_my_team |
3 | 2 | 5 |
STATS |
utilities_stats |
2 | 2 | 4 |
EDIT_CONFIG |
utilities_config |
1 | 1 | 2 |
| 合计 | 6 条 | 26 | 18 | 44 |
[!NOTE] 通道 id 前缀不一致:
GENERAL用serverutilities(与 modid 同名),其余 5 条用utilities_前缀。这是源码中的事实,非本条目的归纳。
init() 方法(:14-65)共执行 45 次注册调用 = 44 个封包 + 1 次 GENERAL.registerBlank()(:17)。registerBlank()(lib/net/NetworkWrapper.java:37)注册一个不处理任何消息的占位封包 —— 其存在是为了在 GENERAL 通道上占住一个序号,本仓代码中没有任何地方发送它。
GENERAL 通道(17)
通用 GUI、通知与同步封包。
S2C(12)
| 封包 | 用途 |
|---|---|
MessageSyncData |
通用配置/数据同步(@Config.Sync 下发的载体) |
MessageNotification |
通知消息 |
MessageCloseGui |
要求客户端关闭 GUI |
MessageAdminPanelGuiResponse |
管理面板 GUI 响应 |
MessageAdminTeamList |
队伍列表 |
MessageAdminTeamAction |
队伍操作结果 |
MessageAdminTeamPlayerList |
队伍成员列表 |
MessageAdminTeamClaimsList |
队伍领地区块列表 |
MessageUpdatePlayTime |
游戏时长更新(world.show_playtime) |
MessageCommandsResponse |
指令列表响应 |
MessageUpdateTabName |
TAB 名称更新(chat.replace_tab_names / tab.*) |
MessageInvseeContainer |
invsee 背包内容(对应 /view) |
MessageTransfer |
切服数据包(对应 /transfer) |
C2S(5)
| 封包 | 用途 |
|---|---|
MessageAdminPanelGui |
请求打开管理面板 |
MessageAdminPanelAction |
提交管理面板操作 |
MessageAdminTeamAction |
提交队伍操作 |
MessageCommandsRequest |
请求指令列表 |
MessageInvseeSwitch |
切换 invsee 背包槽位页 |
MessageAdminPanelGui / MessageAdminPanelAction / MessageAdminTeamAction 双向同名:MessageToClient 与 MessageToServer 各一个(如 MessageAdminPanelAction 是 C2S,MessageAdminPanelGuiResponse / MessageAdminTeamAction 是 S2C),复用同一条通道传输两个方向的同名类。
FILES 通道(9)
文件与崩溃报告相关的 GUI 数据。
S2C(5):MessageEditNBT(NBT 编辑内容)、MessageEditNBTRequest、MessageRanks、MessageViewCrashList、MessageViewCrashResponse
C2S(4):MessageEditNBTResponse、MessageRankModify、MessageViewCrash、MessageViewCrashDelete
MessageRankModify(C2S)与 MessageRanks(S2C)配合,实现 Rank 指令 的 GUI 化编辑。
CLAIMS 通道(7)
领地区块数据与 Navigator 地图同步,是本模组数据量最大的通道。
S2C(3):MessageClaimedChunksUpdate、MessageNavigatorUpdate、MessageNavigatorUpdateKnown
C2S(4):MessageClaimedChunksRequest、MessageClaimedChunksModify、MessageNavigatorRequest、MessageNavigatorValidateKnown
4 个 Navigator 封包由 Navigator 集成 使用。MessageNavigatorUpdateKnown / MessageNavigatorValidateKnown 是一对增量同步:服务端推送客户端尚未知的区块,客户端回报已收到,避免全量重复传输。
MY_TEAM 通道(5)
玩家自己的队伍 GUI。
S2C(3):MessageSelectTeamGui、MessageMyTeamGuiResponse、MessageMyTeamPlayerList
C2S(2):MessageMyTeamGui、MessageMyTeamAction
STATS 通道(4)
排行榜 GUI。
S2C(2):MessageLeaderboardListResponse、MessageLeaderboardResponse
C2S(2):MessageLeaderboardList、MessageLeaderboard
EDIT_CONFIG 通道(2)
最精简的通道,只有一对请求/响应。
S2C:MessageEditConfig
C2S:MessageEditConfigResponse
它承载 /team settings、/team settings_for、/player settings 等配置编辑 GUI。
版本一致性检查
ServerUtilities.checkModLists(ServerUtilities.java:102-105):
@NetworkCheckHandler
public boolean checkModLists(Map<String, String> map, Side side) {
return side != Side.CLIENT || map.containsKey(MOD_ID) && map.get(MOD_ID).equals(VERSION);
}
- 服务端侧(
side == Side.SERVER)恒返回 true - 客户端侧要求服务端存在 modid
serverutilities且版本串与本地VERSION完全相等
VERSION 是 Tags.GRADLETOKEN_VERSION(ServerUtilities.java:38),由 RetroFuturaGradle 在编译时用真实版本号替换(gradle.properties 中 generateGradleTokenClass = serverutils.Tags)。
[!IMPORTANT] 因为是严格字符串相等而非版本区间比较,服务端与客户端的 ServerUtilities 版本必须逐字符一致,否则客户端会被拒绝连接。
读写机制
| 方向 | 基类 | 处理入口 |
|---|---|---|
| S2C | MessageToClient / MessageToClientHandler |
lib/net/MessageToClientHandler.java |
| C2S | MessageToServer / MessageToServerHandler |
lib/net/MessageToServerHandler.java |
编解码基础设施在 lib/io/:DataIn、DataOut、DataReader(及其 FileDataReader、HttpDataReader、ResourceDataReader、StreamDataReader、StringDataReader 五个实现)、RequestMethod。
[!WARNING]
lib/io/HttpDataReader.java:105有一个空的catch (Exception e) {}—— 读取失败被完全静默吞掉,不记日志也不抛出。外部 HTTP 数据源出错时不会有任何提示。