b6c6ddfbc4
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
40 lines
1.4 KiB
Python
40 lines
1.4 KiB
Python
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey
|
|
from sqlalchemy.dialects.postgresql import ENUM
|
|
from sqlalchemy.orm import relationship
|
|
from sqlalchemy.sql import func
|
|
import enum
|
|
|
|
from app.database import Base
|
|
|
|
|
|
class WorldVisibility(enum.Enum):
|
|
PUBLIC = "public"
|
|
PRIVATE = "private"
|
|
FRIENDS_ONLY = "friends_only"
|
|
|
|
|
|
class World(Base):
|
|
__tablename__ = "worlds"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
name = Column(String(255), nullable=False)
|
|
description = Column(Text, nullable=True)
|
|
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
visibility = Column(
|
|
ENUM(
|
|
WorldVisibility,
|
|
name="world_visibility",
|
|
create_type=False,
|
|
values_callable=lambda enum_cls: [e.value for e in enum_cls],
|
|
),
|
|
default=WorldVisibility.PUBLIC,
|
|
)
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
|
|
|
owner = relationship("User", back_populates="worlds")
|
|
assets = relationship("Asset", back_populates="world", cascade="all, delete-orphan")
|
|
# world_content.py / world_persistence_impl.md §4.1. Added here (only)
|
|
# so PC1/PC2 code depending on this module is otherwise untouched.
|
|
rooms = relationship("WorldRoom", back_populates="world", cascade="all, delete-orphan")
|