Skip to content

Game.Prefabs.NetCompositionHelpers

Assembly: Assembly-CSharp
Namespace: Game.Prefabs

Type: public static class

Base: (static) System.Object

Summary:
NetCompositionHelpers provides a collection of static helper functions used when composing net (road/rail/etc.) prefabs at runtime. The class contains utilities to: - convert NetPieceRequirements into CompositionFlags/NetSectionFlags, - test whether geometry sections, sub-sections, pieces, objects, lanes and edges match a given composition, - collect and filter composition pieces for a concrete composed geometry, - compute composition layout data (widths, offsets, heights, LODs, sync vertex offsets), - compute placeable item costs, - build lane and carriageway information from piece lane data, - determine roundabout size and elevation-based composition flags.

These helpers are intended for use by the net prefab building systems in Cities: Skylines 2. They operate heavily on Unity ECS types (NativeArray/NativeList/DynamicBuffer/ComponentLookup/BufferLookup) and math types (Unity.Mathematics) and assume data is in the ECS format used by the game. Many methods are performance-sensitive and use Unity's Native containers; callers must respect allocation and lifetime semantics.


Fields

  • None
    The static helper class contains no persistent instance fields. It defines some private nested helper structs (TempLaneData, TempLaneGroup) used internally in AddCompositionLanes.

Properties

  • None
    There are no properties on this static helper class.

Constructors

  • None
    Static classes in C# have no public constructors. No explicit static constructor is present.

Methods

  • public static void GetRequirementFlags(NetPieceRequirements[] requirements, out CompositionFlags compositionFlags, out NetSectionFlags sectionFlags)
    Converts an array of NetPieceRequirements into aggregate CompositionFlags and NetSectionFlags. Null-safe; iterates and delegates to the single-requirement overload. Useful when you have multiple requirements to produce composition/section flags before filtering geometry.

  • public static void GetRequirementFlags(NetPieceRequirements requirement, ref CompositionFlags compositionFlags, ref NetSectionFlags sectionFlags)
    Maps a single NetPieceRequirements enum value into the provided CompositionFlags / NetSectionFlags references. This method mutates the provided flags according to the requirement.

  • public static CompositionFlags InvertCompositionFlags(CompositionFlags flags)
    Returns an inverted view of the given CompositionFlags suitable for handling mirrored/inverted geometry. Note: this returns a new CompositionFlags constructed from the inputs (does not flip left/right internally beyond the constructor).

  • public static NetSectionFlags InvertSectionFlags(NetSectionFlags flags)
    Returns the section flags for inverted sections. In this implementation this method returns the input unchanged (implemented as identity).

  • public static bool TestSectionFlags(NetGeometrySection section, CompositionFlags compositionFlags)
    Checks whether the provided section satisfies the composition flags. Uses the section's m_CompositionAll/m_CompositionNone/m_CompositionAny criteria.

  • public static bool TestSubSectionFlags(NetSubSection subSection, CompositionFlags compositionFlags, NetSectionFlags sectionFlags)
    Tests whether a subSection should be used given compositionFlags and sectionFlags. Observes the Median bit (removes MedianBreak if no median), tests composition all/none/any as well as section all/none/any masks.

  • public static bool TestPieceFlags(NetSectionPiece piece, CompositionFlags compositionFlags, NetSectionFlags sectionFlags)
    Determines whether a given section piece matches composition/section flags using the piece masks (m_CompositionAll/Any/None and m_SectionAll/Any/None). Also handles MedianBreak removal like other testers.

  • public static bool TestPieceFlags2(NetSectionPiece piece, CompositionFlags compositionFlags, NetSectionFlags sectionFlags)
    A specialized piece test that handles roundabout and transition edge-cases. It attempts to transform composition flags (removing Elevated/Tunnel or mapping LowTransition to Raised/Lowered) then falls back to TestPieceFlags with the modified flags. Returns true only if the modified flags match. Otherwise returns false.

  • public static bool TestObjectFlags(NetPieceObject _object, CompositionFlags compositionFlags, NetSectionFlags sectionFlags)
    Tests a placed object against composition and section flags (similar semantics to TestPieceFlags). Useful when filtering objects tied to a net piece.

  • public static bool TestLaneFlags(AuxiliaryNetLane lane, CompositionFlags compositionFlags)
    Checks if an auxiliary lane matches composition flags. Uses lane.m_CompositionAll/Any/None.

  • public static bool TestEdgeFlags(NetGeometryEdgeState edgeState, CompositionFlags compositionFlags)
    Tests an edge-state's composition masks against compositionFlags.

  • public static bool TestEdgeFlags(NetGeometryNodeState nodeState, CompositionFlags compositionFlags)
    Same as above but for NetGeometryNodeState.

  • public static bool TestEdgeFlags(ElectricityConnectionData electricityConnectionData, CompositionFlags compositionFlags)
    Tests electricity connection data composition masks against compositionFlags.

  • public static bool TestEdgeMatch(NetGeometryNodeState nodeState, bool2 match)
    Evaluates a boolean match pair according to the nodeState.m_MatchType (Both, Any, Exclusive). Returns whether the pair satisfies the required matching rule.

  • public static void GetCompositionPieces(NativeList<NetCompositionPiece> resultBuffer, NativeArray<NetGeometrySection> geometrySections, CompositionFlags flags, BufferLookup<NetSubSection> subSectionData, BufferLookup<NetSectionPiece> sectionPieceData)
    Fills resultBuffer with NetCompositionPiece entries that pass the supplied flags from geometrySections. The method:

  • honors Flip/Invert in flags to mirror sections,
  • filters sections via TestSectionFlags,
  • finds a matching subsection,
  • iterates section pieces and filters them via TestPieceFlags/TestPieceFlags2,
  • sets piece offsets and section indices,
  • marks pieces hidden by section flags (NetPieceFlags). Important: resultBuffer is a NativeList; the caller is responsible for allocation and disposal.

  • public static void CalculateCompositionData(ref NetCompositionData compositionData, NativeArray<NetCompositionPiece> pieces, ComponentLookup<NetPieceData> netPieceData, ComponentLookup<NetLaneData> netLaneData, ComponentLookup<NetVertexMatchData> netVertexMatchData, BufferLookup<NetPieceLane> netPieceLanes)
    Top-level composition data calculation: computes piece offsets, sync vertex offsets and roundabout size. Delegates to the private helper methods below. Mutates compositionData.

  • public static void CalculateMinLod(ref NetCompositionData compositionData, NativeArray<NetCompositionPiece> pieces, ComponentLookup<MeshData> meshDatas)
    Computes m_MinLod for the composition based on mesh LOD biases of included pieces and the composition size. Uses RenderingUtils.CalculateLodLimit and RenderingUtils.GetRenderingSize.

  • private static void CalculateCompositionPieceOffsets(ref NetCompositionData compositionData, NativeArray pieces, ComponentLookup netPieceData)
    Internal: computes width, offsets, node/width offsets, height ranges, edge heights, surface height, and sets CompositionState flags (e.g., HasSurface, BlockUTurn, LowerToTerrain, etc.). This is a complex layout algorithm that iterates pieces grouped by section and aggregates dimensions and flags. It mutates the pieces array to set computed m_Offset and m_Size values.

  • private static void CalculateSyncVertexOffsets(ref NetCompositionData compositionData, NativeArray pieces, ComponentLookup netVertexMatchData)
    Internal: determines sync vertex offsets used for vertex matching along the net (m_SyncVertexOffsetsLeft/Right). Uses NetVertexMatchData when present; computes several clamping/interpolation steps to produce a robust range.

  • public static void CalculatePlaceableData(ref PlaceableNetComposition placeableData, NativeArray<NetCompositionPiece> pieces, ComponentLookup<PlaceableNetPieceData> placeableNetPieceData)
    Aggregates construction, elevation and upkeep costs from placeable pieces. Skips pieces that don't have PlaceableNetPieceData component.

  • public static void AddCompositionLanes<TNetCompositionPieceList>(Entity entity, ref NetCompositionData compositionData, TNetCompositionPieceList pieces, NativeList<NetCompositionLane> netLanes, DynamicBuffer<NetCompositionCarriageway> carriageways, ComponentLookup<NetLaneData> netLaneData, BufferLookup<NetPieceLane> netPieceLanes) where TNetCompositionPieceList : INativeList<NetCompositionPiece>
    Builds the concrete list of lanes (netLanes) and carriageways from piece-level lane buffers. This method:

  • groups lanes into TempLaneGroups to detect combined master/slave lanes,
  • accumulates carriageway bounds and flags,
  • sets NetCompositionLane entries with proper flags (Invert, Twoway, Master/Slave, ParkingLeft/Right, etc.),
  • updates compositionData.m_State with HasForward/Backward lanes, Multilane, Asymmetric and other CompositionState flags. Notes:
  • Uses temporary NativeLists (Allocator.Temp) for internal grouping—callers must ensure not to call from a job that requires different allocators.
  • Throws Exception("Too many lanes: {entity.Index}") if resulting lanes >= 256 (lane index limited to byte).
  • Correctly respects FlipLanes/Invert section flags and extra lane flags from NetPieceLane entries.

  • private static int FindClosestLane(NativeList lanes, int startIndex, float3 position, LaneFlags flags)
    Search helper used by AddCompositionLanes to find the nearest lane with specific flags (e.g., LaneFlags.Road) to pair parking lanes. Performs alternating search outward from startIndex.

  • public static void CalculateRoundaboutSize(ref NetCompositionData compositionData, NativeArray<NetCompositionPiece> pieces, ComponentLookup<NetLaneData> netLaneData, BufferLookup<NetPieceLane> netPieceLanes)
    Estimates a recommended roundabout size based on road lane placement in pieces. Sums and clamps widths of pieces that have road lanes. If no road pieces, falls back to half the composition width. Adds padding derived from max/average piece widths.

  • private static bool HasRoad(Entity piece, ComponentLookup netLaneData, BufferLookup netPieceLanes)
    Returns true if the provided piece contains at least one lane with LaneFlags.Road.

  • public static CompositionFlags GetElevationFlags(Elevation startElevation, Elevation middleElevation, Elevation endElevation, NetGeometryData prefabGeometryData)
    Determines CompositionFlags for a connection based on the three elevation samples and prefab's elevation limits and GeometryFlags. Sets Elevated/Tunnel or Left/Right Raised/Lowered flags depending on thresholds and prefab metadata (e.g., RaisedIsElevated, LoweredIsTunnel, Requirement for Elevated).

Notes and important considerations: - Many methods use NativeArray/NativeList/DynamicBuffer/ComponentLookup/BufferLookup. Respect Unity ECS thread-safety, lifetime and job constraints when calling them. - AddCompositionLanes uses Allocator.Temp native lists and disposes them before return; do not expect those temporary allocations to survive outside the method. - Some methods mutate inputs (for example, CalculateCompositionPieceOffsets writes sizes/offsets into the pieces array). Ensure you pass writable arrays where required. - TestPieceFlags2 can return false even when TestPieceFlags would pass; it attempts normalization for roundabout/transition edge cases before delegating. - AddCompositionLanes will throw if the net ends up with >= 256 lanes (an internal index limitation). Catch or prevent this by ensuring pieces produce fewer lanes.


Usage Example

// Example: gather flags for a set of requirements, select matching pieces and compute composition data.
//
// Note: this snippet assumes a context where ComponentLookup/BufferLookup and Native containers
// are available (e.g., inside a SystemBase/ECS system).

// 1) Convert requirements to flags
CompositionFlags compFlags;
NetSectionFlags sectionFlags;
NetPieceRequirements[] requirements = new NetPieceRequirements[] {
    NetPieceRequirements.Sidewalk,
    NetPieceRequirements.PublicTransportLane,
    NetPieceRequirements.WideSidewalk
};
NetCompositionHelpers.GetRequirementFlags(requirements, out compFlags, out sectionFlags);

// 2) Collect matching composition pieces from prefab geometry
NativeList<NetCompositionPiece> pieces = new NativeList<NetCompositionPiece>(Allocator.Temp);
// geometrySections and section/subsection buffers would come from the prefab's geometry data
NativeArray<NetGeometrySection> geometrySections = /* obtained elsewhere */;
BufferLookup<NetSubSection> subSectionData = /* obtained elsewhere */;
BufferLookup<NetSectionPiece> sectionPieceData = /* obtained elsewhere */;
NetCompositionHelpers.GetCompositionPieces(pieces, geometrySections, compFlags, subSectionData, sectionPieceData);

// 3) Compute composition layout data
NetCompositionData compData = default;
ComponentLookup<NetPieceData> netPieceData = /* obtained from world */;
ComponentLookup<NetLaneData> netLaneData = /* obtained from world */;
ComponentLookup<NetVertexMatchData> netVertexMatchData = /* obtained from world */;
BufferLookup<NetPieceLane> netPieceLanes = /* obtained from world */;
NetCompositionHelpers.CalculateCompositionData(ref compData, pieces.AsArray(), netPieceData, netLaneData, netVertexMatchData, netPieceLanes);

// 4) Optionally compute lanes/carriageways for runtime use
NativeList<NetCompositionLane> netLanes = new NativeList<NetCompositionLane>(Allocator.Temp);
DynamicBuffer<NetCompositionCarriageway> carriageways = /* a buffer on an entity */;
Entity entity = /* some net prefab entity */;
NetCompositionHelpers.AddCompositionLanes(entity, ref compData, pieces, netLanes, carriageways, netLaneData, netPieceLanes);

// Cleanup
netLanes.Dispose();
pieces.Dispose();

This example shows the typical flow: convert requirements → collect pieces → compute composition metrics → generate lanes. In real usage within the game's systems, these calls are integrated into prefab processing pipelines and must respect ECS world/state lifetimes and allocation choices.