Skip to content

Game.LaneOverlapSystem

Assembly:
Namespace: Game.Net

Type: class

Base: GameSystemBase

Summary:
LaneOverlapSystem is a game system responsible for detecting and updating lane overlaps and related lane flags for the net (roads, tracks, parking lanes, pedestrian lanes) in Cities: Skylines 2. It computes collision/overlap ranges between lane curves, updates per-lane flags (car/track/pedestrian behaviors, merge/split info, starting/ending lanes, forbidding passing, roundabout detection, etc.), collects lane directional data, sorts and writes LaneOverlap buffers, and propagates additional overlap data between lanes owned by different entities. The system is highly optimized for performance: it uses Burst-compiled jobs, Native containers (NativeParallelMultiHashMap, NativeQueue, NativeList), parallel job scheduling and deferred job arrays. It also integrates with the city's configuration (left/right-hand traffic) and the update/serialization lifecycle (full update on load vs incremental updates).


Fields

  • private CityConfigurationSystem m_CityConfigurationSystem
    System used to read city configuration settings (notably leftHandTraffic). Initialized in OnCreate and used by jobs to determine traffic handedness.

  • private EntityQuery m_UpdatedOwnersQuery
    Query selecting sub-lane owner entities that were updated (used for incremental processing).

  • private EntityQuery m_UpdatedLanesQuery
    Query selecting lanes that were updated (used for incremental processing).

  • private EntityQuery m_AllOwnersQuery
    Query selecting all sub-lane owners (used for full updates, e.g. on game load).

  • private EntityQuery m_AllLanesQuery
    Query selecting all lanes (used for full updates, e.g. on game load).

  • private bool m_Loaded
    Internal flag set when the system receives OnGameLoaded. Causes the next update to perform a full recalculation of overlaps and lane flags.

  • private TypeHandle __TypeHandle
    Internal helper struct that caches ComponentLookup/BufferLookup/TypeHandle instances used by jobs. Assigned in OnCreateForCompiler.

{{ These fields control when and how lane overlap work is performed (incremental vs full), and provide access to the city configuration and component lookups required by jobs. }}


Properties

  • None (no public properties exposed)

{{ The system exposes no public properties. All behavior is driven by EntityQuery results and the system's scheduled jobs. }}


Constructors

  • public LaneOverlapSystem()
    Default constructor. Marked [Preserve] in the source to avoid stripping; the real init logic is in OnCreate / OnCreateForCompiler.

{{ The constructor only sets up the managed system instance — initialization of internal queries and type handles occurs in OnCreate and OnCreateForCompiler. }}


Methods

  • protected override void OnCreate()
    Initializes EntityQueries (updated/all owners/lanes) and gets a reference to CityConfigurationSystem via World.GetOrCreateSystemManaged(). Sets up queries used to select which entities to process in OnUpdate.

  • protected override void OnCreateForCompiler()
    Called by the ECS compiler setup path; assigns queries and __TypeHandle lookups via __AssignQueries and __TypeHandle.__AssignHandles. This prepares the ComponentLookup/BufferLookup/TypeHandle objects used by jobs.

  • protected override void OnUpdate()
    Main update function. Performs either an incremental update (only updated owner/lane entities) or a full update (all owners/lanes) depending on m_Loaded. Work performed:

  • Builds NativeParallelMultiHashMap and LaneTargetData to collect lane sources/targets.
  • Gathers entities (owners) into a deferred NativeList via ToEntityListAsync.
  • Optionally runs AddNonUpdatedEdgesJob (to include edges connected via ConnectedEdge buffers when incremental).
  • Schedules UpdateLaneOverlapsJob (computes overlaps per lane/sub-lane), ApplyExtraOverlapsJob (applies overlaps that must be added to other owners), SortLaneOverlapsJob (sorts overlap buffers), CollectLaneDirectionsJob (collects lane source/target direction data), and UpdateLaneFlagsJob (updates EdgeLane/CarLane/TrackLane/NodeLane flags from overlap and collected direction data).
  • Uses dependency chaining, disposes native containers with job dependencies.

  • protected override void OnGameLoaded(Context serializationContext)
    Marks m_Loaded = true, which forces the next OnUpdate to perform a full recalculation of overlaps and flags (needed after loading a save).

  • private bool GetLoaded()
    Reads m_Loaded and clears it when true. Used by OnUpdate to decide between full and incremental processing.

  • private void __AssignQueries(ref SystemState state)
    Internal helper used during compiler-time initialization to assign queries (internal code uses an EntityQueryBuilder in the original source).

{{ The core processing is implemented inside several nested Burst-compiled job structs. These are scheduled from OnUpdate and do the heavy lifting in parallel. Important nested jobs and helpers are described below. }}

Nested job types (high level overview) - AddNonUpdatedEdgesJob (IJob)
When running an incremental update, this job adds connected edge entities to the owner/entity list if the connected edges are not marked Updated. Ensures overlaps against edges not directly flagged as updated are still processed.

  • UpdateLaneFlagsJob (IJobChunk)
    Consumes the source/target maps and LaneOverlap buffers to compute resulting flags for EdgeLane, CarLane, TrackLane and NodeLane components. Sets connected start/end counts, merge/split bits, forbid passing, approach/turn flags, roundabout handling, starting/ending track lane flags, and lane cross counts.

  • CollectLaneDirectionsJob (IJobChunk)
    Walks lanes and collects lane "source" and "target" information into NativeParallelMultiHashMaps. Calculates turn amounts and picks which car flags to propagate into the target map. This map is used later by UpdateLaneFlagsJob to decide approach/turn behavior.

  • UpdateLaneOverlapsJob (IJobParallelForDefer)
    The most complex job: for each owner entity it iterates sub-lanes and computes lane overlaps between curves. Uses curve bounds, radius (from lane widths), angle offsets (for parking slots), and a recursive subdivision (FindOverlapRange) to find intersecting ranges. Produces LaneOverlap entries (t ranges, flags, parallelism, crossing side) and enqueues OverlapData for overlaps that need to be applied to lanes owned by a different entity.

Important internal helpers: - FindOverlapRange: recursively subdivides Bezier curve pairs and checks quads/segments for intersection; records overlap param ranges. - CalculateParallelism: estimates how parallel two curve sections are (used to weight overlap behavior). - CheckOverlaps: for a given source lane it scans other lanes (on same owner or between owners) and adds overlap entries accounting for merges, flip/merge flags, unsafe flags, pedestrian/parking special cases, and left/right crossing decisions (respecting left-hand traffic).

  • ApplyExtraOverlapsJob (IJob)
    Consumes OverlapData enqueued by UpdateLaneOverlapsJob and applies them to other owners' LaneOverlap buffers; adjusts NodeLane shared start/end counts when overlaps represent merges across owners.

  • SortLaneOverlapsJob (IJobParallelForDefer)
    Sorts LaneOverlap buffers of sub-lanes that are not secondary lanes. Sorting is done using the buffer's AsNativeArray().Sort() for deterministic ordering.

Other nested types: - LaneSourceData / LaneTargetData: small structs used in the source/target hash maps to describe lane endpoints and flags used by UpdateLaneFlagsJob. - OverlapData: used to pass extra overlap data between jobs via a native queue.


Usage Example

[Preserve]
protected override void OnCreate()
{
    base.OnCreate();

    // Initialize the city configuration reference and queries (matching original system behavior)
    m_CityConfigurationSystem = base.World.GetOrCreateSystemManaged<CityConfigurationSystem>();
    m_UpdatedOwnersQuery = GetEntityQuery(ComponentType.ReadOnly<SubLane>(), ComponentType.ReadOnly<Updated>(), ComponentType.Exclude<Deleted>());
    m_UpdatedLanesQuery  = GetEntityQuery(ComponentType.ReadOnly<Lane>(), ComponentType.ReadOnly<Updated>(), ComponentType.Exclude<Deleted>(), ComponentType.Exclude<SecondaryLane>());
    m_AllOwnersQuery     = GetEntityQuery(ComponentType.ReadOnly<SubLane>(), ComponentType.Exclude<Deleted>());
    m_AllLanesQuery      = GetEntityQuery(ComponentType.ReadOnly<Lane>(), ComponentType.Exclude<Deleted>(), ComponentType.Exclude<SecondaryLane>());
}

{{ Practical tips for modders: - Do not modify the low-level Burst jobs lightly — they are highly optimized and rely on consistent ComponentLookup/BufferLookup usage and correct lifetime/allocators. - To trigger a full recompute (like on load), the system uses OnGameLoaded which sets m_Loaded = true; if you need to force a full recompute at runtime from a mod, you can emulate this behavior by toggling the system's internal loaded flag or by marking entities as Updated so the incrementals will run. (Be careful: m_Loaded is private; prefer interacting via entity Updated components.) - The system respects left/right-hand traffic via CityConfigurationSystem.leftHandTraffic; changing traffic handedness or city configuration may require full recomputation of overlaps/flags. - When debugging overlaps, inspect the LaneOverlap buffers on SubLane entities and the Lane flag components (CarLane, TrackLane, NodeLane, EdgeLane). }}