Skip to content

Game.WaterPipeFlowJob

Assembly: Assembly-CSharp (game assembly)
Namespace: Game.Simulation

Type: struct (Burst-compiled job)

Base: System.ValueType, Unity.Jobs.IJob

Summary:
WaterPipeFlowJob is a Burst-compiled IJob that runs the water pipe flow solver for Cities: Skylines 2. It implements an incremental, multi-phase solver combining a max-flow precomputation and a more detailed fluid flow solver. The job can be run incrementally across multiple frames (controlled via m_FrameCount and m_FinalFrame) and stores intermediate state in Native containers so work can be resumed later. It handles producer/sink labeling, trade connection enabling, max-flow layer-by-layer solving, and an optional fluid-flow phase for per-edge flows. The job relies heavily on Native* containers and other solver/helper types (MaxFlowSolver, FluidFlowSolver, NodeIndices, Connection, Edge, Node, etc.) defined elsewhere in the simulation code.


Fields

  • private const int kShortageNodeLabel = -1
    Stores the label value used to mark shortage nodes (internal constant).

  • private const int kConnectedNodeLabel = -2
    Stores the label value used to mark connected nodes (internal constant).

  • public const int kShortageEdgeLabel = -1
    Public constant label for shortage edges.

  • public const int kConnectedEdgeLabel = -2
    Public constant label for connected edges.

  • private const int kSinkEdgeLabel = -200
    Internal special index used to mark sink-related edges during preprocessing.

  • public NativeReference<State> m_State
    Holds the current solver State (phase, counters, error flag) across job runs. NativeReference allows the job to persist and resume.

  • public NativeArray<Game.Simulation.Flow.Node> m_Nodes
    Array of graph nodes used by both max-flow and fluid-flow solvers.

  • public NativeArray<Game.Simulation.Flow.Edge> m_Edges
    Array of graph edges used by solvers. Edge temporary and final flows are written here.

  • [ReadOnly] public NativeArray<Connection> m_Connections
    Read-only connection list that maps node connections to edges and endpoints.

  • [ReadOnly] public NativeReference<NodeIndices> m_NodeIndices
    Read-only reference containing indices for source/sink nodes and any other indexing helpers.

  • [ReadOnly] public NativeArray<int> m_TradeNodes
    Read-only list of trade node indices (used to enable trade import/export connections).

  • public NativeReference<MaxFlowSolverState> m_MaxFlowState
    Persistent state for the incremental MaxFlowSolver, serialized into a NativeReference so the solver can resume later.

  • public NativeList<LayerState> m_LayerStates
    NativeList used to store layer solver state (saved/loaded between runs).

  • public NativeList<CutElement> m_LayerElements
    NativeList holding cut elements for saved layer state.

  • public NativeList<CutElementRef> m_LayerElementRefs
    NativeList of references to layer elements (for incremental max-flow).

  • public NativeReference<FluidFlowSolverState> m_FluidFlowState
    Persistent state for the FluidFlowSolver so fluid simulation can be resumed.

  • public int m_ImportCapacity
    Configured import capacity used when enabling trade connections for imports.

  • public int m_ExportCapacity
    Configured export capacity used when enabling trade connections for exports.

  • public bool m_FluidFlowEnabled
    If true, after max-flow/trade phases the job runs the more detailed FluidFlow solver to compute per-edge fluid movements.

  • public int m_LayerHeight
    Maximum layer height used by the MaxFlowSolver (controls per-layer data structures).

  • public int m_FrameCount
    Number of frames worth of work to budget when running incrementally (used to compute step limits).

  • public bool m_FinalFrame
    If true the job will complete the whole solve in this run and finalize internal state; otherwise it may stop early to be resumed next frame.

Properties

  • This job struct does not expose C# properties. All state is exposed via public Native containers and primitive fields.

Constructors

  • public WaterPipeFlowJob()
    Default value-type constructor (compiler-provided). In practice the job is created by filling its public fields and scheduling it. All Native containers must be allocated and assigned externally before scheduling.

  • public struct State.State(int lastTotalSteps)
    Constructs a State with Phase.Initial and sets m_LastTotalSteps. Used to initialize persistent state stored in m_State.

  • public struct Data.Data(int lastTotalSteps, Allocator allocator)
    Initializes the nested Data helper, allocating the NativeReference and lists required for an out-of-job representation of job data. Use Data.Dispose() to free allocations.

Methods

  • public void Execute()
    Entry point for the IJob. It reads/writes m_State and runs phases until either the phase becomes Complete or the step budget for this run is reached. If m_FinalFrame is true, the job will attempt to finish and call Finalize to reset state for the next tick. The method sets and clears an error flag to detect and log failures; if an error is detected and m_FinalFrame is true, an error message is logged and Finalize is called.

  • private void Finalize(ref State state)
    Resets the State to Phase.Initial, updates last step counters and clears counters/flags. Called when a full solve completes.

  • private void ExecutePhase(ref State state, int maxSteps)
    Dispatches execution to the appropriate phase handler based on state.m_Phase. Throws if the phase is invalid.

  • private void InitialPhase(ref State state)
    Initial initialization for a solve: resets max-flow state and transitions to the Producer phase.

  • private void ResetMaxFlowState()
    Clears m_MaxFlowState and empties m_LayerStates/m_LayerElements/m_LayerElementRefs to prepare a fresh max-flow run.

  • private void MaxFlowPhase(ref State state, int maxSteps, Phase phaseAfterCompletion)
    Runs the incremental MaxFlowSolver. It creates temporary per-run containers (layers, queues, active queues), initializes or loads solver state from m_MaxFlowState and m_LayerStates/Elements/Refs, runs SolveNextLayer in a loop (respecting maxSteps unless m_FinalFrame), saves state back to m_MaxFlowState and persistent lists, disposes temporary containers, updates state.m_StepCounter and moves to phaseAfterCompletion if the solver completed.

  • private void PostProducerPhase(ref State state)
    After producer max-flow phase: labels shortages (LabelShortages), enables trade connections (EnableTradeConnections), resets max-flow state to allow a trade max-flow run, and transitions to Trade phase.

  • private void LabelShortages(ref State state)
    Labels nodes/edges that are part of the shortage cut. Starts from the sink node connections, assigns cut element identifiers for edges and enqueues subgraphs via LabelShortageSubGraph.

  • private void LabelShortageSubGraph(ref State state, int initialNodeIndex, NativeQueue<int> labelQueue)
    Breadth-first traversal marking nodes and edges reachable in the shortage subgraph. Uses labelQueue and increments state.m_StepCounter for each visited node. Updates m_Nodes and m_Edges cut element ids accordingly.

  • private void EnableTradeConnections()
    Enables or sets capacity on trade connections (imports/exports) for trade nodes depending on their shortage marking. Adjusts m_Edges capacities for trade edges to either m_ImportCapacity/m_ExportCapacity or zero.

  • private void PostTradePhase(ref State state)
    After trade-phase max-flow completes: enables trade connections (import=true), labels connected nodes (LabelConnected) and shortages again, and depending on m_FluidFlowEnabled prepares for FluidFlowPhase (resets nodes for fluid flow, limits import capacities, preflows sink edges, resets non-sink edges, clears m_FluidFlowState). If fluid flow is disabled, FinalizeFlows is called and the job goes to Complete.

  • private void SetTradeConnectionsEnabled(bool import, bool export)
    Helper that sets trade-edge capacities for import/export directions across trade nodes.

  • private void LabelConnected(ref State state)
    Labels nodes/edges reachable from the source (connected), similar to LabelShortages but starting at source and using outgoing capacities.

  • private void LabelConnectedSubGraph(ref State state, int initialNodeIndex, NativeQueue<int> labelQueue)
    BFS for connected subgraph, marking nodes and edges reachable from a given start and updating step counters and cut element ids.

  • private void LimitImportEdgeCapacity()
    Sets import-edge capacity for trade connections to the previously computed flow on that edge (limits imports to actual flow).

  • private void PreflowSinkEdges()
    For each edge into the sink, finalizes its temporary flow into final state, sets its cut id to the sink marker (kSinkEdgeLabel), and transfers that flow to node excess fields preparing the fluid solver.

  • private void ResetNonSinkEdges()
    Resets m_FinalFlow and m_TempFlow to zero for all edges except those marked with the sink edge id.

  • private void FinalizeFlows()
    Calls FinalizeTempFlow() on every edge (commits temp flow into final flow).

  • private void FluidFlowPhase(ref State state, int maxSteps)
    Runs the FluidFlowSolver incrementally: creates label/push priority queues (NativeMinHeap), initializes or loads solver state from m_FluidFlowState, calls SolveStep until completion or until step budget, saves state back, disposes temporary heaps, updates state counter, and sets phase to Complete if done.

  • Nested types: Phase (enum), State (struct), Data (struct implementing IDisposable)

  • Phase defines the solver phases: Initial, Producer, PostProducer, Trade, PostTrade, FluidFlow, Complete.
  • State stores current Phase, counters, and an error flag to allow incremental resumption.
  • Data provides a convenience collection of Native containers and a Dispose() to free them (useful for allocation/cleanup outside the job).

Usage Example

// Example: prepare and run the WaterPipeFlowJob synchronously.
// In real usage you typically allocate the Native containers elsewhere and reuse them.

var lastTotalSteps = 0;
var allocator = Allocator.TempJob;

// Prepare persistent Data (allocates NativeReference and lists)
var jobData = new Game.Simulation.WaterPipeFlowJob.Data(lastTotalSteps, allocator);

// Fill jobData.m_Nodes, m_Edges, m_Connections, etc. (omitted here)

// Create the job
var job = new Game.Simulation.WaterPipeFlowJob {
    m_State = jobData.m_State.AsDeferredJobArray()[0], // example assignment pattern;
    m_Nodes = jobData.m_Nodes.AsDeferredJobArray(),
    m_Edges = jobData.m_Edges.AsDeferredJobArray(),
    m_Connections = /* your connections NativeArray<Connection> */,
    m_NodeIndices = /* NativeReference<NodeIndices> */,
    m_TradeNodes = /* NativeArray<int> */,
    m_MaxFlowState = jobData.m_MaxFlowState,
    m_LayerStates = jobData.m_LayerStates,
    m_LayerElements = jobData.m_LayerElements,
    m_LayerElementRefs = jobData.m_LayerElementRefs,
    m_FluidFlowState = jobData.m_FluidFlowState,
    m_ImportCapacity = 1000,
    m_ExportCapacity = 1000,
    m_FluidFlowEnabled = true,
    m_LayerHeight = 16,
    m_FrameCount = 1,
    m_FinalFrame = true
};

// Run immediately on the calling thread (or Schedule() to run on worker threads)
job.Execute();

// After finished, commit/result reading and dispose persistent allocations:
jobData.Dispose();

Notes and recommendations: - The job is Burst-compiled and uses unsafe/native containers. Allocate and dispose all Native containers with appropriate Allocator (TempJob/Persistent) and be careful with lifetimes when scheduling across frames. - Use m_FrameCount and m_FinalFrame to throttle solver work across frames (set FinalFrame=true to force completion this run). - Persistent solver state is kept in m_MaxFlowState and m_FluidFlowState plus the layer lists. If you intend to run the solver incrementally, do not reset those between runs. - Many helper types (MaxFlowSolver, FluidFlowSolver, NodeIndices, Connection, Edge, Node, Identifier, LayerState, CutElement, etc.) are used by the job and must be available in the mod runtime.