网络协议
Opis 客户端与服务端之间的全部通信定义。
基本信息
| 属性 | 值 |
|---|---|
| 通道名 | "Opis"(PacketManager.java:87 NetworkRegistry.INSTANCE.newChannel("Opis", codec)) |
| 编解码器 | 私有静态类 PacketManager.Codec,继承 FMLIndexedMessageToMessageCodec<PacketBase> |
| 封包类数量 | 5 |
| discriminator 分配 | 0–4,连续 |
| 初始化时机 | preInit 末尾 PacketManager.init()(modOpis.java:174) |
| 远端版本检查 | @NetworkCheckHandler(modOpis.java:121-126) |
功能
5 个封包类
| discriminator | 类 | 方向 | 用途 |
|---|---|---|---|
| 0 | PacketReqChunks |
客户端 → 服务端 | 请求区块数据 |
| 1 | PacketReqData |
客户端 → 服务端 | 请求数据(Message + 可选 ISerializable 参数) |
| 2 | NetDataCommand |
服务端 → 客户端 | 无负载指令 |
| 3 | NetDataList |
服务端 → 客户端 | 列表数据 |
| 4 | NetDataValue |
服务端 → 客户端 | 单值数据 |
addDiscriminator 被覆写(PacketManager.java:126-135),会先检查目标类有无无参构造函数,缺失则提前抛错。
Message 枚举分组
mcp.mobius.opis.network.enums.Message 共 69 个成员,按前缀分 10 组:
| 前缀 | 成员数 | 用途 | 发送方 |
|---|---|---|---|
LIST_ |
22 | 列表类数据请求或下发 | 双向 |
COMMAND_ |
18 | 面板操作指令 | 双向 |
STATUS_ |
10 | 采集状态广播 | 服务端 → 客户端 |
CLIENT_ |
7 | 仅客户端可达的指令 | 服务端 → 客户端 |
VALUE_ |
6 | 单值汇总数据 | 服务端 → 客户端 |
NEXUS_ |
2 | Nexus 远程系统登录与实时数据 | 双向 |
SWING_ / CHAT_ / PLAYER_ / CONNECTION_ |
各 1 | 面板页签、聊天、玩家状态、连接状态 | 双向 |
Message 的每个成员带两个元数据(Message.java:96-118):
accessLevel:默认AccessLevel.NONE,另有 13 个COMMAND_*成员在构造时硬编码为PRIVILEGEDtabEnum:默认EnumSet.of(SelectedTab.ANY),另有 9 个成员绑定到具体页签,用于决定数据是否在该页显示:LIST_PLAYERS、LIST_DIMENSION_DATA、LIST_FORCE_CHUNK_DATA、LIST_PACKETS_OUTBOUND、LIST_PACKETS_INBOUND、LIST_PACKETS_OUTBOUND_250、LIST_PACKETS_INBOUND_250、LIST_THREADS、STATUS_PING
权限校验
Message.canPlayerUseCommand(EntityPlayerMP) 用 PlayerTracker.INSTANCE.getPlayerAccessLevel(player).ordinal() >= this.accessLevel.ordinal() 做序数比较。AccessLevel 的声明顺序即优先级(AccessLevel.java:3-7):NONE < PRIVILEGED < ADMIN。
accessLevel 可在运行期整体改写,见 config 的 ACCESS_RIGHTS:tables 与 ACCESS_RIGHTS:opis。
服务端节流
ServerMessageHandler 对 7 个会遍历区块/探针集合的请求做节流(ServerMessageHandler.java:58-68):LIST_CHUNK_LOADED、LIST_TIMING_CHUNK、LIST_TIMING_CHUNK_DIM、LIST_TIMING_TILEENTS、LIST_TIMING_ENTITIES、LIST_AMOUNT_ENTITIES、LIST_AMOUNT_TILEENTS。同一玩家同一 Message 的最小间隔为 MIN_REQUEST_INTERVAL_MS = 200 毫秒,超出则静默丢弃(注释:Dropped rather than punished: access can be revoked while a client is still polling.)。
命中节流的请求还会被排进 OpisServerTickHandler 的服务端线程队列,且同一玩家同一 Message 只保留最新一个任务(queued.put 返回非 null 即丢弃旧的)。
远端版本兼容
checkRemoteVersion(modOpis.java:121-126)的判定:
- 远端没有 Opis(
remoteVersion == null)→ 放行,Opis 在远端是可选的 - 版本号与本地
Tags.VERSION相同 → 放行 - 版本号 >=
1.4.12-mapless→ 放行(这是 Navigator 覆盖层所需的协议版本) - 其余 → 拒绝
数值
| 数值名 | 值 |
|---|---|
Message 成员总数 |
69 |
| 封包 discriminator 上限 | 4 |
| 通道名 | Opis |
| 服务端请求节流间隔 | 200 ms |
默认硬编码 PRIVILEGED 的 Message 数 |
13 |
绑定具体 SelectedTab 的 Message 数 |
9 |
| 被节流的请求 Message 数 | 7 |
| Navigator 协议版本常量 | 1.4.12-mapless |
已知事实:两个 Message 永不发出
VALUE_TIMING_ENTUPDATE:客户端注册了 handler(ProxyClient.java:177)、PanelSummary也有对应 case(PanelSummary.java:497),但服务端没有任何一处发送。ServerMessageHandler.java:160的分支体是空的{},且它不在PacketManager.sendFullUpdate的下发列表(PacketManager.java:293-318)里。全仓 4 处引用均为接收侧或空分支。LIST_TIMING_CHUNK_DIM与LIST_TIMING_CHUNK是两条独立消息:前者按维度排名(getTopChunks(100, dim)),后者全局排名(getTopChunks(100))。Message.java:90-91的注释说明拆分的动机:Dimension-scoped copy of LIST_TIMING_CHUNK, so overlay polling cannot overwrite the Swing table.—— 地图覆盖层的轮询不会覆盖 Swing 表格里的全局榜单。
相关条目
- extension-api - 接收这些封包的接口与注册器
- config - accessLevel 的运行期改写
- swing-ui - 消费这些消息的 23 个面板
- navigator - 地图覆盖层轮询使用的
LIST_TIMING_CHUNK_DIM