Files
client/matchmaking-api/app/models.py
T
anekdotin d3bed34c6c Add game mode categories, character creation, on-foot ship/hub mode, RAM currency, and in-ship navigation
Bundles several sessions' worth of previously uncommitted work: map
categories with Domination/Conquest/King of the Hill mode stubs and a
server-list mode filter; a procedurally-drawn character-creation screen
replacing the old callsign-only PROFILE overlay; the on-foot groundwork
(walkable station hub, ship interior, character controller) plus the RAM
currency backend; and today's addition, an in-ship Helldivers-2-style
navigation table that QUICK PLAY's queue/connect flow and the Belters/
Military hub travel now live behind, with hub-and-ship return paths and a
context-aware pause menu usable both in-match and inside the ship.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 12:30:22 -04:00

99 lines
4.5 KiB
Python

import enum
import uuid
from datetime import datetime
from sqlalchemy import BigInteger, DateTime, Float, ForeignKey, Integer, String
from sqlalchemy import Enum as SAEnum
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class Mode(str, enum.Enum):
casual = "casual"
class ServerStatus(str, enum.Enum):
available = "available"
full = "full"
offline = "offline"
# No account/auth system yet (GodotSteam auth is still on the networking
# checklist), so callsign is the identity key for now — same as the game
# client itself, which only has a free-text name field today.
class Player(Base):
__tablename__ = "players"
id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
callsign: Mapped[str] = mapped_column(String(32), unique=True, index=True)
mmr: Mapped[int] = mapped_column(Integer, default=1000)
wins: Mapped[int] = mapped_column(Integer, default=0)
losses: Mapped[int] = mapped_column(Integer, default=0)
# Match-history stats (see spacewar/autoload/match_manager.gd's
# _report_match_result(), POST /matches/report) -- kills/deaths accumulate
# across every match reported for this callsign; hours_played is derived
# from each match's full duration for every player present at match end
# (not precise per-player join/leave timing -- a documented simplification).
kills: Mapped[int] = mapped_column(Integer, default=0)
deaths: Mapped[int] = mapped_column(Integer, default=0)
hours_played: Mapped[float] = mapped_column(Float, default=0.0)
# In-game currency, see overview/onfoot.md -- named RAM, stored as a
# single integer count of kilobytes (the smallest denomination); the
# client formats it up into KB/MB/GB/TB (1000 per step, not 1024) for
# display. BigInteger since a long-lived player's total is expected to
# climb well past 32-bit Integer's ~2.1 billion ceiling (2.1 billion KB
# is only ~2.1 TB).
ram_kb: Mapped[int] = mapped_column(BigInteger, default=0)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
class GameServer(Base):
__tablename__ = "game_servers"
id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
ip: Mapped[str] = mapped_column(String(255))
port: Mapped[int] = mapped_column(Integer)
mode: Mapped[Mode] = mapped_column(SAEnum(Mode))
status: Mapped[ServerStatus] = mapped_column(SAEnum(ServerStatus), default=ServerStatus.available)
last_heartbeat: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
# DB-stored fallback shown immediately in the server list; a live server
# is queried directly for its real-time count (see
# spacewar/autoload/network_manager.gd's UDP query responder and
# menu/server_select.gd's ping probe), which overrides this in the UI
# once that probe answers.
player_count: Mapped[int] = mapped_column(Integer, default=0)
max_players: Mapped[int] = mapped_column(Integer, default=50)
# The actual in-match GameMode id (e.g. "team_deathmatch") and map
# display name (e.g. "Sector Alpha") this server is running -- distinct
# from `mode` above, which is only the matchmaking queue mode ("casual").
# Plain strings, not a Mode-style enum: the set of GameMode ids lives in
# the Godot client (spacewar/world/game_modes/, world.gd's MAPS), which
# this API has no reason to duplicate/validate against. Reported by the
# hosting server on every register/heartbeat call (see
# spacewar/autoload/network_manager.gd's host_server()).
game_mode: Mapped[str] = mapped_column(String(64), default="team_deathmatch")
map_name: Mapped[str] = mapped_column(String(64), default="Sector Alpha")
class Match(Base):
__tablename__ = "matches"
id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
mode: Mapped[Mode] = mapped_column(SAEnum(Mode))
server_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("game_servers.id"))
started_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
players: Mapped[list["MatchPlayer"]] = relationship(back_populates="match")
class MatchPlayer(Base):
__tablename__ = "match_players"
id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
match_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("matches.id"))
player_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("players.id"))
team: Mapped[int] = mapped_column(Integer) # 0 or 1
match: Mapped["Match"] = relationship(back_populates="players")