Skip to content

Game.CourseSplitSystem

Assembly:
Game (assembly where modding/game systems reside)

Namespace:
Game.Tools

Type:
class

Base:
GameSystemBase

Summary:
The CourseSplitSystem is the ECS system responsible for analyzing, splitting and finalizing "net courses" (proposed network segments such as roads, rails, waterways and their auxiliaries) into actual course entities ready for creation or replacement in the world. It:

  • Collects CreationDefinition + NetCourse entities that require processing.
  • Finds potential overlaps with existing edges using a quad-tree search.
  • Computes intersection positions (nodes/edge segments), prioritizes and merges them.
  • Samples terrain and water heights, computes elevation constraints and applies elevation/placement logic for elevated/submerged pieces.
  • Splits long courses into multiple buildable segments (including handling fixed elements, auxiliary nets, and special cases like tunnels, transitions, shorelines, and service upgrades).
  • Schedules all heavy work in Burst-compiled jobs (IJobChunk, IJobParallelForDefer, IJob) to run on worker threads and writes results via an EntityCommandBuffer provided by the ToolReadyBarrier.

This system is highly performance-sensitive and tightly integrated with the game's Net, Terrain and Water systems. Modders should be careful when changing prefab geometry, placement flags or creating custom net types because CourseSplitSystem assumes and relies on prefab data (NetGeometryData, NetData, Composition & placeable flags) and owner/connected-edge relationships.


Fields

  • private ToolSystem m_ToolSystem
    This holds a reference to the ToolSystem (used to query editor state and to obtain the ToolReadyBarrier for safe command buffer submission). It's set during OnCreate by resolving the system from the World.

  • private ToolReadyBarrier m_ToolReadyBarrier
    Used to create an EntityCommandBuffer.ParallelWriter so jobs can safely create/destroy/set components on entities once the jobs complete. The barrier integrates the job's producer handle with the main frame.

  • private Game.Net.SearchSystem m_SearchSystem
    Reference to the net search system used to obtain the readonly quad-tree (search tree) for overlap queries. The search tree is read concurrently by burst jobs.

  • private TerrainSystem m_TerrainSystem
    Reference used to get terrain height data for CPU sampling in jobs.

  • private WaterSystem m_WaterSystem
    Reference used to get water surface data for water depth/shoreline logic.

  • private CityConfigurationSystem m_CityConfigurationSystem
    Used for city configuration checks (e.g., left-hand traffic) affecting auxiliary net inversion.

  • private EntityQuery m_CourseQuery
    EntityQuery used to gather CreationDefinition + NetCourse + Updated entities that must be processed by this system.

  • private TypeHandle __TypeHandle
    Struct that caches various EntityTypeHandle / ComponentTypeHandle / ComponentLookup / BufferLookup values used when creating job data. It's assigned in OnCreateForCompiler to avoid repeated lookups per-update.

Properties

  • None (this system exposes no public properties).
    Internally most data is private and accessed inside jobs via component lookups and buffers. If you need to influence behavior from other systems, adjust CreationDefinition/OwnerDefinition/NetCourse components or provide data via systems this system reads (e.g., NetGeometryData, PlaceableNetData).

Constructors

  • public CourseSplitSystem()
    Constructs the system instance. Actual setup is done in OnCreate. The no-argument constructor is preserved for ECS initialization and for the build-time reflection/compilation infrastructure.

Methods

  • protected override void OnCreate() : System.Void
    Initializes references to required world systems (ToolSystem, ToolReadyBarrier, SearchSystem, TerrainSystem, WaterSystem, CityConfigurationSystem). Builds the EntityQuery (m_CourseQuery) for CreationDefinition + NetCourse + Updated and calls RequireForUpdate to ensure the system runs only when needed.

  • protected override void OnUpdate() : System.Void
    Main update method. It:

  • Allocates transient Native containers (NativeHashMap, NativeList, NativeQueue, NativeParallelQueue).
  • Fills a Course list by iterating matching entities (CheckCoursesJob).
  • Uses the SearchSystem quad-tree to find overlapping existing edges (FindOverlapsJob) and collects them to a queue/list.
  • Processes overlaps to generate intersection candidates using geometry tests in CheckCourseIntersectionsJob (Burst-compiled, parallel).
  • Reads the intersection results and executes per-course results job (CheckCourseIntersectionResultsJob) which performs course splitting, elevation computation, optional snapping to grid, merging auxiliary nets, fixed element splitting and ultimately emits entity modifications/creations using the command buffer.
  • Schedules and chains job dependencies correctly, registers readers with systems (search/water/terrain) and hands the final dependency to the ToolReadyBarrier.

This method is the single coordination point that issues a set of Burst jobs which contain most of the logic.

  • protected override void OnCreateForCompiler() : System.Void
    Assigns cached type handles (__TypeHandle.__AssignHandles) used by the jobs. This is used to prepare the system for code generation and job creation; it ensures component lookups are fast and consistent.

  • private static CreationDefinition GetAuxDefinition(CreationDefinition creationDefinition, AuxiliaryNet auxiliaryNet) : CreationDefinition
    Helper that returns a CreationDefinition for an auxiliary net based on the base creation definition. It preserves flags and random seed but replaces the prefab with the auxiliary prefab.

  • private static bool GetAuxCourse(ref NetCourse courseData, AuxiliaryNet auxiliaryNet, bool invert) : bool
    Creates a derived NetCourse for an auxiliary net by offsetting/shortening/vertical-shifting the curve as specified by the AuxiliaryNet entry. If invert is true, it swaps start/end and rotates node rotations appropriately. Returns whether the auxiliary course is valid (e.g., not too short or conflicting with fixed endpoints).

  • Many internal/burst jobs and private helpers (high level overview):

  • CheckCoursesJob (IJobChunk): collects CreationDefinition/NetCourse entities into a native list and notes deletion state of existing entities.
  • FindOverlapsJob (IJobParallelForDefer): queries the net search quad-tree to find possible overlapping edges for each course (using an OverlapIterator). It accounts for auxiliary net width when searching.
  • DequeueOverlapsJob (IJob): converts overlap queue to list for parallel processing.
  • CheckCourseIntersectionsJob (IJobParallelForDefer): the core geometric intersection work. For each overlap it:
    • tests node/edge geometry intersections,
    • computes priority/position/height ranges,
    • merges nearby intersections (Merge, Add),
    • computes whether intersections are nodes or edge splits, and stores IntersectPos results into a parallel queue. This job contains many low-level geometry functions: QuadIntersect, CircleIntersect, CheckNodeGeometry, CheckEdgeSegment, GetHeightRange, etc.
  • CourseHeightData (helper struct): samples terrain/water or estimates heights along a course, stores sampled items, applies limit ranges and can sample back heights for start/end/mid positions. Contains elevation straightening and ApplyLimitRange logic used to ensure placed pieces meet slope and clearance constraints.
  • CheckCourseIntersectionResultsJob (IJobParallelForDefer): reads intersection results per-course and:

    • merges intersections, merges auxiliary intersections,
    • computes final candidate split positions,
    • snaps to grid if required,
    • determines optional positions and constraint ranges,
    • splits courses into buildable pieces (including fixed net elements and auxiliary nets),
    • computes final elevation via CalculateElevation and LimitElevation,
    • calls AddCourse / TryAddCourse to create/modify entities via the command buffer. This is the highest-level job that writes the final changes.
  • Other notable private helpers used inside the jobs:

  • MergePositions, MergeAuxPositions: handle merging multiple intersections along a course and combining auxiliary net intersections.
  • SnapCoursePositions: snap intersection positions to cell grid for cell-snapped meshes.
  • CheckHeightRange: create optional elevated supports and remove unnecessary splits.
  • GetElevationRanges / CalculateElevation / LimitElevation: handle elevation detection relative to terrain/water and placeable constraints.
  • TryAddCourse / TryAddCoursePhase2 / AddCourse: create or modify entities for the final NetCourse pieces, handle fixed element splitting and original-edge replacement detection (FindOriginalEdge / CanReplace).
  • IgnoreOverlappingEdge / MatchingOwner: helper checks for when an overlapping edge should be ignored or when an original edge matches owner data.

Most of the complex logic lives inside these private methods and nested job structs. They are highly interdependent and tuned for performance using Burst and Native containers.

Usage Example

[Preserve]
protected override void OnCreate()
{
    base.OnCreate();
    // Typical initialization mirrors what the game system does:
    m_ToolSystem = base.World.GetOrCreateSystemManaged<ToolSystem>();
    m_ToolReadyBarrier = base.World.GetOrCreateSystemManaged<ToolReadyBarrier>();
    m_SearchSystem = base.World.GetOrCreateSystemManaged<Game.Net.SearchSystem>();
    m_TerrainSystem = base.World.GetOrCreateSystemManaged<TerrainSystem>();
    m_WaterSystem = base.World.GetOrCreateSystemManaged<WaterSystem>();
    m_CityConfigurationSystem = base.World.GetOrCreateSystemManaged<CityConfigurationSystem>();
    m_CourseQuery = GetEntityQuery(ComponentType.ReadOnly<CreationDefinition>(), ComponentType.ReadOnly<NetCourse>(), ComponentType.ReadOnly<Updated>());
    RequireForUpdate(m_CourseQuery);
}

Notes for modders: - This system assumes the shape and semantics of many data components (NetGeometryData, NetData, Composition flags, PlaceableNetData, FixedNetElement, AuxiliaryNet etc.). Changes to those prefabs or composition flags can change course splitting behavior. - The system is multi-threaded/Burst compiled and uses native containers extensively. When writing your own systems that interact with CreationDefinition/NetCourse entities, ensure you follow the same patterns (Updated tag, entity ownership expectations). - If you need to influence course splitting behavior, consider: - modifying NetGeometryData.Flags / PlaceableNetData to influence snapping/elevation/shoreline rules, - adding or adjusting AuxiliaryNet buffers on prefabs, - adjusting CreationDefinition flags (e.g., SubElevation, Invert). - Debugging can be difficult due to asynchronous job execution. Use the game's debug tools or add dedicated editor-only logging and make sure to account for job safety and main-thread restrictions.