- Home /
DOTS - ECS Worlds entity transfer error (Halp!)
Hello, I am making a loading system for my project and one part of it has to do with transporting entities between worlds. It works correctly, but i get this error: ArgumentException: System.InvalidOperationException: The entity does not exist Thrown from job: Unity.Entities.EntityCommandBuffer._mono_to_burst_PlaybackChainChunk
Also, trough testing I found out the error is specifically in the tranfer of entities between worlds, in my main script (bellow).
The entire code message: ArgumentException: System.InvalidOperationException: The entity does not exist Thrown from job: Unity.Entities.EntityCommandBuffer._mono_to_burst_PlaybackChainChunk This Exception was thrown from a function compiled with Burst, which has limited exception support. Turn off burst (Jobs -> Burst -> Enable Compilation) to inspect full exceptions & stacktraces. EntityCommandBuffer was recorded in WorldLoader and played back in Unity.Entities.EndSimulationEntityCommandBufferSystem. at (wrapper managed-to-native) System.Object.wrapper_native_000001DB2D3901E0(intptr,intptr,Unity.Entities.EntityComponentStore/ArchetypeChanges&,Unity.Entities.ECBSharedPlaybackState&,intptr,int,int,bool,Unity.Entities.PlaybackPolicy) at (wrapper delegate-invoke) .invoke_void_intptr_intptr_EntityComponentStore/ArchetypeChanges&_ECBSharedPlaybackState&_intptr_int_int_bool_PlaybackPolicy(intptr,intptr,Unity.Entities.EntityComponentStore/ArchetypeChanges&,Unity.Entities.ECBSharedPlaybackState&,intptr,int,int,bool,Unity.Entities.PlaybackPolicy) at Unity.Entities.EntityCommandBuffer._forward_mono_PlaybackChainChunk (Unity.Entities.EntityDataAccess mgr, Unity.Collections.LowLevel.Unsafe.UnsafeList* managedReferenceIndexRemovalCount, Unity.Entities.EntityComponentStore+ArchetypeChanges& archetypeChanges, Unity.Entities.ECBSharedPlaybackState& playbackState, Unity.Entities.ECBChainPlaybackState* chainStates, System.Int32 currentChain, System.Int32 nextChain, System.Boolean isFirstPlayback, Unity.Entities.PlaybackPolicy playbackPolicy) [0x00000] in C:\Users\Tom\Desktop\Unity\Projects\Qilligon\Library\PackageCache\com.unity.entities@0.16.0-preview.21\Unity.Entities\EntityCommandBuffer.interop.gen.cs:88 at Unity.Entities.EntityCommandBuffer.PlaybackChainChunk (Unity.Entities.EntityDataAccess* mgr, Unity.Collections.LowLevel.Unsafe.UnsafeList* managedReferenceIndexRemovalCount, Unity.Entities.EntityComponentStore+ArchetypeChanges& archetypeChanges, Unity.Entities.ECBSharedPlaybackState& playbackState, Unity.Entities.ECBChainPlaybackState* chainStates, System.Int32 currentChain, System.Int32 nextChain, System.Boolean isFirstPlayback, Unity.Entities.PlaybackPolicy playbackPolicy) [0x00007] in C:\Users\Tom\Desktop\Unity\Projects\Qilligon\Library\PackageCache\com.unity.entities@0.16.0-preview.21\Unity.Entities\EntityCommandBuffer.interop.gen.cs:69 at Unity.Entities.EntityCommandBuffer.PlaybackChain (Unity.Entities.EntityDataAccess* mgr, Unity.Collections.LowLevel.Unsafe.UnsafeList* managedReferenceIndexRemovalCount, Unity.Entities.EntityComponentStore+ArchetypeChanges& archetypeChanges, Unity.Entities.ECBSharedPlaybackState& playbackState, Unity.Entities.ECBChainPlaybackState* chainStates, System.Int32 currentChain, System.Int32 nextChain, System.Boolean isFirstPlayback, Unity.Entities.PlaybackPolicy playbackPolicy) [0x000b2] in C:\Users\Tom\Desktop\Unity\Projects\Qilligon\Library\PackageCache\com.unity.entities@0.16.0-preview.21\Unity.Entities\EntityCommandBuffer.cs:1678 at Unity.Entities.EntityCommandBuffer.PlaybackInternal (Unity.Entities.EntityDataAccess* mgr) [0x0030f] in C:\Users\Tom\Desktop\Unity\Projects\Qilligon\Library\PackageCache\com.unity.entities@0.16.0-preview.21\Unity.Entities\EntityCommandBuffer.cs:1502 at Unity.Entities.EntityCommandBuffer.Playback (Unity.Entities.EntityManager mgr) [0x00000] in C:\Users\Tom\Desktop\Unity\Projects\Qilligon\Library\PackageCache\com.unity.entities@0.16.0-preview.21\Unity.Entities\EntityCommandBuffer.cs:1369 at Unity.Entities.EntityCommandBufferSystem.FlushPendingBuffers (System.Boolean playBack) [0x0007f] in C:\Users\Tom\Desktop\Unity\Projects\Qilligon\Library\PackageCache\com.unity.entities@0.16.0-preview.21\Unity.Entities\EntityCommandBufferSystem.cs:225
Unity.Entities.EntityCommandBufferSystem.FlushPendingBuffers (System.Boolean playBack) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityCommandBufferSystem.cs:292) Unity.Entities.EntityCommandBufferSystem.OnUpdate () (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityCommandBufferSystem.cs:190) Unity.Entities.ComponentSystem.Update () (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystem.cs:113) Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystemGroup.cs:435) UnityEngine.Debug:LogException(Exception) Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/Stubs/Unity/Debug.cs:19) Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystemGroup.cs:440) Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystemGroup.cs:387) Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystem.cs:113) Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ScriptBehaviourUpdateOrder.cs:333)
Can anyone explain to me why it happens and how to fix it, please. Anyway here is my entire code (happy reading trough it :D):
My main code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Collections;
using System.IO;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Entities;
using Unity.Entities.Serialization;
using Unity.Jobs;
using Unity.Burst;
using static WorldUtils;
using static WorldData;
using static ArchetypeLibrary;
public class WorldLoader : SystemBase
{
EndSimulationEntityCommandBufferSystem ECBS;
NativeHashSet<int2> ActiveChunks;
NativeHashSet<int2> LoadedChunks;
NativeHashSet<int2> PendingChunks;
EntityQuery Query;
protected override void OnCreate()
{
ECBS = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
ActiveChunks = new NativeHashSet<int2>(1600, Allocator.Persistent);
LoadedChunks = new NativeHashSet<int2>(1600, Allocator.Persistent);
PendingChunks = new NativeHashSet<int2>(1600, Allocator.Persistent);
Query = GetEntityQuery(typeof(PendingSave));
}
protected override void OnUpdate()
{
ActiveChunks.Clear();
var activeChunks = ActiveChunks;
var loadedChunks = LoadedChunks;
var pendingChunks = PendingChunks;
var chunkSize = ChunkSize;
var chunkArchetype = ChunkArchetype();
var worldSeed = WorldSeed;
var ECB = ECBS.CreateCommandBuffer();
var PECB = ECB.AsParallelWriter();
var saveWorld = FindWorld("SaveWorld");
//Activate chunks around Loaders
Entities.ForEach((in Translation translation, in Loader loader) =>
{
float2 Position = new float2(translation.Value.x, translation.Value.y);
int LoadDistance = loader.LoadDistance;
for (int x = 0; x < LoadDistance; x++)
{
for (int y = 0; y < LoadDistance; y++)
{
int2 chunk = GetChunkFromWorld(Position, chunkSize, new float2(0, 0)) - LoadDistance / 2 + new int2(x, y);
if (!activeChunks.Contains(chunk))
{
activeChunks.Add(chunk);
}
}
}
}).WithBurst().Schedule();
//Load and unload chunks and keep track of newly generated
Job.WithReadOnly(activeChunks).WithCode(() =>
{
var activeChunksArray = activeChunks.ToNativeArray(Allocator.Temp);
for (int i = 0; i < activeChunksArray.Length; i++)
{
int2 chunk = activeChunksArray[i];
if (!loadedChunks.Contains(chunk))
{
loadedChunks.Add(chunk);
pendingChunks.Add(chunk);
}
}
var loadedChunksArray = loadedChunks.ToNativeArray(Allocator.Temp);
for (int i = loadedChunksArray.Length - 1; i != -1; i--)
{
int2 chunk = loadedChunksArray[i];
if (!activeChunks.Contains(chunk))
{
loadedChunks.Remove(chunk);
}
}
}).WithBurst().Schedule();
//Generate new chunks
Job.WithCode(() =>
{
var pendingChunksArray = pendingChunks.ToNativeArray(Allocator.Temp);
for (int i = pendingChunksArray.Length - 1; i != -1; i--)
{
int2 chunk = pendingChunksArray[i];
Entity entity = ECB.CreateEntity(chunkArchetype);
float2 position = GetTileCenter(GetWorldFromChunk(chunk, chunkSize, new float2(0, 0)), chunkSize);
ECB.SetComponent(entity, new Translation { Value = new float3(position.x, position.y, 0) });
ECB.SetComponent(entity, new Unloadable { Serializable = true });
ECB.SetComponent(entity, new ChunkData { NewChunk = true, ChunkTick = true, WorldSeed = worldSeed });
ECB.AddBuffer<TileData>(entity);
pendingChunks.Remove(chunk);
}
}).WithBurst().Schedule();
//Destroy unloaded entities or select them for serialization
Entities.WithReadOnly(loadedChunks).ForEach((int entityInQueryIndex, Entity entity, ref Unloadable unloadable, in Translation translation) =>
{
int2 chunk = GetChunkFromWorld(new float2(translation.Value.x, translation.Value.y), chunkSize, new float2(0, 0));
if (!loadedChunks.Contains(chunk))
{
if (unloadable.Serializable == true)
{
PECB.AddComponent(entityInQueryIndex, entity, new PendingSave { });
}
else
{
PECB.DestroyEntity(entityInQueryIndex, entity);
}
}
}).WithBurst().ScheduleParallel();
ECBS.AddJobHandleForProducer(Dependency);
Dependency.Complete();
//Move selected entities to SaveWorld
if (Query.IsEmpty == false)
{
saveWorld.EntityManager.MoveEntitiesFrom(World.EntityManager, Query);
}
}
protected override void OnDestroy()
{
ActiveChunks.Dispose();
LoadedChunks.Dispose();
PendingChunks.Dispose();
}
}
My main code library:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Collections;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Entities;
using Unity.Jobs;
using Unity.Burst;
public static class WorldUtils
{
public static int2 GetTileFromWorld(float2 WorldPosition, float2 OriginPosition, float TileSize)
{
int2 TilePosition;
TilePosition.x = (int)math.floor((WorldPosition - OriginPosition).x / TileSize);
TilePosition.y = (int)math.floor((WorldPosition - OriginPosition).y / TileSize);
return TilePosition;
}
public static float2 GetWorldFromTile(int2 TilePosition, float TileSize, float2 OriginPosition)
{
float2 WorldPosition;
WorldPosition.x = (TilePosition.x * TileSize) + OriginPosition.x;
WorldPosition.y = (TilePosition.y * TileSize) + OriginPosition.y;
return WorldPosition;
}
public static float2 GetTileCenter(float2 WorldPosition, float TileSize)
{
return new float2((WorldPosition.x + (TileSize * 0.5f)), (WorldPosition.y + (TileSize * 0.5f)));
}
public static int2 GetChunkFromWorld(float2 WorldPosition, int ChunkSize, float2 OriginPosition)
{
int2 ChunkPosition;
ChunkPosition.x = (int)(math.floor((WorldPosition.x - OriginPosition.x) / ChunkSize));
ChunkPosition.y = (int)(math.floor((WorldPosition.y - OriginPosition.y) / ChunkSize));
return ChunkPosition;
}
public static float2 GetWorldFromChunk(int2 ChunkPosition, int ChunkSize, float2 OriginPosition)
{
float2 WorldPosition;
WorldPosition.x = (ChunkPosition.x * ChunkSize) + OriginPosition.x;
WorldPosition.y = (ChunkPosition.y * ChunkSize) + OriginPosition.y;
return WorldPosition;
}
public static float PerlinSeeder(float WorldSeed)
{
float Result = math.cos(WorldSeed);
return Result;
}
public static World FindWorld(string name)
{
var worlds = World.All;
for (int i = 0; i < worlds.Count; i++)
if (worlds[i].Name == name) return worlds[i];
return null;
}
}
And finally my monobehavior script that tests the other codes:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Collections;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Entities;
using Unity.Jobs;
using Unity.Burst;
using static WorldUtils;
using static WorldData;
using static ArchetypeLibrary;
public class Test : MonoBehaviour
{
public float2 Position;
EntityManager EntityManager;
Entity entity;
public float t;
public bool End;
public int EndTime;
public float worldSeed;
public World worldname;
private void Start()
{
EntityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
WorldSeed = worldSeed;
var world = new World("SaveWorld");
}
private void Update()
{
t += 1 * Time.deltaTime;
if (Input.GetKeyDown(KeyCode.G))
{
t = 0;
entity = EntityManager.CreateEntity(typeof (Translation), typeof (Loader));
EntityManager.SetComponentData(entity, new Translation { Value = new float3(Position.x, Position.y, 0) });
EntityManager.SetComponentData(entity, new Loader { LoadDistance = 40 });
}
if (End == true && t >= EndTime)
{
EntityManager.DestroyEntity(entity);
t = 0;
}
}
}
Answer by andrew-lukasik · Dec 26, 2020 at 04:42 PM
ECBS.AddJobHandleForProducer( Dependency );
Dependency.Complete();
Calling Dependency.Complete();
creates pool of commands without executing them yet. So, you're seeing this error precisely because commands are ready and scheduled for old entities with EndSimulationEntityCommandBufferSystem
meanwhile you destroy/move them, before valid buffered commands have any chance of being executed yet, by calling
saveWorld.EntityManager.MoveEntitiesFrom(World.EntityManager, Query);
To fix this, don't schedule commands with EndSimulationEntityCommandBufferSystem
here but just execute them immediately
// ECBS.AddJobHandleForProducer( Dependency );// remove this line
Dependency.Complete();
ECB.Playback( EntityManager );
Or, in other words, replace:
ECBS.AddJobHandleForProducer(Dependency);
Dependency.Complete();
//$$anonymous$$ove selected entities to SaveWorld
if (Query.IsEmpty == false)
{
saveWorld.Entity$$anonymous$$anager.$$anonymous$$oveEntitiesFrom(World.Entity$$anonymous$$anager, Query);
}
with:
//$$anonymous$$ove selected entities to SaveWorld
if( Query.IsEmpty==false )
{
Dependency.Complete();
ECB.Playback( Entity$$anonymous$$anager );
saveWorld.Entity$$anonymous$$anager.$$anonymous$$oveEntitiesFrom(World.Entity$$anonymous$$anager, Query);
}
else
{
// no need for Dependency.Complete() here
ECBS.AddJobHandleForProducer(Dependency);
}
Thanks, that worked, but now I get this error instead:
ArgumentException: Attempt to call Playback() on an EntityCommandBuffer that has already been played back. EntityCommandBuffers created with the SinglePlayback policy can only be played back once. EntityCommandBuffer was recorded in WorldLoader and played back in Unity.Entities.EndSimulationEntityCommandBufferSystem. at Unity.Entities.EntityCommandBuffer.PlaybackInternal (Unity.Entities.EntityDataAccess mgr) [0x004ba] in C:\Users\$$anonymous$$\Desktop\Unity\Projects\Qilligon\Library\PackageCache\com.unity.entities@0.16.0-preview.21\Unity.Entities\EntityCommandBuffer.cs:1545 at Unity.Entities.EntityCommandBuffer.Playback (Unity.Entities.Entity$$anonymous$$anager mgr) [0x00000] in C:\Users\$$anonymous$$\Desktop\Unity\Projects\Qilligon\Library\PackageCache\com.unity.entities@0.16.0-preview.21\Unity.Entities\EntityCommandBuffer.cs:1369 at Unity.Entities.EntityCommandBufferSystem.FlushPendingBuffers (System.Boolean playBack) [0x0007f] in C:\Users\$$anonymous$$\Desktop\Unity\Projects\Qilligon\Library\PackageCache\com.unity.entities@0.16.0-preview.21\Unity.Entities\EntityCommandBufferSystem.cs:225 Unity.Entities.EntityCommandBufferSystem.FlushPendingBuffers (System.Boolean playBack) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityCommandBufferSystem.cs:292) Unity.Entities.EntityCommandBufferSystem.OnUpdate () (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/EntityCommandBufferSystem.cs:190) Unity.Entities.ComponentSystem.Update () (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystem.cs:113) Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystemGroup.cs:435) UnityEngine.Debug:LogException(Exception) Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/Stubs/Unity/Debug.cs:19) Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystemGroup.cs:440) Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystemGroup.cs:387) Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ComponentSystem.cs:113) Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.16.0-preview.21/Unity.Entities/ScriptBehaviourUpdateOrder.cs:333)*
EntityCommandBuffer was recorded in WorldLoader and played back in Unity.Entities.EndSimulationEntityCommandBufferSystem.
As I wrote in code sample from comment above: either execute commands immediately ( ECB.Playback( Entity$$anonymous$$anager );
) or schedule them for execution ( ECBS.AddJobHandleForProducer(Dependency);
); never do both for the same command buffer as it would result in the same commands being executed twice (exception prevents that).
I.e. sequence like this is causing this exception:
ECBS.AddJobHandleForProducer(Dependency);
ECB.Playback( Entity$$anonymous$$anager );
(order doesn't matter, but both being called in single update do)
Another alternative I would like to recommend is to reorder your OnUpdate
from this:
protected override void OnUpdate ()
{
/*
all jobs being scheduled
*/
ECBS.AddJobHandleForProducer(Dependency);
Dependency.Complete();
//$$anonymous$$ove selected entities to SaveWorld
if( Query.IsEmpty==false )
{
saveWorld.Entity$$anonymous$$anager.$$anonymous$$oveEntitiesFrom(World.Entity$$anonymous$$anager, Query);
}
}
To this:
protected override void OnUpdate ()
{
//$$anonymous$$ove selected entities to SaveWorld
if( Query.IsEmpty==false )
{
Dependency.Complete();
saveWorld.Entity$$anonymous$$anager.$$anonymous$$oveEntitiesFrom(World.Entity$$anonymous$$anager, Query);
}
/*
all jobs being scheduled
*/
ECBS.AddJobHandleForProducer(Dependency);
// Dependency.Complete();// no need for that here
}
So $$anonymous$$oveEntitiesFrom
will interfere with command buffer no more.
Thanks so much, you once again saved me. I feel bad that you have to babysit me trought this entire code. I feel like you created it and I just stole it xD. Anyway thanks so much, I think there is good chance we´ll talk again soon :D
Happy to help. Don't worry, switching from $$anonymous$$onobehaviours to Dots is quite a leap and nobody is expected to make it effortlessly. Especially when learning/reference resources are both scarce and confusing due to being out of date much of the time.
Your answer
Follow this Question
Related Questions
Convert to entity script causes Unity to crash? 1 Answer
How can recreate entitymanager after recompile (Unity ECS or DOTS) 0 Answers
Doing a double loop in a job in ecs with Job Entites.forech 1 Answer
Unity Jobs Error - "Job can only create Temp memory" 1 Answer
How to resolve "Multiple Precompiled Assemblies" error 0 Answers