187ec90440
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
98 lines
4.7 KiB
Markdown
98 lines
4.7 KiB
Markdown
# Package Checkpoint 3 (PC3)
|
||
|
||
> **Status: partial.** PC3's planned scope was M4-avatars + M5-multiplayer +
|
||
> Foundation Hardening + M6–M10 (see `docs/build-plan-v1.md`). As of
|
||
> 2026-09-05, **M4, M5, and most of Foundation Hardening are done and
|
||
> checkpoint-verified**; M6–M10 have not been started. This doc records what
|
||
> is real. Whether to call this "PC3" (redefined scope, M6–M10 → PC4) or keep
|
||
> PC3 open is a user decision.
|
||
|
||
Built by Claude directly (fleet offline), branch `pc3` → `main`.
|
||
|
||
## What's real and verified (`scripts/run_checkpoint_tests.py`, all green)
|
||
|
||
| Check | Result |
|
||
|---|---|
|
||
| Alembic migrations, fresh DB | PASS |
|
||
| Backend `pytest` | 77/77 PASS (hermetic — auto-starts an ephemeral Postgres) |
|
||
| Godot smoke tests (~16 files) | ALL PASS |
|
||
| `smoke_test_network_client` (real dedicated server) | 4/4 PASS |
|
||
| 2-player multiplayer (`run_multiplayer_2p_test.sh`) | PASS |
|
||
| Fresh-clone boot (`verify_fresh_clone.sh`) | PASS |
|
||
| Linux + Windows exports | Build as real ELF64 / PE32+ binaries |
|
||
|
||
### M4 — Avatars in the game
|
||
|
||
- Placeholder avatar pack (14 GLBs, rigged 24-bone base body + attachments)
|
||
generated by `scripts/tools/gen_placeholder_avatars.gd`. Real art is a
|
||
content swap — catalog paths are stable.
|
||
- The avatar **loader was fixed** — it had never run against a real skeleton
|
||
(`Skeleton3D.has_bone()` isn't a Godot 4 method; the bone-copy dropped the
|
||
skin binding; attachments were parsed then discarded).
|
||
- `avatar_visualizer.gd` **rewritten** — the old one referenced a loader API
|
||
that never existed and didn't compile.
|
||
- Persistence: `avatar_loadouts` table + `GET/PUT /api/avatar/loadout`
|
||
(loadout stored as one JSON doc; server validates the envelope only).
|
||
- Wired into gameplay: the in-world player renders as the assembled avatar
|
||
(not a capsule); the loadout is fetched at login; "Customize Avatar" from
|
||
the main menu saves through to the backend.
|
||
- **Follow-up:** camera is still first-person, so the local player doesn't
|
||
see their own avatar much (third-person is a polish item); remote players'
|
||
avatar appearance is not replicated yet (they show as labelled capsules).
|
||
|
||
### M5 — Real multiplayer
|
||
|
||
- `server.gd` **actually runs a sim now** — loads the world level, spawns a
|
||
server player per peer, processes each peer's input queue in seq order at a
|
||
fixed 30 Hz, broadcasts full snapshots with a per-peer `last_seq`. (The
|
||
input queue was a no-op before.)
|
||
- `network_client.gd` reconciliation **replays in-flight inputs** from the
|
||
authoritative state (drops acked ones), snaps only on real drift.
|
||
- **Server orchestration** (`backend/app/services/game_servers.py`): one
|
||
headless Godot dedicated-server process per world, spawned on demand by
|
||
`POST /api/worlds/{id}/join`, port-allocated, with a status-file idle
|
||
reaper. Off by default; the playtest launcher (`checkpoint_ops.py`)
|
||
enables it.
|
||
- **Client play path**: world browser → `/join` → `Session` → `game.tscn`
|
||
connects → local player predicts with collision, input forwarded, server
|
||
position is a drift backstop, remote players render from snapshots,
|
||
disconnect returns to the world list. Falls back to local single-player
|
||
when the backend has no orchestration.
|
||
- **Verified**: two real client processes each `register → /join → play`,
|
||
hosted on an orchestrated server, see and move relative to each other
|
||
(`run_game_multiplayer_test.sh`).
|
||
|
||
### Foundation Hardening
|
||
|
||
- **Hermetic backend tests** — `cd backend && pytest` works from a clean
|
||
checkout (auto ephemeral Postgres + migrations, torn down after); skips
|
||
cleanly with a message if there's no Docker.
|
||
- **Fresh-clone boot check** — `verify_fresh_clone.sh` exports a pristine
|
||
tree, imports from scratch, boots headless, fails on any script error.
|
||
Also fixed the root cause: a stale `.godot/global_script_class_cache.cfg`
|
||
was committed to the repo.
|
||
- **Real age verification** — `/register` takes a date of birth (not a
|
||
checkbox), server computes age, 403 under 18. Still self-attested; the
|
||
limitation and the ID-check hook are documented
|
||
(`content_moderation.md` 5.2).
|
||
- **Auth rate limiting** — 10 attempts / min / IP on `/register` + `/login`.
|
||
- **CI workflow** — `.forgejo/workflows/checkpoint.yml` written, **not
|
||
running yet**: needs the operator to enable Forgejo Actions + register a
|
||
`host` runner (one-time; see `docs/ci.md`). Interim gate is
|
||
`run_checkpoint_tests.py` by hand / the dashboard.
|
||
|
||
## Not in PC3 (planned but not started)
|
||
|
||
- **M2** server-side world persistence
|
||
- **M6** scripting sandbox
|
||
- **M7** in-game editor
|
||
- **M8** social / chat
|
||
- **M9** economy
|
||
- **M10** discovery
|
||
|
||
## Playtest
|
||
|
||
Same as PC2 — grab the exe from the dashboard's `/checkpoints`, backend
|
||
pointed at it. New: with multiple people in the same world you now actually
|
||
see each other move.
|