游戏内地图界面(按 M 打开)
基本信息
| 属性 | 值 |
|---|---|
| 触发键位 | key.gwmc.openmap,默认 M(Keyboard.KEY_M) |
| 界面库 | ModularUI2(ModularScreen + ModularPanel) |
| 打开入口 | ClientEvents.onKey(InputEvent.KeyInputEvent) |
| 界面类 | com.gtnewhorizons.gwmclient.client.ModularTest |
⚠️ 官方 README 自述:README.md 原文为
「Access to GWM maps from the client. Not currently in a usable state.」
即仓库自身声明尚不可用。写条目时不要把它当成完成品。
操作
| 操作 | 效果 | 源码 |
|---|---|---|
| 首次打开 | 自动把视口中心对准玩家当前坐标 | ModularTest.java:94-98 focusOnWorldPoint |
| 左键拖动 | 平移视口 | MapDrawer.handleMouseInput(:139-170) |
| 滚轮 ↑ / ↓ | 以光标为中心放大 / 缩小 | ModularTest.onMouseScroll(:140-148) |
Home |
上一张地图 | ModularTest.onKeyPressed(:162-168) |
End |
下一张地图 | ModularTest.onKeyPressed(:169-175) |
| 关闭窗口 | 释放已加载瓦片的 OpenGL 纹理 | MapDrawer.onClose → PerMapTileDataBase.clear → Tile.delete |
地图列表
地图列表来自远端 JSON,按世界嵌套两层后拍平成一个数组
(ModularTest.loadMapList,:55-72):遍历 RemoteConfiguration.getWorlds()
的每个 RemoteWorld,把其所有 RemoteMap 依次加入同一个 ArrayList。
⇒ GUI 中不区分世界,Home/End 会在所有世界的地图之间连续循环,
不会在每个世界末尾停下。
缩放与瓦片
| 数值 | 值 | 来源 |
|---|---|---|
| 瓦片基准边长 | 128 px | PerMapTileDataBase.getTileBaseWidth() |
| 瓦片层级数 | 10(zoom 0–9) | PerMapTileDataBase.java:9 new PerZoomLevel[10] |
| 层级选择条件 | 屏幕上瓦片宽 < 基准宽/2 时升一级,封顶 zoom 9 | MapDrawer.java:77-80 |
| 初始视口 | 宽 = 面板宽 / 128.0 | MapDrawer 构造(:36-37) |
⚠️ 两个独立的"缩放"概念:
①draw() 内的局部变量 zoom 决定取哪一级瓦片,封顶 9;
②viewPortW 决定视野大小,zoomIn/zoomOut 每次把它 ×2 或 ÷2
(:172-189),源码中没有对 viewPortW 的上下限钳制
(updateScreen 仅在 viewPortW <= 0 时重置,:194)。
因此视觉缩放倍率可以超出瓦片层级上限,此时只会取到最粗的 zoom 9 瓦片。
渲染
- 全屏纯黑底(
0xFF000000矩形),drawBackground中先画底再画瓦片。 - 贴图过滤:绘制时用
GL_NEAREST(MapDrawer.java:118-119), 上传纹理时用GL_LINEAR(Tile.java:60-61)——两处不一致, 属可观察的缩放模糊。 MapDrawer.draw()在关闭深度测试与背面剔除后不再恢复 (GL11.glDisable(GL11.GL_CULL_FACE)/GL_DEPTH_TEST,:69-70), 依赖调用方的渲染状态重置。
源码核对(2026-10-01 审计)
- ⚠️ 类名是
ModularTest,但它就是本 mod 唯一的正式界面, 不是测试残留。ClientEvents.java:23唯一地调用ModularTest.createGUI()。 不要因为类名带 Test 就忽略它。 - ⚠️ 线程可见性缺陷:
ModularTest.MapPanel用Executors.newSingleThreadExecutor()(:44, :77)在后台线程跑loadMapList(),该方法写loadingMapList = false(:71); 而渲染线程在drawBackground中读if (loadingMapList) return;(:92)。 该字段非 volatile、无同步,存在读到陈旧值的可见性竞态。 - ⚠️ 异常吞噬:
loadMapList在后台线程执行, 而RemoteConfiguration.load()把IOException/JsonException/MalformedURLException全部包成RuntimeException抛出 (RemoteConfiguration.java:62-68)。远端不可达时该异常抛在 线程池线程里,GUI 只会一直停在loadingMapList = true的纯黑屏, 玩家看不到任何错误提示。 - ⚠️
backgroundLoader是静态单线程池且从不 shutdown。 ClientEvents.java:21有一行被注释掉的mc.displayGuiScreen(new MapScreen()), 属历史实现残留,不是真实注册。