Commit Graph

8 Commits

Author SHA1 Message Date
daddy b6c6ddfbc4 Add world content persistence backend (Postgres chunks, internal API, game-server token auth)
Implements docs/architecture/world_persistence_impl.md §9's backend task:
chunk/room/revision Postgres schema (migration 0005), a Python port of the
world.md §3.6 binary chunk codec with golden-file tests, a WorldStore
service (world.md §5.1 minus flush_dirty/rebuild_index), the public
world-content router (rooms/chunks CRUD, optimistic concurrency, batch
writes) and the internal read-only API for dedicated servers, and
game_servers.py changes: a per-spawn bearer token, --api-url/--server-token
args, and a real "ready"-field wait replacing _await_port, which bound the
UDP port before content existed and failed open (no error) past its
deadline.

create_world now auto-creates the "main" room.

Deviations/findings, not silently decided:
- chunk_codec.py implements real IEEE-754 float32 packing rather than
  mirroring a confirmed bug in scripts/chunk_data_decoder.gd's
  _read_float32/_write_float32, which do a lossy int<->float cast instead
  of a bit reinterpretation (verified against Godot 4.6.2 headless) and
  will corrupt any non-integer position/rotation/scale/bounds value. Not
  fixed here (client-side, out of scope); flagged for the client task.
- world_persistence_impl.md references a world's monotonic
  content_revision (§6/§7.2/§8.1) but never defines where it's stored.
  Resolved as MAX(id) over the append-only world_chunk_revisions table,
  scoped to world_id, rather than adding an undocumented column.
- No existing Godot-side golden fixture/test vectors were found to reuse,
  so tests/fixtures/chunks/*.bin are authored fresh from this
  implementation (see generate_fixtures.py).

Verification: 132/132 backend pytest tests pass against a real ephemeral
Postgres (both reusing an already-running instance and cold via
conftest.py's own docker-compose bootstrap), including migration
upgrade/downgrade/upgrade, codec round-trip + validation-error tests, and
full HTTP round trips for PUT/GET/batch/delete/revisions/rollback and the
internal API's token auth.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2FESCaMXeJpC9gSctiEeD
2026-09-14 19:48:01 +00:00
daddy af85048deb PC3 Phase 1.3: backend avatar loadout persistence
New avatar_loadouts table (one row per user, loadout stored as a single
JSONB document matching the client's AvatarLoadout.to_json() shape) and
GET/PUT /api/avatar/loadout.

The server has no copy of the asset catalog, so it validates only the
envelope (JSON object, non-empty base_body, object slots, size cap) --
which-asset-in-which-slot validation stays client-side. New accounts get a
server default (base_human_male_01, no slots) with is_default:true so the
client can tell "never customised" from "customised back to base".

- models/avatar_loadout.py + 0004 migration + User.avatar_loadout relation
- routers/avatar.py wired into main.py
- tests/test_avatar_loadout.py (7 tests: default, save/read, replace,
  validation, auth) -- full suite 68 passed
- lifted the duplicated _override_db fixture from 3 test files into
  conftest.py (new test files need it too)
- docs/architecture/avatar.md §9a: the persistence design

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
2026-09-05 21:42:10 +00:00
coder-bot ed6811a438 WIP checkpoint: Upgrade auth to RS256 signing, age-verification field, refresh-token sessions 2026-08-19 19:49:41 +00:00
claude-mvp-fix acbc518b4a Fix migration 0001_initial: double CREATE TYPE crashes every clean bootstrap
world_visibility and asset_type were each declared twice: once explicitly
via `.create(checkfirst=True)`, and again implicitly by SQLAlchemy when
create_table() compiled the inline postgresql.ENUM() column type (which
defaults create_type=True). On a genuinely empty database this raises
asyncpg.DuplicateObjectError mid-transaction and the migration never
completes -- verified this was true 100% of the time from a fresh
container, meaning the backend has never successfully bootstrapped from
scratch. Fixed by passing create_type=False on the inline column
declarations, since the type is already created explicitly above.

Also parameterized both services' host ports (POSTGRES_HOST_PORT /
REDIS_HOST_PORT, defaulting to the original 5432/6379) so concurrent
agent workspaces sharing this same docker-compose.yml can run isolated
databases without colliding on the host port.
2026-08-18 21:55:34 +00:00
coder-bot e4d3fe566b WIP checkpoint: Enforce 18+ age gate at account registration 2026-08-18 15:28:25 +00:00
coder-bot 3f707bf6b0 WIP checkpoint: Scaffold FastAPI backend project structure 2026-08-17 23:34:54 +00:00
coder-bot 4df839ccd8 WIP checkpoint: Scaffold FastAPI backend project structure 2026-08-17 23:34:54 +00:00
coder-bot 2b1786558a Recovered orphaned WIP before switching to a new task 2026-08-17 23:34:54 +00:00