Skip to content

Game.PayWageSystem

Assembly: Assembly-CSharp
Namespace: Game.Simulation

Type: class

Base: GameSystemBase

Summary:
PayWageSystem is an ECS system responsible for distributing wages, benefits and pension/unemployment/family allowances to households and charging employers. It iterates household buffers each simulation update (split into kUpdatesPerDay slices), computes per-update payments based on EconomyParameterData and worker status, queues employer payments, and applies payments to household/company resource buffers. The system coordinates with TaxSystem to read tax rates and updates Citizen/TaxPayer state (unemployment counters, average tax rate, untaxed income). Internally it uses Burst-compiled jobs (PayWageJob and PayJob) and a NativeQueue for cross-job communication.


Fields

  • public static readonly int kUpdatesPerDay
    {{ The number of wage-update slices per game day used by the system. Default is 32. Wages, pensions and benefits are divided by this value so payments are applied across multiple smaller simulation updates. Mods can use this constant to align their own timing with the wage/payment cadence. }}

  • private SimulationSystem m_SimulationSystem
    {{ Reference to the SimulationSystem used to compute the current update frame index. Used to determine whether a household chunk should be processed on this sub-frame. }}

  • private TaxSystem m_TaxSystem
    {{ Reference to the TaxSystem. The system requests tax rates from it and registers a job reader for synchronization (m_TaxSystem.AddReader(jobHandle)). }}

  • private EntityQuery m_EconomyParameterGroup
    {{ EntityQuery that selects the singleton EconomyParameterData component. Required for update; the system reads economy parameters (wages, benefits, multipliers, minimums, etc.) from that singleton. }}

  • private EntityQuery m_HouseholdGroup
    {{ EntityQuery targeting households that should be processed (Household, UpdateFrame, HouseholdCitizen buffers), excluding tourists, deleted and temp entities. This query is scheduled when the system runs PayWageJob. }}

  • private NativeQueue<Payment> m_PaymentQueue
    {{ A persistent NativeQueue used to enqueue Payments (struct contains target Entity and amount) from the parallel PayWageJob. After the wage job completes, a separate PayJob dequeues and applies the payments to target resource buffers. This queue is allocated in OnCreate and disposed in OnDestroy — mods must not leak similar native collections and must dispose them properly. }}

  • private TypeHandle __TypeHandle
    {{ Internal generated container holding Entity/Component/Buffer handles required for job scheduling. Used in OnCreateForCompiler to bind handles to the current SystemState. Not intended for direct use by mods. }}

  • Nested types (briefly):

  • private struct Payment
    {{ Simple POD type used to represent a queued payment: m_Target (Entity) and m_Amount (int). Amounts are in-game money units; negative amounts denote charges (e.g., employer pays). }}

  • private struct PayJob : IJob
    {{ Burst-compiled job that consumes m_PaymentQueue and applies resource changes to the target entities' Game.Economy.Resources buffer. Runs after PayWageJob and performs the actual modification of the resource buffers. It checks HasBuffer before applying. }}

  • private struct PayWageJob : IJobChunk
    {{ Burst-compiled chunk job that iterates household archetype chunks whose UpdateFrame matches this sub-frame. For each household it loops household citizens, determines workplace, computes per-update wage/benefit, updates TaxPayer state, and enqueues employer payments to m_PaymentQueue as needed. Contains the internal helper PayWage(...) implementing the per-citizen logic. The job reads/writes several component lookups and buffer lookups. }}


Properties

  • (No public properties)
    {{ PayWageSystem exposes no public properties. All runtime state is private fields and native jobs. Interactions from mod code should be done via ECS (components/singletons) or by patching/overriding systems, not by accessing internal fields. }}

Constructors

  • public PayWageSystem()
    {{ Default constructor — system is created/managed by the World. Initialization is performed in OnCreate (where NativeQueue and EntityQueries are created). Mods should not rely on calling the constructor directly; use World/GetOrCreateSystemManaged to obtain the instance if needed. }}

Methods

  • public override int GetUpdateInterval(SystemUpdatePhase phase)
    {{ Returns the interval used to schedule this system relative to the simulation tick. The method uses kUpdatesPerDay to compute how often the system runs; the exact arithmetic ensures the system is executed on the appropriate sub-frame slices. Typically you don't need to change this; it ensures synchronization with other economy/simulation work. }}

  • protected override void OnCreate()
    {{ Allocates and initializes system state:

  • Gets SimulationSystem and TaxSystem references.
  • Creates the persistent NativeQueue.
  • Sets up the EntityQueries (EconomyParameterData singleton and household group).
  • Calls RequireForUpdate on the queries to prevent running when prerequisites are missing. This method is marked [Preserve] to avoid stripping; it must be paired with proper disposal in OnDestroy. }}

  • protected override void OnDestroy()
    {{ Disposes the NativeQueue and calls base.OnDestroy. Always important to free native collections created in OnCreate to avoid leaks. }}

  • protected override void OnUpdate()
    {{ Main runtime logic:

  • Computes the current sub-frame index using SimulationUtils.GetUpdateFrame with kUpdatesPerDay.
  • Schedules PayWageJob as a parallel IJobChunk over the household query, passing in component/ buffer lookups, the economy parameters singleton, tax rates, and a parallel writer to m_PaymentQueue.
  • Registers the job with TaxSystem via AddReader for correct dependency ordering.
  • Schedules PayJob (IJob) to run after the PayWageJob to dequeue and apply queued payments to resource buffers.
  • Sets base.Dependency to the final combined handle. This method coordinates Burst-compiled jobs and ensures correct read/write ordering across systems. }}

  • private void __AssignQueries(ref SystemState state)
    {{ Internal method used by the generated compiler glue to create any builder queries or to capture state for OnCreateForCompiler. Not intended for use by modders. }}

  • protected override void OnCreateForCompiler()
    {{ Internal hook used by generated/IL2CPP compiler glue to assign component handles in __TypeHandle and perform any compile-time initialization. }}

  • Nested job methods (internal to the jobs):

  • PayWageJob.Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
    {{ Processes a chunk of households — checks UpdateFrame shared component so only the households assigned to this sub-frame are processed. It iterates household citizens and calls PayWage(...) for each. Updates TaxPayer component back to chunk array. }}
  • PayWageJob.PayWage(Entity workplace, Entity worker, Entity household, Worker workerData, ref TaxPayer taxPayer, DynamicBuffer<Game.Economy.Resources> resources, CitizenAge age, bool isCommuter, ref EconomyParameterData economyParameters)
    {{ Core per-citizen logic:
    • If citizen has a workplace (and is listed in the workplace's Employee buffer), compute wage from economyParameters.GetWage(level).
    • Apply commuter wage multiplier if isCommuter.
    • Divide wage for per-update amount using kUpdatesPerDay.
    • Enqueue a negative payment to workplace (charge employer) if workplace has CompanyData.
    • Reset unemployment counter if citizen was previously unemployed.
    • If no workplace, pay family allowance/pension/unemployment benefit as appropriate, increasing unemployment counter and paying until max days reached.
    • Add money to household resources buffer.
    • Compute residential tax rate and update TaxPayer.m_AverageTaxRate and m_UntaxedIncome (unless commuter or outside connection). }}
  • PayJob.Execute()
    {{ Dequeues Payment entries and for each that has a Resources buffer calls EconomyUtils.AddResources(Resource.Money, amount, resources). The job checks HasBuffer before applying. This job applies the queued changes produced by the parallel PayWageJob. }}

Usage Example

// Example: Modifying the economy parameter singleton (e.g. increasing pension) so this system
// will use the updated values on next update. Typical usage in a mod System or MonoBehaviour.
using Unity.Entities;
using Game.Economy;

public void IncreasePensionBy(EntityManager em, int delta)
{
    // Find the singleton entity (EconomyParameterData must exist or the PayWageSystem will not run)
    EntityQuery q = em.CreateEntityQuery(ComponentType.ReadWrite<EconomyParameterData>());
    if (!q.IsEmptyIgnoreFilter)
    {
        Entity econEntity = q.GetSingletonEntity();
        EconomyParameterData p = em.GetComponentData<EconomyParameterData>(econEntity);
        p.m_Pension += delta; // change pension — PayWageSystem will use this value
        em.SetComponentData(econEntity, p);
    }
}

{{ Modding notes and considerations: - PayWageSystem is highly performance-sensitive: it uses Burst, IJobChunk and NativeQueue with a parallel writer. Avoid modifying this system at runtime unless you understand ECS job dependencies. - The NativeQueue is persistent and disposed in OnDestroy; if you create similar native collections in your systems remember to dispose them. - EconomyParameterData changes (wages, pension, family allowance, commuter wage multiplier, unemployment benefit) directly change amounts applied here. To influence wages system-wide, modify that singleton rather than modifying this system. - Tax calculations update TaxPayer.m_AverageTaxRate and m_UntaxedIncome and rely on TaxSystem.GetTaxRates(). If you alter tax rates, update TaxSystem so PayWageJob sees correct values (the system registers itself as a reader to TaxSystem to get correct synchronization). - The system charges employers by enqueuing negative payments to the company entity. If you add custom company-like entities, ensure they have a Game.Companies.CompanyData component and a Resources buffer to be charged/credited. - Because PayWageJob checks whether a worker is listed in the workplace's Employee buffer before paying, modifications to employee lists must remain consistent. - Use kUpdatesPerDay to align any custom per-day payments or counters with the system's temporal slicing. }}