Skip to content

Game.Debug.ComponentDebugUtils

Assembly:
Namespace: Game.Debug

Type: static class

Base: System.Object

Summary:
Utility helpers for inspecting ECS component usage at runtime. Provides functions to enumerate components found in archetypes and chunks, collect counts (archetypes, chunks, entities, capacities) and filter results by a substring. Intended for debugging and profiling modding issues in Cities: Skylines 2 that involve the Unity DOTS/ECS world.


Fields

  • public class ComponentInfo A simple container type returned by GetCommonComponents. Holds summary statistics for a single component (managed Type and several counters). Fields inside ComponentInfo:

  • public Type m_Type
    The managed System.Type returned by ComponentType.GetManagedType() for this component. May be null for purely unmanaged component types.

  • public int m_ArchetypeCount
    Number of archetypes (within the filtered set) that include this component. Used for sorting the results.

  • public int m_EntityCount
    Sum of Entity counts across archetype chunks that include this component (populated when iterating all chunks).

  • public int m_ChunkCapacity
    Sum of chunk capacities across archetype chunks that include this component (populated when iterating all chunks).

  • public int m_ChunkCount
    Accumulated value representing chunk-related counts. Note: the implementation initializes this field to the archetype's ChunkCount the first time the component is seen, but for subsequent archetypes increments it by 1 (instead of adding that archetype's ChunkCount). This makes m_ChunkCount inconsistent and likely not equal to the total chunk count for the component—treat it with caution.

  • public bool m_Matching
    True if the component matched the provided string filter when the dictionary entry was first created.

Properties

  • None.
    This is a static utility class and exposes no properties.

Constructors

  • None (static type).
    No instance constructors; the class is static.

Methods

  • public static List<ComponentInfo> GetCommonComponents(EntityManager entityManager, string filter, bool unusedOnly, out int archetypeCount, out int filteredArchetypeCount, out int chunkCount, out int chunkCapacity, out int entityCount)
    Scans all EntityArchetypes and ArchetypeChunks in the provided EntityManager and returns up to 100 ComponentInfo entries sorted by m_ArchetypeCount (descending).

Behavior/details: - Retrieves all archetypes via entityManager.GetAllArchetypes(...) and sets archetypeCount to the total number of archetypes found. - If filter is non-empty, an archetype is included only if at least one of its component types' managed type FullName contains the filter substring (case-insensitive). filteredArchetypeCount is the number of archetypes that passed both the filter and unusedOnly test. - If unusedOnly is true, archetypes with ChunkCount != 0 are excluded (so only "unused" archetypes with ChunkCount == 0 are considered). - While iterating archetypes the code builds a dictionary keyed by ComponentType. For each component encountered it accumulates: - m_ArchetypeCount (incremented per archetype) - m_ChunkCount (initialized to archetype.ChunkCount for the first archetype seen; incremented by 1 for subsequent archetypes — see caveat above) - m_Matching (set based on the filter at creation time) - m_Type (ComponentType.GetManagedType()) - Then it iterates all chunks returned by entityManager.GetAllChunks(...) to accumulate: - entityCount (total entities across all chunks) - chunkCount (number of chunks visited) - chunkCapacity (sum of chunk.Capacity across all chunks) - For each chunk, for each component in that chunk's archetype, it increments the corresponding ComponentInfo.m_EntityCount by chunk.Count and m_ChunkCapacity by chunk.Capacity. - Returns a managed List containing up to 100 entries ordered by m_ArchetypeCount descending.

Performance / safety notes: - Allocates NativeList and NativeArray temporary containers (Allocator.Temp) and disposes them in the method. Still, calling this frequently can allocate and be expensive. - Uses EntityManager API and should be called from a safe context (usually main thread / where EntityManager access is valid). - The code calls ComponentType.GetManagedType() and then uses .FullName in the filter check. If GetManagedType() returns null for a given component type (possible for purely unmanaged component types), IsMatching will throw a NullReferenceException. The returned ComponentInfo.m_Type may therefore be null for some components — handle that case in calling code. - There is an implementation inconsistency with how m_ChunkCount is accumulated (initialized with archetype.ChunkCount on first hit, then incremented by 1 for subsequent archetypes). Treat m_ChunkCount as unreliable or inspect/patch the method if you need accurate chunk counts per component.

  • private static bool IsMatching(ComponentType type, string filter)
    Returns true if type.GetManagedType().FullName contains the filter text (case-insensitive). Assumes filter is non-empty when called. As noted above, this will throw if GetManagedType() returns null.

Usage Example

// Example: print top components related to "render" substring (case-insensitive),
// including only archetypes that currently have no chunks (unusedOnly = true).
EntityManager em = World.DefaultGameObjectInjectionWorld.EntityManager;

int totalArchetypes, filteredArchetypes, totalChunks, totalChunkCapacity, totalEntities;
List<ComponentDebugUtils.ComponentInfo> components = ComponentDebugUtils.GetCommonComponents(
    em,
    "render",    // filter substring; pass null or "" for no filter
    false,       // unusedOnly: false = include archetypes that have chunks
    out totalArchetypes,
    out filteredArchetypes,
    out totalChunks,
    out totalChunkCapacity,
    out totalEntities
);

Debug.Log($"Archetypes: {totalArchetypes}, Filtered: {filteredArchetypes}, Chunks: {totalChunks}, ChunkCapacity: {totalChunkCapacity}, Entities: {totalEntities}");

foreach (var ci in components)
{
    string typeName = ci.m_Type != null ? ci.m_Type.FullName : "<unmanaged or unknown>";
    Debug.Log($"{typeName} -- Archetypes: {ci.m_ArchetypeCount}, Chunks(approx): {ci.m_ChunkCount}, Entities: {ci.m_EntityCount}, ChunkCapacitySum: {ci.m_ChunkCapacity}, Matching: {ci.m_Matching}");
}

Additional recommendations: - If you rely on the filter matching unmanaged component types, consider updating IsMatching to handle null GetManagedType() safely (e.g., check type.IsSystemStateComponent / type.IsBuffer / or use type.ToString()). - If accurate chunk counts per component are required, review and fix the m_ChunkCount accumulation logic in GetCommonComponents.