Combat¶
Status: ✅ Shipped — All combat schema is committed: CombatLog, CombatStats, Drone, DroneDeployment, DroneCombat, Fleet, FleetMember, FleetBattle … (impl audit 2026-06-16)
Two layers: ship-vs-ship/port/planet combat (CombatLog, CombatStats) and the fleet/drone subsystem (Fleet*, Drone*). CombatLog lives in combat_log.py.
CombatLog¶
Source: services/gameserver/src/models/combat_log.py
Purpose: Immutable per-engagement record (ship-vs-ship, port attack, planet defense). Captures forces, damage, loot, and admin moderation state.
Fields:
| name | type | constraints | notes |
|---|---|---|---|
| id | UUID | PK | |
| attacker_id, defender_id | UUID FK players.id | nullable, SET NULL | nullable so logs survive player deletes |
| attacker_ship_id, defender_ship_id | UUID FK ships.id | nullable, SET NULL | |
| attacker_ship_name, defender_ship_name | String(100) | nullable | denormalized snapshot |
| attacker_ship_type, defender_ship_type | String(50) | nullable | denormalized |
| sector_id | Integer | nullable | human-readable |
| sector_uuid | UUID FK sectors.id | nullable, SET NULL | |
| port_id | UUID FK stations.id | nullable, SET NULL | FK targets stations |
| planet_id | UUID FK planets.id | nullable, SET NULL | |
| combat_type | String(50) | default ship_to_ship |
ship_to_ship/port_attack/planet_defense |
| outcome | String(20) | not null | attacker_win/defender_win/draw/escaped (matches CombatOutcome) |
| attacker_drones, defender_drones | Integer | default 0 | totals at start |
| attacker_attack_drones, attacker_defense_drones, defender_attack_drones, defender_defense_drones | Integer | default 0 | by class |
| attacker_damage_dealt, defender_damage_dealt | Integer | default 0 | |
| attacker_drones_lost, defender_drones_lost | Integer | default 0 | |
| credits_looted | Integer | default 0 | |
| cargo_looted | JSONB | nullable | commodity → quantity |
| experience_gained | Integer | default 0 | |
| combat_duration | Float | default 0.0 | seconds |
| rounds | Integer | default 1 | |
| combat_log | Text | nullable | textual play-by-play |
| timestamp | DateTime | server default now | primary sort |
| started_at, ended_at | DateTime | mixed | |
| admin_notes | Text | nullable | |
| admin_resolved | Boolean | default false | |
| admin_resolved_at | DateTime | nullable | |
| disputed | Boolean | default false | |
| resolved | Boolean | default true | |
| admin_reviewed | Boolean | default false |
Relationships: attacker, defender, attacker_ship, defender_ship, sector, station (via port_id), planet.
Retention: rows are retained indefinitely at launch (small per-row footprint; complete CombatLog row averages <2 KB including the combat_log Text play-by-play). The legacy 14-day-full / 90-day-summary / lifetime-aggregate tiered policy is dropped at launch — operational archive policy can be revisited when storage costs warrant.
Region-deletion handling (per ADR-0050 SK24): CombatLog rows gain a region_id_snapshot UUID column populated at row creation, and the sector_id FK becomes ON DELETE SET NULL. When a region is regenerated (force=true) or terminated, sector rows cascade-delete but combat-log rows persist with sector_id = NULL and region_id_snapshot retaining the original region pointer. Audit queries handle the NULL gracefully ("sector unknown — region was deleted").
CombatStats¶
Source: services/gameserver/src/models/combat_log.py
Purpose: Daily aggregated combat statistics for admin dashboards.
Key fields: date (unique), counts (total_combats, ship_combats, port_attacks, planet_defenses), outcomes (attacker_wins, defender_wins, draws, escapes), economy (total_credits_looted, total_cargo_looted_value, average_loot_per_combat), efficiency (total_drones_lost, average_combat_duration, most_effective_ship_type), most_active_attacker_id / most_active_defender_id FK players, unique_combatants, calculated_at.
Drone¶
Source: services/gameserver/src/models/drone.py
Purpose: Individual deployable combat/scout/mining/repair unit. Drones live in sectors, fight, and accumulate stats.
Fields:
| name | type | constraints | notes |
|---|---|---|---|
| id | UUID | PK | |
| player_id | UUID FK players.id | not null, CASCADE, indexed | |
| team_id | UUID FK teams.id | nullable, SET NULL, indexed | |
| drone_type | String(50) | not null | attack/defense/scout/mining/repair (DroneType enum) |
| name | String(100) | nullable | optional custom name |
| level | Integer | default 1 | |
| health, max_health | Integer | default 100 | |
| attack_power, defense_power | Integer | default 10 | |
| speed | Float | default 1.0 | |
| status | String(50) | default deployed |
deployed/combat/returning/destroyed/damaged |
| sector_id | UUID FK sectors.id | nullable, SET NULL, indexed | |
| deployed_at, last_action | DateTime | nullable | |
| kills, damage_dealt, damage_taken, battles_fought | Integer | default 0 | lifetime stats |
| abilities | String(255) | nullable | comma-separated ability IDs |
| destroyed_at | DateTime | nullable |
Relationships: player, team, sector (back-pop deployed_drones); deployments → DroneDeployment (1:many cascade).
DroneDeployment¶
Per-deployment record. Columns: drone_id FK CASCADE, player_id FK CASCADE, sector_id UUID FK sectors.id CASCADE, deployed_at, recalled_at, is_active, deployment_type (defense/mining/…; patrol is reserved for NPC-spawned patrol drones — players cannot deploy as patrols), target_id (optional UUID, no FK), enemies_destroyed, resources_collected, damage_prevented.
DroneCombat¶
Per-engagement record between two drones. Columns: attacker_drone_id / defender_drone_id FK SET NULL, sector_id UUID FK SET NULL, started_at, ended_at, rounds, winner_drone_id, attacker_damage_dealt, defender_damage_dealt, combat_log String(2000) (JSON-encoded).
Fleet¶
Source: services/gameserver/src/models/fleet.py
Purpose: Group of ships organized for large-scale battle, scoped to a team.
Fields:
| name | type | constraints | notes |
|---|---|---|---|
| id | UUID | PK | |
| team_id | UUID FK teams.id | not null, CASCADE, indexed | |
| commander_id | UUID FK players.id | nullable, SET NULL | |
| name | String(100) | not null | |
| status | String(50) | default forming |
forming/ready/in_battle/retreating/disbanded (FleetStatus) |
| formation | String(50) | default standard |
|
| sector_id | UUID FK sectors.id | nullable, SET NULL, indexed | |
| total_ships, total_firepower, total_shields, total_hull | Integer | default 0 | aggregated from members |
| average_speed | Float | default 0.0 | |
| morale | Integer | default 100 | 0-100 |
| supply_level | Integer | default 100 | 0-100 |
| disbanded_at, last_battle | DateTime | nullable |
Relationships: team, commander, sector, members → FleetMember (1:many cascade).
FleetMember¶
Columns: fleet_id FK CASCADE, ship_id FK CASCADE, player_id FK CASCADE, role (flagship/attacker/defender/support/scout), position, joined_at, ready_status.
FleetBattle¶
Inter-fleet engagement record. Key columns: attacker_fleet_id, defender_fleet_id, sector_id (all SET NULL on delete), phase (preparation/engagement/main_battle/pursuit/aftermath), started_at, ended_at, attacker_ships_initial, defender_ships_initial, winner (attacker/defender/draw), attacker_ships_destroyed, defender_ships_destroyed, attacker_ships_retreated, defender_ships_retreated, total_damage_dealt, attacker_damage_dealt, defender_damage_dealt, battle_log JSON, credits_looted, resources_looted JSON.
Relationships: attacker_fleet, defender_fleet, sector, ship_casualties → FleetBattleCasualty (1:many cascade).
FleetBattleCasualty¶
Per-ship casualty entry. Columns: battle_id FK CASCADE, ship_id (SET NULL), player_id (SET NULL), fleet_id (SET NULL), ship_name and ship_type (denormalized in case of delete), was_attacker, destroyed, retreated, damage_taken, damage_dealt, kills, casualty_time, battle_phase.
Notes¶
CombatLog.combat_typeis a free-form string. Canonical values:ship_to_ship/port_attack/planet_defense/sector_defense/ship_vs_drones/port_defense(six unique values; the per-row example value set at line 32 lists the three most common:ship_to_ship/port_attack/planet_defense).CombatLog.outcomeuses theCombatOutcomestring enum fromcombat_log.py(ongoing/attacker_win/defender_win/draw/escaped).CombatLog.port_idis namedport_idbut the FK targetsstations.