“Structural Changes are not allowed while iterating over entities. Please use EntityCommandBuffer instead.” error when disabling FollowerEntity

I moved from RichAI to FollowerEntity. When disabling the Monobehaviour Component of FollowerEntity I sometimes get the error about structural changes. I think the culprit is when the agent is on top of an OffMeshLink(maybe when he is trying to traverse it). If this is valid behavior how do i go about solving the issue? Could you provide an example of using EntityCommandBuffer to turn the FollowerEntity off? I just started reading about ECS and the Entity System so debugging the issue is kind of alien to me

When you disable the Monobehaviour are you just trying to stop the movement? Or are you trying to disable the whole GameObject?

If you are trying to stop, you should use FollowerEntity.canMove instead.

Do you have a stack trace for this error message?

This is the stack trace of the error.

InvalidOperationException: Structural changes are not allowed while iterating over entities. Please use EntityCommandBuffer instead.
Unity.Entities.EntityDataAccess.CheckIsStructuralChange () (at ./Library/PackageCache/com.unity.entities/Unity.Entities/EntityDataAccess.cs:338)
Unity.Entities.EntityDataAccess.BeforeStructuralChange () (at ./Library/PackageCache/com.unity.entities/Unity.Entities/EntityDataAccess.cs:410)
Unity.Entities.EntityDataAccess.BeginStructuralChanges () (at ./Library/PackageCache/com.unity.entities/Unity.Entities/EntityDataAccess.cs:420)
Unity.Entities.EntityManager.DestroyEntityInternal (Unity.Entities.Entity* entities, System.Int32 count) (at ./Library/PackageCache/com.unity.entities/Unity.Entities/EntityManager.cs:5001)
Unity.Entities.EntityManager.DestroyEntity (Unity.Entities.Entity entity) (at ./Library/PackageCache/com.unity.entities/Unity.Entities/EntityManager.cs:3475)
Pathfinding.FollowerEntity.OnDisable () (at ./Packages/com.arongranberg.astar/Core/AI/FollowerEntity.cs:451)
UnityEngine.Behaviour:set_enabled(Boolean)
EnemyInfo:StopMovement() (at Assets/Scripts/EnemyScripts/EnemyInfo.cs:98)
FleeAction:OnEnd() (at Assets/Scripts/AI/CustomBehaviors/FleeAction.cs:75)
BehaviorDesigner.Runtime.BehaviorManager:PopTask(BehaviorTree, Int32, Int32, TaskStatus&, Boolean, Boolean)
BehaviorDesigner.Runtime.BehaviorManager:DestroyBehavior(Behavior, TaskStatus)
BehaviorDesigner.Runtime.BehaviorManager:DisableBehavior(Behavior, Boolean, TaskStatus)
BehaviorDesigner.Runtime.BehaviorManager:DisableBehavior(Behavior, Boolean)
BehaviorDesigner.Runtime.Behavior:DisableBehavior()
BehaviorDesigner.Runtime.Behavior:OnDisable()
UnityEngine.GameObject:SetActive(Boolean)
JacobLocomotionState:OffMeshLinkBeginTraversal() (at Assets/Scripts/StateMachine/JacobStates/JacobLocomotionState.cs:47)
DD.FollowerOffMeshLinkHandler:Pathfinding.IOffMeshLinkStateMachine.OnTraverseOffMeshLink(AgentOffMeshLinkTraversalContext) (at Assets/Scripts/SO/OffMeshLinkHandlers/FollowerOffMeshLinkHandler.cs:44)
Pathfinding.ECS.JobManagedOffMeshLinkTransition:MoveNext(Entity, ManagedState, LocalTransform&, AgentMovementPlane&, MovementControl&, MovementSettings&, AgentOffMeshLinkTraversal&, ManagedAgentOffMeshLinkTraversal, Single) (at ./Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedOffMeshLinkTransition.cs:38)
Pathfinding.ECS.JobManagedOffMeshLinkTransition:Execute(Entity, ManagedState, LocalTransform&, AgentMovementPlane&, MovementControl&, MovementSettings&, AgentOffMeshLinkTraversal&, ManagedAgentOffMeshLinkTraversal) (at ./Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedOffMeshLinkTransition.cs:17)
Pathfinding.ECS.JobManagedOffMeshLinkTransition:Execute(ArchetypeChunk&, Int32, Boolean, v128&) (at ./JobEntityGenerator/Unity.Entities.SourceGen.JobEntityGenerator.JobEntityGenerator/Temp/GeneratedCode/AstarPathfindingProject/JobManagedOffMeshLinkTransition__JobEntity_193343661011.g.cs:41)
Pathfinding.ECS.JobManagedOffMeshLinkTransition:Unity.Entities.IJobChunk.Execute(ArchetypeChunk&, Int32, Boolean, v128&)
Unity.Entities.JobChunkExtensions:RunByRefWithoutJobs(JobManagedOffMeshLinkTransition&, EntityQuery) (at ./Library/PackageCache/com.unity.entities/Unity.Entities/IJobChunk.cs:271)
Unity.Entities.Internal.JobChunkInterface:RunByRefWithoutJobs(JobManagedOffMeshLinkTransition&, EntityQuery) (at ./Library/PackageCache/com.unity.entities/Unity.Entities/Internal/InternalCompilerInterface.cs:495)
Pathfinding.ECS.RepairPathSystem:__ScheduleViaJobChunkExtension_2(JobManagedOffMeshLinkTransition, EntityQuery, JobHandle, SystemState&, Boolean) (at ./SystemGenerator/Unity.Entities.SourceGen.SystemGenerator.SystemGenerator/Temp/GeneratedCode/AstarPathfindingProject/RepairPathSystem__System_3431296580.g.cs:518)
Pathfinding.ECS.RepairPathSystem:ProcessActiveOffMeshLinkTraversal(SystemState&) (at ./Packages/com.arongranberg.astar/Core/ECS/Systems/RepairPathSystem.cs:199)
Pathfinding.ECS.RepairPathSystem:OnUpdate(SystemState&) (at ./Packages/com.arongranberg.astar/Core/ECS/Systems/RepairPathSystem.cs:69)
Pathfinding.ECS.RepairPathSystem:__codegen__OnUpdate(IntPtr, IntPtr)
Unity.Entities.<>c__DisplayClass9_0:<SelectBurstFn>b__0(IntPtr, IntPtr) (at ./Library/PackageCache/com.unity.entities/Unity.Entities/SystemBaseRegistry.cs:249)
Unity.Entities.UnmanagedUpdate_0000164F$BurstDirectCall:wrapper_native_indirect_000002983036DBB8(IntPtr&, Void*)
Unity.Entities.UnmanagedUpdate_0000164F$BurstDirectCall:Invoke(Void*)
Unity.Entities.WorldUnmanagedImpl:UnmanagedUpdate(Void*) (at ./Library/PackageCache/com.unity.entities/Unity.Entities/WorldUnmanaged.cs:825)
Unity.Entities.WorldUnmanagedImpl:UpdateSystem(SystemHandle) (at ./Library/PackageCache/com.unity.entities/Unity.Entities/WorldUnmanaged.cs:891)
Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at ./Library/PackageCache/com.unity.entities/Unity.Entities/ComponentSystemGroup.cs:717)
Unity.Entities.ComponentSystemGroup:OnUpdate() (at ./Library/PackageCache/com.unity.entities/Unity.Entities/ComponentSystemGroup.cs:687)
Pathfinding.ECS.AIMovementSystemGroup:OnUpdate() (at ./Packages/com.arongranberg.astar/Core/ECS/Systems/AIMovementSystemGroup.cs:165)
Unity.Entities.SystemBase:Update() (at ./Library/PackageCache/com.unity.entities/Unity.Entities/SystemBase.cs:418)
Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at ./Library/PackageCache/com.unity.entities/Unity.Entities/ComponentSystemGroup.cs:723)
Unity.Entities.ComponentSystemGroup:OnUpdate() (at ./Library/PackageCache/com.unity.entities/Unity.Entities/ComponentSystemGroup.cs:681)
Unity.Entities.SystemBase:Update() (at ./Library/PackageCache/com.unity.entities/Unity.Entities/SystemBase.cs:418)
Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at ./Library/PackageCache/com.unity.entities/Unity.Entities/ScriptBehaviourUpdateOrder.cs:523)

For reference my offmeshlink handler

public class FollowerOffMeshLinkHandler : IOffMeshLinkHandler, IOffMeshLinkStateMachine
{
	public event Action OnBeginTraversal;
	public event Action OnEndTraversal;
	public bool CancelTraversal { get; set; }

	private readonly EnemyInfo _enemyInfo;
	private readonly Dictionary<OffMeshLinkType, OffMeshLinkHandler> _offMeshLinkHandlerMap;

	public FollowerOffMeshLinkHandler(
		EnemyInfo enemyInfo,
		Dictionary<OffMeshLinkType, OffMeshLinkHandler> offMeshLinkHandlerMap)
	{
		_enemyInfo = enemyInfo;
		_offMeshLinkHandlerMap = offMeshLinkHandlerMap;
	}

	IOffMeshLinkStateMachine IOffMeshLinkHandler.GetOffMeshLinkStateMachine(AgentOffMeshLinkTraversalContext context) => this;

	void IOffMeshLinkStateMachine.OnFinishTraversingOffMeshLink(AgentOffMeshLinkTraversalContext context)
	{
		OnEndTraversal?.Invoke();
	}

	void IOffMeshLinkStateMachine.OnAbortTraversingOffMeshLink()
	{
		Debug.Log("An agent aborted traversing an off-mesh link");
	}

	IEnumerable IOffMeshLinkStateMachine.OnTraverseOffMeshLink(AgentOffMeshLinkTraversalContext ctx)
	{
		OffMeshLinkInfo linkInfo = ctx.link.gameObject.GetComponent<OffMeshLinkInfo>();
		Assert.IsNotNull(linkInfo, "Link is missing required OffMeshLinkInfo component");
		OnBeginTraversal?.Invoke();
		CancelTraversal = false;
		Assert.IsTrue(_offMeshLinkHandlerMap.ContainsKey(linkInfo.Type), $"The dictionary is missing the required key: '{linkInfo.Type}'");
		return _offMeshLinkHandlerMap[linkInfo.Type].Handle(_enemyInfo, ctx, CancelTraversal);			
	}
}

It looks like you are trying to disable the FollowerEntity component by the event that is starting an off mesh link traversal. This is not supported due to how ECS works, I’m afraid. Also it doesn’t really make sense for most use cases.

Indeed, that is what is happening. What is weird is, let’s say I am in the middle of traversal, and I get hit. I should stop traversing and for example fall down to the ground (if it was a jump link). I want FollowerEntity to be disabled and let the rigidbody physics system take over. Won’t this cause an issue? From my testing it actually calls the OnAbortTraversingOffMeshLink method and there is no structural error even though I again call FollowerEntity.enabled = false. Is it only an issue before the first yield return call when traversing?

You just cannot disable the FollowerEntity from the methods in your IOffMeshLinkStateMachine/IOffMeshLinkHandler. Disabling it from other code is fine.

I see, I changed my architecture to match what you said and now it works fine

1 Like