Game.Simulation.ServiceFeeSystem
Assembly: Game (Game.dll)
Namespace: Game.Simulation
Type: class (public)
Base: GameSystemBase, IServiceFeeSystem, IDefaultSerializable, ISerializable, IPostDeserialize
Summary:
ServiceFeeSystem is responsible for collecting, aggregating and reporting city service fees in Cities: Skylines 2. It:
- Collects per-update service fees from service-producing buildings (patients at healthcare, students at education, etc.), queues fee events and deducts money from households.
- Aggregates queued fee events into per-city CollectedCityServiceFeeData buffers.
- Sends trigger notifications based on trade/balance of service resources.
- Provides static helpers to query and modify service fees, calculate multipliers and estimate income.
- Handles serialization/deserialization of the queued fee events and supports new-game initialization.
Notes: - Much of the runtime work is performed in Burst-compiled jobs (PayFeeJob, FeeToCityJob, TriggerJob) to run on worker threads. The system manages a NativeQueue of FeeEvent and a cached NativeList of CollectedCityServiceFeeData for quick access. - The system updates infrequently (GetUpdateInterval returns 2048), and distributes work to an EndFrameBarrier to synchronize with end-of-frame writers.
Fields
-
private const int kUpdatesPerDay
Used as an internal constant (value 128) representing the number of fee updates per in-game day. Drives normalization of per-tick amounts. -
private CitySystem m_CitySystem
Reference to the global CitySystem. Used to obtain the city entity (m_City) used by jobs and lookups. -
private TriggerSystem m_TriggerSystem
Reference to TriggerSystem used to enqueue triggers/notifications produced by service fee aggregation. -
private EndFrameBarrier m_EndFrameBarrier
Reference to the EndFrameBarrier system. Used to register writer dependencies for jobs that enqueue into managed buffers at end of frame. -
private EntityQuery m_FeeCollectorGroup
EntityQuery selecting entities that can produce fee events (e.g., buildings with Patient or Student buffers and a ServiceFeeCollector component). Used to schedule the PayFeeJob. -
private EntityQuery m_CollectedFeeGroup
EntityQuery selecting entities that contain CollectedCityServiceFeeData buffers (city-level storage). Used to aggregate fees. -
private NativeQueue<FeeEvent> m_FeeQueue
Thread-safe queue where jobs enqueue FeeEvent instances describing collected fees. Serialized/deserialized when saving/loading. -
private NativeList<CollectedCityServiceFeeData> m_CityServiceFees
Cached list of all CollectedCityServiceFeeData across the city. Used for quick, CPU-side queries (e.g., GetServiceFees, income estimates). -
private JobHandle m_Writers
Tracks job dependencies for writers that write into city buffers; combined with other dependencies to ensure proper synchronization. -
private TypeHandle __TypeHandle
Internal struct containing cached Component/Buffer lookup type handles used by jobs. Populated in OnCreateForCompiler.
Nested types (not fields but important): - FeeEvent (struct) — represents a single fee event and implements ISerializable. - PayFeeJob (Burst-compiled IJobChunk) — collects fees from entities and enqueues FeeEvent into m_FeeQueue; deducts household money. - FeeToCityJob (Burst-compiled IJob) — drains m_FeeQueue and accumulates values into CollectedCityServiceFeeData buffers. - TriggerJob (Burst-compiled IJob) — computes balances and enqueues trigger actions (e.g., notifications for electricity, healthcare). - TypeHandle (struct) — holds BufferTypeHandle/ComponentLookup/BufferLookup instances used by the jobs.
Properties
- (No public properties)
This system exposes functionality via methods rather than public properties. Use GetServiceFees(), GetFeeQueue(out JobHandle), AddQueueWriter(JobHandle) and other methods below.
Constructors
public ServiceFeeSystem()
Default constructor. The system uses OnCreate to initialize references, queries and native containers.
Methods
-
public override int GetUpdateInterval(SystemUpdatePhase phase)
Returns the update interval for the system. This system uses a large interval (2048) to control how often it is scheduled in the ECS update pipeline. -
protected override void OnCreate()
Initializes entity queries, acquires references to CitySystem, TriggerSystem and EndFrameBarrier, and allocates native containers: - Creates m_FeeQueue (NativeQueue
) and m_CityServiceFees (NativeList). - Sets up m_FeeCollectorGroup and m_CollectedFeeGroup queries.
-
Calls RequireForUpdate on m_CollectedFeeGroup so the system only runs if collected fee buffers exist.
-
public void PostDeserialize(Context context)
Called after deserialization. If context.purpose == NewGame it resets cached fees (CacheFees(reset: true)), otherwise just caches fees. Ensures fresh starting state for new games. -
protected override void OnDestroy()
Completes outstanding m_Writers JobHandle, disposes m_FeeQueue and m_CityServiceFees, and calls base.OnDestroy(). -
public void Serialize<TWriter>(TWriter writer) where TWriter : IWriter
Serializes the pending fee queue to the writer. Ensures m_Writers is completed, copies the queue to a NativeArray and writes length + each FeeEvent. -
public void Deserialize<TReader>(TReader reader) where TReader : IReader
Deserializes and restores the fee queue. Completes m_Writers and clears existing m_FeeQueue before enqueuing read FeeEvent items. -
public void SetDefaults(Context context)
Resets state for new game defaults: completes writers and clears the fee queue. -
public NativeList<CollectedCityServiceFeeData> GetServiceFees()
Returns the cached m_CityServiceFees NativeList containing collected fee data (internal, export, import amounts) for all resources. Useful for UI, previews and plugin queries. Caller should not dispose the returned list (owned by system). -
public NativeQueue<FeeEvent> GetFeeQueue(out JobHandle deps)
Returns a reference to the internal fee queue and outputs current writer dependencies via deps = m_Writers. Used by jobs that will enqueue fee events; callers must combine dependencies and call AddQueueWriter to register their job handle so the system waits correctly. -
public void AddQueueWriter(JobHandle deps)
Combine a job handle that writes to the queue into the system's writer dependency chain: m_Writers = JobHandle.CombineDependencies(m_Writers, deps). -
public int3 GetServiceFees(PlayerResource resource)
Convenience wrapper calling the static GetServiceFees(resource, m_CityServiceFees). Returns rounded int3 (internal, export, import) amounts for the requested resource. -
public int GetServiceFeeIncomeEstimate(PlayerResource resource, float fee)
Convenience wrapper calling the static GetServiceFeeIncomeEstimate(resource, fee, m_CityServiceFees). Estimates income generated by the specified fee using collected counts. -
public static PlayerResource GetEducationResource(int level)
Maps education level (1..4) to respective PlayerResource enum: BasicEducation, SecondaryEducation, HigherEducation, otherwise PlayerResource.Count. -
public static float GetFee(PlayerResource resource, DynamicBuffer<ServiceFee> fees)
Returns the fee value for the given resource from a ServiceFee buffer. Returns 0f if not found. -
public static bool TryGetFee(PlayerResource resource, DynamicBuffer<ServiceFee> fees, out float fee)
Tries to find a ServiceFee for resource in the buffer; outputs fee and returns true if present. -
public static void SetFee(PlayerResource resource, DynamicBuffer<ServiceFee> fees, float value)
Sets or adds a ServiceFee entry for resource in the provided DynamicBuffer. -
public static float GetConsumptionMultiplier(PlayerResource resource, float relativeFee, in ServiceFeeParameterData feeParameters)
Returns consumption multiplier for a resource relative to fee. Currently delegates to specialized systems for Electricity and Water; returns 1f for others. -
public static float GetEfficiencyMultiplier(PlayerResource resource, float relativeFee, in BuildingEfficiencyParameterData efficiencyParameters)
Returns building efficiency multiplier for the resource based on fees. Delegates to electricity/water adjust systems, 1f otherwise. -
public static int GetHappinessEffect(PlayerResource resource, float relativeFee, in CitizenHappinessParameterData happinessParameters)
Returns integer happiness effect due to fee for certain resources (electricity/water); defaults to 1 for others. -
public static int3 GetServiceFees(PlayerResource resource, NativeList<CollectedCityServiceFeeData> fees)
Static version that aggregates CollectedCityServiceFeeData entries in a provided list and returns rounded int3 {internal, export, import} totals for the resource. -
public static int GetServiceFeeIncomeEstimate(PlayerResource resource, float fee, NativeList<CollectedCityServiceFeeData> fees)
Static estimator: multiplies internal counts by fee to estimate periodic income; returns rounded int. -
private void CacheFees(bool reset = false)
Reads all CollectedCityServiceFeeData buffers from the entities matching m_CollectedFeeGroup and populates the m_CityServiceFees cache. If reset==true, buffer entries are reset to zero except resource id. Called on PostDeserialize(new game) and each OnUpdate to refresh cache. -
protected override void OnUpdate()
Main update orchestrator: - Calls CacheFees() to refresh cache.
- Constructs and schedules PayFeeJob (IJobChunk) over m_FeeCollectorGroup — job collects fees from Patients and Students and enqueues FeeEvent items, and deducts household money.
- Adds base.Dependency to the EndFrameBarrier as a producer.
- Schedules FeeToCityJob (IJob) to drain the NativeQueue and write into CollectedCityServiceFeeData buffers, combining dependencies with writer jobs.
- Stores the combined dependency in m_Writers for later synchronization.
-
Schedules TriggerJob (IJob) to compute aggregated balances and enqueue triggers via TriggerSystem's action buffer.
-
protected override void OnCreateForCompiler()
Internal initialization used by generated/compiled code path: assigns queries and TypeHandle handles. -
private void __AssignQueries(ref SystemState state)
Compiler-internal; used during OnCreateForCompiler.
Additional behavior details: - PayFeeJob: multiplies stored per-fee values to produce per-tick monetary adjustments. It uses GetFee(...) and normalizes by 128 (kUpdatesPerDay). Households are charged using EconomyUtils.AddResources with negative money adjustments. - FeeEvent serialization: FeeEvent implements ISerializable for persistent queue saving/loading.
Threading and safety: - Native containers (m_FeeQueue, m_CityServiceFees) are allocated with persistent lifetime and must be disposed in OnDestroy. - Jobs updating the queue must pass their JobHandle to AddQueueWriter so the system can combine dependencies. The system completes m_Writers before serializing or destroying.
Usage Example
// Example: modify a city's electricity service fee and estimate income.
public void Example(ServiceFeeSystem feeSystem, Entity cityEntity)
{
// Set fee on a building's ServiceFee buffer (example context):
// DynamicBuffer<ServiceFee> fees = entityManager.GetBuffer<ServiceFee>(someBuildingEntity);
// ServiceFeeSystem.SetFee(PlayerResource.Electricity, fees, 0.12f);
// Query aggregated service fees for the city
int3 electricityTotals = feeSystem.GetServiceFees(PlayerResource.Electricity);
// electricityTotals.x == internal, .y == export, .z == import (rounded ints)
// Estimate income for a proposed fee (per update period)
int estimatedIncome = feeSystem.GetServiceFeeIncomeEstimate(PlayerResource.Electricity, 0.12f);
UnityEngine.Debug.Log($"Electricity fees: internal={electricityTotals.x}, export={electricityTotals.y}, import={electricityTotals.z}");
UnityEngine.Debug.Log($"Estimated income at fee 0.12: {estimatedIncome}");
}
Notes: - When writing jobs that enqueue FeeEvent into the system queue, obtain the queue via GetFeeQueue(out JobHandle deps) to get the current m_Writers and ensure you combine dependencies and call AddQueueWriter(...) so the system knows about your writer job. - Use PostDeserialize to reset state for new games (the system does this automatically via the IPostDeserialize implementation).