Navigator 集成
在 GTNewHorizons Navigator 地图上叠加两个 Opis 图层。
基本信息
| 属性 | 值 |
|---|---|
| 依赖模组 | Navigator(modid navigator) |
| 依赖类型 | 可选软依赖,运行时检测 |
| 最低版本要求 | 1.1.7 |
| 声明方式 | devOnlyNonPublishable('com.github.GTNewHorizons:Navigator:1.1.8:dev')(dependencies.gradle:4) |
| 图层数量 | 2 |
| 图层注册 API | NavigatorApi.registerLayerManager(...) |
| 图标资源 | opis:textures/icon/chunk_timing.png、chunk_loaded.png |
| 相关配置键 | 3 个(overlay.refresh_ms、overlay.alpha.timing、overlay.alpha.loaded) |
功能
版本门禁
ProxyClient.isNavigatorSupported()(ProxyClient.java:209-222):
Loader.instance().getIndexedModList().get("navigator"),为 null 直接返回 falseVersionParser.parseRange("[1.1.7,)").containsVersion(navigator.getProcessedVersion())通过则返回 true- 否则打一条 WARN:
Navigator {} is too old for the Opis map overlays, which need 1.1.7 or newer. Overlays disabled.并返回 false
代码注释说明了为什么需要版本检查(ProxyClient.java:209):The layers use APIs added in Navigator 1.1.7, and nothing pins the version for an optional dependency.
Navigator 的全部 import 都在分支内,ProxyClient.java:203 的注释为 Keeps every Navigator class behind this branch so Opis still loads without it. —— 没有 Navigator 时模组照常加载。
延迟注册
ProxyClient.init() 只调用 OpisLayers.init()(ProxyClient.java:203-207),它做三件事(OpisLayers.java:15-19):
ChunkTimeLayerManager.init()/LoadedChunkLayerManager.init()—— 注册消息 handler,开始静默接收数据但不出按钮- 把
OpisLayers自身注册到 FML 事件总线
真正的图层注册推迟到 OpisLayers.show()(OpisLayers.java:36-43),由收到 Message.CLIENT_SHOW_SWING 触发(ProxyClient.java:240-246)。注释:Deferred until first use of Opis so the buttons do not clutter every map. show() 内有 shown 布尔量保证只注册一次。
注释还说明了线程约束:Must run on the client thread, as Navigator's layer list is iterated while rendering.,所以调用方 ProxyClient 用 OpisClientTickHandler.INSTANCE.scheduleOnClientThread(OpisLayers::show) 包了一层。
图层 1:区块更新耗时热力图
ChunkTimeLayerManager,按钮文字 Opis chunk update time(ChunkTimeButtonManager.java:20),图标 chunk_timing.png。
- 消费
Message.LIST_TIMING_CHUNK_DIM - 轮询:
onUpdatePre里检查距上次请求是否超过modOpis.overlayRefreshInterval,未到就返回;到点则发PacketReqData(LIST_TIMING_CHUNK_DIM, new SerialInt(player.dimension)) - 归一化:
generateVisibleLocations按维度取该维度内最大的getDataSum()作为分母(ChunkTimeLayerManager.java:91-97),注释解释为the server's top 100 is global, so a hotter dimension would wash this one out—— 即服务端返回的是全局 Top 100,不按维度排 - 透明度:Navigator 原生渲染用
modOpis.overlayAlphaTiming(ChunkTimeRenderStep.java:35),JM6 覆盖用overlayAlphaTiming / 255f(ChunkTimeLayerManager.java:68) - 颜色:
ChunkTimeLocation.getColor()用red = (int) Math.ceil(heat * 255.0),返回(red << 16) | (255 - red),即 heat=0 纯蓝0x0000FF→ heat=1 纯红0xFF0000,绿通道线性衰减 - 视口裁剪:按
chunk.x + 15 < minBlockX || chunk.x > maxBlockX等条件剔除 - 交互:双击才响应(
click.isDoubleClick()),跳转SwingUI.showTab(SelectedTab.TIMINGCHUNKS)并选中对应行;单击不消费事件
图层 2:已加载区块
LoadedChunkLayerManager,按钮文字 Opis loaded chunks(LoadedChunkButtonManager.java:20),图标 chunk_loaded.png。
- 消费
Message.LIST_CHUNK_LOADED与Message.LIST_CHUNK_LOADED_CLEAR - 服务端分批下发一组区块,以 CLEAR 消息结尾,客户端收到后才显示(
ServerMessageHandler.java:124注释:Sent last: the client treats it as "batch complete" and only then shows it.) - 透明度:Navigator 原生渲染用
modOpis.overlayAlphaLoaded(LoadedChunkRenderStep.java:31),JM6 覆盖用overlayAlphaLoaded / 255f(LoadedChunkLayerManager.java:67) - 颜色:
LoadedChunkLocation.getColor()返回isForced() ? 0x0000FF : 0x00FF00—— 强制加载区块为蓝色,普通加载区块为绿色 isForced()判据是chunk.metadata != 0- 交互:只有强制加载区块才响应双击(
if (!((LoadedChunkLocation) ...).isForced()) return false;),跳转SwingUI.showTab(SelectedTab.FORCELOADS);普通区块的点击留给下层 onLayerToggled(true)时把lastRequest归零,使重新启用后立即重绘(注释:Snapshot is kept so re-enabling redraws immediately.)
断线清理
OpisLayers 订阅 FMLNetworkEvent.ClientDisconnectionFromServerEvent(OpisLayers.java:22-30),在客户端线程上 clearState() 两个图层。注释说明原因:缓存属于单个服务器,保留会把上一个服的区块画到下一个会话里;事件在 netty 线程触发,必须切回客户端线程。
数值
| 数值名 | 值 |
|---|---|
| Navigator 最低版本 | 1.1.7 |
| 轮询间隔默认值 | 1000 ms(overlay.refresh_ms) |
| 轮询间隔下界 | 250 ms(overlay.refresh_ms 的 min 参数) |
| 服务端对轮询的节流 | 200 ms(ServerMessageHandler.MIN_REQUEST_INTERVAL_MS) |
| 服务端每次返回的区块数 | 100(getTopChunks(100, dim)) |
| 耗时图层配色 | 蓝 #0000FF → 红 #FF0000 |
| 加载区块配色 | 强制 #0000FF,普通 #00FF00 |
| 区块边长 | 16(视口裁剪用 chunk.x + 15) |
| 点击阈值 | 双击 |
交互
| 触发 | 行为 |
|---|---|
| 安装 Navigator ≥ 1.1.7 | 覆盖层可用但地图上无按钮 |
玩家执行 /opis 后 |
地图上出现两个 Opis 图层按钮 |
| 点击按钮 | 每 overlay.refresh_ms 毫秒向服务端轮询一次 |
调整 overlay.refresh_ms 后重启 |
轮询频率改变 |
| 双击热力图区块 | 打开面板 Chunks 页并选中该行 |
| 双击绿色(普通)区块 | 无响应 |
| 双击蓝色(强制)区块 | 打开面板 Forced Chunks 页 |
| 断开服务器 | 覆盖层缓存清空 |
相关条目
- journeymap - 同一图层在 JourneyMap 6 上的原生覆盖实现
- config -
overlay.*配置键定义 - swing-ui - 覆盖层跳转到的面板
- network-protocol - 轮询使用的消息与 200 ms 服务端节流
- /opis - 触发
OpisLayers.show()的唯一途径