Tilemap: Zellen kompakt speichern #134

Closed
opened 2026-09-18 08:03:56 +02:00 by meleki · 1 comment
Owner

Folge-Issue zu #128, dort unter „Nicht enthalten“ aufgeführt.

Befund

  • Pro Layer werden die Zellen als Map "x,y" → Tile-Id gespeichert (src/core/model/tilemap.ts:6). Jede bemalte Zelle trägt ihren Schlüssel und die volle Asset-Id. Eine voll bemalte Karte von 100 × 100 mit zwei Layern sind damit 20 000 Einträge in der Projektdatei.
  • Im Speicher ist die Map gewollt: Malen, Radieren und Nachbarn lesen wollen genau das. Es geht nur um die Datei.

Zu bauen

  • Ein kompaktes Speicherformat für die Zellen, nur in serialize.ts (Schreiben und Lesen). Das Modell im Speicher bleibt die Map.
  • Alte Projekte laden weiter: Das bisherige Format wird beim Laden erkannt und still übernommen, gespeichert wird im neuen.
  • Tests: Unit-Tests für die Hin- und Rückrichtung (leer, eine Zelle, volle Karte, mehrere Tiles, Zellen außerhalb des Rasters), Laden des alten Formats. Dazu eine Messung der Dateigröße vorher/nachher an einer großen Karte.

Offen (mit Vorschlag)

  • Format: Tile-Ids pro Layer einmal als Liste, die Zellen dann als Index darauf, zeilenweise lauflängenkodiert. Vorschlag: so. Gut lesbar im JSON und klein bei großen einheitlichen Flächen.
  • Zellen außerhalb des Rasters: Sie bleiben heute erhalten und kommen beim Vergrößern zurück. Vorschlag: im neuen Format genauso erhalten, nichts stillschweigend wegwerfen.
  • Wann: Erst sinnvoll, wenn Karten wirklich groß werden. Vorschlag: vorher mit einer echten Karte messen, ob es sich lohnt.
Folge-Issue zu #128, dort unter „Nicht enthalten“ aufgeführt. ## Befund - Pro Layer werden die Zellen als Map `"x,y"` → Tile-Id gespeichert (`src/core/model/tilemap.ts:6`). Jede bemalte Zelle trägt ihren Schlüssel und die volle Asset-Id. Eine voll bemalte Karte von 100 × 100 mit zwei Layern sind damit 20 000 Einträge in der Projektdatei. - Im Speicher ist die Map gewollt: Malen, Radieren und Nachbarn lesen wollen genau das. Es geht nur um die **Datei**. ## Zu bauen - Ein kompaktes Speicherformat für die Zellen, nur in `serialize.ts` (Schreiben und Lesen). Das Modell im Speicher bleibt die Map. - **Alte Projekte laden weiter:** Das bisherige Format wird beim Laden erkannt und still übernommen, gespeichert wird im neuen. - Tests: Unit-Tests für die Hin- und Rückrichtung (leer, eine Zelle, volle Karte, mehrere Tiles, Zellen außerhalb des Rasters), Laden des alten Formats. Dazu eine Messung der Dateigröße vorher/nachher an einer großen Karte. ## Offen (mit Vorschlag) - **Format:** Tile-Ids pro Layer einmal als Liste, die Zellen dann als Index darauf, zeilenweise lauflängenkodiert. Vorschlag: so. Gut lesbar im JSON und klein bei großen einheitlichen Flächen. - **Zellen außerhalb des Rasters:** Sie bleiben heute erhalten und kommen beim Vergrößern zurück. Vorschlag: im neuen Format genauso erhalten, nichts stillschweigend wegwerfen. - **Wann:** Erst sinnvoll, wenn Karten wirklich groß werden. Vorschlag: vorher mit einer echten Karte messen, ob es sich lohnt.
admin added this to the Development project 2026-09-18 08:26:24 +02:00
admin moved this to Next in Development on 2026-09-18 08:26:44 +02:00
Author
Owner

Erledigt in 10b49fa, Wiki a0f279a.

Lohnt es sich?

Vor dem Bauen gemessen, pretty-printed wie der Server schreibt:

Karte vorher nachher
echte Karte (3 Layer, 445 Zellen) 15 396 B 1 154 B
100 × 100, 2 Layer, einer gesprenkelt 736 084 B 10 964 B

Format

Umgesetzt wie vorgeschlagen, pro Layer:

"tiles": ["grass", "path"],
"origin": { "x": 0, "y": 0 },
"rows": ["1*40 0*3 2", "1*44"]
  • Ein String pro Zeile statt Zahlen-Arrays — wegen JSON_PRETTY_PRINT stünde sonst jede Zahl auf einer eigenen Zeile.
  • index zählt ab 1 in tiles, 0 ist leer; *1 entfällt, leere Zellen am Zeilenende werden nicht geschrieben.
  • Zellen außerhalb des Rasters bleiben erhalten (das Rechteck kommt aus den bemalten Zellen, negative Koordinaten gehen).
  • Im Speicher bleibt die Map; geändert ist nur serialize.ts, der Kodierer liegt in src/core/store/tilemapCells.ts.
  • Alte Projekte mit cells laden weiter und werden beim nächsten Speichern umgeschrieben.
  • Nachsichtiges Lesen: ein kaputter Lauf beendet seine Zeile, ein Index ohne Tile gilt als leer, ein Lauf ist höchstens 65 536 lang.

Tests (lokal)

npm run check                                          → 97 Files, 1323 Tests (vorher 1309), CHECK EXIT 0
npm run build                                          → BUILD EXIT 0
GM_STEPS=1-16,24,25,33,36,38,39,40 (frisch geseedet)   → ALL PASSED – 587 checks
  • 14 neue Unit-Tests (tests/core/tilemap-cells.test.ts): Rundwege (leer, einzeln, Lücken, außerhalb des Rasters, volle 100er-Karte, 500 Zufallszellen), Map bleibt unverändert, altes Format lesen/neu schreiben, Größenschranke, vier Fälle nachsichtiges Lesen.
  • Gegenprobe mit 4 absichtlichen Fehlern, alle gefangen: Index um eins verschoben (11 rot), Lücke zu kurz (4), keine Laufobergrenze (1), neues Format beim Laden ignoriert (3).
  • e2e Modul 40, Schritt 40k: speichern, Datei vom Server lesen (keine cells, dafür tiles/rows), neu laden, Zellen identisch. +4 Checks.

Doku

Wiki Tilemaps.md mit neuem Abschnitt In the project file, Architecture.md (Store-Zeile), Development.md. Check-Summe 1017 in README und Wiki — gerechnet, CI steht noch aus.

Erledigt in `10b49fa`, Wiki `a0f279a`. ## Lohnt es sich? Vor dem Bauen gemessen, pretty-printed wie der Server schreibt: | Karte | vorher | nachher | |---|---|---| | echte Karte (3 Layer, 445 Zellen) | 15 396 B | 1 154 B | | 100 × 100, 2 Layer, einer gesprenkelt | 736 084 B | 10 964 B | ## Format Umgesetzt wie vorgeschlagen, pro Layer: ```json "tiles": ["grass", "path"], "origin": { "x": 0, "y": 0 }, "rows": ["1*40 0*3 2", "1*44"] ``` - Ein String pro Zeile statt Zahlen-Arrays — wegen `JSON_PRETTY_PRINT` stünde sonst jede Zahl auf einer eigenen Zeile. - `index` zählt ab 1 in `tiles`, `0` ist leer; `*1` entfällt, leere Zellen am Zeilenende werden nicht geschrieben. - Zellen außerhalb des Rasters bleiben erhalten (das Rechteck kommt aus den bemalten Zellen, negative Koordinaten gehen). - Im Speicher bleibt die Map; geändert ist nur `serialize.ts`, der Kodierer liegt in `src/core/store/tilemapCells.ts`. - Alte Projekte mit `cells` laden weiter und werden beim nächsten Speichern umgeschrieben. - Nachsichtiges Lesen: ein kaputter Lauf beendet seine Zeile, ein Index ohne Tile gilt als leer, ein Lauf ist höchstens 65 536 lang. ## Tests (lokal) ``` npm run check → 97 Files, 1323 Tests (vorher 1309), CHECK EXIT 0 npm run build → BUILD EXIT 0 GM_STEPS=1-16,24,25,33,36,38,39,40 (frisch geseedet) → ALL PASSED – 587 checks ``` - 14 neue Unit-Tests (`tests/core/tilemap-cells.test.ts`): Rundwege (leer, einzeln, Lücken, außerhalb des Rasters, volle 100er-Karte, 500 Zufallszellen), Map bleibt unverändert, altes Format lesen/neu schreiben, Größenschranke, vier Fälle nachsichtiges Lesen. - Gegenprobe mit 4 absichtlichen Fehlern, alle gefangen: Index um eins verschoben (11 rot), Lücke zu kurz (4), keine Laufobergrenze (1), neues Format beim Laden ignoriert (3). - e2e Modul 40, Schritt 40k: speichern, Datei vom Server lesen (keine `cells`, dafür `tiles`/`rows`), neu laden, Zellen identisch. +4 Checks. ## Doku Wiki `Tilemaps.md` mit neuem Abschnitt *In the project file*, `Architecture.md` (Store-Zeile), `Development.md`. Check-Summe **1017** in README und Wiki — gerechnet, CI steht noch aus.
admin moved this to Done in Development on 2026-09-18 09:28:37 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#134