Skip to content

Game.StuckMovingObjectSystem

Assembly:
Namespace: Game.Simulation

Type: class

Base: GameSystemBase

Summary:
System that detects moving objects (vehicles, creatures, ride needers, group members, animals) that are stuck behind slow or blocking entities. It runs as a parallel IJobChunk (StuckCheckJob) over entities that have a Blocker component and an UpdateFrame shared component, and marks path owners or animal lane components as stuck when a blocking chain prevents movement. The system: - Skips temporary or non-existent blockers and high-speed blockers (m_MaxSpeed >= 6). - Treats parked trains (and parked cars when not processing cars) as blocking. - For crossings on cars, sets a RequestSpace flag on the car's current lane. - Traverses blocker chains (following Controller indirections) up to 100 links to detect cycles or a blocker that is one of the involved entities. - Marks PathOwner.m_State with PathFlags.Stuck (or AnimalCurrentLane.m_Flags with CreatureLaneFlags.Stuck) when blocked.

Burst-compiled job for performance; uses shared component UpdateFrame to spread work across frames (frameIndex >> 2 % 16). The system updates every 4 simulation frames (GetUpdateInterval returns 4).


Fields

  • private SimulationSystem m_SimulationSystem
    Holds a reference to the SimulationSystem used to read the current simulation frame index (used to compute which UpdateFrame filter to use).

  • private EntityQuery m_ObjectQuery
    Query matching entities that have Blocker and UpdateFrame shared components, and exclude Deleted and Temp. Used to schedule the StuckCheckJob over the relevant objects.

  • private TypeHandle __TypeHandle
    Internal struct that stores Entity/Component type handles and ComponentLookup handles used by the job. Populated at OnCreateForCompiler and updated before scheduling the job.

  • private struct StuckCheckJob (nested)
    Burst-compiled job implementing IJobChunk that performs the per-chunk stuck detection. Contains component type handles and lookups used inside Execute.

  • private struct TypeHandle (nested)
    Helper struct that bundles all EntityTypeHandle, ComponentTypeHandle and ComponentLookup instances and provides __AssignHandles to initialize them from a SystemState.

Properties

  • (none public)

Constructors

  • public StuckMovingObjectSystem()
    Default constructor. The system initialization logic happens in OnCreate / OnCreateForCompiler.

Methods

  • public override int GetUpdateInterval(SystemUpdatePhase phase)
    Returns 4. The system will be scheduled with an update interval of 4 simulation frames (this is likely used by the game framework to throttle system updates).

  • [Preserve] protected override void OnCreate()
    Initializes the system:

  • Resolves the SimulationSystem from the World (m_SimulationSystem).
  • Builds an EntityQuery matching Blocker + UpdateFrame and excluding Deleted/Temp.
  • Calls RequireForUpdate on the query so the system runs only if matching entities exist.

  • [Preserve] protected override void OnUpdate()
    Main scheduling method:

  • Chooses a shared UpdateFrame index based on the simulation frame index: (m_SimulationSystem.frameIndex >> 2) % 16. This staggers processing over multiple frames.
  • Resets and sets a shared component filter on m_ObjectQuery with UpdateFrame(index).
  • Constructs a StuckCheckJob populated with all required EntityTypeHandle, ComponentTypeHandle and ComponentLookup references using InternalCompilerInterface helpers and the stored __TypeHandle.
  • Schedules the job with JobChunkExtensions.ScheduleParallel against m_ObjectQuery, attaching to base.Dependency.

The job iterates chunk entities and for each Blocker: - Ignores missing/temporary blockers. - If chunk represents cars and blocker is a crossing, sets CarCurrentLane.m_LaneFlags |= CarLaneFlags.RequestSpace on the controller entity (resolving Controller indirection). - Skips blockers with m_MaxSpeed >= 6. - Determines a related vehicle/target entity where appropriate (CurrentVehicle, RideNeeder → Dispatched handler, GroupMember → leader's CurrentVehicle, Target). - Uses IsBlocked(Entity, Blocker) or IsBlocked(Entity, Entity, Blocker) to inspect the blocker chain and decide if the subject is blocked. - If blocked, marks PathOwner.m_State with PathFlags.Stuck (if PathOwner component exists on the entity) else marks AnimalCurrentLane.m_Flags with CreatureLaneFlags.Stuck.

  • private void __AssignQueries(ref SystemState state)
    Called by OnCreateForCompiler; present for compiler support. Currently contains an empty EntityQueryBuilder allocation/dispose; the real query is set up in OnCreate.

  • protected override void OnCreateForCompiler()
    Compiler-time helper that calls __AssignQueries and initializes __TypeHandle by calling __AssignHandles on it.

  • private bool IsBlocked(Entity entity, Blocker blocker) (inside StuckCheckJob)
    Follows the chain of blockers starting from blocker.m_Blocker, resolving Controller indirections to controllers where applicable. Traverses up to 100 links. Returns true if:

  • traversal reaches the original entity (cycle or self-block), or
  • traversal reached 100 iterations (safety limit). Returns false if it reaches a null blocker or encounters temporary blocker, or a blocker with m_MaxSpeed >= 6.

  • private bool IsBlocked(Entity entity1, Entity entity2, Blocker blocker) (inside StuckCheckJob)
    Same as above but considers both entity1 and entity2 as stopping/blocking targets. Returns true if traversal reaches either entity or reaches 100 links; false for null/temporary/high-speed blockers.

  • void IJobChunk.Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) (inside StuckCheckJob)
    Explicit interface implementation that forwards to the typed Execute method.

Notes about component types referenced (game-specific): - Blocker: represents blocking relations between moving objects; contains m_Blocker (entity), m_Type, m_MaxSpeed. - Controller: used to resolve to the controlling entity (m_Controller) for certain objects. - ParkedCar / ParkedTrain: indicates parked vehicles — parked trains are always considered blockers. - CurrentVehicle / Dispatched: used to find the vehicle entity associated to a creature or ride request. - PathOwner: pathfinding state component with m_State flags (PathFlags.Pending, PathFlags.Stuck, etc). - AnimalCurrentLane / CarCurrentLane: lane state components for animals/cars; lane flags include stuck/request space markers.

Implementation details and behavior: - The system ignores blockers where Blocker.m_Blocker == Entity.Null or Blocker.m_Type == BlockerType.Temporary. - Blockers with m_MaxSpeed >= 6 are considered fast enough and do not count as causing stuck. - When processing car chunks, a crossing blocker will mark the blocked car's current lane with CarLaneFlags.RequestSpace (requests access). - Uses a safety limit of 100 iterations when following blocker chains to avoid infinite loops. - Designed to run in parallel with Burst for performance.

Usage Example

// Example: check if a PathOwner on an entity was marked stuck by StuckMovingObjectSystem
EntityManager em = World.DefaultGameObjectInjectionWorld.EntityManager;
if (em.HasComponent<PathOwner>(someEntity))
{
    PathOwner owner = em.GetComponentData<PathOwner>(someEntity);
    if ((owner.m_State & PathFlags.Stuck) != 0)
    {
        // entity has been marked stuck by the StuckMovingObjectSystem
        // handle recovery, replanning, or logging here
    }
}

Additional notes for modders: - You generally don't instantiate this system manually — it is part of the simulation systems run by the game's world. If you need similar behavior for custom entity types, follow the pattern: - Provide Blocker-like components and Controller indirection as appropriate. - Use a shared UpdateFrame filter to spread processing across frames. - Mark PathOwner or lane flags to indicate stuck state so other systems can respond. - Be careful when altering Blocker chains or Controller indirections — IsBlocked traverses Controller references and other Blocker components and uses a 100-step cap to avoid endless loops.