# 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.