diff --git a/src/Arch.Tests/ChunkTest.cs b/src/Arch.Tests/ChunkTest.cs index dce46d85..dbf689e1 100644 --- a/src/Arch.Tests/ChunkTest.cs +++ b/src/Arch.Tests/ChunkTest.cs @@ -140,4 +140,21 @@ public void ChunkHas() That(_chunk.Has(), Is.False); That(_chunk.Has(), Is.True); } + + [Test] + public void ChunkErrors() + { + _chunk = new Chunk(1000, _types); + + Throws(() => _chunk.GetArray()); + Throws(() => _chunk.Get(0)); + Throws(() => _chunk.Set(0, default)); + Throws(() => _chunk.GetFirst()); + + var type = (ComponentType)typeof(Ai); + + Throws(() => _chunk.GetArray(type)); + Throws(() => _chunk.Get(0, type)); + Throws(() => _chunk.Set(0, (object)default(Ai))); + } } diff --git a/src/Arch.Tests/WorldTest.cs b/src/Arch.Tests/WorldTest.cs index d0626369..531b3da1 100644 --- a/src/Arch.Tests/WorldTest.cs +++ b/src/Arch.Tests/WorldTest.cs @@ -188,7 +188,7 @@ public void Reference() #endif // Entity reference null is NOT alive. - EntityReference cons = new EntityReference{}; + EntityReference cons = new EntityReference { }; EntityReference refs = EntityReference.Null; #if PURE_ECS @@ -411,7 +411,7 @@ public void SetByQueryDescription() { var queryDesc = new QueryDescription().WithAll(); - _world.Set(in queryDesc, new Transform{ X = 100, Y = 100}); + _world.Set(in queryDesc, new Transform { X = 100, Y = 100 }); _world.Query(in queryDesc, (ref Transform transform) => { That(transform.X, Is.EqualTo(100)); @@ -476,7 +476,7 @@ public void AddByQueryDescriptionValue() for (int index = 0; index < 1000; index++) { var entity = world.Create(_entityGroup); - world.Add(entity,10); + world.Add(entity, 10); } // Add int to all entities without int @@ -486,8 +486,10 @@ public void AddByQueryDescriptionValue() var counter = 0; world.Query(in withIntQueryDesc, (ref int i) => { - if (i == 10) previousCounter++; - if (i == 100) counter++; + if (i == 10) + previousCounter++; + if (i == 100) + counter++; }); That(world.CountEntities(in withIntQueryDesc), Is.EqualTo(2000)); @@ -527,7 +529,6 @@ public partial class WorldTest [Test] public void SetGetAndHas() { - var entity = _world.Create(_entityGroup); True(_world.Has(entity)); @@ -536,6 +537,9 @@ public void SetGetAndHas() That(transform.X, Is.EqualTo(10)); That(transform.Y, Is.EqualTo(10)); + + Throws(() => _world.Get(entity)); + Throws(() => _world.Set(entity, default)); } /// @@ -544,7 +548,6 @@ public void SetGetAndHas() [Test] public void Remove() { - var entity = _world.Create(_entityGroup); var entity2 = _world.Create(_entityGroup); _world.Remove(entity); @@ -553,6 +556,8 @@ public void Remove() That(_world.GetArchetype(entity2), Is.EqualTo(_world.GetArchetype(entity))); That(_world.GetArchetype(entity).ChunkCount, Is.EqualTo(1)); That(_world.GetArchetype(entity).Chunks[0].Size, Is.EqualTo(2)); + + Throws(() => _world.Remove(entity)); } /// @@ -569,6 +574,8 @@ public void Add() _world.TryGetArchetype(_entityAiGroup, out var arch); That(_world.GetArchetype(entity2), Is.EqualTo(_world.GetArchetype(entity))); That(arch, Is.EqualTo(_world.GetArchetype(entity))); + + Throws(() => _world.Add(entity)); } } @@ -591,6 +598,9 @@ public void SetGetAndHas_NonGeneric() That(transform.X, Is.EqualTo(10)); That(transform.Y, Is.EqualTo(10)); + + Throws(() => _world.Get(entity, typeof(Ai))); + Throws(() => _world.Set(entity, (object)default(Ai))); } /// @@ -607,6 +617,8 @@ public void Remove_NonGeneric() That(_world.GetArchetype(entity2), Is.EqualTo(_world.GetArchetype(entity))); That(_world.GetArchetype(entity).ChunkCount, Is.EqualTo(1)); That(_world.GetArchetype(entity).Chunks[0].Size, Is.EqualTo(2)); + + Throws(() => _world.RemoveRange(entity, typeof(Ai))); } /// @@ -623,6 +635,8 @@ public void Add_NonGeneric() _world.TryGetArchetype(_entityAiGroup, out var arch); That(_world.GetArchetype(entity2), Is.EqualTo(_world.GetArchetype(entity))); That(arch, Is.EqualTo(_world.GetArchetype(entity))); + + Throws(() => _world.AddRange(entity, new Ai())); } } diff --git a/src/Arch/Core/Chunk.cs b/src/Arch/Core/Chunk.cs index 69ed9d70..82d77af9 100644 --- a/src/Arch/Core/Chunk.cs +++ b/src/Arch/Core/Chunk.cs @@ -230,23 +230,8 @@ public override string ToString() public partial struct Chunk { - - /// - /// Returns the component array index of a component. - /// - /// The componen type. - /// The index in the array. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Pure] - private int Index() - { - var id = Component.ComponentType.Id; - Debug.Assert(id != -1 && id < ComponentIdToArrayIndex.Length, $"Index is out of bounds, component {typeof(T)} with id {id} does not exist in this chunk."); - return ComponentIdToArrayIndex.DangerousGetReferenceAt(id); - } - /// - /// Returns the component array for a given component in an unsafe manner. + /// Returns the component array for a given component. /// /// The component type. /// The array. @@ -254,15 +239,24 @@ private int Index() [Pure] public T[] GetArray() { - var index = Index(); - Debug.Assert(index != -1 && index < Components.Length, $"Index is out of bounds, component {typeof(T)} with id {index} does not exist in this chunk."); - var array = Components.DangerousGetReferenceAt(index); + var id = Component.ComponentType.Id; + if (id == -1 || id >= ComponentIdToArrayIndex.Length) + { + ThrowHelper.Throw_ComponentDoesNotExists(typeof(T)); + } + + var index = ComponentIdToArrayIndex.DangerousGetReferenceAt(id); + if (index == -1 || index >= Components.Length) + { + ThrowHelper.Throw_ComponentDoesNotExists(typeof(T)); + } + + ref var array = ref Components.DangerousGetReferenceAt(index); return Unsafe.As(array); } - /// - /// Returns the component array for a given component in an unsafe manner. + /// Returns the component array for a given component. /// /// The component type. /// The array . @@ -334,33 +328,26 @@ public bool Has(ComponentType t) } /// - /// Returns the component array index of a component by its type. + /// Returns the component array for a given component type. /// - /// The . - /// The index in the array. + /// The type. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] - private int Index(ComponentType type) + public Array GetArray(ComponentType type) { var id = type.Id; if (id >= ComponentIdToArrayIndex.Length) { - return -1; + ThrowHelper.Throw_ComponentDoesNotExists(type); } - return ComponentIdToArrayIndex.DangerousGetReferenceAt(id); - } + var index = ComponentIdToArrayIndex.DangerousGetReferenceAt(id); + if (index == -1 || index >= Components.Length) + { + ThrowHelper.Throw_ComponentDoesNotExists(type); + } - /// - /// Returns the component array for a given component type. - /// - /// The type. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Pure] - public Array GetArray(ComponentType type) - { - var index = Index(type); return Components.DangerousGetReferenceAt(index); } } diff --git a/src/Arch/Core/Utils/ThrowHelper.cs b/src/Arch/Core/Utils/ThrowHelper.cs new file mode 100644 index 00000000..14eb5fe6 --- /dev/null +++ b/src/Arch/Core/Utils/ThrowHelper.cs @@ -0,0 +1,18 @@ +using Arch.Core.Utils; + +namespace Arch; + +internal static class ThrowHelper +{ + [DoesNotReturn] + public static void Throw_ComponentDoesNotExists(ComponentType type) + { + throw new InvalidOperationException($"You are trying to access a component({type}) that does not exist in the entity or chunk."); + } + + [DoesNotReturn] + public static void Throw_SameArchetype() + { + throw new InvalidOperationException($"From-Archetype is the same as the To-Archetype. Entities cannot move within the same archetype using this function. Probably an attempt was made to attach already existing components to the entity or to remove non-existing ones."); + } +} \ No newline at end of file diff --git a/src/Arch/Core/World.cs b/src/Arch/Core/World.cs index b55ab099..a5b32438 100644 --- a/src/Arch/Core/World.cs +++ b/src/Arch/Core/World.cs @@ -325,8 +325,11 @@ public Entity Create(Span types) internal void Move(Entity entity, Archetype source, Archetype destination, out Slot destinationSlot) { // A common mistake, happening in many cases. - Debug.Assert(source != destination, "From-Archetype is the same as the To-Archetype. Entities cannot move within the same archetype using this function. Probably an attempt was made to attach already existing components to the entity or to remove non-existing ones."); - + if (source == destination) + { + ThrowHelper.Throw_SameArchetype(); + } + // Copy entity to other archetype ref var slot = ref EntityInfo.GetSlot(entity.Id); var created = destination.Add(entity, out destinationSlot);