网络与同步 API
OpenModsLib 提供 4 条独立的 FML 网络通道,覆盖数据存储 ID 同步、远程过程调用、状态同步与网络事件广播。这是它作为库的第二层对外契约(第一层是 openmods.api 的接口混入,见公开 API 接口)。
基本信息
| 属性 | 值 |
|---|---|
| 通道数 | 4 |
| 通道名前缀 | OpenMods| |
network 包类数 |
41(根 5 + rpc 13 + event 11 + senders 6 + targets 3 + rpc/targets 3) |
sync 包类数 |
34(含 drops 子包) |
| 通道注册时机 | SyncChannelHolder.ensureLoaded() 在 preInit 首条语句(src/main/java/openmods/OpenMods.java:72);其余在各自实例首次访问时 |
| 崩溃报告条目 | "Stencil buffer state"(客户端,渲染相关,与通道无关) |
功能
4 条通道
| 通道名 | 常量 | 管理类 | 用途 | 声明行 | 注册行 |
|---|---|---|---|---|---|
OpenMods|I |
IdSyncManager.CHANNEL_NAME |
openmods.network.IdSyncManager |
数据存储 ID 在客户端/服务端间的同步 | src/main/java/openmods/network/IdSyncManager.java:48 |
:111 |
OpenMods|RPC |
RpcCallDispatcher.CHANNEL_NAME |
openmods.network.rpc.RpcCallDispatcher |
远程过程调用(动态代理) | src/main/java/openmods/network/rpc/RpcCallDispatcher.java:20 |
:35-38 |
OpenMods|M |
SyncChannelHolder.CHANNEL_NAME |
openmods.sync.SyncChannelHolder |
TileEntity / 实体状态增量同步 | src/main/java/openmods/sync/SyncChannelHolder.java:23 |
:30-32 |
OpenMods|E |
NetworkEventDispatcher.CHANNEL_NAME |
openmods.network.event.NetworkEventDispatcher |
自定义网络事件广播 | src/main/java/openmods/network/event/NetworkEventDispatcher.java:13 |
:20-22 |
四条通道全部经 NetworkRegistry.INSTANCE.newChannel(...) 注册(4 处调用点已交叉核对)。包在 FMLProxyPacket 中作为通道标识(IdSyncManager.java:107、SyncChannelHolder.java:40,44),ExtendedOutboundHandler.install 统一安装出站处理器(RpcCallDispatcher.java:39、SyncChannelHolder.java:35、ExtendedOutboundHandler.java:75-79)并按 NetworkRegistry.CHANNEL_SOURCE 判断发送侧(ExtendedOutboundHandler.java:49)。
ensureLoaded() 的空方法陷阱
SyncChannelHolder.ensureLoaded()(src/main/java/openmods/sync/SyncChannelHolder.java:49)的方法体是空的:
public static void ensureLoaded() {}
它不执行任何显式逻辑,通道注册完全发生在类初始化的副作用中:调用静态方法会触发 <clinit> → 初始化 INSTANCE(:25)→ 执行私有构造器(:29-38)→ 在其中调用 newChannel 并为每个 Side 建立发送器。OpenMods.preInit 把这条空调用放在第一条语句(OpenMods.java:72),正是为了保证通道在任何其它 mod 的 preInit 之前就绪。
阅读源码时若只看方法体会误判其为无效代码 —— 实际效果由 JVM 类初始化语义保证,不能据此认为该通道未注册。
RPC —— 远程过程调用
RpcCallDispatcher(src/main/java/openmods/network/rpc/RpcCallDispatcher.java:17)是单例(:19 INSTANCE),提供动态代理式 RPC:
注册阶段(必须在 LoaderState.PREINITIALIZATION 内,OpenMods.java:76-81):
RpcCallDispatcher.INSTANCE.startRegistration()
.registerInterface(IRpcDirectionBitMap.class)
.registerInterface(IRpcIntBitMap.class)
.registerTargetWrapper(EntityRpcTarget.class)
.registerTargetWrapper(TileEntityRpcTarget.class)
.registerTargetWrapper(SyncRpcTarget.SyncEntityRpcTarget.class)
.registerTargetWrapper(SyncRpcTarget.SyncTileEntityRpcTarget.class);
startRegistration(RpcCallDispatcher.java:49)带 Preconditions.checkState(Loader.instance().isInState(LoaderState.PREINITIALIZATION)) 守卫(:50-53)—— 在其它加载阶段调用会直接抛异常。注册 2 个接口(RpcSetup.registerInterface,src/main/java/openmods/network/rpc/RpcSetup.java:44)+ 4 个 target wrapper(RpcSetup.java:73)。postInit 阶段由 finishRegistration()(OpenMods.java:124)收尾并把 setup 置空(RpcCallDispatcher.java:56-61),之后不可再注册。
使用:createProxy(wrapper, sender, mainIntf, extraIntf...)(RpcCallDispatcher.java:61)为任意接口生成客户端代理,调用时自动编码为网络包发往服务端。方法与目标实体的对应关系由 MethodIdRegistry 与 TargetWrapperRegistry 维护。
RpcSetup.ID_FIELDS_SEPARATOR = ";"(RpcSetup.java:19)是 RPC 方法签名(声明类名 + 字段名 + 参数类型)的拼接分隔符(:60-62)。
状态同步 —— sync 包
SyncChannelHolder(src/main/java/openmods/sync/SyncChannelHolder.java)为客户端与服务端各建一条通道(:30-32),承载 ISyncableObject(src/main/java/openmods/sync/ISyncableObject.java:11)的增量同步:
| 方法 | 用途 |
|---|---|
isDirty() / markDirty() / markClean() |
脏标记三件套 |
writeToStream / readFromStream |
网络增量传输 |
writeToNBT / readFromNBT |
存档持久化 |
提供 20 个 Syncable* 实现(src/main/java/openmods/sync/ 目录下),覆盖常用数据类型:
| 类别 | 实现 |
|---|---|
| 基础数值 | SyncableBoolean SyncableByte SyncableShort SyncableInt SyncableFloat SyncableDouble SyncableVarInt SyncableUnsignedByte |
| 数组 | SyncableByteArray SyncableIntArray |
| 字符串与标识 | SyncableString SyncableUUID SyncableEnum |
| 游戏对象 | SyncableBlock SyncableItemStack SyncableNBT SyncableTank |
| 复合 | SyncableFlags SyncableSides SyncableObjectBase(基类) |
配套的高层封装:SyncableBlock(方块状态)、SyncMap / SyncMapEntity / SyncMapTile(Map 结构同步)、SyncedTileEntity / SyncableObjectBase(TileEntity 集成)、SyncObjectScanner(自动扫描)。drops 子包专用于掉落物同步。
ID 同步 —— IdSyncManager
IdSyncManager(src/main/java/openmods/network/IdSyncManager.java:46)继承 DataStoreManager,解决的是「服务端为数据存储分配的 ID 如何告知客户端」这一问题。提供 createDataStore 两个重载(:114,121),返回 DataStoreBuilder。在 FMLNetworkEvent.ClientConnectedToServerEvent 时向客户端同步(:76-80),并在 ClientDisconnectionFromServerEvent 时清理(onDisconnect,:157)。finishLoading()(:162)在 postInit 末尾调用(OpenMods.java:127,源码注释 // must be after all builders are done)。
网络事件 —— NetworkEventDispatcher
NetworkEventDispatcher(src/main/java/openmods/network/event/NetworkEventDispatcher.java)提供 NetworkEventCodec(编解码)+ NetworkEventInboundHandler(入站)两个处理器,注册于通道 OpenMods|E(:20-22)。NetworkEventCodec 会把事件对象回填 sender 字段(:110),使接收端能知道事件来自哪个玩家。
数值
| 数值名 | 值 |
|---|---|
| 网络通道数 | 4(OpenMods|I / |RPC / |M / |E) |
newChannel 注册调用点 |
4 |
Syncable* 实现类数 |
20(含基类 SyncableObjectBase) |
ISyncableObject 方法数 |
6 |
| RPC 注册的接口数 | 2(IRpcDirectionBitMap、IRpcIntBitMap) |
| RPC 注册的 target wrapper 数 | 4 |
network 包类数 |
41 |
sync 包类数 |
34 |
交互
4 条通道均无玩家可见交互。它们只在下层被 OpenBlocks 等下游 mod 调用。
唯一与网络相关的玩家侧提示是崩溃报告条目 "Stencil buffer state"(src/main/java/openmods/proxy/OpenClientProxy.java:113),它输出当前 OpenGL 方法集与模板缓冲池状态(StencilPoolManager),与上述 4 条通道无关,属于渲染诊断。
错误处理
ExtendedOutboundHandler(src/main/java/openmods/network/ExtendedOutboundHandler.java)在发送失败时catch (Throwable)记录日志(:61)IdSyncManager(:58)与NetworkEventCodec(:52,84)在通道侧别不匹配时走异常分支RpcCallDispatcher.startRegistration用Preconditions.checkState硬失败,阻止在错误加载阶段注册SyncChannelHolder.ensureLoaded()被OpenMods.preInit的第一条语句调用(OpenMods.java:72),通过类初始化保证其它 mod 在 preInit 中使用时通道已就绪