Table of Contents

Namespace Core.Systems

Classes

AdjacencySystem

ENGINE LAYER: Province adjacency system

Stores which provinces are adjacent (share a border) for movement validation. Populated from FastAdjacencyScanner during map initialization.

Architecture:

  • Dictionary{ushort, HashSet{ushort}}: provinceID → set of neighbor IDs
  • Bidirectional: if A is adjacent to B, then B is adjacent to A
  • Read-only after initialization

Usage:

  • Initialize with SetAdjacencies(adjacencyData)
  • Query with IsAdjacent(province1, province2)
  • Get all neighbors with GetNeighbors(provinceID)

Performance:

  • IsAdjacent: O(1) HashSet lookup
  • GetNeighbors: O(1) dictionary lookup
  • Memory: ~6 neighbors × 13,350 provinces × 2 bytes = ~160 KB
CountrySystem

Single source of truth for all country/nation data Manages CountryHotData (8-byte) array for frequently accessed data Lazy-loads CountryColdData for detailed information Performance: Structure of Arrays, hot/cold separation, zero allocations

GameSystem

ENGINE LAYER - Base class for all game systems with standardized lifecycle

Responsibilities:

  • Define standard system lifecycle (Initialize, Shutdown, Save, Load)
  • Enforce dependency validation before initialization
  • Prevent re-initialization and initialization of missing dependencies
  • Provide consistent logging for system operations

Architecture:

  • Pure mechanism, no game-specific knowledge
  • Systems declare dependencies explicitly via GetDependencies()
  • Initialization order determined by dependency graph
  • All state changes go through standard lifecycle methods

Usage (Game Layer): public class EconomySystem : GameSystem { public override string SystemName => "Economy";

protected override IEnumerable{GameSystem} GetDependencies()
{
    yield return timeManager;
    yield return provinceSystem;
}

protected override void OnInitialize()
{
    // Dependencies guaranteed to be initialized here
    timeManager.OnMonthlyTick += CollectTaxes;
}

}

Benefits:

  • No load order bugs (dependencies validated)
  • No circular dependency crashes (detected at startup)
  • Easy testing (mock dependencies)
  • Save/load support (standard serialization hooks)
  • Self-documenting (dependencies explicit)
PathCache

ENGINE: LRU cache for pathfinding results.

Caches computed paths to avoid redundant A* searches. Uses simple LRU eviction when cache is full.

Cache key: (start, goal) pair Cache invalidation: Manual clear or frame-based expiry

Thread safety: NOT thread-safe (single-threaded pathfinding assumed)

PathfindingSystem
ProvinceSystem

Single source of truth for all province data Owns the 8-byte ProvinceState array for deterministic simulation Manages province ownership, development, terrain, and flags Performance: Structure of Arrays design, Burst-compatible, zero allocations

StandardCalendar

ENGINE: Default Earth calendar with real month lengths.

Features:

  • Real month lengths (31/30/28 days)
  • 365 days per year (no leap years for determinism)
  • Standard AD/BC era formatting
  • English month names (GAME layer can override with localized names)

For simplified 360-day calendar, use SimplifiedCalendar instead.

SystemRegistry

ENGINE LAYER - Manages game system registration and initialization order

Responsibilities:

  • Register all game systems
  • Determine initialization order via topological sort
  • Initialize systems in dependency order
  • Detect circular dependencies
  • Provide access to registered systems

Architecture:

  • Pure mechanism, no game-specific knowledge
  • Systems register themselves (or are registered by initializer)
  • Dependency graph built from GameSystem.GetDependencies()
  • Initialization order computed automatically

Usage: var registry = new SystemRegistry(); registry.Register(timeManager); registry.Register(economySystem); registry.InitializeAll(); // Initializes in dependency order

Benefits:

  • No manual initialization order management
  • Circular dependency detection at startup
  • Missing dependency errors before runtime crashes
  • Easy to add new systems (just register)
TerrainMovementCostCalculator

ENGINE LAYER: Movement cost calculator based on terrain types.

Provides MECHANISM only - looks up terrain costs from registry. Does NOT decide what's passable (that's GAME layer policy).

Usage: // ENGINE: Just terrain costs, everything passable var calculator = new TerrainMovementCostCalculator(provinceSystem, terrainRegistry);

// GAME: Wrap with game-specific traversability rules var gameCalculator = new LandUnitCostCalculator(calculator); // blocks water var navalCalculator = new NavalUnitCostCalculator(calculator); // blocks land

GAME layer implements IMovementCostCalculator with:

  • Unit type checks (land/naval/amphibious)
  • Ownership penalties (enemy territory)
  • Supply/attrition considerations
TimeManager

Deterministic time manager for multiplayer-ready simulation Uses fixed-point math and tick-based progression to ensure identical behavior across all clients

CRITICAL ARCHITECTURE REQUIREMENTS:

  • Fixed-point accumulator (NO float drift)
  • Real Earth calendar (365 days, no leap years for determinism)
  • Tick counter for command synchronization
  • Exact fraction speed multipliers
  • NO Time.time dependencies (non-deterministic)
  • ICalendar abstraction for custom calendars

See: Assets/Docs/Engine/time-system-architecture.md

UniformCostCalculator

Default cost calculator - all provinces cost 1. Use when no terrain or ownership modifiers needed.

Structs

AdjacencyStats

Queryable adjacency statistics struct.

BurstPathfindingJob

Burst-compiled A* pathfinding job. Uses binary min-heap for O(log n) priority queue operations.

CountryColorChangedEvent
CountrySystemInitializedEvent

Country-related events for EventBus Extracted from CountrySystem.cs for better organization

DailyTickEvent
GameLoadedEvent

Event emitted when a game is successfully loaded from a save file UI systems should listen to this and refresh their displays

GameSavedEvent

Event emitted when a game is successfully saved to a file

GameTime

Represents a point in game time (deterministic calendar). Supports real Earth calendar (365 days, variable month lengths). All operations are deterministic for multiplayer compatibility.

HourlyTickEvent
MonthlyTickEvent
NativeAdjacencyData

Read-only native adjacency data for Burst jobs. Use this struct in IJob implementations for parallel graph algorithms.

NativeProvinceData

Read-only native province data for Burst jobs. Use this struct in IJob implementations for parallel algorithms.

PathContext

Context passed to cost calculator for unit-specific pathfinding.

PathOptions

ENGINE: Options for pathfinding requests. Allows customization of cost calculation, forbidden zones, and preferences.

PathResult

ENGINE: Result of a pathfinding request. Contains path and metadata about the search.

ProvinceDevelopmentChangedEvent
ProvinceInitialStatesLoadedEvent
ProvinceOwnershipChangedEvent
ProvinceSystemInitializedEvent

Province-related events for EventBus Extracted from ProvinceSystem.cs for better organization

TimeChangedEvent
TimeStateChangedEvent
WeeklyTickEvent
YearlyTickEvent

Interfaces

ICalendar

ENGINE: Calendar abstraction for custom date systems.

PATTERN: Engine-Game Separation (Pattern 1)

  • ENGINE provides: ICalendar interface + StandardCalendar default
  • GAME provides: Custom calendars (Roman AUC, Fantasy 13-month, etc.)

Use Cases:

  • Historical games: AD/BC eras, real month names
  • Roman games: AUC (Ab Urbe Condita) dating
  • Fantasy games: Custom month names, different year lengths

All implementations must be deterministic for multiplayer compatibility.

IMovementCostCalculator

ENGINE: Interface for calculating movement costs between provinces.

Allows GAME layer to provide custom cost calculations based on:

  • Terrain type (mountains cost more than plains)
  • Province ownership (enemy territory costs more)
  • Unit type (cavalry faster on plains, slower in forests)
  • Weather, supply, fortifications, etc.

Default implementation: UniformCostCalculator (all costs = 1)

Enums

PathContextFlags

Flags for special pathfinding behavior.

PathStatus

Status codes for pathfinding results.

Delegates

MovementValidator

ENGINE LAYER: Pathfinding system for multi-province unit movement

Uses A* algorithm to find shortest path between provinces. Burst-compiled for high performance with large maps.

Features:

  • IMovementCostCalculator for terrain/ownership-based costs
  • PathOptions for forbidden zones and avoid preferences
  • LRU path caching for repeated requests
  • Burst path when using uniform costs (fast)
  • Managed path with custom costs (flexible)

Performance:

  • Burst: O(E log V) with binary heap, ~0.1ms typical
  • Pre-allocated collections, zero gameplay allocations
  • LRU cache reduces redundant searches