From 8a4f2ad7a6c6ad6f085bb049ae244ef4a84d2037 Mon Sep 17 00:00:00 2001 From: sebas77 Date: Wed, 6 Oct 2021 10:53:57 +0100 Subject: [PATCH] Svelto.ECS 3.2.3 - fixed bugs and improved code --- .gitignore | 1 + com.sebaslab.svelto.common | 2 +- .../Core/CheckEntityUtilities.cs | 9 +- com.sebaslab.svelto.ecs/Core/EGID.cs | 33 +-- ...EnginesRoot.DoubleBufferedEntitiesToAdd.cs | 167 ++++++------- .../Core/EnginesRoot.Engines.cs | 2 +- .../Core/EnginesRoot.Entities.cs | 23 +- .../EnginesRoot.GenericEntityFunctions.cs | 10 +- .../Core/EnginesRoot.Submission.cs | 9 +- .../Core/EntitiesDB.FindGroups.cs | 2 +- com.sebaslab.svelto.ecs/Core/EntityFactory.cs | 12 +- .../EntityReference/EnginesRoot.LocatorMap.cs | 2 +- com.sebaslab.svelto.ecs/Core/GroupHashMap.cs | 66 ++--- com.sebaslab.svelto.ecs/Core/GroupNamesMap.cs | 12 +- .../Core/Groups/ExclusiveBuildGroup.cs | 10 +- .../Core/Groups/ExclusiveGroup.cs | 50 ++-- .../Core/Groups/ExclusiveGroupStruct.cs | 131 ++++++---- .../Core/Groups/GroupCompound.cs | 229 ++++++++++-------- .../Core/Groups/NamedExclusiveGroup.cs | 4 +- .../DataStructures/ITypeSafeDictionary.cs | 3 +- .../DataStructures/TypeSafeDictionary.cs | 109 ++++----- .../Unmanaged/NativeDynamicArray.cs | 7 +- .../Dispatcher/ReactiveValue.cs | 14 +- .../Extensions/Svelto/AllGroupsEnumerable.cs | 2 +- .../Native/EnginesRoot.NativeOperation.cs | 2 +- .../Unity/DOTS/UECS/UECSSveltoEGID.cs | 6 +- .../EnginesRoot.SerializableEntityHeader.cs | 2 +- com.sebaslab.svelto.ecs/Svelto.ECS.csproj | 4 +- com.sebaslab.svelto.ecs/package.json | 4 +- com.sebaslab.svelto.ecs/version.json | 2 +- 30 files changed, 491 insertions(+), 438 deletions(-) diff --git a/.gitignore b/.gitignore index eea704c..4cd8b26 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ com.sebaslab.svelto.ecs/obj/ .vs/Svelto.ECS/DesignTimeBuild/.dtbcache.v2 .vs/Svelto.ECS/v16/.suo com.sebaslab.svelto.ecs/bin/ +com.sebaslab.svelto.ecs/Svelto.ECS.sdpkg.user diff --git a/com.sebaslab.svelto.common b/com.sebaslab.svelto.common index 4062f99..8a1924d 160000 --- a/com.sebaslab.svelto.common +++ b/com.sebaslab.svelto.common @@ -1 +1 @@ -Subproject commit 4062f995396f9d34f8a58c7646f819c315356775 +Subproject commit 8a1924de0f6017ccc03396b45a3ee78ae9195dac diff --git a/com.sebaslab.svelto.ecs/Core/CheckEntityUtilities.cs b/com.sebaslab.svelto.ecs/Core/CheckEntityUtilities.cs index d8088c9..83a0427 100644 --- a/com.sebaslab.svelto.ecs/Core/CheckEntityUtilities.cs +++ b/com.sebaslab.svelto.ecs/Core/CheckEntityUtilities.cs @@ -29,7 +29,7 @@ namespace Svelto.ECS .FastConcat(" previous operation was: ") .FastConcat(_multipleOperationOnSameEGIDChecker[egid] == 1 ? "add" : "remove")); - if (_idChecker.TryGetValue((uint) egid.groupID, out var hash)) + if (_idChecker.TryGetValue(egid.groupID, out var hash)) if (hash.Contains(egid.entityID) == false) throw new ECSException("Trying to remove an Entity never submitted in the database " .FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID) @@ -57,7 +57,7 @@ namespace Svelto.ECS .FastConcat(" previous operation was: ") .FastConcat(_multipleOperationOnSameEGIDChecker[egid] == 1 ? "add" : "remove")); - var hash = _idChecker.GetOrCreate((uint) egid.groupID, () => new HashSet()); + var hash = _idChecker.GetOrCreate(egid.groupID, () => new HashSet()); if (hash.Contains(egid.entityID) == true) throw new ECSException("Trying to add an Entity already submitted to the database " .FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID) @@ -67,13 +67,12 @@ namespace Svelto.ECS : "not available")); hash.Add(egid.entityID); _multipleOperationOnSameEGIDChecker.Add(egid, 1); - } #if DONT_USE [Conditional("CHECK_ALL")] #endif - void RemoveGroupID(ExclusiveBuildGroup groupID) { _idChecker.Remove((uint)groupID); } + void RemoveGroupID(ExclusiveBuildGroup groupID) { _idChecker.Remove(groupID); } #if DONT_USE [Conditional("CHECK_ALL")] @@ -81,6 +80,6 @@ namespace Svelto.ECS void ClearChecks() { _multipleOperationOnSameEGIDChecker.FastClear(); } readonly FasterDictionary _multipleOperationOnSameEGIDChecker = new FasterDictionary(); - readonly FasterDictionary> _idChecker = new FasterDictionary>(); + readonly FasterDictionary> _idChecker = new FasterDictionary>(); } } \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/Core/EGID.cs b/com.sebaslab.svelto.ecs/Core/EGID.cs index 43591fd..a3c2fd9 100644 --- a/com.sebaslab.svelto.ecs/Core/EGID.cs +++ b/com.sebaslab.svelto.ecs/Core/EGID.cs @@ -9,11 +9,11 @@ namespace Svelto.ECS [Serialization.DoNotSerialize] [Serializable] [StructLayout(LayoutKind.Explicit)] - public struct EGID:IEquatable,IComparable + public struct EGID : IEquatable, IComparable { [FieldOffset(0)] public readonly uint entityID; [FieldOffset(4)] public readonly ExclusiveGroupStruct groupID; - [FieldOffset(0)] readonly ulong _GID; + [FieldOffset(0)] readonly ulong _GID; public static bool operator ==(EGID obj1, EGID obj2) { @@ -27,17 +27,17 @@ namespace Svelto.ECS public EGID(uint entityID, ExclusiveGroupStruct groupID) : this() { -#if DEBUG && !PROFILE_SVELTO - if (groupID == (ExclusiveGroupStruct)default) - throw new Exception("Trying to use a not initialised group ID"); -#endif - - _GID = MAKE_GLOBAL_ID(entityID, (uint) groupID); +#if DEBUG && !PROFILE_SVELTO + if (groupID == (ExclusiveGroupStruct) default) + throw new Exception("Trying to use a not initialised group ID"); +#endif + + _GID = MAKE_GLOBAL_ID(entityID, groupID.ToIDAndBitmask()); } - + static ulong MAKE_GLOBAL_ID(uint entityId, uint groupId) { - return (ulong)groupId << 32 | ((ulong)entityId & 0xFFFFFFFF); + return (ulong) groupId << 32 | ((ulong) entityId & 0xFFFFFFFF); } public static explicit operator uint(EGID id) @@ -46,7 +46,10 @@ namespace Svelto.ECS } //in the way it's used, ulong must be always the same for each id/group - public static explicit operator ulong(EGID id) { return id._GID; } + public static explicit operator ulong(EGID id) + { + return id._GID; + } public bool Equals(EGID other) { @@ -72,7 +75,7 @@ namespace Svelto.ECS { return _GID.CompareTo(other._GID); } - + internal EGID(uint entityID, uint groupID) : this() { _GID = MAKE_GLOBAL_ID(entityID, groupID); @@ -81,14 +84,14 @@ namespace Svelto.ECS public override string ToString() { var value = groupID.ToName(); - + return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(value); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public EntityReference ToEntityReference(EntitiesDB entitiesDB) { return entitiesDB.GetEntityReference(this); } } -} +} \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/Core/EnginesRoot.DoubleBufferedEntitiesToAdd.cs b/com.sebaslab.svelto.ecs/Core/EnginesRoot.DoubleBufferedEntitiesToAdd.cs index 95028c5..e407d8b 100644 --- a/com.sebaslab.svelto.ecs/Core/EnginesRoot.DoubleBufferedEntitiesToAdd.cs +++ b/com.sebaslab.svelto.ecs/Core/EnginesRoot.DoubleBufferedEntitiesToAdd.cs @@ -7,173 +7,127 @@ namespace Svelto.ECS { internal class DoubleBufferedEntitiesToAdd { - const int MaximumNumberOfItemsPerFrameBeforeToClear = 100; + const int MAX_NUMBER_OF_ITEMS_PER_FRAME_BEFORE_TO_CLEAR = 100; - internal void Swap() + public DoubleBufferedEntitiesToAdd() { - Swap(ref current, ref other); - Swap(ref currentEntitiesCreatedPerGroup, ref otherEntitiesCreatedPerGroup); - } + _currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA; + _otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB; - void Swap(ref T item1, ref T item2) - { - var toSwap = item2; - item2 = item1; - item1 = toSwap; + current = _entityComponentsToAddBufferA; + other = _entityComponentsToAddBufferB; } public void ClearOther() { //do not clear the groups created so far, they will be reused, unless they are too many! var otherCount = other.count; - if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear) + if (otherCount > MAX_NUMBER_OF_ITEMS_PER_FRAME_BEFORE_TO_CLEAR) { - FasterDictionary[] otherValuesArray = other.unsafeValues; - for (int i = 0; i < otherCount; ++i) + var otherValuesArray = other.unsafeValues; + for (var i = 0; i < otherCount; ++i) { - var safeDictionariesCount = otherValuesArray[i].count; - ITypeSafeDictionary[] safeDictionaries = otherValuesArray[i].unsafeValues; + var safeDictionariesCount = otherValuesArray[i].count; + var safeDictionaries = otherValuesArray[i].unsafeValues; { - for (int j = 0; j < safeDictionariesCount; ++j) - { + for (var j = 0; j < safeDictionariesCount; ++j) //clear the dictionary of entities create do far (it won't allocate though) safeDictionaries[j].Dispose(); - } } } - + //reset the number of entities created so far - otherEntitiesCreatedPerGroup.FastClear(); + _otherEntitiesCreatedPerGroup.FastClear(); other.FastClear(); return; } { - FasterDictionary[] otherValuesArray = other.unsafeValues; - for (int i = 0; i < otherCount; ++i) + var otherValuesArray = other.unsafeValues; + for (var i = 0; i < otherCount; ++i) { - var safeDictionariesCount = otherValuesArray[i].count; - ITypeSafeDictionary[] safeDictionaries = otherValuesArray[i].unsafeValues; + var safeDictionariesCount = otherValuesArray[i].count; + var safeDictionaries = otherValuesArray[i].unsafeValues; //do not remove the dictionaries of entities per type created so far, they will be reused - if (safeDictionariesCount <= MaximumNumberOfItemsPerFrameBeforeToClear) + if (safeDictionariesCount <= MAX_NUMBER_OF_ITEMS_PER_FRAME_BEFORE_TO_CLEAR) { - for (int j = 0; j < safeDictionariesCount; ++j) - { + for (var j = 0; j < safeDictionariesCount; ++j) //clear the dictionary of entities create do far (it won't allocate though) safeDictionaries[j].FastClear(); - } } else { - for (int j = 0; j < safeDictionariesCount; ++j) - { + for (var j = 0; j < safeDictionariesCount; ++j) //clear the dictionary of entities create do far (it won't allocate though) safeDictionaries[j].Dispose(); - } otherValuesArray[i].FastClear(); } } //reset the number of entities created so far - otherEntitiesCreatedPerGroup.FastClear(); + _otherEntitiesCreatedPerGroup.FastClear(); } } - - //Before I tried for the third time to use a SparseSet instead of FasterDictionary, remember that - //while group indices are sequential, they may not be used in a sequential order. Sparseset needs - //entities to be created sequentially (the index cannot be managed externally) - internal FasterDictionary> current; - internal FasterDictionary> other; - - /// - /// To avoid extra allocation, I don't clear the groups, so I need an extra data structure - /// to keep count of the number of entities built this frame. At the moment the actual number - /// of entities built is not used - /// - FasterDictionary currentEntitiesCreatedPerGroup; - FasterDictionary otherEntitiesCreatedPerGroup; - - readonly FasterDictionary> - _entityComponentsToAddBufferA = - new FasterDictionary>(); - - readonly FasterDictionary> - _entityComponentsToAddBufferB = - new FasterDictionary>(); - - readonly FasterDictionary _entitiesCreatedPerGroupA = new FasterDictionary(); - readonly FasterDictionary _entitiesCreatedPerGroupB = new FasterDictionary(); - - public DoubleBufferedEntitiesToAdd() - { - currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA; - otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB; - - current = _entityComponentsToAddBufferA; - other = _entityComponentsToAddBufferB; - } public void Dispose() { { var otherValuesArray = other.unsafeValues; - for (int i = 0; i < other.count; ++i) + for (var i = 0; i < other.count; ++i) { var safeDictionariesCount = otherValuesArray[i].count; var safeDictionaries = otherValuesArray[i].unsafeValues; //do not remove the dictionaries of entities per type created so far, they will be reused - for (int j = 0; j < safeDictionariesCount; ++j) - { + for (var j = 0; j < safeDictionariesCount; ++j) //clear the dictionary of entities create do far (it won't allocate though) safeDictionaries[j].Dispose(); - } } } { var currentValuesArray = current.unsafeValues; - for (int i = 0; i < current.count; ++i) + for (var i = 0; i < current.count; ++i) { var safeDictionariesCount = currentValuesArray[i].count; var safeDictionaries = currentValuesArray[i].unsafeValues; //do not remove the dictionaries of entities per type created so far, they will be reused - for (int j = 0; j < safeDictionariesCount; ++j) - { + for (var j = 0; j < safeDictionariesCount; ++j) //clear the dictionary of entities create do far (it won't allocate though) safeDictionaries[j].Dispose(); - } } } } - internal void IncrementEntityCount(ExclusiveGroupStruct groupID) + internal bool AnyEntityCreated() { - currentEntitiesCreatedPerGroup.GetOrCreate(groupID)++; + return _currentEntitiesCreatedPerGroup.count > 0; } - internal bool AnyEntityCreated() + internal bool AnyOtherEntityCreated() { - return currentEntitiesCreatedPerGroup.count > 0; + return _otherEntitiesCreatedPerGroup.count > 0; } - internal bool AnyOtherEntityCreated() + internal void IncrementEntityCount(ExclusiveGroupStruct groupID) { - return otherEntitiesCreatedPerGroup.count > 0; + _currentEntitiesCreatedPerGroup.GetOrCreate(groupID)++; } internal void Preallocate (ExclusiveGroupStruct groupID, uint numberOfEntities, IComponentBuilder[] entityComponentsToBuild) { - void PreallocateDictionaries(FasterDictionary> fasterDictionary1) + void PreallocateDictionaries + (FasterDictionary> dic) { - FasterDictionary group = - fasterDictionary1.GetOrCreate((uint) groupID, () => new FasterDictionary()); + var group = dic.GetOrCreate(groupID, () => new FasterDictionary()); foreach (var componentBuilder in entityComponentsToBuild) { var entityComponentType = componentBuilder.GetEntityComponentType(); - var safeDictionary = @group.GetOrCreate(new RefWrapperType(entityComponentType) - , () => componentBuilder.CreateDictionary(numberOfEntities)); + var safeDictionary = group.GetOrCreate(new RefWrapperType(entityComponentType) + , () => componentBuilder + .CreateDictionary(numberOfEntities)); componentBuilder.Preallocate(safeDictionary, numberOfEntities); } } @@ -181,9 +135,48 @@ namespace Svelto.ECS PreallocateDictionaries(current); PreallocateDictionaries(other); - currentEntitiesCreatedPerGroup.GetOrCreate(groupID); - otherEntitiesCreatedPerGroup.GetOrCreate(groupID); + _currentEntitiesCreatedPerGroup.GetOrCreate(groupID); + _otherEntitiesCreatedPerGroup.GetOrCreate(groupID); + } + + internal void Swap() + { + Swap(ref current, ref other); + Swap(ref _currentEntitiesCreatedPerGroup, ref _otherEntitiesCreatedPerGroup); + } + + void Swap(ref T item1, ref T item2) + { + var toSwap = item2; + item2 = item1; + item1 = toSwap; } + + //Before I tried for the third time to use a SparseSet instead of FasterDictionary, remember that + //while group indices are sequential, they may not be used in a sequential order. Sparseset needs + //entities to be created sequentially (the index cannot be managed externally) + internal FasterDictionary> current; + internal FasterDictionary> other; + + readonly FasterDictionary _entitiesCreatedPerGroupA = + new FasterDictionary(); + + readonly FasterDictionary _entitiesCreatedPerGroupB = + new FasterDictionary(); + + readonly FasterDictionary> _entityComponentsToAddBufferA = + new FasterDictionary>(); + + readonly FasterDictionary> _entityComponentsToAddBufferB = + new FasterDictionary>(); + + /// + /// To avoid extra allocation, I don't clear the groups, so I need an extra data structure + /// to keep count of the number of entities built this frame. At the moment the actual number + /// of entities built is not used + /// + FasterDictionary _currentEntitiesCreatedPerGroup; + FasterDictionary _otherEntitiesCreatedPerGroup; } } } \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/Core/EnginesRoot.Engines.cs b/com.sebaslab.svelto.ecs/Core/EnginesRoot.Engines.cs index 2247f6f..bed5cc8 100644 --- a/com.sebaslab.svelto.ecs/Core/EnginesRoot.Engines.cs +++ b/com.sebaslab.svelto.ecs/Core/EnginesRoot.Engines.cs @@ -101,7 +101,7 @@ namespace Svelto.ECS try { entityList.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemoveOnDispose, profiler - , new ExclusiveGroupStruct(groups.Key)); + , groups.Key); } catch (Exception e) { diff --git a/com.sebaslab.svelto.ecs/Core/EnginesRoot.Entities.cs b/com.sebaslab.svelto.ecs/Core/EnginesRoot.Entities.cs index 126822d..ab1035a 100644 --- a/com.sebaslab.svelto.ecs/Core/EnginesRoot.Entities.cs +++ b/com.sebaslab.svelto.ecs/Core/EnginesRoot.Entities.cs @@ -26,7 +26,7 @@ namespace Svelto.ECS { CheckAddEntityID(entityID, descriptorType); - DBC.ECS.Check.Require((uint)entityID.groupID != 0 + DBC.ECS.Check.Require(entityID.groupID.isInvalid == false , "invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?"); var reference = _entityLocator.ClaimReference(); @@ -152,7 +152,7 @@ namespace Svelto.ECS var wrapper = new RefWrapperType(entityComponentType); ITypeSafeDictionary fromTypeSafeDictionary = - GetTypeSafeDictionary((uint) entityGID.groupID, fromGroup, wrapper); + GetTypeSafeDictionary(entityGID.groupID, fromGroup, wrapper); #if DEBUG && !PROFILE_SVELTO if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) @@ -176,7 +176,7 @@ namespace Svelto.ECS { //add all the entities var refWrapper = new RefWrapperType(entityComponentType); - var fromTypeSafeDictionary = GetTypeSafeDictionary((uint) entityGID.groupID, fromGroup, refWrapper); + var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper); ITypeSafeDictionary toEntitiesDictionary = null; if (toGroup != null) @@ -201,7 +201,7 @@ namespace Svelto.ECS using (sampler.Sample("RemoveEntityFromDictionary")) { var refWrapper = new RefWrapperType(entityComponentType); - var fromTypeSafeDictionary = GetTypeSafeDictionary((uint) entityGID.groupID, fromGroup, refWrapper); + var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper); fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID); } @@ -224,7 +224,7 @@ namespace Svelto.ECS FasterDictionary fromGroup = GetDBGroup(fromIdGroupId); FasterDictionary toGroup = GetOrCreateDBGroup(toGroupId); - _entityLocator.UpdateAllGroupReferenceLocators(fromIdGroupId, (uint) toGroupId); + _entityLocator.UpdateAllGroupReferenceLocators(fromIdGroupId, toGroupId); foreach (var dictionaryOfEntities in fromGroup) { @@ -235,12 +235,11 @@ namespace Svelto.ECS var groupsOfEntityType = _groupsPerEntity[dictionaryOfEntities.Key]; var groupOfEntitiesToCopyAndClear = groupsOfEntityType[fromIdGroupId]; - toEntitiesDictionary.AddEntitiesFromDictionary(groupOfEntitiesToCopyAndClear, (uint) toGroupId, this); + toEntitiesDictionary.AddEntitiesFromDictionary(groupOfEntitiesToCopyAndClear, toGroupId, this); //call all the MoveTo callbacks dictionaryOfEntities.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesSwap - , dictionaryOfEntities.Value, new ExclusiveGroupStruct(fromIdGroupId) - , new ExclusiveGroupStruct(toGroupId), profiler); + , dictionaryOfEntities.Value, fromIdGroupId, toGroupId, profiler); //todo: if it's unmanaged, I can use fastclear groupOfEntitiesToCopyAndClear.Clear(); @@ -254,7 +253,7 @@ namespace Svelto.ECS if (_groupEntityComponentsDB.TryGetValue(fromIdGroupId , out FasterDictionary fromGroup) == false) - throw new ECSException("Group doesn't exist: ".FastConcat((uint) fromIdGroupId)); + throw new ECSException("Group doesn't exist: ".FastConcat(fromIdGroupId.ToName())); return fromGroup; } @@ -289,11 +288,11 @@ namespace Svelto.ECS } static ITypeSafeDictionary GetTypeSafeDictionary - (uint groupID, FasterDictionary @group, RefWrapperType refWrapper) + (ExclusiveGroupStruct groupID, FasterDictionary @group, RefWrapperType refWrapper) { if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false) { - throw new ECSException("no group found: ".FastConcat(groupID)); + throw new ECSException("no group found: ".FastConcat(groupID.ToName())); } return fromTypeSafeDictionary; @@ -308,7 +307,7 @@ namespace Svelto.ECS foreach (var dictionaryOfEntities in dictionariesOfEntities) { dictionaryOfEntities.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemove, profiler - , new ExclusiveGroupStruct(groupID)); + , groupID); dictionaryOfEntities.Value.FastClear(); var groupsOfEntityType = _groupsPerEntity[dictionaryOfEntities.Key]; diff --git a/com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFunctions.cs b/com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFunctions.cs index 23e045a..2aa097d 100644 --- a/com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFunctions.cs +++ b/com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFunctions.cs @@ -28,7 +28,7 @@ namespace Svelto.ECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveEntity(EGID entityEGID) where T : IEntityDescriptor, new() { - DBC.ECS.Check.Require((uint)entityEGID.groupID != 0, "invalid group detected"); + DBC.ECS.Check.Require(entityEGID.groupID.isInvalid == false, "invalid group detected"); var descriptorComponentsToBuild = EntityDescriptorTemplate.descriptor.componentsToBuild; _enginesRoot.Target.CheckRemoveEntityID(entityEGID, TypeCache.type); @@ -40,7 +40,7 @@ namespace Svelto.ECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveEntitiesFromGroup(ExclusiveBuildGroup groupID) { - DBC.ECS.Check.Require((uint)groupID != 0, "invalid group detected"); + DBC.ECS.Check.Require(groupID.isInvalid == false, "invalid group detected"); _enginesRoot.Target.RemoveGroupID(groupID); _enginesRoot.Target.QueueEntitySubmitOperation( @@ -108,7 +108,7 @@ namespace Svelto.ECS public void SwapEntityGroup(EGID fromID, ExclusiveBuildGroup toGroupID) where T : IEntityDescriptor, new() { - SwapEntityGroup(fromID, new EGID(fromID.entityID, (uint) toGroupID)); + SwapEntityGroup(fromID, new EGID(fromID.entityID, toGroupID)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -147,8 +147,8 @@ namespace Svelto.ECS public void SwapEntityGroup(EGID fromID, EGID toID) where T : IEntityDescriptor, new() { - DBC.ECS.Check.Require((uint)fromID.groupID != 0, "invalid group detected"); - DBC.ECS.Check.Require((uint)toID.groupID != 0, "invalid group detected"); + DBC.ECS.Check.Require(fromID.groupID.isInvalid == false, "invalid group detected"); + DBC.ECS.Check.Require(toID.groupID.isInvalid == false, "invalid group detected"); var enginesRootTarget = _enginesRoot.Target; var descriptorComponentsToBuild = EntityDescriptorTemplate.descriptor.componentsToBuild; diff --git a/com.sebaslab.svelto.ecs/Core/EnginesRoot.Submission.cs b/com.sebaslab.svelto.ecs/Core/EnginesRoot.Submission.cs index eee0275..b360598 100644 --- a/com.sebaslab.svelto.ecs/Core/EnginesRoot.Submission.cs +++ b/com.sebaslab.svelto.ecs/Core/EnginesRoot.Submission.cs @@ -97,7 +97,7 @@ namespace Svelto.ECS //each group is indexed by entity view type. for each type there is a dictionary indexed by entityID foreach (var groupToSubmit in _groupedEntityToAdd.other) { - var groupID = new ExclusiveGroupStruct(groupToSubmit.Key); + var groupID = groupToSubmit.Key; var groupDB = GetOrCreateDBGroup(groupID); //add the entityComponents in the group @@ -111,7 +111,7 @@ namespace Svelto.ECS groupID, groupDB, wrapper, targetTypeSafeDictionary); //Fill the DB with the entity components generated this frame. - dbDic.AddEntitiesFromDictionary(targetTypeSafeDictionary, (uint) groupID, this); + dbDic.AddEntitiesFromDictionary(targetTypeSafeDictionary, groupID, this); } } } @@ -122,7 +122,7 @@ namespace Svelto.ECS { foreach (var groupToSubmit in _groupedEntityToAdd.other) { - var groupID = new ExclusiveGroupStruct(groupToSubmit.Key); + var groupID = groupToSubmit.Key; var groupDB = GetDBGroup(groupID); //entityComponentsToSubmit is the array of components found in the groupID per component type. //if there are N entities to submit, and M components type to add for each entity, this foreach will run NxM times. @@ -131,8 +131,7 @@ namespace Svelto.ECS var realDic = groupDB[new RefWrapperType(entityComponentsToSubmit.Key)]; entityComponentsToSubmit.Value.ExecuteEnginesAddOrSwapCallbacks( - _reactiveEnginesAddRemove, realDic, null, new ExclusiveGroupStruct(groupID) - , in profiler); + _reactiveEnginesAddRemove, realDic, null, groupID, in profiler); numberOfOperations += entityComponentsToSubmit.Value.count; diff --git a/com.sebaslab.svelto.ecs/Core/EntitiesDB.FindGroups.cs b/com.sebaslab.svelto.ecs/Core/EntitiesDB.FindGroups.cs index 329cc60..817ef40 100644 --- a/com.sebaslab.svelto.ecs/Core/EntitiesDB.FindGroups.cs +++ b/com.sebaslab.svelto.ecs/Core/EntitiesDB.FindGroups.cs @@ -61,7 +61,7 @@ namespace Svelto.ECS //if the same group is found used with both T1 and T2 if (groupID == fasterDictionaryNodes2[j].key) { - result.Add(new ExclusiveGroupStruct(groupID)); + result.Add(groupID); break; } } diff --git a/com.sebaslab.svelto.ecs/Core/EntityFactory.cs b/com.sebaslab.svelto.ecs/Core/EntityFactory.cs index 0c723ed..b686b56 100644 --- a/com.sebaslab.svelto.ecs/Core/EntityFactory.cs +++ b/com.sebaslab.svelto.ecs/Core/EntityFactory.cs @@ -28,11 +28,11 @@ namespace Svelto.ECS.Internal static FasterDictionary FetchEntityGroup (ExclusiveGroupStruct groupID, EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityComponentsByType) { - if (groupEntityComponentsByType.current.TryGetValue((uint) groupID, out var group) == false) + if (groupEntityComponentsByType.current.TryGetValue(groupID, out var group) == false) { group = new FasterDictionary(); - groupEntityComponentsByType.current.Add((uint) groupID, group); + groupEntityComponentsByType.current.Add(groupID, group); } //track the number of entities created so far in the group. @@ -49,9 +49,9 @@ namespace Svelto.ECS.Internal #endif ) { -#if DEBUG && !PROFILE_SVELTO +#if DEBUG && !PROFILE_SVELTO DBC.ECS.Check.Require(componentBuilders != null, $"Invalid Entity Descriptor {descriptorType}"); -#endif +#endif var numberOfComponents = componentBuilders.Length; #if DEBUG && !PROFILE_SVELTO @@ -81,7 +81,9 @@ namespace Svelto.ECS.Internal , IComponentBuilder componentBuilder, IEnumerable implementors) { var entityComponentType = componentBuilder.GetEntityComponentType(); - var safeDictionary = group.GetOrCreate(new RefWrapperType(entityComponentType), (ref IComponentBuilder cb) => cb.CreateDictionary(1), ref componentBuilder); + var safeDictionary = group.GetOrCreate(new RefWrapperType(entityComponentType) + , (ref IComponentBuilder cb) => cb.CreateDictionary(1) + , ref componentBuilder); //if the safeDictionary hasn't been created yet, it will be created inside this method. componentBuilder.BuildEntityAndAddToList(safeDictionary, entityID, implementors); diff --git a/com.sebaslab.svelto.ecs/Core/EntityReference/EnginesRoot.LocatorMap.cs b/com.sebaslab.svelto.ecs/Core/EntityReference/EnginesRoot.LocatorMap.cs index 69dca5d..44b5151 100644 --- a/com.sebaslab.svelto.ecs/Core/EntityReference/EnginesRoot.LocatorMap.cs +++ b/com.sebaslab.svelto.ecs/Core/EntityReference/EnginesRoot.LocatorMap.cs @@ -129,7 +129,7 @@ namespace Svelto.ECS _egidToReferenceMap.Remove(groupId); } - internal void UpdateAllGroupReferenceLocators(ExclusiveGroupStruct fromGroupId, uint toGroupId) + internal void UpdateAllGroupReferenceLocators(ExclusiveGroupStruct fromGroupId, ExclusiveGroupStruct toGroupId) { if (_egidToReferenceMap.TryGetValue(fromGroupId, out var groupMap) == false) return; diff --git a/com.sebaslab.svelto.ecs/Core/GroupHashMap.cs b/com.sebaslab.svelto.ecs/Core/GroupHashMap.cs index 6702aae..9297545 100644 --- a/com.sebaslab.svelto.ecs/Core/GroupHashMap.cs +++ b/com.sebaslab.svelto.ecs/Core/GroupHashMap.cs @@ -11,7 +11,6 @@ namespace Svelto.ECS /// /// c# Static constructors are guaranteed to be thread safe /// - public static void Init() { List assemblies = AssemblyUtility.GetCompatibleAssemblies(); @@ -19,12 +18,13 @@ namespace Svelto.ECS { try { - var typeOfExclusiveGroup = typeof(ExclusiveGroup); - var typeOfExclusiveGroupStruct = typeof(ExclusiveGroupStruct); - + var typeOfExclusiveGroup = typeof(ExclusiveGroup); + var typeOfExclusiveGroupStruct = typeof(ExclusiveGroupStruct); + foreach (Type type in AssemblyUtility.GetTypesSafe(assembly)) { - if (type != null && type.IsClass && type.IsSealed && type.IsAbstract) //this means only static classes + if (type != null && type.IsClass && type.IsSealed + && type.IsAbstract) //this means only static classes { var fields = type.GetFields(); @@ -34,25 +34,27 @@ namespace Svelto.ECS { if (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType)) { - var group = (ExclusiveGroup)field.GetValue(null); - var name = $"{type.FullName}.{field.Name}"; -#if DEBUG - GroupNamesMap.idToName[(uint)@group] = $"{name} {(uint)group})"; + var group = (ExclusiveGroup) field.GetValue(null); +#if DEBUG + GroupNamesMap.idToName[@group] = + $"{$"{type.FullName}.{field.Name}"} {group.id})"; #endif - RegisterGroup(group, name); + //The hashname is independent from the actual group ID. this is fundamental because it is want + //guarantees the hash to be the same across different machines + RegisterGroup(group, $"{type.FullName}.{field.Name}"); } else - { if (typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType)) { - var group = (ExclusiveGroupStruct)field.GetValue(null); - var name = $"{type.FullName}.{field.Name}"; -#if DEBUG - GroupNamesMap.idToName[(uint)@group] = $"{name} {(uint)group})"; + var group = (ExclusiveGroupStruct) field.GetValue(null); +#if DEBUG + GroupNamesMap.idToName[@group] = + $"{$"{type.FullName}.{field.Name}"} {group.id})"; #endif - RegisterGroup(@group, name); + //The hashname is independent from the actual group ID. this is fundamental because it is want + //guarantees the hash to be the same across different machines + RegisterGroup(@group, $"{type.FullName}.{field.Name}"); } - } } } } @@ -61,45 +63,53 @@ namespace Svelto.ECS catch { Console.LogDebugWarning( - "something went wrong while gathering group names on the assembly: ".FastConcat(assembly.FullName)); + "something went wrong while gathering group names on the assembly: ".FastConcat( + assembly.FullName)); } } } + /// + /// The hashname is independent from the actual group ID. this is fundamental because it is want + /// guarantees the hash to be the same across different machines + /// + /// + /// + /// public static void RegisterGroup(ExclusiveGroupStruct exclusiveGroupStruct, string name) { //Group already registered by another field referencing the same group if (_hashByGroups.ContainsKey(exclusiveGroupStruct)) return; - + var nameHash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(name)); - - if(_groupsByHash.ContainsKey(nameHash)) + + if (_groupsByHash.ContainsKey(nameHash)) throw new ECSException($"Group hash collision with {name} and {_groupsByHash[nameHash]}"); - - Console.LogDebug($"Registering group {name} with ID {(uint)exclusiveGroupStruct} to {nameHash}"); - + + Console.LogDebug($"Registering group {name} with ID {exclusiveGroupStruct.id} to {nameHash}"); + _groupsByHash.Add(nameHash, exclusiveGroupStruct); _hashByGroups.Add(exclusiveGroupStruct, nameHash); } - + public static uint GetHashFromGroup(ExclusiveGroupStruct groupStruct) { #if DEBUG if (_hashByGroups.ContainsKey(groupStruct) == false) throw new ECSException($"Attempted to get hash from unregistered group {groupStruct}"); #endif - + return _hashByGroups[groupStruct]; } - + public static ExclusiveGroupStruct GetGroupFromHash(uint groupHash) { #if DEBUG if (_groupsByHash.ContainsKey(groupHash) == false) throw new ECSException($"Attempted to get group from unregistered hash {groupHash}"); #endif - + return _groupsByHash[groupHash]; } diff --git a/com.sebaslab.svelto.ecs/Core/GroupNamesMap.cs b/com.sebaslab.svelto.ecs/Core/GroupNamesMap.cs index 2731ce6..5df712f 100644 --- a/com.sebaslab.svelto.ecs/Core/GroupNamesMap.cs +++ b/com.sebaslab.svelto.ecs/Core/GroupNamesMap.cs @@ -4,23 +4,23 @@ using Svelto.ECS; static class GroupNamesMap { #if DEBUG - static GroupNamesMap() { idToName = new Dictionary(); } + static GroupNamesMap() { idToName = new Dictionary(); } - internal static readonly Dictionary idToName; + internal static readonly Dictionary idToName; #endif #if DEBUG public static string ToName(this in ExclusiveGroupStruct group) { - var idToName = GroupNamesMap.idToName; - if (idToName.TryGetValue((uint)@group, out var name) == false) - name = $""; + Dictionary idToName = GroupNamesMap.idToName; + if (idToName.TryGetValue(@group, out var name) == false) + name = $""; return name; } #else public static string ToName(this in ExclusiveGroupStruct group) { - return ((uint)group).ToString(); + return ((uint)group.ToIDAndBitmask()).ToString(); } #endif } \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/Core/Groups/ExclusiveBuildGroup.cs b/com.sebaslab.svelto.ecs/Core/Groups/ExclusiveBuildGroup.cs index 465c841..e2e395d 100644 --- a/com.sebaslab.svelto.ecs/Core/Groups/ExclusiveBuildGroup.cs +++ b/com.sebaslab.svelto.ecs/Core/Groups/ExclusiveBuildGroup.cs @@ -19,12 +19,7 @@ namespace Svelto.ECS public static implicit operator ExclusiveGroupStruct(ExclusiveBuildGroup group) { - return new ExclusiveGroupStruct(group.group); - } - - public static explicit operator uint(ExclusiveBuildGroup groupStruct) - { - return (uint) groupStruct.@group; + return group.group; } public override string ToString() @@ -32,6 +27,7 @@ namespace Svelto.ECS return this.group.ToName(); } - internal ExclusiveGroupStruct @group { get; } + internal ExclusiveGroupStruct @group { get; } + public bool isInvalid => group == ExclusiveGroupStruct.Invalid; } } \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroup.cs b/com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroup.cs index 2047525..2bf6676 100644 --- a/com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroup.cs +++ b/com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroup.cs @@ -23,59 +23,49 @@ namespace Svelto.ECS { public const uint MaxNumberOfExclusiveGroups = 2 << 20; - public ExclusiveGroup(ExclusiveGroupBitmask bitmask = 0) + public ExclusiveGroup() { - _group = ExclusiveGroupStruct.Generate((byte) bitmask); + _group = ExclusiveGroupStruct.Generate(); } - public ExclusiveGroup(string recognizeAs, ExclusiveGroupBitmask bitmask = 0) + public ExclusiveGroup(string recognizeAs) { - _group = ExclusiveGroupStruct.Generate((byte) bitmask); + _group = ExclusiveGroupStruct.Generate(); _knownGroups.Add(recognizeAs, _group); } - + + public ExclusiveGroup(ExclusiveGroupBitmask bitmask) + { + _group = ExclusiveGroupStruct.Generate((byte) bitmask); + } + public ExclusiveGroup(ushort range) { - _group = new ExclusiveGroupStruct(range); + _group = ExclusiveGroupStruct.GenerateWithRange(range); #if DEBUG _range = range; #endif } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Disable() - { - _group.Disable(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Enable() - { - _group.Enable(); - } - public static implicit operator ExclusiveGroupStruct(ExclusiveGroup group) { return group._group; } - public static explicit operator uint(ExclusiveGroup group) - { - return (uint) @group._group; - } - - public static ExclusiveGroupStruct operator +(ExclusiveGroup a, uint b) + public static ExclusiveGroupStruct operator+(ExclusiveGroup @group, uint b) { #if DEBUG - if (a._range == 0) - throw new ECSException($"Adding values to a not ranged ExclusiveGroup: {(uint) a}"); - if (b >= a._range) - throw new ECSException($"Using out of range group: {(uint) a} + {b}"); + if (@group._range == 0) + throw new ECSException($"Adding values to a not ranged ExclusiveGroup: {@group.id}"); + if (b >= @group._range) + throw new ECSException($"Using out of range group: {@group.id} + {b}"); #endif - return a._group + b; + return group._group + b; } + public uint id => _group.id; + //todo document the use case for this method public static ExclusiveGroupStruct Search(string holderGroupName) { @@ -96,6 +86,6 @@ namespace Svelto.ECS #if DEBUG readonly ushort _range; #endif - ExclusiveGroupStruct _group; + readonly ExclusiveGroupStruct _group; } } \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroupStruct.cs b/com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroupStruct.cs index e38290e..bee1781 100644 --- a/com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroupStruct.cs +++ b/com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroupStruct.cs @@ -1,26 +1,26 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Svelto.DataStructures; +using System.Threading; namespace Svelto.ECS { [StructLayout(LayoutKind.Explicit, Size = 4)] //the type doesn't implement IEqualityComparer, what implements it is a custom comparer - public struct ExclusiveGroupStruct : IEquatable, IComparable + public readonly struct ExclusiveGroupStruct : IEquatable, IComparable { public static readonly ExclusiveGroupStruct Invalid = default; //must stay here because of Burst public ExclusiveGroupStruct(byte[] data, uint pos):this() { - _id = (uint)( + _idInternal = (uint)( data[pos] | data[++pos] << 8 | data[++pos] << 16 ); _bytemask = (byte) (data[++pos] << 24); - DBC.ECS.Check.Ensure(_id < _globalId, "Invalid group ID deserialiased"); + DBC.ECS.Check.Ensure(id < _globalId, "Invalid group ID deserialiased"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -32,7 +32,7 @@ namespace Svelto.ECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { - return (int) _id; + return (int) id; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -50,86 +50,123 @@ namespace Svelto.ECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ExclusiveGroupStruct other) { - return other._id == _id; +#if DEBUG && !PROFILE_SVELTO + if ((other.id != this.id || other._bytemask == this._bytemask) == false) + throw new ECSException( + "if the groups are correctly initialised, two groups with the same ID and different bitmask cannot exist"); +#endif + return other.id == this.id; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(ExclusiveGroupStruct other) { - return other._id.CompareTo(_id); + return other.id.CompareTo(id); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool IsEnabled() + public bool IsEnabled() { return (_bytemask & (byte)ExclusiveGroupBitmask.DISABLED_BIT) == 0; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void Disable() - { - _bytemask |= (byte)ExclusiveGroupBitmask.DISABLED_BIT; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void Enable() - { - _bytemask &= (byte)(~ExclusiveGroupBitmask.DISABLED_BIT); - } - public override string ToString() { return this.ToName(); } + + public bool isInvalid => this == Invalid; + public uint id => _idInternal & 0xFFFFFF; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator uint(ExclusiveGroupStruct groupStruct) - { - return groupStruct._id; - } + public uint ToIDAndBitmask() => _idInternal; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, uint b) { - var group = new ExclusiveGroupStruct {_id = a._id + b}; - + var aID = a.id + b; +#if DEBUG && !PROFILE_SVELTO + if (aID >= 0xFFFFFF) + throw new IndexOutOfRangeException(); +#endif + var group = new ExclusiveGroupStruct(aID); + return @group; } - - internal static ExclusiveGroupStruct Generate(byte bitmask = 0) + + internal static ExclusiveGroupStruct Generate() { - ExclusiveGroupStruct groupStruct; - - groupStruct._id = _globalId; - groupStruct._bytemask = bitmask; - DBC.ECS.Check.Require(_globalId + 1 < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created"); - _globalId++; + var newValue = Interlocked.Increment(ref _staticGlobalID); + + ExclusiveGroupStruct groupStruct = new ExclusiveGroupStruct((uint) newValue - (uint) 1); + + DBC.ECS.Check.Require(_globalId < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created"); return groupStruct; } - internal ExclusiveGroupStruct(ExclusiveGroupStruct @group):this() { this = group; } + internal static ExclusiveGroupStruct Generate(byte bitmask) + { + var newValue = Interlocked.Increment(ref _staticGlobalID); + + ExclusiveGroupStruct groupStruct = new ExclusiveGroupStruct((uint) newValue - (uint) 1, bitmask); + + DBC.ECS.Check.Require(_globalId < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created"); + + return groupStruct; + } /// - /// Use this constructor to reserve N groups + /// Use this to reserve N groups. We of course assign the current ID and then increment the index + /// by range so that the next reserved index will take the range in consideration. This method is used + /// internally by ExclusiveGroup. /// - internal ExclusiveGroupStruct(ushort range):this() + internal static ExclusiveGroupStruct GenerateWithRange(ushort range) { - _id = _globalId; - DBC.ECS.Check.Require(_globalId + range < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created"); - _globalId += range; + var newValue = Interlocked.Add(ref _staticGlobalID, (int)range); + + ExclusiveGroupStruct groupStruct = new ExclusiveGroupStruct((uint) newValue - (uint)range); + + DBC.ECS.Check.Require(_globalId < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created"); + + return groupStruct; } + /// + /// used internally only by the framework to convert uint in to groups. ID must be generated by the framework + /// so only the framework can assure that this method is not being abused + /// internal ExclusiveGroupStruct(uint groupID):this() { - DBC.ECS.Check.Require(groupID < 0xFFFFFF); - - _id = groupID; +#if DEBUG && !PROFILE_SVELTO + if (groupID >= 0xFFFFFF) + throw new IndexOutOfRangeException(); +#endif + _idInternal = groupID; + } + + ExclusiveGroupStruct(uint groupID, byte bytemask):this() + { +#if DEBUG && !PROFILE_SVELTO + if (groupID >= 0xFFFFFF) + throw new IndexOutOfRangeException(); +#endif + _idInternal = groupID; + _bytemask = bytemask; + } + + static ExclusiveGroupStruct() + { + _staticGlobalID = 1; } - [FieldOffset(0)] uint _id; - [FieldOffset(3)] byte _bytemask; + [FieldOffset(0)] readonly uint _idInternal; + //byte mask can be used to add special flags to specific groups that can be checked for example when swapping groups + //however at the moment we are not letting the user access it, because if we do so we should give access only to + //4 bits are the other 4 bits will stay reserved for Svelto use (at the moment of writing using only the disable + //bit) + [FieldOffset(3)] readonly byte _bytemask; - static uint _globalId = 1; //it starts from 1 because default EGID is considered not initialized value + static int _staticGlobalID; + static uint _globalId => (uint) _staticGlobalID; } } \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/Core/Groups/GroupCompound.cs b/com.sebaslab.svelto.ecs/Core/Groups/GroupCompound.cs index 3a29e63..08940bf 100644 --- a/com.sebaslab.svelto.ecs/Core/Groups/GroupCompound.cs +++ b/com.sebaslab.svelto.ecs/Core/Groups/GroupCompound.cs @@ -6,10 +6,10 @@ using Svelto.DataStructures; namespace Svelto.ECS { /// - /// This mechanism is not for thread-safety but to be sure that all the permutations of group tags always - /// point to the same group ID. - /// A group compound can generate several permutation of tags, so that the order of the tag doesn't matter, - /// but for this to work, each permutation must be identified by the same ID (generated by the unique combination) + /// This mechanism is not for thread-safety but to be sure that all the permutations of group tags always + /// point to the same group ID. + /// A group compound can generate several permutation of tags, so that the order of the tag doesn't matter, + /// but for this to work, each permutation must be identified by the same ID (generated by the unique combination) /// static class GroupCompoundInitializer { @@ -23,34 +23,26 @@ namespace Svelto.ECS where G3 : GroupTag where G4 : GroupTag { - static readonly FasterList _Groups; - static readonly HashSet _GroupsHashSet; - - public static FasterReadOnlyList Groups => - new FasterReadOnlyList(_Groups); - - public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); - - static int isInitializing; - static GroupCompound() { //avoid race conditions if compounds are using on multiple thread if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 && GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value == false) { - _Groups = new FasterList(1); + var group = new ExclusiveGroup(); //todo: it's a bit of a waste to create a class here even if this is a static constructor - var group = new ExclusiveGroup(); + _Groups = new FasterList(1); _Groups.Add(group); - var name = - $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint) group}"; #if DEBUG - GroupNamesMap.idToName[(uint) group] = name; + var name = + $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint) group.id}"; + GroupNamesMap.idToName[group] = name; #endif - GroupHashMap.RegisterGroup(group, name); - + //The hashname is independent from the actual group ID. this is fundamental because it is want + //guarantees the hash to be the same across different machines + GroupHashMap.RegisterGroup(group, typeof(GroupCompound).FullName); + _GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _)); GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = true; @@ -80,7 +72,7 @@ namespace Svelto.ECS GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; - + //all the permutations are warmed up now GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = false; @@ -107,7 +99,7 @@ namespace Svelto.ECS GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; - + GroupCompound.Add(group); GroupCompound.Add(group); GroupCompound.Add(group); @@ -129,66 +121,56 @@ namespace Svelto.ECS } } - internal static void Add(ExclusiveGroupStruct @group) - { - for (int i = 0; i < _Groups.count; ++i) - if (_Groups[i] == group) - throw new Exception("temporary must be transformed in unit test"); - - _Groups.Add(group); - _GroupsHashSet.Add(group); - } - - public static bool Includes(ExclusiveGroupStruct @group) - { - return _GroupsHashSet.Contains(@group); - } - } - - public abstract class GroupCompound - where G1 : GroupTag where G2 : GroupTag where G3 : GroupTag - { - static readonly FasterList _Groups; - static readonly HashSet _GroupsHashSet; - public static FasterReadOnlyList Groups => new FasterReadOnlyList(_Groups); public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); - static int isInitializing; + public static bool Includes(ExclusiveGroupStruct group) + { + return _GroupsHashSet.Contains(group); + } internal static void Add(ExclusiveGroupStruct group) { +#if DEBUG && !PROFILE_SVELTO for (var i = 0; i < _Groups.count; ++i) if (_Groups[i] == group) - throw new Exception("temporary must be transformed in unit test"); + throw new Exception("this test must be transformed in unit test"); +#endif _Groups.Add(group); _GroupsHashSet.Add(group); } + + static readonly FasterList _Groups; + static readonly HashSet _GroupsHashSet; - public static bool Includes(ExclusiveGroupStruct @group) - { - return _GroupsHashSet.Contains(@group); - } + //we are changing this with Interlocked, so it cannot be readonly + static int isInitializing; + } + public abstract class GroupCompound + where G1 : GroupTag where G2 : GroupTag where G3 : GroupTag + { static GroupCompound() { - if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 && - GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value == false) + if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 + && GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value == false) { - _Groups = new FasterList(1); + var group = new ExclusiveGroup(); //todo: it's a bit of a waste to create a class here even if this is a static constructor - var group = new ExclusiveGroup(); + _Groups = new FasterList(1); _Groups.Add(group); - var name = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name} ID {(uint) group}"; #if DEBUG - GroupNamesMap.idToName[(uint) group] = name; + var name = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name} ID {(uint) group.id}"; + GroupNamesMap.idToName[group] = name; #endif - GroupHashMap.RegisterGroup(group, name); - + //The hashname is independent from the actual group ID. this is fundamental because it is want + //guarantees the hash to be the same across different machines + GroupHashMap.RegisterGroup(group, typeof(GroupCompound).FullName); + _GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _)); GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = true; @@ -208,7 +190,7 @@ namespace Svelto.ECS GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; - + GroupCompound.Add(group); // and must share the same array GroupCompound.Add(group); GroupCompound.Add(group); @@ -220,122 +202,157 @@ namespace Svelto.ECS GroupTag.Add(group); } } - } - - public abstract class GroupCompound where G1 : GroupTag where G2 : GroupTag - { - static readonly FasterList _Groups; - static readonly HashSet _GroupsHashSet; public static FasterReadOnlyList Groups => new FasterReadOnlyList(_Groups); public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); - static int isInitializing; + public static bool Includes(ExclusiveGroupStruct group) + { + return _GroupsHashSet.Contains(group); + } internal static void Add(ExclusiveGroupStruct group) { +#if DEBUG && !PROFILE_SVELTO for (var i = 0; i < _Groups.count; ++i) if (_Groups[i] == group) - throw new Exception("temporary must be transformed in unit test"); + throw new Exception("this test must be transformed in unit test"); +#endif _Groups.Add(group); _GroupsHashSet.Add(group); } + + static readonly FasterList _Groups; + static readonly HashSet _GroupsHashSet; - public static bool Includes(ExclusiveGroupStruct @group) - { - return _GroupsHashSet.Contains(@group); - } + //we are changing this with Interlocked, so it cannot be readonly + static int isInitializing; + } + public abstract class GroupCompound where G1 : GroupTag where G2 : GroupTag + { static GroupCompound() { - if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 && - GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value == false) + if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 + && GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value == false) { - var group = new ExclusiveGroup(); + var group = new ExclusiveGroup(); //todo: it's a bit of a waste to create a class here even if this is a static constructor _Groups = new FasterList(1); _Groups.Add(group); - var groupName = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {(uint) group}"; #if DEBUG - GroupNamesMap.idToName[(uint) group] = groupName; + GroupNamesMap.idToName[group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {group.id}"; #endif - GroupHashMap.RegisterGroup(group, groupName); - + //The hashname is independent from the actual group ID. this is fundamental because it is want + //guarantees the hash to be the same across different machines + GroupHashMap.RegisterGroup(group, typeof(GroupCompound).FullName); + _GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _)); GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = true; GroupCompound._Groups = _Groups; GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = false; GroupCompound._GroupsHashSet = _GroupsHashSet; - + //every abstract group preemptively adds this group, it may or may not be empty in future GroupTag.Add(group); GroupTag.Add(group); } } - } - - /// - ///A Group Tag holds initially just a group, itself. However the number of groups can grow with the number of - ///combinations of GroupTags including this one. This because a GroupTag is an adjective and different entities - ///can use the same adjective together with other ones. However since I need to be able to iterate over all the - ///groups with the same adjective, a group tag needs to hold all the groups sharing it. - /// - /// - public abstract class GroupTag where T : GroupTag - { - static readonly FasterList _Groups = new FasterList(1); - static readonly HashSet _GroupsHashSet; public static FasterReadOnlyList Groups => new FasterReadOnlyList(_Groups); public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); + public static bool Includes(ExclusiveGroupStruct group) + { + return _GroupsHashSet.Contains(group); + } + + internal static void Add(ExclusiveGroupStruct group) + { +#if DEBUG && !PROFILE_SVELTO + for (var i = 0; i < _Groups.count; ++i) + if (_Groups[i] == group) + throw new Exception("this test must be transformed in unit test"); +#endif + + _Groups.Add(group); + _GroupsHashSet.Add(group); + } + + static readonly FasterList _Groups; + static readonly HashSet _GroupsHashSet; + static int isInitializing; + } + /// + /// A Group Tag holds initially just a group, itself. However the number of groups can grow with the number of + /// combinations of GroupTags including this one. This because a GroupTag is an adjective and different entities + /// can use the same adjective together with other ones. However since I need to be able to iterate over all the + /// groups with the same adjective, a group tag needs to hold all the groups sharing it. + /// + /// + public abstract class GroupTag where T : GroupTag + { static GroupTag() { if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0) { var group = new ExclusiveGroup(); _Groups.Add(group); - +#if DEBUG var typeInfo = typeof(T); + var name = $"Compound: {typeInfo.Name} ID {(uint) group.id}"; - var name = $"Compound: {typeInfo.Name} ID {(uint) @group}"; -#if DEBUG var typeInfoBaseType = typeInfo.BaseType; - if (typeInfoBaseType.GenericTypeArguments[0] != typeInfo) + if (typeInfoBaseType.GenericTypeArguments[0] != typeInfo) //todo: this should shield from using a pattern different than public class GROUP_NAME : GroupTag {} however I am not sure it's working throw new ECSException("Invalid Group Tag declared"); - - GroupNamesMap.idToName[(uint) group] = name; + + GroupNamesMap.idToName[group] = name; #endif - GroupHashMap.RegisterGroup(group, name); - + //The hashname is independent from the actual group ID. this is fundamental because it is want + //guarantees the hash to be the same across different machines + GroupHashMap.RegisterGroup(group, typeof(GroupTag).FullName); + _GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _)); } } + public static FasterReadOnlyList Groups => + new FasterReadOnlyList(_Groups); + + public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); + + public static bool Includes(ExclusiveGroupStruct group) + { + return _GroupsHashSet.Contains(group); + } + //Each time a new combination of group tags is found a new group is added. internal static void Add(ExclusiveGroupStruct group) { +#if DEBUG && !PROFILE_SVELTO for (var i = 0; i < _Groups.count; ++i) if (_Groups[i] == group) - throw new Exception("temporary must be transformed in unit test"); + throw new Exception("this test must be transformed in unit test"); +#endif _Groups.Add(group); _GroupsHashSet.Add(group); } + + static readonly FasterList _Groups = new FasterList(1); + static readonly HashSet _GroupsHashSet; - public static bool Includes(ExclusiveGroupStruct @group) - { - return _GroupsHashSet.Contains(@group); - } + //we are changing this with Interlocked, so it cannot be readonly + static int isInitializing; } } \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/Core/Groups/NamedExclusiveGroup.cs b/com.sebaslab.svelto.ecs/Core/Groups/NamedExclusiveGroup.cs index 1f60756..9d7b0ad 100644 --- a/com.sebaslab.svelto.ecs/Core/Groups/NamedExclusiveGroup.cs +++ b/com.sebaslab.svelto.ecs/Core/Groups/NamedExclusiveGroup.cs @@ -14,8 +14,10 @@ namespace Svelto.ECS static NamedExclusiveGroup() { #if DEBUG - GroupNamesMap.idToName[(uint) Group] = $"{name} ID {(uint)Group}"; + GroupNamesMap.idToName[Group] = $"{name} ID {Group.id}"; #endif + //The hashname is independent from the actual group ID. this is fundamental because it is want + //guarantees the hash to be the same across different machines GroupHashMap.RegisterGroup(Group, $"{name}"); } // protected NamedExclusiveGroup(string recognizeAs) : base(recognizeAs) {} diff --git a/com.sebaslab.svelto.ecs/DataStructures/ITypeSafeDictionary.cs b/com.sebaslab.svelto.ecs/DataStructures/ITypeSafeDictionary.cs index e43d159..7cb2cea 100644 --- a/com.sebaslab.svelto.ecs/DataStructures/ITypeSafeDictionary.cs +++ b/com.sebaslab.svelto.ecs/DataStructures/ITypeSafeDictionary.cs @@ -28,7 +28,8 @@ namespace Svelto.ECS.Internal void ExecuteEnginesRemoveCallbacks(FasterDictionary> entityComponentEnginesDB, in PlatformProfiler profiler, ExclusiveGroupStruct @group); - void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId, EnginesRoot enginesRoot); + void AddEntitiesFromDictionary + (ITypeSafeDictionary entitiesToSubmit, ExclusiveGroupStruct groupId, EnginesRoot enginesRoot); void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup); void RemoveEntityFromDictionary(EGID fromEntityGid); diff --git a/com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionary.cs b/com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionary.cs index 313178b..84921d2 100644 --- a/com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionary.cs +++ b/com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionary.cs @@ -8,29 +8,24 @@ namespace Svelto.ECS.Internal { sealed class TypeSafeDictionary : ITypeSafeDictionary where TValue : struct, IEntityComponent { - static readonly Type _type = typeof(TValue); - static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type); - static readonly bool _hasReference = typeof(INeedEntityReference).IsAssignableFrom(_type); + static readonly Type _type = typeof(TValue); + static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type); + static readonly bool _hasReference = typeof(INeedEntityReference).IsAssignableFrom(_type); internal static readonly bool isUnmanaged = _type.IsUnmanagedEx() && (typeof(IEntityViewComponent).IsAssignableFrom(_type) == false); - internal SveltoDictionary>, ManagedStrategy, - ManagedStrategy> implMgd; - - //used directly by native methods - internal SveltoDictionary>, NativeStrategy, - NativeStrategy> implUnmgd; - public TypeSafeDictionary(uint size) { if (isUnmanaged) - implUnmgd = new SveltoDictionary>, - NativeStrategy, NativeStrategy>(size, Allocator.Persistent); + implUnmgd = + new SveltoDictionary>, + NativeStrategy, NativeStrategy>(size, Allocator.Persistent); else { - implMgd = new SveltoDictionary>, - ManagedStrategy, ManagedStrategy>(size, Allocator.Managed); + implMgd = + new SveltoDictionary>, + ManagedStrategy, ManagedStrategy>(size, Allocator.Managed); } } @@ -42,7 +37,7 @@ namespace Svelto.ECS.Internal else implMgd.Add(egidEntityId, entityComponent); } - + /// todo: Is this really needed, cannot I just use AddEntitiesFromDictionary? Needs to be checked public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup) { @@ -88,7 +83,7 @@ namespace Svelto.ECS.Internal /// /// public void AddEntitiesFromDictionary - (ITypeSafeDictionary entitiesToSubmit, uint groupId, EnginesRoot enginesRoot) + (ITypeSafeDictionary entitiesToSubmit, ExclusiveGroupStruct groupId, EnginesRoot enginesRoot) { var safeDictionary = (entitiesToSubmit as TypeSafeDictionary); if (isUnmanaged) @@ -100,9 +95,8 @@ namespace Svelto.ECS.Internal { var egid = new EGID(tuple.Key, groupId); if (_hasEgid) - SetEGIDWithoutBoxing.SetIDWithoutBoxing( - ref tuple.Value, egid); - + SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref tuple.Value, egid); + if (_hasReference) SetEGIDWithoutBoxing.SetRefWithoutBoxing( ref tuple.Value, enginesRoot.entityLocator.GetEntityReference(egid)); @@ -112,7 +106,7 @@ namespace Svelto.ECS.Internal catch (Exception e) { Console.LogException( - e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key)); + e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId.ToName()).FastConcat(", id ").FastConcat(tuple.Key)); throw; } @@ -133,7 +127,7 @@ namespace Svelto.ECS.Internal catch (Exception e) { Console.LogException( - e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key)); + e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId.ToName()).FastConcat(", id ").FastConcat(tuple.Key)); throw; } @@ -207,7 +201,10 @@ namespace Svelto.ECS.Internal } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ITypeSafeDictionary Create() { return TypeSafeDictionaryFactory.Create(1); } + public ITypeSafeDictionary Create() + { + return TypeSafeDictionaryFactory.Create(1); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public uint GetIndex(uint valueEntityId) @@ -297,7 +294,7 @@ namespace Svelto.ECS.Internal var index = toGroupCasted.GetIndex(toEntityID.Value.entityID); ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(engines, ref toGroupCasted.GetDirectValueByRef(index) - , previousGroup, in profiler, toEntityID.Value); + , previousGroup, in profiler, toEntityID.Value); } //remove else @@ -323,7 +320,7 @@ namespace Svelto.ECS.Internal var index = toGroupCasted.GetIndex(toEntityID.Value.entityID); ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(engines, ref toGroupCasted.GetDirectValueByRef(index) - , previousGroup, in profiler, toEntityID.Value); + , previousGroup, in profiler, toEntityID.Value); } else { @@ -465,28 +462,14 @@ namespace Svelto.ECS.Internal } } - static void ExecuteEnginesRemoveCallbackOnSingleEntity - (FasterDictionary> engines, ref TValue entity - , in PlatformProfiler profiler, EGID egid) + public void Dispose() { - if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines)) - return; - - for (var i = 0; i < entityComponentsEngines.count; i++) - try - { - using (profiler.Sample(entityComponentsEngines[i].name)) - { - (entityComponentsEngines[i].engine as IReactOnAddAndRemove).Remove(ref entity, egid); - } - } - catch - { - Console.LogError( - "Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString())); + if (isUnmanaged) + implUnmgd.Dispose(); + else + implMgd.Dispose(); - throw; - } + GC.SuppressFinalize(this); } void ExecuteEnginesAddOrSwapCallbacksOnSingleEntity @@ -509,8 +492,7 @@ namespace Svelto.ECS.Internal } catch { - Console.LogError( - "Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString())); + Console.LogError("Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString())); throw; } @@ -528,22 +510,41 @@ namespace Svelto.ECS.Internal } catch (Exception) { - Console.LogError( - "Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString())); + Console.LogError("Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString())); throw; } } } - public void Dispose() + static void ExecuteEnginesRemoveCallbackOnSingleEntity + (FasterDictionary> engines, ref TValue entity + , in PlatformProfiler profiler, EGID egid) { - if (isUnmanaged) - implUnmgd.Dispose(); - else - implMgd.Dispose(); + if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines)) + return; - GC.SuppressFinalize(this); + for (var i = 0; i < entityComponentsEngines.count; i++) + try + { + using (profiler.Sample(entityComponentsEngines[i].name)) + { + (entityComponentsEngines[i].engine as IReactOnAddAndRemove).Remove(ref entity, egid); + } + } + catch + { + Console.LogError("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString())); + + throw; + } } + + internal SveltoDictionary>, ManagedStrategy, + ManagedStrategy> implMgd; + + //used directly by native methods + internal SveltoDictionary>, NativeStrategy, + NativeStrategy> implUnmgd; } } \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeDynamicArray.cs b/com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeDynamicArray.cs index b92dfec..c3ec902 100644 --- a/com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeDynamicArray.cs +++ b/com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeDynamicArray.cs @@ -377,8 +377,11 @@ namespace Svelto.ECS.DataStructures } } -#if UNITY_COLLECTIONS || UNITY_JOBS || UNITY_BURST - [global::Unity.Burst.NoAlias] [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] +#if UNITY_COLLECTIONS || UNITY_JOBS +#if UNITY_BURST + [global::Unity.Burst.NoAlias] +#endif + [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] #endif unsafe UnsafeArray* _list; #if DEBUG && !PROFILE_SVELTO diff --git a/com.sebaslab.svelto.ecs/Dispatcher/ReactiveValue.cs b/com.sebaslab.svelto.ecs/Dispatcher/ReactiveValue.cs index d62540e..122aabb 100644 --- a/com.sebaslab.svelto.ecs/Dispatcher/ReactiveValue.cs +++ b/com.sebaslab.svelto.ecs/Dispatcher/ReactiveValue.cs @@ -39,8 +39,7 @@ namespace Svelto.ECS { set { - if (_notifyOnChange == ReactiveType.ReactOnSet || - EqualityComparer.Default.Equals(_value) == false) + if (_notifyOnChange == ReactiveType.ReactOnSet || _comp.Equals(_value, value) == false) { if (_paused == false) _subscriber(_senderID, value); @@ -82,11 +81,12 @@ namespace Svelto.ECS _paused = true; } - readonly ReactiveType _notifyOnChange; - readonly EntityReference _senderID; - bool _paused; - Action _subscriber; - T _value; + readonly ReactiveType _notifyOnChange; + readonly EntityReference _senderID; + bool _paused; + Action _subscriber; + T _value; + static readonly EqualityComparer _comp = EqualityComparer.Default; } public enum ReactiveType diff --git a/com.sebaslab.svelto.ecs/Extensions/Svelto/AllGroupsEnumerable.cs b/com.sebaslab.svelto.ecs/Extensions/Svelto/AllGroupsEnumerable.cs index 38d4537..ca0f099 100644 --- a/com.sebaslab.svelto.ecs/Extensions/Svelto/AllGroupsEnumerable.cs +++ b/com.sebaslab.svelto.ecs/Extensions/Svelto/AllGroupsEnumerable.cs @@ -47,7 +47,7 @@ namespace Svelto.ECS continue; _array.collection = new EntityCollection(typeSafeDictionary.GetValues(out var count), count); - _array.@group = new ExclusiveGroupStruct(group.Key); + _array.@group = group.Key; return true; } diff --git a/com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/Native/EnginesRoot.NativeOperation.cs b/com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/Native/EnginesRoot.NativeOperation.cs index 6c10a86..5556364 100644 --- a/com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/Native/EnginesRoot.NativeOperation.cs +++ b/com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/Native/EnginesRoot.NativeOperation.cs @@ -105,7 +105,7 @@ namespace Svelto.ECS var reference = buffer.Dequeue(); var componentCounts = buffer.Dequeue(); - Check.Require((uint)egid.groupID != 0, "invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?"); + Check.Require(egid.groupID.isInvalid == false, "invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?"); var componentBuilders = _nativeAddOperations[componentsIndex].components; #if DEBUG && !PROFILE_SVELTO diff --git a/com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/UECSSveltoEGID.cs b/com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/UECSSveltoEGID.cs index b5307af..84d9dcb 100644 --- a/com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/UECSSveltoEGID.cs +++ b/com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/UECSSveltoEGID.cs @@ -12,16 +12,16 @@ namespace Svelto.ECS.Extensions.Unity public struct UECSSveltoGroupID : ISharedComponentData { - public readonly uint group; + public readonly ExclusiveGroupStruct group; public UECSSveltoGroupID(ExclusiveGroupStruct exclusiveGroup) { - @group = (uint) exclusiveGroup; + @group = exclusiveGroup; } public static implicit operator ExclusiveGroupStruct(UECSSveltoGroupID group) { - return new ExclusiveGroupStruct(group.@group); + return group.@group; } } diff --git a/com.sebaslab.svelto.ecs/Serialization/EnginesRoot.SerializableEntityHeader.cs b/com.sebaslab.svelto.ecs/Serialization/EnginesRoot.SerializableEntityHeader.cs index 35913a0..6e80b10 100644 --- a/com.sebaslab.svelto.ecs/Serialization/EnginesRoot.SerializableEntityHeader.cs +++ b/com.sebaslab.svelto.ecs/Serialization/EnginesRoot.SerializableEntityHeader.cs @@ -59,7 +59,7 @@ namespace Svelto.ECS serializationData.data[serializationData.dataPos++] = (byte) ((entityID >> 24) & 0xff); // Splitting the groupID (uint, 32 bit) into four bytes. - uint groupID = (uint) egid.groupID; + var groupID = egid.groupID.ToIDAndBitmask(); serializationData.data[serializationData.dataPos++] = (byte) (groupID & 0xff); serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 8) & 0xff); serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 16) & 0xff); diff --git a/com.sebaslab.svelto.ecs/Svelto.ECS.csproj b/com.sebaslab.svelto.ecs/Svelto.ECS.csproj index bcb474c..6119d6b 100644 --- a/com.sebaslab.svelto.ecs/Svelto.ECS.csproj +++ b/com.sebaslab.svelto.ecs/Svelto.ECS.csproj @@ -30,8 +30,8 @@ true - - + + diff --git a/com.sebaslab.svelto.ecs/package.json b/com.sebaslab.svelto.ecs/package.json index 471ea37..ad08f9f 100644 --- a/com.sebaslab.svelto.ecs/package.json +++ b/com.sebaslab.svelto.ecs/package.json @@ -3,13 +3,13 @@ "category": "Svelto", "description": "Svelto ECS C# Lightweight Data Oriented Entity Component System Framework", "dependencies": { - "com.sebaslab.svelto.common": "3.2.1" + "com.sebaslab.svelto.common": "3.2.2" }, "keywords": [ "svelto" ], "name": "com.sebaslab.svelto.ecs", - "version": "3.2.2", + "version": "3.2.3", "type": "library", "unity": "2019.3" } \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/version.json b/com.sebaslab.svelto.ecs/version.json index 3253af1..7c5a772 100644 --- a/com.sebaslab.svelto.ecs/version.json +++ b/com.sebaslab.svelto.ecs/version.json @@ -1,3 +1,3 @@ { - "version": "3.2.2" + "version": "3.2.3" }