Skip to content

Game.Zones.CellCheckHelpers

Assembly: Assembly-CSharp (in-game)
Namespace: Game.Zones

Type: public static class

Base: System.Object

Summary:
CellCheckHelpers is a collection of helper types and Burst-compiled jobs used by the zoning/block update system. It provides small data containers and several Unity Jobs that: - Query a spatial NativeQuadTree for blocks that intersect update bounds (single- and double-iteration variants). - Collect and deduplicate block Entities from multiple producer queues. - Find overlapping blocks (including priority/order handling and neighbor checks). - Group overlapping blocks into contiguous overlap groups. - Apply final per-block updates (visibility/state changes) to cells of a block.

The contained nested structs and jobs are designed to run efficiently with Unity's ECS/Jobs/Burst stack and use NativeQuadTree, NativeQueue, NativeList, and Component/Buffer lookups. This file is typically used during zone/cell validation and block updating passes in Cities: Skylines 2 modding (e.g., when blocks are rebuilt, moved, or when the valid area changes).

Fields

  • public struct SortedEntity { public Entity m_Entity; }
    Represents a block Entity used for sorting/deduplication. m_Entity is the Entity reference used to compare and sort blocks by index.

  • public struct BlockOverlap { public int m_Group; public uint m_Priority; public Entity m_Block; public Entity m_Other; public Entity m_Left; public Entity m_Right; }
    Describes an overlap relationship between two blocks (or a single block with no other). Contains grouping id (m_Group), build priority/order (m_Priority), the block Entity (m_Block), optionally the other overlapping block (m_Other) and optional left/right neighbor hints (m_Left/m_Right).

  • public struct OverlapGroup { public int m_StartIndex; public int m_EndIndex; }
    Describes a contiguous range inside the BlockOverlap list that belongs to the same overlap group.

  • FindUpdatedBlocksSingleIterationJob public fields:

  • [ReadOnly] public NativeArray<Bounds2> m_Bounds;
    The array of bounds to query (one per work index).
  • [ReadOnly] public NativeQuadTree<Entity, Bounds2> m_SearchTree;
    QuadTree containing block Entities and their Bounds2.
  • public NativeQueue<Entity>.ParallelWriter m_ResultQueue;
    Parallel queue to enqueue found block Entities.

  • FindUpdatedBlocksDoubleIterationJob public fields:

  • [ReadOnly] public NativeArray<Bounds2> m_Bounds;
  • [ReadOnly] public NativeQuadTree<Entity, Bounds2> m_SearchTree;
  • public NativeQueue<Entity>.ParallelWriter m_ResultQueue; Same purpose as the single-iteration job, but performs a first pass to compute an expanded result bound and then a second pass to collect entities.

  • CollectBlocksJob public fields:

  • public NativeQueue<Entity> m_Queue1; m_Queue2; m_Queue3; m_Queue4;
    Up to four producer queues of Entities to collect from.
  • public NativeList<SortedEntity> m_ResultList;
    Result list where deduplicated and sorted SortedEntity items are appended.

  • FindOverlappingBlocksJob public fields:

  • [ReadOnly] public NativeArray<SortedEntity> m_Blocks;
    Array of blocks to check for overlaps (input).
  • [ReadOnly] public NativeQuadTree<Entity, Bounds2> m_SearchTree;
    QuadTree to search for potential overlaps.
  • [ReadOnly] public ComponentLookup<Block> m_BlockData;
  • [ReadOnly] public ComponentLookup<ValidArea> m_ValidAreaData;
  • [ReadOnly] public ComponentLookup<BuildOrder> m_BuildOrderData;
  • public NativeQueue<BlockOverlap>.ParallelWriter m_ResultQueue;
    Queue where discovered BlockOverlap entries are written.

  • GroupOverlappingBlocksJob public fields:

  • [ReadOnly] public NativeArray<SortedEntity> m_Blocks;
    Blocks used to initialize internal maps (size hint).
  • public NativeQueue<BlockOverlap> m_OverlapQueue;
    Input queue with BlockOverlap items to group.
  • public NativeList<BlockOverlap> m_BlockOverlaps;
    Output list with BlockOverlap entries annotated with group ids.
  • public NativeList<OverlapGroup> m_OverlapGroups;
    Output list of OverlapGroup ranges describing contiguous groups inside m_BlockOverlaps.

  • UpdateBlocksJob public fields:

  • [ReadOnly] public NativeArray<SortedEntity> m_Blocks;
  • [ReadOnly] public ComponentLookup<Block> m_BlockData;
  • [NativeDisableParallelForRestriction] public BufferLookup<Cell> m_Cells;
    Writes final visibility / state changes to each block's cells.

Properties

  • No public properties are declared on the static CellCheckHelpers type itself. The nested types rely on public fields (ECS-style job parameter pattern).

Constructors

  • No explicit constructors defined on the static class. All nested structs/jobs use implicit default constructors and are populated field-by-field prior to scheduling.

Methods

  • public int SortedEntity.CompareTo(SortedEntity other)
    Compares two SortedEntity instances by their Entity.Index. Used to sort and deduplicate blocks.

  • public int BlockOverlap.CompareTo(BlockOverlap other)
    Comparison for ordering BlockOverlap entries first by group id, then by priority, then by block Entity index. Used when sorting overlaps to group and order items deterministically.

  • Iterator/FirstIterator/SecondIterator (private nested iterators in the jobs) methods:

  • bool Intersect(Bounds2 bounds)
    QuadTree iterator filter method used to early-exit nodes that do not intersect the query bounds. Typically implemented via MathUtils.Intersect.
  • void Iterate(Bounds2 bounds, Entity blockEntity)
    Called for each blockEntity whose node intersects. Each iterator implements logic to either enqueue the block Entity, accumulate a result bounds, or evaluate overlap conditions (in the case of FindOverlappingBlocksJob's iterator).

  • public void FindUpdatedBlocksSingleIterationJob.Execute(int index)
    Job entry point — constructs an Iterator with the given bounds and uses the NativeQuadTree.Iterate API to enqueue each intersecting block Entity into m_ResultQueue.

  • public void FindUpdatedBlocksDoubleIterationJob.Execute(int index)
    Two-pass job: first pass accumulates a tight result bounds by iterating candidates; second pass iterates using the accumulated bounds to enqueue matching block Entities. Useful when bounds expansion reduces search overhead.

  • public void CollectBlocksJob.Execute()
    Dequeues Entities from up to four input queues, wraps them into SortedEntity, adds to m_ResultList, then sorts and removes duplicates. Helper methods:

  • private void ProcessQueue(NativeQueue<Entity> queue)
    Drains a queue and adds each item to m_ResultList.
  • private void RemoveDuplicates()
    Sorts m_ResultList by Entity index and removes repeated entries (keeps one copy per Entity).

  • public void FindOverlappingBlocksJob.Execute(int index)
    For each block in m_Blocks:

  • Reads Block, ValidArea and BuildOrder components.
  • If the valid area has positive size, builds search bounds and a corner Quad2 and iterates the search tree.
  • For each candidate, checks geometry overlap (with small epsilon), neighbor rules, and ordering constraints.
  • Enqueues BlockOverlap entries for detected cases. If no overlaps found for a block, enqueues a BlockOverlap with m_Other == Entity.Null so the single block forms its own group.

  • public void GroupOverlappingBlocksJob.Execute()
    Consumes all BlockOverlap items from m_OverlapQueue and builds disjoint-set style grouping using a NativeParallelHashMap (Entity -> group id) and an auxiliary NativeList groups for union/merge bookkeeping. It:

  • Creates/merges groups when overlaps connect blocks.
  • Writes every processed BlockOverlap into m_BlockOverlaps annotated with the group id.
  • After all items consumed, normalizes group ids, sorts the m_BlockOverlaps list, and produces m_OverlapGroups that point to contiguous ranges in m_BlockOverlaps for each group.

Helper methods: - private int CreateGroup(NativeList<int> groups) — creates a new group id. - private int MergeGroups(NativeList<int> groups, int group1, int group2) — performs a simple union by assigning the minimum id to both entries (path compression-like step deferred).

  • public void UpdateBlocksJob.Execute(int index)
    Reads Block and its DynamicBuffer then calls SetVisible to update each cell's flags.

  • private void SetVisible(Block blockData, DynamicBuffer<Cell> cells)
    Iterates the block's grid (m_Size.x by m_Size.y), and for each cell:

    • If cell flags indicate Blocked or Redundant, ensures Shared and Visible bits are cleared.
    • Otherwise sets Visible.
    • Always clears the Updating flag.
    • Writes the updated Cell back into the buffer.

Notes on flags: this method uses bitwise checks/update against CellFlags (CellFlags.Blocked, CellFlags.Redundant, CellFlags.Shared, CellFlags.Visible, CellFlags.Updating). The exact enum values live elsewhere; ensure you match the game's definition when writing compatible code.

Usage Example

Example sequence showing how these pieces are typically used together. This is a schematic example; adapt job scheduling, memory allocators, and dependency handling to your system and ensure proper allocation/disposal of Native containers.

// Setup input bounds and the search tree (filled elsewhere)
NativeArray<Bounds2> boundsArray = ...; // Allocator.TempJob or persistent
NativeQuadTree<Entity, Bounds2> searchTree = ...;

// Prepare a result queue and schedule a search job (single iteration)
NativeQueue<Entity> resultQueue = new NativeQueue<Entity>(Allocator.TempJob);
var findJob = new CellCheckHelpers.FindUpdatedBlocksSingleIterationJob {
    m_Bounds = boundsArray,
    m_SearchTree = searchTree,
    m_ResultQueue = resultQueue.AsParallelWriter()
};
JobHandle findHandle = findJob.Schedule(boundsArray.Length, 64, default);

// Collect results from possibly multiple producer queues into a list
NativeQueue<Entity> q1 = resultQueue; // in real code you may have several queues
NativeList<CellCheckHelpers.SortedEntity> resultList = new NativeList<CellCheckHelpers.SortedEntity>(Allocator.TempJob);
var collectJob = new CellCheckHelpers.CollectBlocksJob {
    m_Queue1 = q1,
    m_Queue2 = new NativeQueue<Entity>(Allocator.TempJob),
    m_Queue3 = new NativeQueue<Entity>(Allocator.TempJob),
    m_Queue4 = new NativeQueue<Entity>(Allocator.TempJob),
    m_ResultList = resultList
};
JobHandle collectHandle = collectJob.Schedule(findHandle);

// After collecting, find overlaps (requires component lookups and the search tree)
ComponentLookup<Block> blockLookup = ...;
ComponentLookup<ValidArea> validAreaLookup = ...;
ComponentLookup<BuildOrder> buildOrderLookup = ...;
NativeQueue<CellCheckHelpers.BlockOverlap> overlapQueue = new NativeQueue<CellCheckHelpers.BlockOverlap>(Allocator.TempJob);

var overlapJob = new CellCheckHelpers.FindOverlappingBlocksJob {
    m_Blocks = resultList.AsDeferredJobArray(), // or create a NativeArray copy
    m_SearchTree = searchTree,
    m_BlockData = blockLookup,
    m_ValidAreaData = validAreaLookup,
    m_BuildOrderData = buildOrderLookup,
    m_ResultQueue = overlapQueue.AsParallelWriter()
};
JobHandle overlapHandle = overlapJob.Schedule(resultList.Length, 64, collectHandle);

// Group overlaps and then update blocks (grouping and updating are non-parallel or schedule accordingly)
NativeList<CellCheckHelpers.BlockOverlap> blockOverlaps = new NativeList<CellCheckHelpers.BlockOverlap>(Allocator.TempJob);
NativeList<CellCheckHelpers.OverlapGroup> overlapGroups = new NativeList<CellCheckHelpers.OverlapGroup>(Allocator.TempJob);

var groupJob = new CellCheckHelpers.GroupOverlappingBlocksJob {
    m_Blocks = resultList.AsDeferredJobArray(),
    m_OverlapQueue = overlapQueue,
    m_BlockOverlaps = blockOverlaps,
    m_OverlapGroups = overlapGroups
};
JobHandle groupHandle = groupJob.Schedule(overlapHandle);

// Finally update blocks' cells visibility (requires BufferLookup<Cell>)
BufferLookup<Cell> cellsLookup = ...;
var updateJob = new CellCheckHelpers.UpdateBlocksJob {
    m_Blocks = resultList.AsDeferredJobArray(),
    m_BlockData = blockLookup,
    m_Cells = cellsLookup
};
JobHandle updateHandle = updateJob.Schedule(resultList.Length, 64, groupHandle);

// Complete and dispose
updateHandle.Complete();
boundsArray.Dispose();
resultQueue.Dispose();
resultList.Dispose();
blockOverlaps.Dispose();
overlapGroups.Dispose();
// ... dispose other native containers used above

Notes: - All jobs in this file are Burst-compatible and use Unity.Collections patterns (NativeQueue, NativeList, NativeQuadTree). Make sure you use the correct Allocator and keep life-times and dependencies safe. - This file expects ECS-style ComponentLookup/BufferLookup usage and may require conversion to the latest Entities API depending on your modding environment and Entities package version. Adjust scheduling and query sizes to match your architecture. - When adapting examples, ensure proper parallel writer usage (AsParallelWriter) and matching job scheduling APIs (IJobParallelForDefer requires a deferred array or valid length source).