The Best Resource for Minecraft
The Best Resource for Minecraft

Від'ємні координати ламають математику чанків у двох місцях, а не в одному

Від'ємні координати ламають математику чанків у двох місцях, а не в одному

Ділення з усіканням поміщає блок −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 rounds toward negative infinity. They agree for positives and disagree for every negative that is not an exact multiple:

  • блок −1floor(−1/16) = −1 ✓, but trunc(−1/16) = 0

Блок −1 знаходиться на один блок на захід від початку координат і належить до чанку −1. Усікання поміщає його в чанк 0 разом із блоком +1. Кожен від'ємний індекс чанку виходить на одиницю більшим.

Остача. У JavaScript, Java, C та більшості інших, % keeps the sign of the left operand:

  • блок −1 → correct offset 15, but −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 блоків від його початку — що саме і повертає скоригована остача.

Перетворення для Nether втрачає точність в одному напрямку

Перехід зі Звичайного світу до Nether ділить на 8 і також округлює вниз:

netherX = floor(overworldX / 8)

Повернення назад множить на 8. Це не зворотний шлях. floor throws away the remainder, so returning lands you on a multiple of 8:

  • Звичайний світ 1000 → Nether 125 → back to 1000
  • Звичайний світ 1007 → Nether 125 → back to 1000 ✗ — seven blocks short

Похибка становить від 0 до 7 блоків на вісь, тобто до 7 на X та 7 на Z одночасно. Саме тому для точного зв'язування порталів потрібно записувати координати Nether, а не обчислювати їх заново: після ділення початкову позицію у Звичайному світі вже не відновити.

Для хабу в Nether це рідко має значення — 7 блоків знаходяться в межах радіусу захоплення самого порталу. Для зв'язування двох порталів, які не повинні перехоплювати зв'язок один одного, це дуже важливо.

Від'ємні координати у Звичайному світі погіршують ситуацію вже знайомим чином: координата Звичайного світу −1 gives Nether floor(−1/8) = −1, while truncation gives 0 and sends you to the wrong side of the axis.

Спробувати інструмент: Coordinate Toolbox →