Negative Koordinaten brechen die Chunk-Mathematik an zwei Stellen, nicht an einer
Die abschneidende Division steckt Block −1 in Chunk 0 statt in −1, und ein einfaches % liefert einen Offset von −1 innerhalb des Chunks statt 15. Beides ist falsch, und beides sieht richtig aus, bis man westlich des Spawns geht.
Jede Chunk-Berechnung ist im positiven Quadranten trivial und in den anderen drei falsch. Es gibt genau zwei Stellen, an denen es schiefgeht, sie sind unabhängig voneinander, und beide liefern Ergebnisse, die plausibel aussehen.
Die beiden Operationen
chunk = floor(block / 16) not trunc(block / 16)
offset = ((block % 16) + 16) % 16 not block % 16
Regionskoordinaten sind dasselbe Paar, eine Ebene höher, mit 32 statt 16:
region = floor(chunk / 32) -> r.<x>.<z>.mca
regionOff = ((chunk % 32) + 32) % 32
Eine Region ist also 32 × 32 Chunks groß, was 512 × 512 Blöcken entspricht.
Wo jede einzelne zubeißt
Division. Die meisten Sprachen schneiden gegen null ab, und floor rundet gegen minus Unendlichkeit. Bei positiven Werten stimmen sie überein, bei jeder negativen Zahl, die kein exaktes Vielfaches ist, nicht:
- Block
−1→floor(−1/16) = −1✓, abertrunc(−1/16) = 0✗
Block −1 liegt einen Block westlich vom Ursprung und gehört zu Chunk −1. Durch das Abschneiden landet er in Chunk 0, zusammen mit Block +1. Jeder negative Chunk-Index kommt um eins zu hoch heraus.
Rest. In JavaScript, Java, C und den meisten anderen behält % das Vorzeichen des linken Operanden:
- Block
−1→ korrekter Offset15, aber−1 % 16 = −1✗
Ein Offset von −1 ist überhaupt keine Position innerhalb eines 16 Blöcke breiten Chunks. Wenn man damit ein Block-Array indiziert, wirft es entweder einen Fehler oder liest still die falsche Zelle.
Ein durchgerechnetes Beispiel
Block −1290 auf einer Achse:
| Schritt | korrekt | naiv |
|---|---|---|
| Chunk | floor(−1290/16) = −81 | trunc = −80 ✗ |
| Offset im Chunk | 6 | −1290 % 16 = −10 ✗ |
| Region | floor(−81/32) = −3 | trunc = −2 ✗ |
| Offset in der Region | 15 | −81 % 32 = −17 ✗ |
Vier Werte, vier falsche Antworten, keine davon so weit außerhalb des Bereichs, dass sie offensichtlich kaputt aussieht. Region −3 umfasst die Chunks −96 bis −65, und Chunk −81 liegt 15 vom Anfang entfernt — genau das, was der korrigierte Rest zurückgibt.
Die Nether-Umrechnung ist in eine Richtung verlustbehaftet
Von der Oberwelt in den Nether wird durch 8 geteilt, und dabei wird ebenfalls abgerundet:
netherX = floor(overworldX / 8)
Zurück multipliziert man seine exakte Position mit 8, also wird der gesamte Nether-Block 125 zurück auf irgendetwas in 1000–1007 abgebildet, je nachdem, wo in diesem Block man steht. Das ist kein Hin und Zurück für Blockkoordinaten. floor wirft den Rest weg:
- Oberwelt
1000→ Nether125→ zurück zu1000✓ - Oberwelt
1007→ Nether125→ die Blockkoordinate sagt1000✗ — sieben Blöcke zu kurz
Der Fehler beträgt 0 bis 7 Blöcke pro Achse, also bis zu 7 auf X und 7 auf Z gleichzeitig. Genau deshalb muss man für eine präzise Portalverknüpfung die Nether-Koordinate aufschreiben, statt sie neu herzuleiten: Sobald man geteilt hat, ist die Oberwelt-Position, von der sie stammte, nicht mehr rekonstruierbar.
Für einen Nether-Hub spielt das selten eine Rolle — 7 Blöcke liegen innerhalb des eigenen Einfangradius eines Portals. Für die Verknüpfung zweier Portale, die sich gegenseitig keinen Verkehr stehlen dürfen, spielt es eine sehr große Rolle.
Negative Oberwelt-Koordinaten machen es auf die bekannte Weise schlimmer: Oberwelt −1 ergibt Nether floor(−1/8) = −1, während das Abschneiden 0 ergibt und einen auf die falsche Seite der Achse schickt.