Руководства / Команды и данные
Отрицательные координаты ломают математику чанков в двух местах, а не в одном
Усекающее деление помещает блок −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, так что весь блок Незера 125 отображается обратно куда-то в 1000–1007, в зависимости от того, где внутри этого блока вы стоите. Для блочных координат это не круговой рейс. floor отбрасывает остаток:
- Верхний мир
1000→ Незер125→ обратно1000✓ - Верхний мир
1007→ Незер125→ блочная координата даёт1000✗ — на семь блоков меньше
Погрешность составляет от 0 до 7 блоков на ось, то есть до 7 по X и 7 по Z одновременно. Именно поэтому для точной привязки порталов координату Незера нужно записывать, а не выводить заново: после деления исходную позицию в Верхнем мире уже не восстановить.
Для хаба в Незере это редко имеет значение — 7 блоков укладываются в собственный радиус захвата портала. Но для связывания двух порталов, которые не должны перехватывать трафик друг друга, это очень важно.
Отрицательные координаты Верхнего мира усугубляют ситуацию привычным образом: Верхний мир −1 даёт Незер floor(−1/8) = −1, тогда как усечение даёт 0 и отправляет вас не в ту сторону оси.