Skip to content

Game.Simulation.XPSystem

Assembly:
Assembly-CSharp (typical for Cities: Skylines 2 mods)

Namespace:
Game.Simulation

Type:
public class (CompilerGenerated) — XPSystem

Base:
GameSystemBase, IXPSystem

Summary:
XPSystem is a game simulation system that collects XP gain requests from jobs (via a NativeQueue), applies them to the city's XP component (XP), and emits XPMessage entries (NativeQueue) for later consumption by a main-thread message handler (IXPMessageHandler). It coordinates job dependencies so multiple producers can enqueue XP gains concurrently and ensures the single-city XP component is updated safely inside a scheduled IJob. The system persists its native queues across frames and disposes them on teardown.


Fields

  • private NativeQueue<XPMessage> m_XPMessages
    A persistent NativeQueue used to store XPMessage items produced when XP amounts are applied. Messages are consumed on the main thread via TransferMessages(IXPMessageHandler). Allocated with Allocator.Persistent in OnCreate and disposed in OnDestroy.

  • private NativeQueue<XPGain> m_XPQueue
    A persistent NativeQueue used by producers (jobs / other systems) to enqueue XPGain requests. External producers obtain this queue through GetQueue(out JobHandle) and must coordinate dependencies via AddQueueWriter(JobHandle). Allocated with Allocator.Persistent in OnCreate and disposed in OnDestroy.

  • private JobHandle m_QueueWriters
    Accumulated combined JobHandle representing the dependencies of all producers that have written (or will write) to m_XPQueue. Used to ensure the processing job waits for all writers to complete. AddQueueWriter combines incoming writer handles into this field.

  • private CitySystem m_CitySystem
    Cached reference to the CitySystem instance from the World (used to get the Entity representing the current city).

  • private SimulationSystem m_SimulationSystem
    Cached reference to the SimulationSystem instance (used to read the current frame index for XPMessage timestamping).

  • private TypeHandle __TypeHandle
    Generated struct used to cache ComponentLookup handles (here: ComponentLookup) for use within jobs. Populated during OnCreateForCompiler via __AssignHandles.

  • (Nested) private struct XPQueueProcessJob : IJob
    Job that dequeues XPGain entries from m_XPQueue, applies their amounts to the city's XP component (ComponentLookup), and enqueues corresponding XPMessage entries into m_XPMessages. Runs on a worker thread scheduled from OnUpdate.

  • (Nested) private struct TypeHandle
    Contains a ComponentLookup field and a method __AssignHandles to initialize it from a SystemState. Used for job-safe access to components.

Properties

  • (No public C# auto-properties are declared on XPSystem)
    Note: The system exposes the NativeQueue and its producer JobHandle via GetQueue(out JobHandle) rather than as a property.

Constructors

  • public XPSystem()
    Default (compiler-generated) constructor. System initialization happens in OnCreate.

Methods

  • public void TransferMessages(IXPMessageHandler handler)
    Completes the system's current dependency (base.Dependency.Complete()) to ensure all jobs that could produce messages have finished, then dequeues all XPMessage entries from m_XPMessages and forwards them to the provided IXPMessageHandler via handler.AddMessage(message). Call this from the main thread (e.g., a UI or message dispatch system) to receive XP notifications.

  • public NativeQueue<XPGain> GetQueue(out JobHandle deps)
    Returns the system's NativeQueue for producers to write into. Also returns the current combined producer dependencies (m_QueueWriters) in the out parameter deps — producers should use deps when scheduling jobs that write to the queue to maintain correct ordering. Note: obtaining the queue does not itself create a dependency; producers must call AddQueueWriter with the JobHandle of their writer jobs.

  • public void AddQueueWriter(JobHandle handle)
    Combine the provided writer job handle into the system's m_QueueWriters via JobHandle.CombineDependencies. Call this after scheduling any job that writes to the XPGain queue so the system can wait on these writers before processing the queue.

  • protected override void OnCreate()
    System lifecycle initialization. Retrieves CitySystem and SimulationSystem from the World, and allocates both m_XPMessages and m_XPQueue with Allocator.Persistent.

  • protected override void OnDestroy()
    Disposes the persistent NativeQueues (m_XPQueue and m_XPMessages) and calls base.OnDestroy(). Always called on main thread; ensures no memory leaks. Ensure any outstanding JobHandles that touch these queues are completed before disposal (the system schedules and tracks its own dependency).

  • protected override void OnUpdate()
    If a valid city entity exists, constructs XPQueueProcessJob with:

  • m_City (city entity),
  • m_FrameIndex (current simulation frame),
  • m_XPMessages and m_XPQueue queues,
  • and a ComponentLookup (via InternalCompilerInterface.GetComponentLookup and the cached __TypeHandle). Schedules the job with dependencies combined from m_QueueWriters and base.Dependency. The scheduled JobHandle is stored back into base.Dependency and into m_QueueWriters to track the processing job as a dependency for further queue writers.

  • private void __AssignQueries(ref SystemState state)
    Compiler-generated helper: here the method creates an empty EntityQueryBuilder and disposes it. Present for compiler plumbing; not used directly by mod code.

  • protected override void OnCreateForCompiler()
    Compiler helper called by the runtime; initializes queries and assigns component lookup handles by calling __AssignQueries and __TypeHandle.__AssignHandles. Required for the internal component lookup wiring used by the job.

  • (Nested) XPQueueProcessJob.Execute() : void
    Job's Execute method: reads the city's XP component (ComponentLookup), dequeues all XPGain entries, accumulates amounts to value.m_XP, and enqueues XPMessage entries for each non-zero gain. Writes the updated XP component back to the city's component. Runs on a worker thread.

Notes on thread-safety and lifecycle: - The system's design expects multiple producer jobs to enqueue XPGain entries into m_XPQueue concurrently. Producers must: - Obtain the queue via GetQueue(out deps). - Schedule their writer job(s) so they have a JobHandle. - After scheduling, call AddQueueWriter(writerHandle) to register the dependency with the XPSystem. - The XPQueueProcessJob is scheduled with a combined dependency of existing m_QueueWriters and system base.Dependency. This ensures the job runs only after all registered writer jobs complete. - TransferMessages completes base.Dependency before dequeuing m_XPMessages; this guarantees all message-producing jobs have finished. - The NativeQueues are allocated with Allocator.Persistent; they must not be disposed while there are outstanding jobs that access them. The system disposes them safely in OnDestroy.

Usage Example

// Example: producer scheduling a job that enqueues XP gains
public struct MyXPProducerJob : IJob
{
    public NativeQueue<XPGain>.ParallelWriter xpQueueWriter;
    public int amount;
    public XPReason reason; // hypothetical enum

    public void Execute()
    {
        // Enqueue an XPGain for processing by XPSystem
        xpQueueWriter.Enqueue(new XPGain { amount = amount, reason = reason });
    }
}

// Elsewhere in a mod/system update:
var xpSystem = World.GetOrCreateSystemManaged<XPSystem>();

// Get queue and current deps
JobHandle deps;
var xpQueue = xpSystem.GetQueue(out deps);

// Schedule a producer job that writes to the queue
var producerJob = new MyXPProducerJob
{
    xpQueueWriter = xpQueue.AsParallelWriter(),
    amount = 10,
    reason = XPReason.ServiceLevelUp
};
JobHandle producerHandle = producerJob.Schedule(deps);

// Inform XPSystem about the writer job so it waits for it before processing
xpSystem.AddQueueWriter(producerHandle);

// Later, on the main thread (e.g., in a UI or message dispatcher), collect messages:
xpSystem.TransferMessages(myIXPMessageHandler);

Additional tips: - Always call AddQueueWriter after scheduling your writer job; failing to do so can lead to the XP processing job running before your writer finishes, causing lost XP. - TransferMessages should be called on the main thread (synchronously) to dispatch XPMessage entries to your UI or log. The handler you provide must be main-thread safe. - Avoid scheduling long-running writers that hold the queue for many frames, as that will delay XP processing. - If you need to inspect or mutate XP on the main thread, be aware that the authoritative XP value is updated inside the job; call base.Dependency.Complete() (or use TransferMessages which does it) before reading the XP component to ensure a consistent value.

This documentation describes the runtime behavior and recommended usage to interact with XPSystem when modding Cities: Skylines 2.