MC Toolkit

指南 / 命令与数据

负坐标在两处破坏区块运算,而非一处

截断除法会把方块 −1 归入区块 0 而非 −1,而普通的 % 会给出 −1 的区块内偏移而非 15。两者都错了,而且在出生点以西之前都看起来没问题,区域坐标和区块坐标是同一套换算再往上一层,一个区域相当于 512×512 个方块。

每个区块计算在正象限里都微不足道,在另外三个象限里却都是错的。出错的地方恰好有两处,它们彼此独立,而且都会给出看似合理的答案。

两个运算

chunk  = floor(block / 16)          not  trunc(block / 16)
offset = ((block % 16) + 16) % 16   not  block % 16

区域坐标是同一对数值,只是高一级,用 32 代替 16:

region     = floor(chunk / 32)   ->  r.<x>.<z>.mca
regionOff  = ((chunk % 32) + 32) % 32

所以一个区域是 32 × 32 个区块,也就是 512 × 512 个方块。

各自在哪里出问题

除法。 大多数语言向零截断,而 floor 向负无穷取整。它们在正数上一致,在所有非整数倍的负数上都不一致:

  • 方块 −1 → floor(−1/16) = −1 ✓,但 trunc(−1/16) = 0 ✗

方块 −1 位于原点以西一个方块,属于区块 −1。截断把它归入区块 0,和方块 +1 放在一起。每一个负的区块索引都会算出来高一位。

取余。 在 JavaScript、Java、C 以及大多数其他语言中,% 保留左操作数的符号:

  • 方块 −1 → 正确偏移 15,但 −1 % 16 = −1 ✗

偏移 −1 根本不是一个 16 宽区块内的位置。用它去索引方块数组,要么抛异常,要么静默读到错误的格子。

一个实例演算

某一轴上的方块 −1290:

步骤正确朴素做法
区块floor(−1290/16) = −81trunc = −80 ✗
区块内偏移6−1290 % 16 = −10 ✗
区域floor(−81/32) = −3trunc = −2 ✗
区域内偏移15−81 % 32 = −17 ✗

四个值,四个错误答案,而且没有一个超出范围到看起来明显坏掉。区域 −3 覆盖区块 −96 到 −65,区块 −81 位于其起点之后 15 处——这正是修正后的取余所返回的结果。

下界转换在一个方向上是不可逆的

主世界到下界是除以 8,而且同样向下取整:

netherX = floor(overworldX / 8)

回来时会把你的精确位置乘以 8,所以整个下界方块 125 会映射回 1000–1007 中的某处,具体取决于你站在该方块内的哪个位置。对于方块坐标来说,这不是一次往返。floor 丢弃了余数:

  • 主世界 1000 → 下界 125 → 回到 1000 ✓
  • 主世界 1007 → 下界 125 → 方块坐标显示 1000 ✗ —— 少了七个方块

误差是每轴 0 到 7 个方块,所以 X 和 Z 上可以同时各差 7 个。这正是精确传送门链接需要把下界坐标写下来、而不是重新推导的全部原因:一旦除了,它来自的主世界位置就不再可恢复。

对于下界枢纽来说,这很少要紧——7 个方块还在传送门自身的捕获半径之内。对于链接两个不能互相抢流量的传送门来说,这就非常要紧了。

负的主世界坐标会以熟悉的方式让情况更糟:主世界 −1 得到下界 floor(−1/8) = −1,而截断得到 0,把你送到轴的另一侧。

坐标工具箱 →

更多指南

查看全部 →