677 lines
58 KiB
JavaScript
677 lines
58 KiB
JavaScript
window.GDD = window.GDD || {};
|
||
|
||
function BackendPage() {
|
||
const [activeSection, setActiveSection] = React.useState('tables');
|
||
const [galaxySubSection, setGalaxySubSection] = React.useState('galaxy-overview');
|
||
|
||
return (
|
||
<div className="content-inner">
|
||
<h1 style={{ marginBottom: '8px' }}>SpacetimeDB Backend Model</h1>
|
||
<p style={{ color: 'var(--fg-dim)', fontSize: '0.95rem', maxWidth: '680px' }}>
|
||
The backend holds persistent, authoritative state and exposes server-side reducers for game
|
||
actions. Clients subscribe to the rows they need and update reactively.
|
||
</p>
|
||
|
||
<div style={{ display: 'flex', gap: 'var(--sp-2)', marginBottom: 'var(--sp-6)' }}>
|
||
{[
|
||
{ id: 'tables', label: 'Tables' },
|
||
{ id: 'reducers', label: 'Reducers' },
|
||
{ id: 'movement', label: 'Movement Model' },
|
||
{ id: 'galaxy', label: 'Galaxy Simulation' },
|
||
{ id: 'er', label: 'ER Diagram' },
|
||
].map(t => (
|
||
<button key={t.id} className={`btn btn-sm${activeSection === t.id ? ' btn-primary' : ''}`}
|
||
onClick={() => setActiveSection(t.id)}>{t.label}</button>
|
||
))}
|
||
</div>
|
||
|
||
{activeSection === 'tables' && (
|
||
<>
|
||
<h3>Data Tables</h3>
|
||
<div style={{ overflowX: 'auto' }}>
|
||
<table className="data-table">
|
||
<thead>
|
||
<tr><th>Table</th><th>Purpose</th><th>Key Fields</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{[
|
||
{ name: 'players', purpose: 'Player account/session profile', fields: 'player_id, identity, display_name, current_system_id, created_at' },
|
||
{ name: 'ships', purpose: 'Active ship state', fields: 'ship_id, owner_player_id, system_id, x/y/z, destination, speed, status' },
|
||
{ name: 'regions', purpose: 'Galaxy regions (core, frontier, deep null)', fields: 'region_id, name, description, faction_id, security_profile, x/y/z (galaxy coords)' },
|
||
{ name: 'constellations', purpose: 'Star clusters within regions', fields: 'constellation_id, region_id, name, gate_connections (json), x/y/z (region coords)' },
|
||
{ name: 'factions', purpose: 'NPC factions with territory, goals, and military/economic strength', fields: 'faction_id, name, ideology, territory_region_ids (json), military_strength (f64), economy_strength (f64), diplomatic_stance (enum)' },
|
||
{ name: 'systems', purpose: 'Star systems within the single galaxy', fields: 'system_id, name, region_id, constellation_id, security_level, x/y/z (galaxy coords), star_type, description' },
|
||
{ name: 'planets', purpose: 'Planets orbiting stars', fields: 'planet_id, system_id, name, planet_type, orbit_radius, orbit_period, resources' },
|
||
{ name: 'moons', purpose: 'Moons orbiting planets', fields: 'moon_id, planet_id, name, moon_type, orbit_radius, resources' },
|
||
{ name: 'orbiting_objects', purpose: 'Stations, belts, anomalies in orbital slots', fields: 'object_id, parent_body_id, object_type, orbit_radius, name, state' },
|
||
{ name: 'stations', purpose: 'Docking and trading locations', fields: 'station_id, system_id, name, x/y/z, services' },
|
||
{ name: 'asteroids', purpose: 'Mineable resource nodes', fields: 'asteroid_id, system_id, resource_type, quantity, x/y/z' },
|
||
{ name: 'inventory_items', purpose: 'Player/station item storage', fields: 'item_id, owner_player_id, location, item_type, quantity' },
|
||
{ name: 'market_orders', purpose: 'Buy/sell orders', fields: 'order_id, station_id, seller_id, item_type, price, quantity, status' },
|
||
{ name: 'chat_messages', purpose: 'Local/system chat stream', fields: 'message_id, channel_id, sender_id, body, created_at' },
|
||
{ name: 'active_actions', purpose: 'Long-running actions', fields: 'action_id, player_id, action_type, target_id, started_at, completes_at' },
|
||
{ name: 'faction_relations', purpose: 'Dynamic NPC faction relationship matrix', fields: 'faction_a_id, faction_b_id, standing (-10 to +10), trend (rising/falling/stable), last_event_id' },
|
||
{ name: 'world_events', purpose: 'Active world simulation events', fields: 'event_id, event_type, system_id, severity, started_at, expires_at, state, participants' },
|
||
{ name: 'galaxy_story_log', purpose: 'Persistent server story timeline', fields: 'log_id, event_id, chapter, headline, body, timestamp' },
|
||
{ name: 'bookmarks', purpose: 'Player-saved locations', fields: 'bookmark_id, player_id, system_id, x/y/z, name, created_at' },
|
||
{ name: 'waypoints', purpose: 'Multi-stop navigation routes', fields: 'route_id, player_id, stops (ordered system list), name, shared' },
|
||
{ name: 'bounties', purpose: 'Bounty pool per target', fields: 'target_player_id, total_pool, tier, last_hostile_act' },
|
||
{ name: 'bounty_contributions', purpose: 'Individual bounty payments', fields: 'contribution_id, target_id, contributor_id, amount, timestamp' },
|
||
{ name: 'kill_feed', purpose: 'Ship destruction events', fields: 'kill_id, victim_id, killer_id, ship_type, system_id, bounty_collected, timestamp' },
|
||
{ name: 'player_skills', purpose: 'XP and levels per skill', fields: 'player_id, skill_name, xp, level, last_action_at' },
|
||
{ name: 'fleet_beacons', purpose: 'Temporary fleet rally points (post-MVP)', fields: 'beacon_id, fleet_id, creator_id, system_id, x/y/z, expires_at' },
|
||
{ name: 'ship_ai_soul', purpose: 'Soul document and personality state per ship', fields: 'ship_id, soul_md (text), growth_vectors (json), personality_state (json), soul_depth (u32), created_at, last_updated_at' },
|
||
{ name: 'ship_ai_modules', purpose: 'Installed AI modules and fitting state', fields: 'module_id, ship_id, module_type (enum), tier, slot (med/low), cpu_cost, grid_cost, active, fitted_at' },
|
||
{ name: 'ship_ai_tools', purpose: 'Tool registry derived from fitted modules', fields: 'tool_id, ship_id, tool_name, source_module_id, parameters_schema (json), return_schema (json)' },
|
||
{ name: 'ship_ai_memory', purpose: 'Event log and conversation history', fields: 'memory_id, ship_id, category, content, related_event_id, timestamp, importance_score' },
|
||
{ name: 'ship_ai_directives', purpose: 'Player-set goals for autonomous mode', fields: 'directive_id, ship_id, description, priority, deadline, status, created_at' },
|
||
{ name: 'ship_ai_agent_runtime', purpose: 'Per-ship agent loop state and tick schedule', fields: 'ship_id, implementation_tier (0/1/2), tick_interval_ms, next_tick_at, token_budget, status' },
|
||
{ name: 'ship_ai_soul_events', purpose: 'Audit log of soul-shaping events', fields: 'event_id, ship_id, event_type, soul_md_delta, applied_at' },
|
||
{ name: 'ship_types', purpose: 'Ship class definitions (stats, slot layout)', fields: 'type_id, name, class, hull, armor, shield, high_slots, med_slots, low_slots, cpu, power_grid, cargo, speed, mass, base_hull_value' },
|
||
{ name: 'modules_catalog', purpose: 'Module definitions (stats, slot type, costs)', fields: 'module_id, name, slot_type (high/med/low), cpu_cost, grid_cost, category, tier, effects_json' },
|
||
{ name: 'ship_fittings', purpose: 'Which modules are fitted to which ship slots', fields: 'fitting_id, ship_id, slot_index, module_id, online (bool)' },
|
||
{ name: 'npc_entities', purpose: 'Active NPC pirates and hostiles', fields: 'npc_id, system_id, class_id, behavior_template, x/y/z, hull/armor/shield, target_id, state, spawn_location, spawn_time' },
|
||
{ name: 'npc_class_templates', purpose: 'NPC type definitions and stats', fields: 'class_id, name, tier, hull_base, armor_base, shield_base, speed, damage, behavior, loot_table_id, bounty' },
|
||
{ name: 'loot_tables', purpose: 'Drop tables for NPC kills and wrecks', fields: 'table_id, entries (item_type, min_qty, max_qty, drop_chance), security_band' },
|
||
{ name: 'blueprints', purpose: 'Manufacturing recipes', fields: 'bp_id, product_type, product_qty, materials_json, time_seconds, skill_requirements' },
|
||
{ name: 'manufacturing_jobs', purpose: 'Active manufacturing queues', fields: 'job_id, player_id, station_id, bp_id, started_at, completes_at, status, output_location' },
|
||
{ name: 'skills_catalog', purpose: 'Skill definitions and XP curves', fields: 'skill_id, name, category, xp_curve (array), unlocks_json, max_level' },
|
||
{ name: 'chat_channels', purpose: 'Channel definitions and properties', fields: 'channel_id, name, type (local/trade/private/corp), scope, owner_id, created_at' },
|
||
{ name: 'insurance_policies', purpose: 'Active ship insurance contracts', fields: 'policy_id, player_id, ship_id, tier, premium_paid, payout_value, purchased_at, expires_at, active' },
|
||
{ name: 'ship_type_base_values', purpose: 'Base hull values for insurance', fields: 'ship_type_id, base_hull_value, insurance_premium_mult' },
|
||
{ name: 'station_commodity_demand', purpose: 'Per-station per-commodity demand state for NPC pricing', fields: 'station_id, commodity_id, flow_ema (f64), demand_pressure (f64, [0.8–1.4]), volume_sold_to_npc, volume_bought_from_npc, npc_stock_remaining, last_tick' },
|
||
{ name: 'commodity_price_params', purpose: 'Base prices and adjustment parameters per commodity', fields: 'commodity_id, base_price (f64), buy_spread (f64, [0.65–0.85]), sell_spread (f64, [1.10–1.35]), ema_alpha (f64), pressure_beta (f64), decay_gamma (f64)' },
|
||
{ name: 'regional_price_seeds', purpose: 'Static regional modifiers set at galaxy generation', fields: 'region_id, commodity_id, modifier (f64, [0.6–1.5])' },
|
||
{ name: 'npc_agents', purpose: 'NPC agents at stations offering missions', fields: 'agent_id, name, faction_id, station_id, specialty (kill/courier/mining/survey/trade/escort), quality (u32), mission_levels_offered, dialogue_seed' },
|
||
{ name: 'mission_templates', purpose: 'Mission type definitions and objectives', fields: 'template_id, type (enum), level (1–4), title, description_template, objectives_json, reward_base, time_limit_seconds, security_band_min, skill_requirements_json, faction_id' },
|
||
{ name: 'active_missions', purpose: 'Currently active player missions', fields: 'mission_id, player_id, agent_id, template_id, objectives_state_json, status (active/completed/failed/expired), accepted_at, expires_at, completed_at' },
|
||
{ name: 'player_standing', purpose: 'Player standing with agents and factions', fields: 'player_id, entity_id, entity_type (agent/faction), standing (f64, −10 to +10), last_mission_at' },
|
||
{ name: 'player_loyalty_points', purpose: 'Faction loyalty point balances', fields: 'player_id, faction_id, lp_balance (u64), lifetime_earned (u64)' },
|
||
{ name: 'mission_offers', purpose: 'Current mission offerings at stations', fields: 'offer_id, agent_id, station_id, template_id, reward_modifier, expires_at, generated_at' },
|
||
{ name: 'balance_metrics', purpose: 'Balancing Agent metric tracking', fields: 'metric_name, current_value (f64), healthy_min, healthy_max, last_updated, trend (rising/falling/stable)' },
|
||
{ name: 'balance_levers', purpose: 'Balancing Agent control levers', fields: 'lever_name, current_multiplier (f64), target_multiplier (f64), clamp_min, clamp_max, last_adjusted_at' },
|
||
{ name: 'balance_audit', purpose: 'Balancing Agent intervention log', fields: 'audit_id, tick_time, metrics_snapshot_json, adjustments_json, reason' },
|
||
].map((row, i) => (
|
||
<tr key={i}>
|
||
<td><code>{row.name}</code></td>
|
||
<td style={{ color: 'var(--fg-dim)' }}>{row.purpose}</td>
|
||
<td style={{ fontSize: '0.75rem', color: 'var(--muted)' }}>{row.fields}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</>
|
||
)}
|
||
|
||
{activeSection === 'reducers' && (
|
||
<>
|
||
<h3>Reducers (Server Commands)</h3>
|
||
<div style={{ overflowX: 'auto' }}>
|
||
<table className="data-table">
|
||
<thead>
|
||
<tr><th>Reducer</th><th>Client Trigger</th><th>Server Responsibility</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{[
|
||
{ name: 'connect_player(display_name)', trigger: 'Player opens app', resp: 'Create/update player row, spawn initial ship if needed.' },
|
||
{ name: 'set_destination(ship_id, x, y, z)', trigger: 'Click in space', resp: 'Validate ownership/status, update destination/vector.' },
|
||
{ name: 'dock(station_id)', trigger: 'Click dock', resp: 'Check distance, set ship docked, update location/state.' },
|
||
{ name: 'start_mining(asteroid_id)', trigger: 'Click mine', resp: 'Check range, asteroid quantity, ship status, create active action.' },
|
||
{ name: 'complete_mining(action_id)', trigger: 'Timer/event', resp: 'Transfer ore into inventory, reduce asteroid quantity.' },
|
||
{ name: 'sell_item(item_type, qty, station)', trigger: 'Sell from UI', resp: 'Validate inventory and station, exchange ore for ISK.' },
|
||
{ name: 'place_market_order(...)', trigger: 'Market UI', resp: 'Reserve inventory, create sell order.' },
|
||
{ name: 'send_chat(channel_id, body)', trigger: 'Chat box', resp: 'Validate/rate-limit, append chat message row.' },
|
||
{ name: 'world_tick(ctx)', trigger: 'Server timer (5 min)', resp: 'Evaluate galaxy state, player density, faction matrix. Conditionally spawn PvE events (faction conflicts, anomalies, migrations, raids). Propagate active events.' },
|
||
{ name: 'spawn_world_event(event_type, system_id, params)', trigger: 'World tick evaluation', resp: 'Create world_event row, generate story log entry, notify nearby players via sensors, set expiration timer.' },
|
||
{ name: 'resolve_world_event(event_id, outcome)', trigger: 'Event timer or player action', resp: 'Update galaxy state based on outcome, write story log chapter, adjust faction relations, trigger cascading events.' },
|
||
].map((row, i) => (
|
||
<tr key={i}>
|
||
<td><code style={{ fontSize: '0.75rem' }}>{row.name}</code></td>
|
||
<td style={{ color: 'var(--fg-dim)' }}>{row.trigger}</td>
|
||
<td style={{ color: 'var(--fg-dim)' }}>{row.resp}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</>
|
||
)}
|
||
|
||
{activeSection === 'movement' && (
|
||
<>
|
||
<h3>Movement Model</h3>
|
||
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
|
||
Avoid sending per-frame movement. Store destination and speed. Clients interpolate visually;
|
||
the backend periodically updates authoritative positions.
|
||
</div>
|
||
|
||
<div className="card card-accent">
|
||
<h4>Movement Flow</h4>
|
||
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
|
||
1. Client calls <code>set_destination(ship_id, x, y, z)</code><br/>
|
||
2. Server validates ownership + ship status<br/>
|
||
3. Server updates <code>ships.destination</code> + calculates velocity vector<br/>
|
||
4. Server broadcasts updated ship state to subscribers<br/>
|
||
5. Client interpolates visual position between last known + destination<br/>
|
||
6. Server periodically updates authoritative <code>x/y/z</code> position
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid-2" style={{ marginTop: 'var(--sp-5)' }}>
|
||
<div className="card">
|
||
<h4 style={{ color: 'var(--green)' }}>Client-side interpolation</h4>
|
||
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0 }}>
|
||
Smooth visual movement between authoritative position updates. Uses dead reckoning
|
||
with periodic server correction. Handles latency spikes gracefully.
|
||
</p>
|
||
</div>
|
||
<div className="card">
|
||
<h4 style={{ color: 'var(--cyan)' }}>Server authority</h4>
|
||
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0 }}>
|
||
Backend is the source of truth for all positions. Clients never modify their own
|
||
position directly — they submit intentions and wait for confirmation.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)}
|
||
|
||
{activeSection === 'galaxy' && (
|
||
<>
|
||
<div style={{ display: 'flex', gap: 'var(--sp-2)', marginBottom: 'var(--sp-5)' }}>
|
||
{[
|
||
{ id: 'galaxy-overview', label: 'Overview' },
|
||
{ id: 'galaxy-gen', label: 'Galaxy Generation' },
|
||
{ id: 'galaxy-events', label: 'World Events' },
|
||
].map(g => (
|
||
<button key={g.id} className={`btn btn-sm${galaxySubSection === g.id ? ' btn-primary' : ''}`}
|
||
onClick={() => setGalaxySubSection(g.id)}>{g.label}</button>
|
||
))}
|
||
</div>
|
||
|
||
{galaxySubSection === 'galaxy-overview' && (<>
|
||
<h3>Galaxy Simulation Layer</h3>
|
||
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
|
||
<strong>Single galaxy, simulated world.</strong> The server maintains one persistent galaxy with a connected graph of star systems,
|
||
each containing planets, moons, asteroid belts, and stations. A world simulation layer runs on top, spawning dynamic PvE events
|
||
that create a unique story per server. This is not instanced content — every player in the galaxy shares the same world state.
|
||
</div>
|
||
|
||
<div className="card card-accent" style={{ padding: 'var(--sp-6) var(--sp-8)', marginBottom: 'var(--sp-5)' }}>
|
||
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Galaxy Topology</h4>
|
||
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
|
||
<span style={{ color: 'var(--accent)' }}>Galaxy</span> → contains <span style={{ color: 'var(--cyan)' }}>Regions</span> (4–8 regions, each with distinct character)<br/>
|
||
<span style={{ color: 'var(--cyan)' }}>Region</span> → contains <span style={{ color: 'var(--green)' }}>Constellations</span> (3–6 per region, connected clusters)<br/>
|
||
<span style={{ color: 'var(--green)' }}>Constellation</span> → contains <span style={{ color: 'var(--purple)' }}>Systems</span> (2–8 per constellation, gate-connected)<br/>
|
||
<span style={{ color: 'var(--purple)' }}>System</span> → contains <span style={{ color: 'var(--fg)' }}>Star + Planets + Moons + Belts + Stations + Anomalies</span><br/>
|
||
<span style={{ color: 'var(--fg)' }}>Planet</span> → has <span style={{ color: 'var(--accent)' }}>orbiting_objects</span> (stations, moon mining outposts, customs offices)
|
||
</div>
|
||
</div>
|
||
|
||
<h3 style={{ marginBottom: 'var(--sp-4)' }}>World Simulation Tables</h3>
|
||
|
||
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)', fontSize: '0.82rem' }}>
|
||
<strong>Overlap note:</strong> <code>faction_relations</code>, <code>world_events</code>, and <code>galaxy_story_log</code> also appear in the Tables tab with abbreviated field descriptions. The definitions below are the expanded versions with full field detail. Both tabs describe the same underlying tables.
|
||
</div>
|
||
<div style={{ overflowX: 'auto' }}>
|
||
<table className="data-table">
|
||
<thead>
|
||
<tr><th>Table</th><th>Purpose</th><th>Key Fields</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{[
|
||
{ name: 'regions', purpose: 'Galaxy regions (core, frontier, deep null)', fields: 'region_id, name, description, faction_id, security_profile' },
|
||
{ name: 'constellations', purpose: 'Star clusters within regions', fields: 'constellation_id, region_id, name, gate_connections' },
|
||
{ name: 'factions', purpose: 'NPC factions with territory and goals', fields: 'faction_id, name, ideology, territory_region_ids, military_strength, economy_strength' },
|
||
{ name: 'faction_relations', purpose: 'Dynamic relationship matrix', fields: 'faction_a_id, faction_b_id, standing (-10 to +10), trend (rising/falling/stable), last_event_id' },
|
||
{ name: 'world_events', purpose: 'Active PvE events in the galaxy', fields: 'event_id, event_type, system_id, severity (1–5), started_at, expires_at, state, participants_json, params_json' },
|
||
{ name: 'world_event_templates', purpose: 'Event blueprints with spawn conditions', fields: 'template_id, type, name, min_severity, max_severity, spawn_weight, required_faction_state, cooldown_hours' },
|
||
{ name: 'galaxy_story_log', purpose: 'Persistent server timeline — the “history” of this galaxy', fields: 'log_id, event_id, chapter_index, headline, body, affected_systems, timestamp' },
|
||
{ name: 'space_fauna', purpose: 'Migrating space creatures', fields: 'fauna_id, species, current_system_id, migration_route (json), next_waypoint, arrival_at, cycle_phase' },
|
||
{ name: 'anomalies', purpose: 'Temporary spatial phenomena', fields: 'anomaly_id, type (wormhole/nebula/storm/void), system_id, x/y/z, severity, expires_at, loot_table' },
|
||
].map((row, i) => (
|
||
<tr key={i}>
|
||
<td><code>{row.name}</code></td>
|
||
<td style={{ color: 'var(--fg-dim)' }}>{row.purpose}</td>
|
||
<td style={{ fontSize: '0.75rem', color: 'var(--muted)' }}>{row.fields}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<h3 style={{ marginTop: 'var(--sp-6)', marginBottom: 'var(--sp-4)' }}>Event Spawn Logic</h3>
|
||
<div className="card" style={{ padding: 0, overflow: 'hidden' }}>
|
||
<div style={{ padding: 'var(--sp-3) var(--sp-4)', background: 'var(--surface-raised)', borderBottom: '1px solid var(--border)' }}>
|
||
<span style={{ fontFamily: 'var(--font-mono)', fontSize: '0.7rem', color: 'var(--accent)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
||
World Tick Reducer — Pseudocode
|
||
</span>
|
||
</div>
|
||
<div className="code-block" style={{ margin: 0, borderRadius: 0 }}>
|
||
<code>
|
||
<span className="cm">// Runs every 5 minutes on the server</span><br/>
|
||
<span className="kw">reducer</span> <span className="fn">world_tick</span>(ctx) {'{'}<br/>
|
||
<span className="cm">// 1. Evaluate faction tension matrix</span><br/>
|
||
<span className="kw">for</span> each faction_pair {'{'}<br/>
|
||
<span className="kw">if</span> standing {'<'} -5 && random {'<'} tension_weight {'{'}<br/>
|
||
<span className="fn">spawn_event</span>(<span className="str">"faction_skirmish"</span>, contested_system);<br/>
|
||
<span className="fn">log_story</span>({'"'}Hostilities erupt between {'{'}A{'}'} and {'{'}B{'}'} in {'{'}system{'}'}{'"'});<br/>
|
||
{'}'}<br/>
|
||
{'}'}<br/>
|
||
<br/>
|
||
<span className="cm">// 2. Check anomaly spawn slots</span><br/>
|
||
<span className="kw">if</span> active_anomalies {'<'} max_anomalies {'{'}<br/>
|
||
pick random system weighted by distance_from_hub;<br/>
|
||
<span className="fn">spawn_anomaly</span>(system, random_type);<br/>
|
||
{'}'}<br/>
|
||
<br/>
|
||
<span className="cm">// 3. Advance fauna migration routes</span><br/>
|
||
<span className="kw">for</span> each fauna {'{'}<br/>
|
||
<span className="kw">if</span> now {'>='} next_waypoint.arrival_at {'{'}<br/>
|
||
advance fauna to next system in route;<br/>
|
||
<span className="fn">log_story</span>({'"'}{'{'}species{'}'} migration enters {'{'}system{'}'}{'"'});<br/>
|
||
{'}'}<br/>
|
||
{'}'}<br/>
|
||
<br/>
|
||
<span className="cm">// 4. Cascade: check if any active events should trigger follow-ons</span><br/>
|
||
<span className="kw">for</span> each active_event near expiry {'{'}<br/>
|
||
evaluate_outcome(participants, event_state);<br/>
|
||
<span className="fn">resolve_event</span>(event_id, outcome);<br/>
|
||
<span className="cm">// outcome may shift faction relations → future events</span><br/>
|
||
{'}'}<br/>
|
||
{'}'}
|
||
</code>
|
||
</div>
|
||
</div>
|
||
</>)}
|
||
|
||
{galaxySubSection === 'galaxy-gen' && (<>
|
||
<div className="section-header">
|
||
<span className="section-num">GALAXY-GEN</span>
|
||
<h2 style={{ margin: 0 }}>Galaxy Generation — Seeded Parameters & Algorithm</h2>
|
||
</div>
|
||
|
||
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
|
||
<strong>Every galaxy begins with a seed.</strong> The galaxy generation algorithm is deterministic: the same seed always produces
|
||
the same galaxy. This means server operators can share a seed for a known-good galaxy layout, or generate a unique one.
|
||
Generation runs once at server bootstrap and writes immutable topology tables (regions, constellations, systems, stargates,
|
||
planets, moons, stations, asteroid belts). Faction territories and NPC agent placement are also seeded at this stage.
|
||
</div>
|
||
|
||
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Galaxy Parameters</h3>
|
||
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
|
||
<table className="data-table">
|
||
<thead>
|
||
<tr><th>Parameter</th><th>MVP Value</th><th>Full Launch</th><th>Rationale</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{[
|
||
{ param: 'Regions', mvp: '4', full: '6–8', reason: 'Core, Frontier, Null, Deep Null for MVP. Add 2–4 faction-specific regions at launch.' },
|
||
{ param: 'Constellations per region', mvp: '3–4', full: '4–8', reason: 'Minimum 3 for gate connectivity. More in Core region, fewer in Deep Null.' },
|
||
{ param: 'Systems per constellation', mvp: '2–5', full: '3–8', reason: 'Density varies by region type. Core constellations are denser (trade hubs).' },
|
||
{ param: 'Total systems (MVP)', mvp: '~50', full: '~300–500', reason: '4 regions × 3.5 const × 3.5 sys ≈ 49 systems. Enough for economic loops without barren stretches.' },
|
||
{ param: 'Stargates per system', mvp: '1–4', full: '1–6', reason: 'Minimum 1 (no dead ends). Hub systems get 4+. Frontier gets 2–3.' },
|
||
{ param: 'Stations per system', mvp: '1–3', full: '1–6', reason: 'High-sec: 2–3 stations. Low-sec: 1–2. Null: 0–1 NPC stations. Stations are where the economy lives.' },
|
||
{ param: 'Planets per system', mvp: '1–5', full: '2–8', reason: 'Aesthetic + resource variety. Orbital mechanics run on a slow tick (no gameplay impact in MVP).' },
|
||
{ param: 'Asteroid belts per system', mvp: '1–3', full: '1–5', reason: 'Belts are where mining happens. More belts in lower-sec = better ore = risk/reward.' },
|
||
{ param: 'Factions', mvp: '4', full: '4–6', reason: 'One per region in MVP. Each has territory, ideology, and agent networks.' },
|
||
{ param: 'NPC agents per station', mvp: '1–2', full: '1–4', reason: 'Mission-givers. Specialty and quality randomized from seed.' },
|
||
].map((row, i) => (
|
||
<tr key={i}>
|
||
<td style={{ fontWeight: 600, color: 'var(--accent)' }}>{row.param}</td>
|
||
<td className="mono" style={{ color: 'var(--cyan)' }}>{row.mvp}</td>
|
||
<td className="mono" style={{ color: 'var(--fg-dim)' }}>{row.full}</td>
|
||
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.reason}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Galaxy Shape & Layout</h3>
|
||
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
|
||
<strong>Spiral galaxy with 4 arms.</strong> The galaxy is rendered as a top-down 2D map (with a Z-depth dimension for 3D system
|
||
coordinates within each system). Regions are assigned to spiral arm segments:
|
||
Core (center), Frontier (inner arms), Null (outer arms), Deep Null (tips and gaps between arms).
|
||
Systems within a region are placed using a Poisson disk distribution to ensure minimum spacing while maintaining natural clustering.
|
||
</div>
|
||
|
||
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
|
||
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
|
||
<h4 style={{ color: 'var(--accent)' }}>Region Assignment Rules</h4>
|
||
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
|
||
<li><strong style={{ color: 'var(--fg)' }}>Core (sec +0.8 → +1.0):</strong> Center of galaxy map. Dense, high station count, trade hub. Starter systems live here. 1 region.</li>
|
||
<li><strong style={{ color: 'var(--fg)' }}>Frontier (sec +0.1 → +0.7):</strong> Inner spiral arms. Moderate density. Faction border zones. Mission territory. 1–2 regions.</li>
|
||
<li><strong style={{ color: 'var(--fg)' }}>Null (sec 0.0 → −0.4):</strong> Outer spiral arms. Sparse stations. Rich belts. PvP-free. 1 region.</li>
|
||
<li><strong style={{ color: 'var(--fg)' }}>Deep Null (sec −0.5 → −1.0):</strong> Tips and gaps. Very sparse. Wormhole connections only. Elite content. 1 region.</li>
|
||
</ul>
|
||
</div>
|
||
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
|
||
<h4 style={{ color: 'var(--cyan)' }}>System Placement Algorithm</h4>
|
||
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
|
||
1. Place constellation centroids via Poisson disk (min 40px apart on map)<br/>
|
||
2. For each centroid, place 2–5 systems in a cluster (Gaussian offset from centroid, σ = 15px)<br/>
|
||
3. Assign security level band based on region assignment<br/>
|
||
4. Add jitter to security within band (±0.1 random) for natural variation<br/>
|
||
5. Place star type (O/B/A/F/G/K/M) weighted by frequency — G/K most common<br/>
|
||
6. System name generated from faction language + sequential number
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Stargate Topology</h3>
|
||
<div className="callout callout-warn" style={{ marginBottom: 'var(--sp-5)' }}>
|
||
<strong>No disconnected components. Every system must be reachable from every other system.</strong>
|
||
The stargate graph is the transportation backbone. If a system has only one gate, it\'s a dead-end —
|
||
risky because you can\'t flee without going back through the same gate. The algorithm ensures minimum 2 gates
|
||
per system (MVP) and creates "choke point" systems with 4+ gates that become natural trade hubs and conflict zones.
|
||
</div>
|
||
|
||
<div className="card card-accent" style={{ marginBottom: 'var(--sp-5)' }}>
|
||
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Stargate Connectivity Algorithm</h4>
|
||
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
|
||
<span style={{ color: 'var(--green)' }}>Phase 1 — Minimum Spanning Tree:</span> Compute MST over all systems using Euclidean distance. This guarantees full connectivity with minimum total gate length.<br/>
|
||
<span style={{ color: 'var(--cyan)' }}>Phase 2 — Intra-constellation edges:</span> For each constellation, add 1–2 extra gates between systems within the constellation. This creates local redundancy and multiple routes within a cluster.<br/>
|
||
<span style={{ color: 'var(--accent)' }}>Phase 3 — Inter-region choke points:</span> Identify 2–4 systems on region boundaries. Add gates between them to create known choke points. These become strategic PvP locations.<br/>
|
||
<span style={{ color: 'var(--purple)' }}>Phase 4 — Shortcut edges:</span> Add 10–15% extra gates weighted toward connecting high-sec systems to create trade route variety. Never add shortcuts into/out of Deep Null (wormhole-only access preserved).<br/>
|
||
<span style={{ color: 'var(--red)' }}>Validation:</span> After generation, verify: (a) graph is fully connected (BFS from any node reaches all), (b) no system has <2 gates, (c) Deep Null systems have no direct high-sec gates, (d) average path length <15 jumps for MVP galaxy.
|
||
</div>
|
||
</div>
|
||
|
||
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Starter System Template</h3>
|
||
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
|
||
<div className="card" style={{ borderLeft: '3px solid var(--green)' }}>
|
||
<h4 style={{ color: 'var(--green)' }}>Starter System Layout</h4>
|
||
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
|
||
<strong style={{ color: 'var(--fg)' }}>Security:</strong> +1.0 (maximum safety)<br/>
|
||
<strong style={{ color: 'var(--fg)' }}>Stations:</strong> 3 — Home Station, Trade Hub, Factory<br/>
|
||
<strong style={{ color: 'var(--fg)' }}>Belts:</strong> 3 — Veldspar/Scordite (easy ore)<br/>
|
||
<strong style={{ color: 'var(--fg)' }}>NPC agents:</strong> 2 — Tutorial agent + Level 1 kill agent<br/>
|
||
<strong style={{ color: 'var(--fg)' }}>Gates:</strong> 3 — connects to 3 adjacent high-sec systems<br/>
|
||
<strong style={{ color: 'var(--fg)' }}>NPC pirates:</strong> None (CONCORD-protected + no belt spawns in 1.0)<br/>
|
||
<strong style={{ color: 'var(--fg)' }}>Services:</strong> Refinery, Factory, Market, Fitting, Insurance, Medical
|
||
</div>
|
||
</div>
|
||
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
|
||
<h4 style={{ color: 'var(--cyan)' }}>New Player Spawn Rules</h4>
|
||
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
|
||
<li>New players always spawn in a starter system (sec 1.0)</li>
|
||
<li>Each faction has exactly 1 starter system in their Core region</li>
|
||
<li>Player receives a <strong style={{ color: 'var(--fg)' }}>Rookie Frigate</strong> (free, uninsurable, untradeable)</li>
|
||
<li>Player receives the tutorial mission sequence from the tutorial agent</li>
|
||
<li>Starter system is guaranteed to have Veldspar and Scordite at NPC buy prices that make the first-30-minute walkthrough viable</li>
|
||
<li>Multiple new players can share the same starter system (no instancing)</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Station & Belt Placement Rules</h3>
|
||
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
|
||
<table className="data-table">
|
||
<thead>
|
||
<tr><th>Entity</th><th>Placement Rule</th><th>Density by Sec</th><th>Notes</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{[
|
||
{ entity: 'Station', rule: 'Orbiting a planet or moon. Placed at galaxy gen, never moved.', density: 'High-sec: 2–3/station. Low: 1–2. Null: 0–1 NPC. Player stations (post-MVP) can be anchored.', notes: 'Stations define where economy happens. Every station has a Market. Refinery and Factory depend on station size.' },
|
||
{ entity: 'Asteroid Belt', rule: 'Circular orbit around star. 3–5 asteroids per belt.', density: 'High-sec: 1–2. Low: 2–3. Null: 2–4. Deep Null: 3–5.', notes: 'Belt ore quality scales with sec level. High-sec: Veldspar/Scordite only. Null: adds Arkonor/Megacyte.' },
|
||
{ entity: 'Moon', rule: 'Orbiting planets. 0–3 moons per planet.', density: '1–3 moons per planet (uniform distribution).', notes: 'Moons have moon minerals (post-MVP). MVP: moons exist for visual flavor and as station anchors.' },
|
||
{ entity: 'Stargate', rule: 'At system edge, paired with gate in target system. Placed at fixed (x,y) = system edge toward destination.', density: 'Matches graph topology. 2–4 per system typically.', notes: 'Gates are always paired. Jumping gate A→B always works. No fuel cost for jumping (MVP).' },
|
||
{ entity: 'NPC Agent', rule: 'Located at a station. 1–2 per station in MVP.', density: 'High-sec stations: 2. Low-sec: 1. Null: 1 or 0.', notes: 'Agent specialty drawn from faction pool. Quality randomized from seed.' },
|
||
].map((row, i) => (
|
||
<tr key={i}>
|
||
<td style={{ fontWeight: 600, color: 'var(--accent)' }}>{row.entity}</td>
|
||
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.rule}</td>
|
||
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.density}</td>
|
||
<td style={{ color: 'var(--muted)', fontSize: '0.82rem' }}>{row.notes}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Faction Territory Seeding</h3>
|
||
<div className="card card-accent" style={{ marginBottom: 'var(--sp-5)' }}>
|
||
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Faction Assignment at Galaxy Gen</h4>
|
||
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
|
||
<span style={{ color: 'var(--green)' }}>1. Assign regions:</span> Each faction claims 1 region as home territory. The Core region is shared (contested).<br/>
|
||
<span style={{ color: 'var(--cyan)' }}>2. Place capitals:</span> Each faction gets a capital station in their home region — largest station, most agents, best services.<br/>
|
||
<span style={{ color: 'var(--accent)' }}>3. Seed diplomatic stance:</span> Initial faction relations set to baseline matrix (allies: +5, neutral: 0, rivals: −3). This drifts via world simulation.<br/>
|
||
<span style={{ color: 'var(--purple)' }}>4. Distribute agents:</span> NPC agents placed at stations within faction territory. Specialty weighted by faction ideology (militarist → kill agents, trader → trade/escort agents).<br/>
|
||
<span style={{ color: 'var(--fg)' }}>5. Set regional price seeds:</span> <code>regional_price_seeds</code> table populated at gen time. Each region gets commodity modifiers (0.6–1.5) that create baseline price differences for traders to discover.<br/>
|
||
<span style={{ color: 'var(--red)' }}>6. Faction military/economy:</span> Initial <code>military_strength</code> and <code>economy_strength</code> set from faction template. These are the starting values the world simulation modifies.
|
||
</div>
|
||
</div>
|
||
|
||
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Generation Pseudocode</h3>
|
||
<div className="card" style={{ padding: 0, overflow: 'hidden', marginBottom: 'var(--sp-5)' }}>
|
||
<div style={{ padding: 'var(--sp-3) var(--sp-4)', background: 'var(--surface-raised)', borderBottom: '1px solid var(--border)' }}>
|
||
<span style={{ fontFamily: 'var(--font-mono)', fontSize: '0.7rem', color: 'var(--accent)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
||
Galaxy Generation — Pseudocode
|
||
</span>
|
||
</div>
|
||
<div className="code-block" style={{ margin: 0, borderRadius: 0 }}>
|
||
<code>
|
||
<span className="kw">fn</span> <span className="fn">generate_galaxy</span>(seed: u64) {'{'}<br/>
|
||
<span className="kw">let</span> rng = SeededRng::new(seed);<br/>
|
||
<br/>
|
||
<span className="cm">// 1. Create regions</span><br/>
|
||
<span className="kw">let</span> regions = ['{'Core, Frontier_A, Null, Deep_Null'}'];<br/>
|
||
<span className="kw">for</span> region <span className="kw">in</span> regions {'{'}<br/>
|
||
insert region row (id, name, faction_id, security_profile);<br/>
|
||
{'}'}<br/>
|
||
<br/>
|
||
<span className="cm">// 2. Place constellation centroids (Poisson disk)</span><br/>
|
||
<span className="kw">let</span> centroids = poisson_disk(min_dist=40, rng);<br/>
|
||
<span className="kw">for</span> centroid <span className="kw">in</span> centroids {'{'}<br/>
|
||
<span className="kw">let</span> region = assign_region(centroid.position);<br/>
|
||
insert constellation row (region_id, centroid.x, centroid.y);<br/>
|
||
{'}'}<br/>
|
||
<br/>
|
||
<span className="cm">// 3. Place systems within constellations (Gaussian cluster)</span><br/>
|
||
<span className="kw">for</span> constellation <span className="kw">in</span> constellations {'{'}<br/>
|
||
<span className="kw">let</span> count = rng.range(2..5); <span className="cm">// MVP: 2–5 systems per constellation</span><br/>
|
||
<span className="kw">for</span> i <span className="kw">in</span> 0..count {'{'}<br/>
|
||
<span className="kw">let</span> offset = gaussian(σ=15, rng);<br/>
|
||
<span className="kw">let</span> sec = assign_security(constellation.region) + rng.range(-0.1..+0.1);<br/>
|
||
insert system row (name, constellation_id, sec, star_type, x, y);<br/>
|
||
place_planets(system, rng);<br/>
|
||
place_stations(system, sec, rng);<br/>
|
||
place_belts(system, sec, rng);<br/>
|
||
{'}'}<br/>
|
||
{'}'}<br/>
|
||
<br/>
|
||
<span className="cm">// 4. Stargate topology</span><br/>
|
||
<span className="kw">let</span> mst = minimum_spanning_tree(all_systems, euclidean_distance);<br/>
|
||
<span className="kw">for</span> edge <span className="kw">in</span> mst {'{'} insert_gate(edge.a, edge.b); {'}'}<br/>
|
||
add_intra_constellation_edges(rng); <span className="cm">// +1–2 per constellation</span><br/>
|
||
add_region_chokepoints(rng); <span className="cm">// 2–4 cross-region gates</span><br/>
|
||
add_shortcut_edges(percentage=0.12, rng); <span className="cm">// 12% extra high-sec shortcuts</span><br/>
|
||
validate_connectivity(); <span className="cm">// BFS from node 0 reaches all?</span><br/>
|
||
<br/>
|
||
<span className="cm">// 5. Faction seeding</span><br/>
|
||
seed_faction_territories(factions, regions);<br/>
|
||
seed_capital_stations(factions);<br/>
|
||
seed_diplomatic_matrix(factions);<br/>
|
||
seed_npc_agents(stations, factions, rng);<br/>
|
||
seed_regional_prices(regions, commodities, rng);<br/>
|
||
{'}'}
|
||
</code>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
|
||
<strong>Determinism guarantee:</strong> Given the same seed, <code>generate_galaxy</code> always produces identical topology.
|
||
This enables: (1) shared "known-good" galaxy seeds for competitive servers, (2) reproducible bug reports with exact galaxy layout,
|
||
(3) automated testing against fixed galaxy configurations. The seed is stored in a single <code>galaxy_meta</code> table row:
|
||
<code>{'{'} seed: u64, generated_at: timestamp, system_count: u32, total_gates: u32 {'}'}</code>.
|
||
</div>
|
||
|
||
<div className="callout callout-warn">
|
||
<strong>MVP scope note:</strong> For Phase 0, the galaxy can be hand-authored (a 5–10 system "mini galaxy") as long as
|
||
it follows these rules. The procedural generator ships when the galaxy needs to scale beyond ~50 systems (Phase 7+).
|
||
Hand-authored galaxies must still pass the same connectivity validation.
|
||
</div>
|
||
</>)}
|
||
|
||
{galaxySubSection === 'galaxy-events' && (<>
|
||
<h3>World Simulation Tables</h3>
|
||
|
||
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)', fontSize: '0.82rem' }}>
|
||
<strong>Overlap note:</strong> <code>faction_relations</code>, <code>world_events</code>, and <code>galaxy_story_log</code> also appear in the Tables tab with abbreviated field descriptions. The definitions below are the expanded versions with full field detail.
|
||
</div>
|
||
<div style={{ overflowX: 'auto' }}>
|
||
<table className="data-table">
|
||
<thead>
|
||
<tr><th>Table</th><th>Purpose</th><th>Key Fields</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{[
|
||
{ name: 'faction_relations', purpose: 'Dynamic relationship matrix', fields: 'faction_a_id, faction_b_id, standing (-10 to +10), trend, last_event_id' },
|
||
{ name: 'world_events', purpose: 'Active PvE events in the galaxy', fields: 'event_id, event_type, system_id, severity (1–5), started_at, expires_at, state, participants_json' },
|
||
{ name: 'world_event_templates', purpose: 'Event blueprints with spawn conditions', fields: 'template_id, type, name, spawn_weight, required_faction_state, cooldown_hours' },
|
||
{ name: 'galaxy_story_log', purpose: 'Persistent server timeline', fields: 'log_id, event_id, chapter_index, headline, body, affected_systems, timestamp' },
|
||
{ name: 'space_fauna', purpose: 'Migrating space creatures', fields: 'fauna_id, species, current_system_id, migration_route (json), cycle_phase' },
|
||
{ name: 'anomalies', purpose: 'Temporary spatial phenomena', fields: 'anomaly_id, type, system_id, x/y/z, severity, expires_at, loot_table' },
|
||
].map((row, i) => (
|
||
<tr key={i}>
|
||
<td><code>{row.name}</code></td>
|
||
<td style={{ color: 'var(--fg-dim)' }}>{row.purpose}</td>
|
||
<td style={{ fontSize: '0.75rem', color: 'var(--muted)' }}>{row.fields}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</>)}
|
||
</>
|
||
)}
|
||
|
||
{activeSection === 'er' && (
|
||
<>
|
||
<div className="section-header">
|
||
<span className="section-num">BACKEND-ER</span>
|
||
<h2 style={{ margin: 0 }}>Entity-Relationship Diagram</h2>
|
||
</div>
|
||
|
||
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
|
||
<strong>50+ tables organized into 5 clusters.</strong> This diagram shows the core entity relationships
|
||
and how data flows between clusters. Each cluster is color-coded. Foreign key relationships shown as arrows.
|
||
Subscription patterns indicate which tables clients subscribe to for reactive updates.
|
||
</div>
|
||
|
||
{[
|
||
{
|
||
name: 'Player & Identity',
|
||
color: 'var(--cyan)',
|
||
desc: 'Core player data, ships, inventory, skills, and session state.',
|
||
tables: [
|
||
{ name: 'players', pk: 'player_id', fk: '', note: 'Root entity. One row per identity.' },
|
||
{ name: 'ships', pk: 'ship_id', fk: '\u2192 players.player_id', note: 'Multiple ships per player. owner_player_id.' },
|
||
{ name: 'ship_fittings', pk: 'fitting_id', fk: '\u2192 ships.ship_id, \u2192 modules_catalog.module_id', note: 'Many-to-many: ships \u2194 modules.' },
|
||
{ name: 'inventory_items', pk: 'item_id', fk: '\u2192 players.player_id', note: 'Location field: ship cargo or station hangar.' },
|
||
{ name: 'player_skills', pk: 'player_id + skill_name', fk: '\u2192 players.player_id', note: 'XP and level per skill per player.' },
|
||
{ name: 'player_standing', pk: 'player_id + entity_id', fk: '\u2192 players.player_id', note: 'Standing with agents and factions.' },
|
||
{ name: 'player_loyalty_points', pk: 'player_id + faction_id', fk: '\u2192 players.player_id', note: 'LP balance per faction.' },
|
||
],
|
||
},
|
||
{
|
||
name: 'Economy & Industry',
|
||
color: 'var(--green)',
|
||
desc: 'Market, manufacturing, blueprints, and NPC pricing.',
|
||
tables: [
|
||
{ name: 'market_orders', pk: 'order_id', fk: '\u2192 stations.station_id, \u2192 players.player_id', note: 'Buy/sell orders. Core market table.' },
|
||
{ name: 'blueprints', pk: 'bp_id', fk: '', note: 'Manufacturing recipes. Materials JSON.' },
|
||
{ name: 'manufacturing_jobs', pk: 'job_id', fk: '\u2192 players.player_id, \u2192 stations.station_id, \u2192 blueprints.bp_id', note: 'Active production queues.' },
|
||
{ name: 'station_commodity_demand', pk: 'station_id + commodity_id', fk: '\u2192 stations.station_id', note: 'Per-station demand state for NPC pricing.' },
|
||
{ name: 'commodity_price_params', pk: 'commodity_id', fk: '', note: 'Base prices and EMA parameters.' },
|
||
{ name: 'regional_price_seeds', pk: 'region_id + commodity_id', fk: '', note: 'Static modifiers from galaxy gen.' },
|
||
{ name: 'insurance_policies', pk: 'policy_id', fk: '\u2192 players.player_id, \u2192 ships.ship_id', note: 'Active insurance contracts.' },
|
||
],
|
||
},
|
||
{
|
||
name: 'World & Galaxy',
|
||
color: 'var(--accent)',
|
||
desc: 'Galaxy topology, world events, factions, anomalies, and fauna.',
|
||
tables: [
|
||
{ name: 'systems', pk: 'system_id', fk: '\u2192 regions.region_id', note: 'Star systems. Security level immutable.' },
|
||
{ name: 'stations', pk: 'station_id', fk: '\u2192 systems.system_id', note: 'Docking locations with services.' },
|
||
{ name: 'asteroids', pk: 'asteroid_id', fk: '\u2192 systems.system_id', note: 'Mineable resource nodes.' },
|
||
{ name: 'factions', pk: 'faction_id', fk: '', note: 'NPC factions with territory.' },
|
||
{ name: 'faction_relations', pk: 'faction_a_id + faction_b_id', fk: '\u2192 factions.faction_id (\u00d72)', note: 'Dynamic relationship matrix.' },
|
||
{ name: 'world_events', pk: 'event_id', fk: '\u2192 systems.system_id', note: 'Active PvE events. Severity, state, params.' },
|
||
{ name: 'galaxy_story_log', pk: 'log_id', fk: '\u2192 world_events.event_id', note: 'Persistent server timeline.' },
|
||
{ name: 'anomalies', pk: 'anomaly_id', fk: '\u2192 systems.system_id', note: 'Temporary spatial phenomena.' },
|
||
{ name: 'space_fauna', pk: 'fauna_id', fk: '\u2192 systems.system_id', note: 'Migrating creatures. Route JSON.' },
|
||
],
|
||
},
|
||
{
|
||
name: 'Social & PvP',
|
||
color: 'var(--red)',
|
||
desc: 'Chat, bounty, kill feed, waypoints, and missions.',
|
||
tables: [
|
||
{ name: 'chat_channels', pk: 'channel_id', fk: '', note: 'Channel definitions: local, trade, private.' },
|
||
{ name: 'chat_messages', pk: 'message_id', fk: '\u2192 chat_channels.channel_id, \u2192 players.player_id', note: 'Message stream with delay.' },
|
||
{ name: 'bounties', pk: 'target_player_id', fk: '\u2192 players.player_id', note: 'Bounty pool per target.' },
|
||
{ name: 'bounty_contributions', pk: 'contribution_id', fk: '\u2192 bounties.target_player_id, \u2192 players.player_id', note: 'Individual payments into pools.' },
|
||
{ name: 'kill_feed', pk: 'kill_id', fk: '\u2192 players.player_id (victim + killer)', note: 'Ship destruction events.' },
|
||
{ name: 'npc_agents', pk: 'agent_id', fk: '\u2192 factions.faction_id, \u2192 stations.station_id', note: 'NPC agents at stations.' },
|
||
{ name: 'active_missions', pk: 'mission_id', fk: '\u2192 players.player_id, \u2192 npc_agents.agent_id', note: 'Player mission state.' },
|
||
{ name: 'waypoints', pk: 'route_id', fk: '\u2192 players.player_id', note: 'Multi-stop routes.' },
|
||
],
|
||
},
|
||
{
|
||
name: 'Ship AI (Zora)',
|
||
color: 'var(--purple)',
|
||
desc: 'Soul state, modules, tools, memory, directives, and runtime.',
|
||
tables: [
|
||
{ name: 'ship_ai_soul', pk: 'ship_id', fk: '\u2192 ships.ship_id', note: 'One-to-one with ships. Soul document + personality.' },
|
||
{ name: 'ship_ai_modules', pk: 'module_id', fk: '\u2192 ships.ship_id', note: 'Installed AI modules. Medium/low slots.' },
|
||
{ name: 'ship_ai_tools', pk: 'tool_id', fk: '\u2192 ship_ai_modules.module_id', note: 'Derived from modules. Tool registry.' },
|
||
{ name: 'ship_ai_memory', pk: 'memory_id', fk: '\u2192 ships.ship_id', note: 'Event log. Category + importance scoring.' },
|
||
{ name: 'ship_ai_directives', pk: 'directive_id', fk: '\u2192 ships.ship_id', note: 'Player-set goals for autonomous mode.' },
|
||
{ name: 'ship_ai_agent_runtime', pk: 'ship_id', fk: '\u2192 ships.ship_id', note: 'Per-ship agent loop state. One-to-one.' },
|
||
],
|
||
},
|
||
].map((cluster, ci) => (
|
||
<div key={ci} style={{ marginBottom: 'var(--sp-6)' }}>
|
||
<h3 style={{ color: cluster.color, marginBottom: 'var(--sp-4)' }}>
|
||
{cluster.name}
|
||
<span style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', fontWeight: 400, marginLeft: 'var(--sp-3)' }}>{cluster.desc}</span>
|
||
</h3>
|
||
<div style={{ overflowX: 'auto' }}>
|
||
<table className="data-table">
|
||
<thead>
|
||
<tr><th>Table</th><th>PK</th><th>FK Relationships</th><th>Notes</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{cluster.tables.map((t, ti) => (
|
||
<tr key={ti}>
|
||
<td><code style={{ color: cluster.color }}>{t.name}</code></td>
|
||
<td className="mono" style={{ fontSize: '0.75rem' }}>{t.pk}</td>
|
||
<td style={{ fontSize: '0.75rem', color: 'var(--fg-dim)' }}>{t.fk || '\u2014'}</td>
|
||
<td style={{ fontSize: '0.75rem', color: 'var(--muted)' }}>{t.note}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
))}
|
||
|
||
<div className="callout callout-info" style={{ marginTop: 'var(--sp-4)' }}>
|
||
<strong>Cross-cluster flows:</strong> The most important cross-cluster relationships are:
|
||
(1) <code style={{ color: 'var(--cyan)' }}>players</code> \u2192 <code style={{ color: 'var(--green)' }}>market_orders</code> \u2192 <code style={{ color: 'var(--accent)' }}>stations</code> (the economic loop),
|
||
(2) <code style={{ color: 'var(--cyan)' }}>ships</code> \u2192 <code style={{ color: 'var(--accent)' }}>systems</code> \u2192 <code style={{ color: 'var(--red)' }}>chat_messages</code> (the spatial-social loop),
|
||
(3) <code style={{ color: 'var(--purple)' }}>ship_ai_soul</code> \u2192 <code style={{ color: 'var(--green)' }}>market_orders</code> (Zora reads market data for intelligence).
|
||
Every reducer call touches at least two clusters.
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
window.GDD.BackendPage = BackendPage;
|