Skip to content

Game.Serialization.DataMigration.PlaceholderCleanupSystem

Assembly:
{{ Likely part of the game runtime assembly that contains game systems (e.g. Assembly-CSharp / Game). Confirm actual assembly in your build. }}

Namespace: Game.Serialization.DataMigration

Type: PlaceholderCleanupSystem

Base: GameSystemBase

Summary:
PlaceholderCleanupSystem is a DOTS/ECS system used during data migration when loading older saves. It finds entities marked as Placeholder (specifically Placeholder + Building + Renter archetypes) and removes several components that are no longer desired on placeholder building entities in newer save versions. The system is gated by the loaded game's version: it runs only when the loaded game's version is older than Version.placeholderCleanup and there are matching entities. The actual cleanup work is performed in a Burst-compiled IJobChunk (PlaceholderCleanupJob) that removes a predefined ComponentTypeSet from each entity and also marks any notification icon entities referenced by IconElement buffers with a Deleted component via an EntityCommandBuffer. The job is scheduled as a parallel JobChunk and its JobHandle is registered with a DeserializationBarrier to ensure correct ordering with deserialization/command buffer usage.


Fields

  • private LoadGameSystem m_LoadGameSystem
    Holds a reference to the game's LoadGameSystem. This is used to inspect the load context/version (m_LoadGameSystem.context.version) to decide if the placeholder cleanup migration should run.

  • private DeserializationBarrier m_DeserializationBarrier
    Used to create an EntityCommandBuffer (AsParallelWriter) to enqueue structural changes (RemoveComponent / AddComponent Deleted) from within the scheduled job. The barrier is used to safely integrate producer jobs with the entity command buffering system.

  • private EntityQuery m_Query
    An EntityQuery configured in OnCreate to match entities with ComponentType.ReadOnly(), ComponentType.ReadOnly(), and ComponentType.ReadOnly(). Used to find placeholder building+tenant entities that need cleanup.

  • private ComponentTypeSet m_ComponentSet
    A ComponentTypeSet containing the list of components to remove from each matched entity. The set includes:

  • Renter (ReadWrite)
  • PropertyToBeOnMarket (ReadWrite)
  • PropertyOnMarket (ReadWrite)
  • ElectricityConsumer (ReadWrite)
  • WaterConsumer (ReadWrite)
  • GarbageProducer (ReadWrite)
  • TelecomConsumer (ReadWrite)

This is passed into the command buffer RemoveComponent call inside the job.

  • private TypeHandle __TypeHandle
    Holds cached type handles (EntityTypeHandle and BufferTypeHandle) used by the job. It is populated via __AssignHandles(ref SystemState) when the system is created for the compiler.

  • (Nested) private struct PlaceholderCleanupJob : IJobChunk
    Burst-compiled job that performs the cleanup. See Methods section for details.

  • (Nested) private struct TypeHandle
    Contains cached EntityTypeHandle and BufferTypeHandle and a method to assign them from a SystemState.

Properties

  • None declared on this system.

Constructors

  • public PlaceholderCleanupSystem()
    [Preserve] parameterless constructor. Standard ECS system construction; initializes default instance. No custom initialization logic here beyond what OnCreate performs.

Methods

  • protected override void OnCreate()
    Initializes internal references and queries:
  • Retrieves m_LoadGameSystem via World.GetOrCreateSystemManaged().
  • Retrieves m_DeserializationBarrier via World.GetOrCreateSystemManaged().
  • Builds m_Query to select entities with Placeholder, Building, and Renter (all ReadOnly).
  • Initializes m_ComponentSet with the list of components that should be removed from placeholder entities during migration.

The method is annotated with [Preserve] in the source, ensuring the method is kept by linkers.

  • protected override void OnUpdate()
    Main runtime method that decides whether to run the cleanup:
  • Checks game version via m_LoadGameSystem.context.version. If the loaded save version is older than Version.placeholderCleanup (i.e., the condition !(version >= Version.placeholderCleanup) is true) and the m_Query is not empty, the cleanup job is scheduled.
  • Prepares a PlaceholderCleanupJob instance, filling:
    • m_EntityType and m_IconElementType using InternalCompilerInterface.GetEntityTypeHandle / GetBufferTypeHandle with cached handles in __TypeHandle and base.CheckedStateRef.
    • m_ComponentSet from the field.
    • m_CommandBuffer from m_DeserializationBarrier.CreateCommandBuffer().AsParallelWriter().
  • Schedules the job using JobChunkExtensions.ScheduleParallel with the EntityQuery and base.Dependency.
  • Adds the returned JobHandle to m_DeserializationBarrier via AddJobHandleForProducer and updates base.Dependency with the scheduled job's handle so subsequent systems/jobs correctly chain dependencies.

This ensures the structural changes (component removals / Deleted additions) are executed safely and in order with deserialization barriers.

  • private void __AssignQueries(ref SystemState state)
    A helper used by compiler-generated OnCreateForCompiler to establish queries. In this source it creates and promptly disposes an EntityQueryBuilder(Allocator.Temp) — likely a placeholder used by compiler tooling; the real query is created in OnCreate.

  • protected override void OnCreateForCompiler()
    Compiler helper invoked to assign queries and type handles prior to normal OnCreate. Calls __AssignQueries(ref base.CheckedStateRef) and __TypeHandle.__AssignHandles(ref base.CheckedStateRef) to populate type handles used by generated job code.

  • (Nested) private struct TypeHandle.__AssignHandles(ref SystemState state)
    Assigns:

  • __Unity_Entities_Entity_TypeHandle = state.GetEntityTypeHandle();
  • __Game_Notifications_IconElement_RO_BufferTypeHandle = state.GetBufferTypeHandle(isReadOnly: true); This caches the EntityTypeHandle and BufferTypeHandle used by PlaceholderCleanupJob.

  • (Nested) private struct PlaceholderCleanupJob.Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
    Burst-compiled chunk job execution logic:

  • Obtains a NativeArray for the chunk via chunk.GetNativeArray(m_EntityType).
  • Gets a BufferAccessor via chunk.GetBufferAccessor(ref m_IconElementType).
  • For each entity in the chunk:
    • Calls m_CommandBuffer.RemoveComponent(unfilteredChunkIndex, entity, in m_ComponentSet) to remove all components listed in m_ComponentSet from the entity.
    • If the IconElement buffer exists for that element index (CollectionUtils.TryGet(bufferAccessor, i, out var value)), iterates all IconElement entries and for each adds a Deleted component to the icon entity via m_CommandBuffer.AddComponent(unfilteredChunkIndex, value[j].m_Icon, default(Deleted)).
  • This effectively removes the old components and marks any linked notification icons as Deleted so they will be cleaned up.

The job implements IJobChunk.Execute explicitly and forwards to the typed Execute method shown above.

Notes: - The job uses EntityCommandBuffer.ParallelWriter to safely enqueue structural changes from parallel execution. - It is annotated [BurstCompile] in the source for performance.

Usage Example

// The system is automatically created and run by the DOTS World. The OnUpdate logic in the system
// schedules a Burst IJobChunk when the loaded game's version is older than Version.placeholderCleanup.
// Example snippet (simplified) reproducing the scheduling behavior:

[Preserve]
protected override void OnUpdate()
{
    // If the loaded save is older than the migration version and there are placeholder entities,
    // schedule the cleanup job to remove legacy components and mark icons as Deleted.
    if (!(m_LoadGameSystem.context.version >= Version.placeholderCleanup) && !m_Query.IsEmptyIgnoreFilter)
    {
        var job = new PlaceholderCleanupJob
        {
            m_EntityType = InternalCompilerInterface.GetEntityTypeHandle(ref __TypeHandle.__Unity_Entities_Entity_TypeHandle, ref base.CheckedStateRef),
            m_IconElementType = InternalCompilerInterface.GetBufferTypeHandle(ref __TypeHandle.__Game_Notifications_IconElement_RO_BufferTypeHandle, ref base.CheckedStateRef),
            m_ComponentSet = m_ComponentSet,
            m_CommandBuffer = m_DeserializationBarrier.CreateCommandBuffer().AsParallelWriter()
        };

        JobHandle jobHandle = JobChunkExtensions.ScheduleParallel(job, m_Query, base.Dependency);
        m_DeserializationBarrier.AddJobHandleForProducer(jobHandle);
        base.Dependency = jobHandle;
    }
}

Notes and tips: - This system is migration-specific: only intended to run during loading older saves. Once the save version threshold (Version.placeholderCleanup) is reached, the system no longer schedules the job. - If you modify the set of components to remove, update m_ComponentSet in OnCreate accordingly. - Because the cleanup is performed with an EntityCommandBuffer.ParallelWriter, structural changes are deferred and executed safely by the barrier system — do not attempt structural modifications directly inside the job. - If you need to add unit tests or tooling for migration, ensure the LoadGameSystem.context.version and the EntityQuery state match representative scenarios to exercise the branch that schedules the job.