Skip to content

Game.Simulation.DivorceSystem

Assembly:
Namespace: Game.Simulation

Type: class

Base: GameSystemBase

Summary:
DivorceSystem is a simulation system responsible for processing divorces among citizens' households. It runs on a schedule derived from kUpdatesPerDay and iterates households (excluding tourists, commuters, deleted and temp households) using a parallel IJobChunk (CheckDivorceJob). When a divorce occurs the system: - chooses a leaving adult citizen, - creates a new household entity from a household prefab, - transfers half of the household resources and money to the new household, - updates HouseholdMember/HouseholdCitizen buffers, - enqueues TriggerAction events (TriggerType.CitizenDivorced) for both involved citizens, - updates debug counters to expose the number of divorces processed that frame.

The system uses: - EndFrameBarrier to produce entity commands, - TriggerSystem for enqueuing triggers, - RandomSeed for per-chunk randomness, - NativeCounter / NativeValue for debug counters, - EntityQuery to collect household prefabs and citizen parameter singleton.

This system is multithreaded (jobs) and relies on ECS buffer/component lookups passed into the jobs. The divorce probability comes from CitizenParametersData.m_DivorceRate and is sampled per update interval.


Fields

  • public static readonly int kUpdatesPerDay = 4
    Defines the number of simulation updates per in-game day used to scale divorce probability. Changing this constant affects update sampling timing and probabilities.

  • private EndFrameBarrier m_EndFrameBarrier
    Used to create EntityCommandBuffer. The system creates household entities and updates components through the barrier so structural changes are applied safely at the end of the frame.

  • private SimulationSystem m_SimulationSystem
    Reference to the SimulationSystem used to compute the current update frame index (SimulationUtils.GetUpdateFrame).

  • [DebugWatchValue] private NativeValue<int> m_DebugDivorce
    A persistent NativeValue used to store the final divorce count (readable by debug/watch tools). Updated each tick by SumDivorceJob.

  • private NativeCounter m_DebugDivorceCount
    A persistent NativeCounter used concurrently inside the job to count divorces processed in parallel. Its value is copied into m_DebugDivorce after job completion.

  • private EntityQuery m_HouseholdQuery
    Query selecting active households to check for divorces. Constructed with filters:

  • ReadOnly Household
  • ReadOnly HouseholdCitizen buffer
  • ReadOnly UpdateFrame shared component
  • Excludes TouristHousehold, CommuterHousehold, Deleted, Temp

  • private EntityQuery m_HouseholdPrefabQuery
    Query selecting household prefabs (ArchetypeData, HouseholdData, DynamicHousehold). This list is sampled randomly to pick a new household archetype for the leaving spouse.

  • private EntityQuery m_CitizenParametersQuery
    Query to get the CitizenParametersData singleton, which contains m_DivorceRate used to determine divorce probabilities.

  • private TriggerSystem m_TriggerSystem
    Reference to TriggerSystem used to create TriggerAction buffers (to notify other systems of CitizenDivorced).

  • private TypeHandle __TypeHandle
    Internal struct containing all the ComponentTypeHandle / BufferTypeHandle / SharedComponentTypeHandle / ComponentLookup handles used by the job. __AssignHandles is called in OnCreateForCompiler to populate them.

Properties

  • None (this system exposes no public properties).

Constructors

  • public DivorceSystem()
    Default constructor. All major initialization occurs in OnCreate (system registration, Native allocations, and queries).

Methods

  • public override int GetUpdateInterval(SystemUpdatePhase phase)
    Returns the system's update interval: 262144 / (kUpdatesPerDay * 16). This determines how frequently the system is run relative to the engine's scheduling.

  • [Preserve] protected override void OnCreate()
    Initializes subsystem references and persistent native containers, sets up EntityQueries, and registers required queries for updates. Specifically:

  • Acquires EndFrameBarrier, SimulationSystem and TriggerSystem references from the world.
  • Allocates m_DebugDivorce (NativeValue) and m_DebugDivorceCount (NativeCounter).
  • Builds m_HouseholdQuery, m_HouseholdPrefabQuery and m_CitizenParametersQuery.
  • Calls RequireForUpdate for prefab, parameters and household queries to prevent running when they are not present.

  • [Preserve] protected override void OnDestroy()
    Disposes persistent native containers (m_DebugDivorce, m_DebugDivorceCount) and calls base.OnDestroy.

  • [Preserve] protected override void OnUpdate()
    Main logic that:

  • Computes the current updateFrame via SimulationUtils.GetUpdateFrame.
  • Prepares and schedules CheckDivorceJob (IJobChunk) in parallel:
    • Passes entity/buffer/component handles, shared component update frame index, random seed, household prefab entity list (ToEntityListAsync), the citizen parameter singleton, and parallel writers: TriggerSystem action buffer and EndFrameBarrier command buffer.
  • Adds producer job handle to the EndFrameBarrier and TriggerSystem.
  • Schedules SumDivorceJob (IJob) to copy the parallel NativeCounter into the NativeValue used for debugging.

Notes on logic carried by CheckDivorceJob: - For each household chunk matching the current update frame, it counts adult/elderly members. If fewer than two, no divorce is possible. - With probability m_DivorceRate / kUpdatesPerDay per update, chooses a random adult to leave. - Creates a new household entity using a randomly chosen household prefab from m_HouseholdPrefabs. - Sets the new household's resources and moves half the money. - Updates HouseholdMember to point the leaving citizen to the new household, adjusts buffers, and enqueues two TriggerAction events (one for each spouse). - Increments the job-local NativeCounter for each divorce.

  • private void __AssignQueries(ref SystemState state)
    Compiler helper used for query assignment during codegen; currently creates an EntityQueryBuilder (no-op here).

  • protected override void OnCreateForCompiler()
    Compiler hook: assigns handles in __TypeHandle and calls __AssignQueries to ensure generated code gets proper type handles at authoring/compilation stage.

Nested/jobs (brief)

  • CheckDivorceJob : IJobChunk (BurstCompile)
  • Fields: many handles including m_DebugDivorceCount (concurrent), m_EntityType, m_HouseholdType, m_HouseholdCitizenType, m_ResourceType, m_UpdateFrameType, component lookups for Citizens/HouseholdMember/ArchetypeData, m_HouseholdPrefabs, m_UpdateFrameIndex, m_CitizenParametersData, RandomSeed, Trigger buffer writer and CommandBuffer writer.
  • Key methods:
    • Divorce(...) — performs the actual entity creation and data transfer when a citizen divorces.
    • Execute(...) — processes each household chunk, decides divorces, picks leaving/staying citizens, and calls Divorce.
  • Notes: uses m_HouseholdPrefabs.ToEntityListAsync to choose the new household archetype and m_TriggerBuffer.Enqueue to notify other systems.

  • SumDivorceJob : IJob (BurstCompile)

  • Copies m_DebugDivorceCount.Count into the persistent m_DebugDivorce NativeValue after the parallel job finishes. Used to expose the divorce count to debug UI.

Usage Example

[Preserve]
protected override void OnCreate()
{
    base.OnCreate();
    // This is the system's own OnCreate (simplified): it acquires barriers/systems and allocates debug counters.
    m_EndFrameBarrier = base.World.GetOrCreateSystemManaged<EndFrameBarrier>();
    m_SimulationSystem = base.World.GetOrCreateSystemManaged<SimulationSystem>();
    m_TriggerSystem = base.World.GetOrCreateSystemManaged<TriggerSystem>();
    m_DebugDivorce = new NativeValue<int>(Allocator.Persistent);
    m_DebugDivorceCount = new NativeCounter(Allocator.Persistent);
    m_HouseholdQuery = GetEntityQuery(ComponentType.ReadOnly<Household>(), ComponentType.ReadOnly<HouseholdCitizen>(), ComponentType.ReadOnly<UpdateFrame>(), ComponentType.Exclude<TouristHousehold>(), ComponentType.Exclude<CommuterHousehold>(), ComponentType.Exclude<Deleted>(), ComponentType.Exclude<Temp>());
    m_HouseholdPrefabQuery = GetEntityQuery(ComponentType.ReadOnly<ArchetypeData>(), ComponentType.ReadOnly<HouseholdData>(), ComponentType.ReadOnly<DynamicHousehold>());
    m_CitizenParametersQuery = GetEntityQuery(ComponentType.ReadOnly<CitizenParametersData>());
    RequireForUpdate(m_HouseholdPrefabQuery);
    RequireForUpdate(m_CitizenParametersQuery);
    RequireForUpdate(m_HouseholdQuery);
}

Additional modding notes and tips: - Debug counters: m_DebugDivorce (NativeValue) is annotated with DebugWatchValue — it can be useful when exposing runtime stats in debug overlays. The running divorce count is written after the job finishes. - Prefab selection: the new household entity is created by sampling a random entity from the m_HouseholdPrefabQuery results. Ensure household prefabs exist or the system won't run (RequireForUpdate on prefab query). - Determinism: CheckDivorceJob uses RandomSeed.Next() with per-chunk seeds to get deterministic but parallelizable randomness. Keep that in mind when reproducing behavior. - Safety and structural changes: structural changes (CreateEntity, SetBuffer on new entity, SetComponent) are done through EndFrameBarrier's command buffer, avoiding immediate structural changes during job execution. - Performance: heavy work is done in a Burst-compiled IJobChunk and scheduled parallel. Avoid adding heavy expensive operations in trigger consumers to prevent stalls. If you add listeners to TriggerType.CitizenDivorced, ensure they are efficient. - Extending behavior: to modify divorce probability, adjust CitizenParametersData.m_DivorceRate (singleton) or change how kUpdatesPerDay is used. To change who leaves or what resources are transferred, provide a post-processing system that listens for TriggerType.CitizenDivorced and adjusts households/entities accordingly.

If you want, I can also generate a short patch example that changes the divorce selection logic or redirects new households to a custom prefab list for a mod.