MTR 集成

装了 MTR(我的世界铁路) 时,问道会把它的车站与线路读进来, 变成自己的站点与公共交通线路——你不必为了导航,把已经建好的地铁网络再手画一遍。 自己开图(单人 / 对局域网开放)时读到的是整张铁路;联机时以 MTR 发到客户端的那一段为准, 并合并记住。这一切都是反射只读的:问道的代码里没有任何一个 MTR 的类, 所以 MTR 没装、改了包名或换了版本,问道最多是读不到,不会编译不过,也不会崩。

读进来什么,变成什么

MTR 那边 问道这边 说明
车站 站点、导航目的地 和机械动力车站一样,出现在目的地列表里自己的数据源下,可以直接选作终点。
线路 公共交通线路 参与公共交通规划;在线路配置界面里以 [MTR] 标记、只读列出。
轨道 只读的铁路 / 水路道路 只有把这条线的轨道标记点亮时才会标出来,并且从不写进你的路网。

整个功能由配置项 mtr_transit(默认开)控制。关掉它,MTR 相关的一切立刻消失, 其余功能一件不少;再打开,下一次读取就会把内容带回来。

读到什么范围:附近的窗口、记忆,以及整张地图

MTR 把它的世界放在自己的服务端,只把玩家附近的数据发给客户端。所以最基本的一次读取 都只是一个窗口,而这个窗口会随着你走开而合上。

如果只拿当次读数用,那份读数就会把你的铁路一起带走:走远了,线路从规划与线路配置界面里消失, 车站不再是目的地,标记轨道用的那批轨道也没了,连你刚刚坐过的那条线都算不出来。 这不是读取方式有问题,而是让一次读数承担了它没有说的东西。所以问道把每次读到的东西 合并保存下来——线路、车站、轨道三样都记——之后所有答案都从合并后的全量里取。

三种读取,合并成一份

读取 什么时候有 读到什么
窗口
(MTR 发给客户端的)
总是有。 玩家附近的一小段铁路(默认 192 格以内)的车站、线路与轨道。它随你移动而刷新。
整张地图
(配置项 mtr_full_map,默认开)
自己开图时:单人存档,或者把世界「对局域网开放」。 MTR 服务端此刻正在模拟的那整张铁路——全部车站与线路,不受同步窗口限制。 读取发生在 MTR 自己的线程上(Simulator.run,它的机制),只把不可变的结果带回来, 从不写回 MTR 的数据。
MTR Map Overlay
(配置项 mtr_map_overlay,默认开)
客户端与服务器都装了 MTR Map Overlay(模组 id mtrmap)时。 那个模组向服务器要来并缓存在客户端的整张网络快照:每条线、每个车站、每段轨道。 它也是三种读取里唯一说得出「每条线走哪几段轨道」的一种, 所以由它画出来的线路是沿着真正的铁路走的,而不是在车站之间连直线。

三者合并成同一份数据,重复的部分以 MTR 自己给的说法为准。是否用得上后两种取决于你的玩法: 自己开图有整张地图;在别人的服务器上则要靠 Map Overlay,而它需要服务器也装。

  • 线路与车站按 MTR 自己的 id 记「最新的说法」:走回走过的地面会更新它们, 走开则保留最后一次说法。一条线路只被「至少放了同样多站点」的读数替换, 所以一个站点更少的窗口不会把线路缩成「此刻在范围内的那一截」。
  • 轨道取的是「迄今为止发现过的轨道的并集」:轨道本来就是一段一段被发现的, 所以是累加而不是替换。同一段轨道不会被收两遍(两端距离 1.5 格以内算同一段)。
  • 记忆有上限,但都开得很大:线路 512 条、车站 5000 个、轨道 2 万段。上限存在的意义是 「不要无界增长」,不是「定期清理」——一个普通大小的铁路网络碰不到它。

整图读取发生在 MTR 自己的线程上,只把不可变的结果带回来,也从不写回 MTR 的数据。 所以它的开销不在客户端线程上:一次读取拿到的是当时那张网络,之后所有查询都用合并后的全量, 不会每一帧去翻 MTR 的模拟器。MTR 那边后来新增的线路,靠窗口读取与下一次读取补进来。

结论分两种情况:自己开图(或两边都装了 MTR Map Overlay)时整张铁路一次到手; 联机在别人的服务器上、又没有 Map Overlay 时,走出同步范围不会让线路消失, 但从没靠近过的线路确实读不到——想让它出现,坐车或走路靠近一次即可。

MTR Map Overlay 的两个开关会被记住

MTR Map Overlay 自己的地图上还有「线路」与「轨道」两个开关。它以前每次开游戏都回到「都画」, 于是上一局特意关掉的图层,下一局又出现了。0.2.2 起问道把这两个开关记在 config/howtogo/mtr_overlay_toggles.json:改了立刻保存,下一局恢复成上次的样子。 它自己的配置文件不动;删掉这个文件就不再记,回到「都画」。 没装那个模组时整段功能不存在。

线路类型与站点

MTR 的线路有很多种类型,问道只认识能对应到自己道路等级的那些,其余的不猜:

MTR 的线路类型 问道的线路类型
列车、缆车 铁路线路(沿铁路规划)
船 水路线路(沿水路规划)
飞机,以及 MTR 以后新加的类型 不处理,也不显示——硬套成铁路只会给出错误的行程

站点取的是站台的中点,不是车站区域的中点。车站的中心是整片区域的中点, 大站的这个点可能离轨道很远;而车停的是站台,所以站点落在站台的中间。只有读不到站台时, 才退回区域中心。

一个车站的多个站台会合成一个地点。MTR 把玩家拖出的每一个区域都算一个车站, 于是「一个站台一个框」建出来的车站,会以多个同名、相隔几十格的车站出现:目的地列表里同一个站出现四次、 地图上四个标记、线路只停在其中某一个框里,在这个站换乘还会被算成两个相隔上百格的车站之间的换乘。 现在同名(忽略大小写与首尾空格)且候车点相距不超过 mtr_station_merge_blocks(默认 256 格)的车站合成一个地点: 列表一条、地图一个标记、线路停一次,位置取它们所有站台的中点。 没名字的车站从不合并(否则所有无名站都会变成一个);把这一项设成 0,就完全按 MTR 原样提供。

站台的所属车站是 MTR 拿客户端当前持有的车站去解析的,所以一个「人在窗口外」的车站下的站台 会归属为空。这种情况按「不知道」处理,而不是把它当成「0 号车站」—— 否则所有这样的孤儿站台会被凑到一起取中点,得到一条停在荒郊野外、从没人建过的车站上的线路。

还有两条「不凑数」的规矩:

  • 客户端还没收到的车站会被跳过,而不是随便找个位置顶替——一个标错的站点比少一个站点更糟。
  • 一条线路剩下不足两站时根本不会被列出来:一条没有行程可坐的线路,只会回答「没有路线」。

只读的边界与「复制为我的」

MTR 的车站与线路属于 MTR。它们的名字、类型、站位都是 MTR 的数据,问道在这里改, 只会得到一条描述着「不存在的车站」的线路。所以在线路配置界面里:

  • MTR 的线路带 [MTR] 标记列出,站点与类型都不能改;
  • 但可以按行尾的 「复制为我的」,得到一份完全属于你的副本——站点顺序、类型、名字都可以随便改;
  • 站点列表里来自 MTR 的车站同样只读,它们的名字与类型仍然由 MTR 负责。

打开线路配置界面的方式:在世界地图上按 R 进入编辑模式,再按 L。 线路是路网的性质而不是某个选中物的性质,所以不需要先选中什么。

轨道标记:为什么需要它

要让公共交通的行程沿着 MTR 真正铺的轨道来算,就得知道这条线走的是哪几段轨道。 问题是:MTR 的协议里没有「某条线路对应哪段轨道」这个信息——它发过来的是车站、线路和它自己的一堆轨道, 但两者之间没有连线。

所以问道是反推的:

  1. 取这条线路上每一对相邻的站点;
  2. 在 MTR 的轨道上规划一次,从这一站走到下一站;
  3. 走过的那些轨道,就是这条线实际走的那一段,以只读的铁路 / 水路道路的形式标上去。

标出来的这些道路画在线路下方,会被规划当作可以通行的道路, 但从不保存进你的路网,也从不可编辑——它们和 MTR 的线路本身一样,是只读图层。 一条线的标记是它自己的类型:船线标出来的就是水路。

有一个例外:装了 MTR Map Overlay 时,它抓回的快照里直接写着每条线走哪几段轨道, 那种情况下轨道不是反推出来的,而是查出来的。上面这套反推仍然是其它两种读取的工作方式。

逐条线路的开关

在线路配置界面里,每条 MTR 线路的行尾有一个小方块: 点亮=沿这条线标记轨道(方块里写着 轨 或 水,说明会标成哪种道路), 熄灭=不标记(方块里什么都没有,两个状态一眼能分清)。 默认值来自配置项 mtr_auto_route_marks(默认开),而每条线给过的答案会单独记住 (存在 config/howtogo/mtr_line_marks.json,按 MTR 的线路 id 记), 所以一条条改过之后,下次进游戏还是你上次的选择。

点亮:沿这条线标记轨道

问道把那几段轨道标成只读的铁路(或水路)道路,行程就沿着 MTR 真正铺的轨道来规划与计时。 适合轨道是这条线自己独有的线路——绝大多数列车线都是这样。

熄灭:不标记,站点改按你画的路匹配

这条线不贡献任何轨道,它的站点改按老规矩去匹配你自己画的路—— 这正是「线路本来就走在你已经画好的路或水路上」时想要的答案,比如在你自己挖的水道上跑的船线。

轨道是为每条线都记着的,哪怕这条线的标记是熄灭的: 开关管的是「要不要把这些轨道作为道路加进路网」,而这条线画在地图上时, 仍然沿着它自己实际走的那段轨道来画。所以你现在点亮,它会立刻用上走出范围之前就已经记下的轨道。

短轨道先拼成长段

MTR 交给客户端的轨道常常是一小段一小段的。直接拿这些碎片来画与规划,同一条会变成 几十条互不相干的边:选中只选中其中一截,命名、描边与寻路的边数都被碎片放大。所以读进来之后先做一次拼接: 端点相接、类型相同的片段接成一条「路口到路口」的长折线,只有岔口 (三条及以上轨道交汇)和没接上的地方才断开。机械动力的轨道走的是同一套。 拼接只发生在机器读到的只读图层里:轨道标记、线路与站点的说法都不受影响。

换乘站

MTR 的线路和你自己建的线路是同一批线路:某条 MTR 线路与另一条线路(不管是 MTR 的还是你的) 共用同一个站点时,它同样参与换乘站的判定,在屏幕上画成橙色方块。 换乘站是「两站及以上的线路经停」这件事的结果,与线路来自哪里无关; 融合与分开的规则见 公共交通线路。

公共交通的行程也可以由 MTR 的线路来承运:上车地点是你走去 MTR 车站的这一段步行, 然后是 MTR 线路上的一段车程,需要换线时按换乘再算一次等车时间。规划本身仍在你自己的道路网上, 所以「走不到的 MTR 车站」不会凭空出现在路线里。

支持哪些 MTR 版本

读取全靠反射:问道的代码里没有 MTR 的任何类型,每一个类名、字段名、方法名都是 先查再用,任何一处对不上就整体退回「窗口 + 记忆」的老路,不报错也不崩。 代价是 MTR 改了包名或类名,读取器要跟着改——而这件事不能靠猜,只能对着真的 jar 查。

0.2.1 修掉的就是这样一个缺口:MTR 4.1 把入口类改名成了 org.mtr.MTR (原来是 org.mtr.Init 与 org.mtr.mod.Init,其它类也从 org.mtr.mod.* 搬到了 org.mtr.*)。「读整张地图」按类型找「持有 Main 的那个类」, 但类名只试了那两个旧拼法,而 4.1 上它们根本不存在——于是整图读取报告自己不可用, 模组安静地退回窗口读取,玩家看到的现象是范围外的车站又读不到了。 现在三个拼法都试,org.mtr.MTR 排在第一个。

MTR 构建 窗口读取(MtrClientData) 整图读取(MtrWholeMap) 结果
4.0.0 + 1.20.1 不认:Vector.x() 不存在 认 不可用(4.0.4 之前 Vector 的取值方法还不是 x())
4.0.4 + 1.20.1 / 1.20.4(Forge)
4.0.4 + 1.20.1(Fabric)
认,绑到 org.mtr.mod.client.MinecraftClientData 认,绑到 org.mtr.mod.Init 两个握手全通过
4.1.0-beta.2 + 1.21.1(NeoForge) 认,绑到 org.mtr.client.MinecraftClientData 认,绑到 org.mtr.MTR(4.1 的新名字) 两个握手全通过

所以读取器实际要求 MTR 4.0.4 及以上。另外要留意:MTR 4.0.4 没有 1.21.1 的构建 (4.0.x 到 1.20.4 为止,1.21.1 只有 4.1.0-beta.1/2),所以 4.0.4 属于 1.20.1-* / 1.20.4-neoforge 那几条分支。上表证明的是读取器的名字表覆盖 4.0.4 (反射按描述符解析,与 Minecraft 版本无关);真正的一局里能不能用,仍然要在对应分支上用那个版本跑一次。

还要分清两件事:版本支持说的是「读取器认得哪个 MTR」,而模组自己的构建是另一回事。 0.2.2 的 21 个构建(11 Fabric + 10 NeoForge)都能在对应的 Minecraft 版本上装, 但 MTR 读取只会在那个版本真的装了 MTR 时发生——没装,或者装的是 4.0.4 以下的版本, 问道就是读不到,其余功能一件不少。

这些结论不是读更新日志读出来的,而是量出来的:把那个版本的真 jar 放进回归测试的 classpath, 逐条核对读取器用到的每一个类、字段与方法。你自己也能这样核对任意版本:

tools\routing-harness\run-fast.ps1 -MtrJar <jar>(另有 -MtrMapJar), 两个握手各查一遍。它不需要你把 jar 放进 run\mods——那会把开发客户端也一起拖下水。

限制与排错

  • 没装 MTR 时这一切都不存在,也没有任何副作用:图层为空,车站列表里没有这一项,其余功能照常。
  • 线路能读多远取决于玩法:自己开图有整张地图;联机时如果两边都没装 MTR Map Overlay, 就只有靠近过的线路与车站——问道记得住,所以走开不会丢,但从没靠近过的确实读不到。
  • 线路类型不认识(例如飞机)就直接不处理,不会退化成铁路。
  • 轨道标记是反推出来的:如果 MTR 的轨道在站点之间并不连通, 那一对相邻站点之间就规划不过去,这段标记会缺失,线路图上就会出现断口。 碰到这种情况,把该线的标记关掉,让它按你画的路走,通常是更好的答案。
  • 看不到 MTR 的线路时,先确认:mtr_transit 没有关;自己开图时整张地图本来就该有, 联机时两边是否都装了 MTR Map Overlay;如果都没有,你至少靠近过那条线。 日志里 [HowToGo] MTR import | 开头的那一行会写明这次读到了多少站点、几条线路、 跳过了几条,以及累计记住了多少——那是判断「到底没读到,还是只是没画出来」最快的一条线索, 但这一行属于模组自己的诊断,要先在配置里把 debug_log 打开(默认关); 警告与错误照旧一直写。