Merge branch 'feat/engine-sync'
All checks were successful
CI / test (push) Successful in 21s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lukas Müllner 2026-11-13 16:02:11 +01:00
commit ff37b52711
312 changed files with 10337 additions and 7 deletions

View file

@ -87,6 +87,9 @@ Abrufe (403).
anspricht, in der Devvit-Doku prüfen (https://developers.reddit.com/docs).
- Aktuelle Devvit-Doku lesen, bevor etwas gebaut wird – die Plattform ändert sich schnell; nicht aus dem Gedächtnis
arbeiten.
- **Engine-Kopie:** `scripts/sync-engine.sh <ninelands-tag>` (Stand in `src/engine/VERSION`), holt auch Sprites und
Musik nach `public/`. `src/engine/` nie von Hand ändern; Biome ignoriert es. Unit-Tests werden vor `node --test` mit
esbuild gebündelt (die Engine importiert ohne Dateiendung).
- `.github/workflows/ci.yaml` stammt aus der Vorlage. Forgejo führt `.github/workflows` aus, wenn es kein
`.forgejo/workflows` gibt; `runs-on: ubuntu-latest` ist der gemeinsame Hetzner-Runner (für `npm run test` reicht er).
@ -118,7 +121,7 @@ Abrufe (403).
- CI: Die `parallel:`-Stufe der Vorlage ersetzt (Forgejo-Runner v13: „Invalid run/uses syntax“), Schritte laufen
nacheinander.
- `PLAN.md` angelegt, Lizenz auf GPL-3.0 umgestellt.
- Nächste Schritte: offene Fragen in `PLAN.md` klären, dann Phase 0 (Sync-Skript, Engine, Assets).
- Phase 0 erledigt (Engine `v0.11.0`, Tageskarte `src/shared/daily.ts`, Assets). Nächster Schritt: Phase 1.
- Subreddit `r/Ninelands` ist frei (Besitzer hat nachgesehen); anlegen erst für den Demo-Post.
Diese Datei bei neuen Arbeitsregeln und Entscheidungen aktualisieren.

31
PLAN.md
View file

@ -90,6 +90,16 @@ Abgabe: **9. Dezember 2026, 03:00 MEZ**. Eingereicht wird in „Best Daily Game
legt der Besitzer erst für den Demo-Post an. Hochgeladene, nicht veröffentlichte Apps lassen sich nur in Subreddits
mit < 200 Mitgliedern installieren – für die Jam-Demo reicht das.
## Devvit-Vorgaben, die das Design betreffen (Doku, 2026-11-13)
- **Ton:** erst nach einer Nutzeraktion abspielen, **Stumm-Knopf** im Spiel, bei `visibilitychange` stumm schalten.
- **Gesten:** nur Tippen/Klicken; kein Scroll-Abfangen, kein Zoom/Pan – man muss am Post vorbeiscrollen können.
- **Expanded Mode** nur auf ausdrückliche Aktion (Knopf), nie automatisch; die Standardansicht bleibt im Post.
- **Responsive**, die meisten Spieler sind mobil.
- **`localStorage` wird bei jedem App-Update gelöscht** → Streak, Freischaltungen, gewähltes Thema, Stumm-Schalter in
Redis speichern.
- Server: serverlos, max. 30 s je Anfrage, 4 MB Anfrage, 10 MB Antwort; kein `fs`. Client: keine externen Abrufe.
## Risiken
- Devvit ändert sich schnell: Scheduler, Redis, Flair und Journeys vor dem Bauen in der aktuellen Doku lesen
@ -109,3 +119,24 @@ Abgabe: **9. Dezember 2026, 03:00 MEZ**. Eingereicht wird in „Best Daily Game
keinen Zeilenumbruch am Ende.
- Lizenz auf GPL-3.0 umgestellt (`LICENSE` aus dem Hauptspiel, `package.json` → `GPL-3.0-only`). Der BSD-Text der
Vorlage bleibt als `LICENSE-TEMPLATE.md`, denn BSD verlangt, dass der Hinweis erhalten bleibt. `CREDITS.md` angelegt.
### 2026-11-13 – Phase 0: Engine und Assets
- `scripts/sync-engine.sh <tag>` holt per `git archive` (nur lesend) `src/core`, `src/campaign`, `src/puzzles` nach
`src/engine/` und Sprites (Tiles, Brett-Themen) sowie Musik nach `public/`. Stand in `src/engine/VERSION`
(`v0.11.0`). Avatare nicht übernommen (braucht die Basic-Version nicht).
- `src/engine/` hat eine eigene `tsconfig.json` (strict, `noUncheckedIndexedAccess` aus, `resolveJsonModule`,
`lib` WebWorker für `crypto`/`structuredClone`). **`erasableSyntaxOnly` aus**, weil `duel.ts` Parameter-Properties
nutzt. Biome ignoriert `src/engine/`.
- **Fallstrick:** Die Engine importiert ohne Dateiendung (`'./Board'`). `node --experimental-strip-types` kann das
nicht auflösen. Deshalb bündelt `test:unit` die Tests jetzt erst mit esbuild nach `dist/test/` (`--packages=external`,
damit `@devvit/web` als ein Modul geladen wird und die Redis-Attrappen im Server-Test greifen), dann `node --test`.
Der Glob `src/**/*.test.ts` läuft unter `sh` ohne globstar = nur eine Ebene tief (`src/*/…`).
- `src/shared/daily.ts`: Tageskarte mit Seed `fnv1a('ninelands-reddit-daily-' + UTC-Tag)`, Nummer ab 2026-11-13 (#1),
Welt-Regel wie im Hauptspiel `rolledTrait(seed)`. **Präfix und `FIRST_DAY` nie ändern** (sonst andere Karten und
Nummern rückwirkend); Schutztests in `daily.test.ts`.
- Ziele (`computeGoals`, 7 Bot-Partien) plus Nachspielen dauern in Node ≈ 80 ms – unproblematisch für den Server.
Für die Prüfung **nicht** `verifyProgress` aus `progress.ts` nehmen: das zieht Kampagnen-JSONs ins Bundle und kennt
nur die Website-Tageskarte. Eigene Prüfung aus `applyMoves` + `computeGoals` + `starsOf`.
- Musik: vorerst ein Titel („Peaceful Village“, MP3, 1,6 MB) – MP3 läuft auch in iOS-Webviews. Ein Größenlimit für
Webview-Assets steht nicht in der Doku; weitere Titel in `scripts/sync-engine.sh` (`musik=(…)`) ergänzen.

View file

@ -1,7 +1,8 @@
{
"$schema": "https://biomejs.dev/schemas/2.6.6/schema.json",
"css": {"formatter": {"enabled": true, "quoteStyle": "single"}},
"files": {"includes": ["**", "!dist/", "!public/*.js*"]},
// src/engine/ ist eine Kopie aus Ninelands (scripts/sync-engine.sh) und wird nicht umformatiert
"files": {"includes": ["**", "!dist/", "!public/*.js*", "!src/engine"]},
"formatter": {
"bracketSpacing": false,
"enabled": true,

View file

@ -14,7 +14,7 @@
"playtest": "devvit playtest",
"test": "npm run test:types && npm run lint && npm run test:unit && npm run build",
"test:types": "tsc --build",
"test:unit": "node --experimental-strip-types --no-warnings=ExperimentalWarning --test src/**/*.test.ts",
"test:unit": "esbuild --bundle --log-level=warning --platform=node --format=esm --packages=external --outdir=dist/test --outbase=src src/**/*.test.ts && node --test dist/test/**/*.test.js",
"publish": "npm run clean && npm run build && devvit publish",
"watch": "sh -c 'trap \"kill 0\" exit; npm run build:client -- --watch=forever& npm run build:server -- --watch=forever& wait' --"
},

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Some files were not shown because too many files have changed in this diff Show more