Table of Contents

Namespace Core.Diplomacy

Classes

BreakAllianceCommand

ENGINE LAYER - Command to break alliance between two countries

BreakNonAggressionPactCommand

ENGINE LAYER - Command to break non-aggression pact

DeclareWarCommand

ENGINE LAYER - Command to declare war between two countries

Validation:

  • Both countries exist
  • Not already at war
  • Not the same country

Execution:

  • Set war state in DiplomacySystem
  • Add "Declared War" opinion modifier
  • Emit DiplomacyWarDeclaredEvent
DiplomacyColdData

ENGINE LAYER - Cold data for diplomatic relations (rarely accessed)

Architecture:

  • Separate from hot RelationData struct
  • Only loaded when needed (UI, calculations, save/load)
  • Modifiers and history stored here
  • Managed heap (Dictionary) storage acceptable for cold data

Memory: Variable (~200 bytes + modifiers)

Storage Pattern: Dictionary{(ushort, ushort), DiplomacyColdData} coldData;

  • Same key as RelationData
  • Only created when modifiers exist
DiplomacyKeyHelper

ENGINE LAYER - Helper for packing/unpacking country pairs into relationship keys. PATTERN: Static utility class (no state). Ensures consistent key generation (always smaller ID first). Prevents duplicate relationships (A to B and B to A use same key). KEY FORMAT: ulong 64 bits, country1 always less than country2 (normalized order).

DiplomacyModifierProcessor

ENGINE LAYER - Processes opinion modifier decay with Burst compilation

RESPONSIBILITY:

  • Decay opinion modifiers (DecayOpinionModifiers with Burst job)
  • Rebuild modifier cache after compaction

PATTERN: Stateless processor (receives data references from DiplomacySystem)

  • Does NOT own NativeCollections (passed as parameters)
  • Uses Burst-compiled parallel job for performance
  • Sequential compaction for determinism

PERFORMANCE:

  • 610,750 modifiers processed in 3ms (87% improvement from Burst)
  • Parallel marking (IJobParallelFor) + sequential compaction (deterministic)
  • O(1) cache rebuild for GetOpinion performance

ARCHITECTURE:

  • Step 1: Burst job marks decayed modifiers (parallel, read-only)
  • Step 2: Sequential compaction (deterministic ordering)
  • Step 3: Rebuild modifierCache for O(1) GetOpinion lookups
DiplomacyRelationManager

ENGINE LAYER - Manages opinion calculations and modifiers for diplomatic relations

RESPONSIBILITY:

  • Opinion queries (GetOpinion, GetBaseOpinion)
  • Opinion filtering (GetCountriesWithOpinionAbove/Below)
  • Opinion modifier management (Add/Remove)

PATTERN: Stateless manager (receives data references from DiplomacySystem)

  • Does NOT own NativeCollections (passed as parameters)
  • Pure methods for opinion calculations
  • Zero allocations in hot paths

PERFORMANCE:

  • GetOpinion: O(1) cache lookup + O(m) modifier scan (m = modifiers per relationship, ~10)
  • GetBaseOpinion: O(1) dictionary lookup
  • AddOpinionModifier: O(1) append to flat array
DiplomacySaveLoadHandler

ENGINE LAYER - Handles diplomacy save/load serialization

RESPONSIBILITY:

  • Save diplomatic state to binary format
  • Load diplomatic state and rebuild indices
  • Rebuild derived data (activeWars, warsByCountry, modifierCache)

PATTERN: Stateless handler (receives data references from DiplomacySystem)

  • Does NOT own NativeCollections (passed as parameters)
  • Binary serialization for efficiency
  • Rebuilds indices after load (Pattern 14: Hybrid Save/Load)

FORMAT:

  • relationCount: int32
  • For each relationship:
    • country1, country2: ushort × 2
    • baseOpinion: int64 (FixedPoint64.RawValue)
    • atWar: bool
    • treatyFlags: byte
    • modifierCount: int32
    • For each modifier:
      • modifierTypeID: ushort
      • value: int64 (FixedPoint64.RawValue)
      • appliedTick: int32
      • decayRate: int32
DiplomacySystem

ENGINE LAYER - Facade for diplomatic relations between countries

PATTERN 6: Facade Pattern

  • Owns all NativeCollections (data ownership)
  • Delegates operations to specialized managers (stateless processors)
  • Provides unified API for external systems

ARCHITECTURE:

  • DiplomacyRelationManager: Opinion calculations and modifiers
  • DiplomacyWarManager: War state and declarations
  • DiplomacyTreatyManager: Treaties (Alliance, NAP, Guarantee, Military Access)
  • DiplomacyModifierProcessor: Burst-optimized modifier decay
  • DiplomacySaveLoadHandler: Serialization/deserialization

Performance Targets (Paradox Scale):

  • 1000 countries, 30k active relationships
  • GetOpinion() <0.1ms (O(1) cache + O(m) modifiers)
  • IsAtWar() <0.01ms (HashSet O(1))
  • DecayOpinionModifiers() <5ms for 610k modifiers (Burst parallel)

Pattern Compliance:

  • Pattern 6: Facade (delegates to specialized managers)
  • Pattern 8: Sparse Collections (store active only)
  • Pattern 4: Hot/Cold Separation (RelationData hot, modifiers cold)
  • Pattern 5: Fixed-Point Determinism (FixedPoint64 opinions)
  • Pattern 17: Single Source of Truth (owns all diplomatic state)
DiplomacyTreatyManager

ENGINE LAYER - Manages treaties between countries

RESPONSIBILITY:

  • Treaty queries (AreAllied, HasNonAggressionPact, IsGuaranteeing, HasMilitaryAccess)
  • Treaty state changes (Form/Break Alliance, NAP, Guarantee, Military Access)
  • Treaty relationship queries (GetAllies, GetAlliesRecursive, GetGuaranteeing, GetGuaranteedBy)

PATTERN: Stateless manager (receives data references from DiplomacySystem)

  • Does NOT own NativeCollections (passed as parameters)
  • Uses RelationData.treatyFlags bitfield for treaty storage
  • Directional treaties (Guarantee, Military Access) use separate bits for each direction

PERFORMANCE:

  • Treaty checks: O(1) dictionary lookup + bitfield check
  • GetAllies: O(n) where n = total relationships
  • GetAlliesRecursive: O(n) BFS traversal
DiplomacyWarManager

ENGINE LAYER - Manages war state and war-related queries

RESPONSIBILITY:

  • War state queries (IsAtWar, GetEnemies, GetAllWars)
  • War declarations and peace treaties
  • War index management (warsByCountry for fast lookups)

PATTERN: Stateless manager (receives data references from DiplomacySystem)

  • Does NOT own NativeCollections (passed as parameters)
  • Manages activeWars HashSet and warsByCountry index
  • Emits events through GameState.EventBus

PERFORMANCE:

  • IsAtWar: O(1) HashSet lookup
  • GetEnemies: O(k) where k = enemies for this country
  • DeclareWar: O(1) + event emission
FormAllianceCommand

ENGINE LAYER - Command to form alliance between two countries

Validation:

  • Both countries exist
  • Not already allied
  • Not at war

Execution:

  • Set Alliance bit in DiplomacySystem
  • Emit AllianceFormedEvent
FormNonAggressionPactCommand

ENGINE LAYER - Command to form non-aggression pact

GrantMilitaryAccessCommand

ENGINE LAYER - Command to grant military access (directional)

GuaranteeIndependenceCommand

ENGINE LAYER - Command to guarantee another country's independence (directional)

ImproveRelationsCommand

ENGINE LAYER - Command to improve relations between two countries

Architecture:

  • ENGINE: Generic mechanism using ushort resourceId (resource-agnostic)
  • GAME: Policy layer sets which resource (gold) via factory: (ushort)ResourceType.Gold

Validation:

  • Both countries exist
  • Not at war
  • Source country has enough of the specified resource (if cost > 0)

Execution:

  • Deduct resource cost (if specified)
  • Add "Improved Relations" opinion modifier
  • Emit DiplomacyOpinionChangedEvent
MakePeaceCommand

ENGINE LAYER - Command to make peace between two countries

Validation:

  • Both countries exist
  • Currently at war

Execution:

  • Remove war state in DiplomacySystem
  • Add "Made Peace" opinion modifier
  • Emit DiplomacyPeaceMadeEvent
RevokeGuaranteeCommand

ENGINE LAYER - Command to revoke guarantee of independence

RevokeMilitaryAccessCommand

ENGINE LAYER - Command to revoke military access

Structs

AllianceBrokenEvent

Emitted when an alliance is broken GAME layer uses this to add opinion penalties, prestige loss

AllianceFormedEvent

Emitted when an alliance is formed between two countries GAME layer uses this to add opinion bonuses, UI notifications

DecayModifiersJob

Burst-compiled job for marking decayed opinion modifiers

ARCHITECTURE: Flat storage with relationship keys

  • All modifiers stored in single NativeList{ModifierWithKey}
  • Job marks decayed modifiers (parallel READ-ONLY operation)
  • Main thread compacts array SEQUENTIALLY (deterministic order preserved)

DETERMINISM GUARANTEE:

  • Job is READ-ONLY (no race conditions, no order dependency)
  • Compaction is SEQUENTIAL on main thread
  • Insertion order preserved (append-only)
  • Result identical across all game clients

Performance Target: <5ms for 610k modifiers (parallel SIMD processing)

DiplomacyColdDataNative

ENGINE LAYER - Cold data for diplomatic relations using NativeCollections

Architecture:

  • Struct (not class) for Burst compatibility
  • NativeList for modifiers (dynamic growth, zero GC)
  • Stored in NativeList{DiplomacyColdDataNative} in DiplomacySystem

Memory: ~24 bytes + modifiers

DiplomacyOpinionChangedEvent

Emitted when opinion changes between two countries AI uses this to: detect improving/worsening relations, adjust diplomacy UI uses this to: update relations panel, show opinion trends

DiplomacyPeaceMadeEvent

Emitted when two countries make peace AI uses this to: recalculate threat levels, consider new targets UI uses this to: show notifications, update war list

DiplomacyStats

Statistics for debugging and validation

DiplomacySystemInitializedEvent

Emitted when DiplomacySystem initializes Used by UI and AI to know when diplomatic queries are available

DiplomacyWarDeclaredEvent

ENGINE LAYER - Diplomatic event definitions

Architecture:

  • Structs for zero-allocation EventBus
  • Emitted by DiplomacySystem on state changes
  • GAME layer subscribes to trigger UI updates, notifications, AI reactions

Pattern 3: Event-Driven Architecture (Zero-Allocation)

  • Events are structs (no boxing)
  • EventBus uses EventQueue{T} wrapper
  • Frame-coherent processing
GuaranteeGrantedEvent

Emitted when a country guarantees another's independence (directional)

GuaranteeRevokedEvent

Emitted when a guarantee is revoked

MilitaryAccessGrantedEvent

Emitted when military access is granted (directional)

MilitaryAccessRevokedEvent

Emitted when military access is revoked

ModifierEntry

Single modifier entry in the flattened modifier array Stores both the modifier data AND which relationship it belongs to

This enables Burst-compiled parallel processing of ALL modifiers at once instead of iterating relationships sequentially

ModifierRange

Tracks the range of modifiers for a single relationship in the flat modifier array

DETERMINISM GUARANTEE:

  • Modifiers for each relationship stored in STABLE order (insertion order preserved)
  • Compaction is SEQUENTIAL on main thread (deterministic)
  • Parallel Burst job ONLY marks decayed (read-only, no order dependency)

Architecture: allModifiers[startIndex ... startIndex+count-1] = this relationship's modifiers

ModifierWithKey

Opinion modifier tagged with its relationship key

ARCHITECTURE:

  • Enables flat storage without range tracking
  • allModifiers = NativeList{ModifierWithKey} (all modifiers from all relationships)
  • GetOpinion filters by relationshipKey
  • Burst job processes entire array in parallel

DETERMINISM:

  • Insertion order preserved (append-only)
  • Decay marks modifiers for removal (parallel read-only)
  • Compaction rebuilds array sequentially (deterministic)

Memory: ~32 bytes per modifier (8 bytes key + 24 bytes modifier)

NonAggressionPactBrokenEvent

Emitted when a non-aggression pact is broken GAME layer uses this to add opinion penalties

NonAggressionPactFormedEvent

Emitted when a non-aggression pact is formed

OpinionModifier

ENGINE LAYER - Individual opinion modifier affecting relations between countries

Architecture:

  • Fixed-size struct for deterministic serialization
  • Decays linearly over time (value * (1 - elapsed/decayRate))
  • Fully decayed modifiers removed automatically

Memory: ~20 bytes per modifier (aligned)

Example Usage: var modifier = new OpinionModifier { modifierTypeID = OpinionModifierTypes.DeclaredWar, value = FixedPoint64.FromInt(-50), appliedTick = currentTick, decayRate = 3600 // 10 years (360 ticks/year) };

// Calculate current value after 5 years: FixedPoint64 current = modifier.CalculateCurrentValue(currentTick); // Returns -25 (50% decay after half the decay period)

RelationData

ENGINE LAYER - Hot data for diplomatic relations between two countries

Architecture:

  • Fixed-size struct for cache efficiency
  • Only stores essential hot data (opinion, war state)
  • Cold data (modifiers, history) stored separately
  • Stored in sparse Dictionary (only active relationships)

Memory: 16 bytes per relationship

Storage Pattern: Dictionary{(ushort, ushort), RelationData} relations;

  • Key: (country1, country2) sorted pair
  • Value: this struct
  • Only store relationships that exist (sparse)

Example: 1000 countries × 30% interaction = ~30k relationships × 16 bytes = ~480KB

Enums

TreatyFlags

Treaty type bitfield flags Used in RelationData.treatyFlags to track active treaties