Отрицательные координаты нарушают математику чанков в двух местах, а не в одном
Усекающее деление помещает блок −1 в чанк 0 вместо −1, а обычный % даёт смещение внутри чанка −1 вместо 15. Оба неверны, и оба выглядят правильно, пока вы не пойдёте к западу от точки появления.
Каждое вычисление чанка тривиально в положительном квадранте и неверно в трёх других. Есть ровно два места, где оно ошибается, они независимы друг от друга, и оба дают правдоподобные ответы.
Две операции
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) = −81 | trunc = −80 ✗ |
| смещение в чанке | 6 | −1290 % 16 = −10 ✗ |
| регион | floor(−81/32) = −3 | trunc = −2 ✗ |
| смещение в регионе | 15 | −81 % 32 = −17 ✗ |
Четыре значения, четыре неверных ответа, ни один из которых не выходит за пределы диапазона настолько, чтобы выглядеть явно сломанным. Регион −3 охватывает чанки от −96 до −65, а чанк −81 находится на 15 единиц от его начала — что в точности соответствует тому, что возвращает исправленный остаток.
Преобразование в Незер является с потерями в одном направлении
Преобразование из Верхнего мира в Незер делит на 8 и также округляет в меньшую сторону:
netherX = floor(overworldX / 8)
Обратное преобразование умножает на 8. Это не круговой путь. floor отбрасывает остаток, поэтому возвращение приземляет вас на кратное 8:
- Верхний мир
1000→ Незер125→ обратно в1000✓ - Верхний мир
1007→ Незер125→ обратно в1000✗ — не хватает семи блоков
Ошибка составляет от 0 до 7 блоков на ось, то есть до 7 по X и 7 по Z одновременно. Это вся причина, по которой для точной привязки порталов необходимо записывать координаты Незера, а не вычислять их заново: как только вы разделили, исходное положение в Верхнем мире уже не восстанавливается.
Для хаба в Незере это редко имеет значение — 7 блоков находится внутри радиуса захвата самого портала. Для связывания двух порталов, которые не должны перехватывать трафик друг друга, это имеет большое значение.
Отрицательные координаты Верхнего мира усугубляют ситуацию привычным образом: Верхний мир −1 даёт Незер floor(−1/8) = −1, в то время как усечение даёт 0 и отправляет вас на неверную сторону оси.