187ec90440
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
4.7 KiB
4.7 KiB
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.gdrewritten — the old one referenced a loader API that never existed and didn't compile.- Persistence:
avatar_loadoutstable +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.gdactually 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-peerlast_seq. (The input queue was a no-op before.)network_client.gdreconciliation 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 byPOST /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.tscnconnects → 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 && pytestworks 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.shexports 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.cfgwas committed to the repo. - Real age verification —
/registertakes 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.md5.2). - Auth rate limiting — 10 attempts / min / IP on
/register+/login. - CI workflow —
.forgejo/workflows/checkpoint.ymlwritten, not running yet: needs the operator to enable Forgejo Actions + register ahostrunner (one-time; seedocs/ci.md). Interim gate isrun_checkpoint_tests.pyby 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.