Browse Source

Svelto.ECS 3.2.3 - fixed bugs and improved code

pull/72/head
sebas77 2 years ago
parent
commit
8a4f2ad7a6
30 changed files with 491 additions and 438 deletions
  1. +1
    -0
      .gitignore
  2. +1
    -1
      com.sebaslab.svelto.common
  3. +4
    -5
      com.sebaslab.svelto.ecs/Core/CheckEntityUtilities.cs
  4. +18
    -15
      com.sebaslab.svelto.ecs/Core/EGID.cs
  5. +80
    -87
      com.sebaslab.svelto.ecs/Core/EnginesRoot.DoubleBufferedEntitiesToAdd.cs
  6. +1
    -1
      com.sebaslab.svelto.ecs/Core/EnginesRoot.Engines.cs
  7. +11
    -12
      com.sebaslab.svelto.ecs/Core/EnginesRoot.Entities.cs
  8. +5
    -5
      com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFunctions.cs
  9. +4
    -5
      com.sebaslab.svelto.ecs/Core/EnginesRoot.Submission.cs
  10. +1
    -1
      com.sebaslab.svelto.ecs/Core/EntitiesDB.FindGroups.cs
  11. +7
    -5
      com.sebaslab.svelto.ecs/Core/EntityFactory.cs
  12. +1
    -1
      com.sebaslab.svelto.ecs/Core/EntityReference/EnginesRoot.LocatorMap.cs
  13. +38
    -28
      com.sebaslab.svelto.ecs/Core/GroupHashMap.cs
  14. +6
    -6
      com.sebaslab.svelto.ecs/Core/GroupNamesMap.cs
  15. +3
    -7
      com.sebaslab.svelto.ecs/Core/Groups/ExclusiveBuildGroup.cs
  16. +20
    -30
      com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroup.cs
  17. +84
    -47
      com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroupStruct.cs
  18. +123
    -106
      com.sebaslab.svelto.ecs/Core/Groups/GroupCompound.cs
  19. +3
    -1
      com.sebaslab.svelto.ecs/Core/Groups/NamedExclusiveGroup.cs
  20. +2
    -1
      com.sebaslab.svelto.ecs/DataStructures/ITypeSafeDictionary.cs
  21. +55
    -54
      com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionary.cs
  22. +5
    -2
      com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeDynamicArray.cs
  23. +7
    -7
      com.sebaslab.svelto.ecs/Dispatcher/ReactiveValue.cs
  24. +1
    -1
      com.sebaslab.svelto.ecs/Extensions/Svelto/AllGroupsEnumerable.cs
  25. +1
    -1
      com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/Native/EnginesRoot.NativeOperation.cs
  26. +3
    -3
      com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/UECSSveltoEGID.cs
  27. +1
    -1
      com.sebaslab.svelto.ecs/Serialization/EnginesRoot.SerializableEntityHeader.cs
  28. +2
    -2
      com.sebaslab.svelto.ecs/Svelto.ECS.csproj
  29. +2
    -2
      com.sebaslab.svelto.ecs/package.json
  30. +1
    -1
      com.sebaslab.svelto.ecs/version.json

+ 1
- 0
.gitignore View File

@@ -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

+ 1
- 1
com.sebaslab.svelto.common

@@ -1 +1 @@
Subproject commit 4062f995396f9d34f8a58c7646f819c315356775
Subproject commit 8a1924de0f6017ccc03396b45a3ee78ae9195dac

+ 4
- 5
com.sebaslab.svelto.ecs/Core/CheckEntityUtilities.cs View File

@@ -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<uint>());
var hash = _idChecker.GetOrCreate(egid.groupID, () => new HashSet<uint>());
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<EGID, uint> _multipleOperationOnSameEGIDChecker = new FasterDictionary<EGID, uint>();
readonly FasterDictionary<uint, HashSet<uint>> _idChecker = new FasterDictionary<uint, HashSet<uint>>();
readonly FasterDictionary<ExclusiveGroupStruct, HashSet<uint>> _idChecker = new FasterDictionary<ExclusiveGroupStruct, HashSet<uint>>();
}
}

+ 18
- 15
com.sebaslab.svelto.ecs/Core/EGID.cs View File

@@ -9,11 +9,11 @@ namespace Svelto.ECS
[Serialization.DoNotSerialize]
[Serializable]
[StructLayout(LayoutKind.Explicit)]
public struct EGID:IEquatable<EGID>,IComparable<EGID>
public struct EGID : IEquatable<EGID>, IComparable<EGID>
{
[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);
}
}
}
}

+ 80
- 87
com.sebaslab.svelto.ecs/Core/EnginesRoot.DoubleBufferedEntitiesToAdd.cs View File

@@ -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<T>(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<RefWrapperType, ITypeSafeDictionary>[] 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<RefWrapperType, ITypeSafeDictionary>[] 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<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> current;
internal FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> other;

/// <summary>
/// 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
/// </summary>
FasterDictionary<ExclusiveGroupStruct, uint> currentEntitiesCreatedPerGroup;
FasterDictionary<ExclusiveGroupStruct, uint> otherEntitiesCreatedPerGroup;

readonly FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>
_entityComponentsToAddBufferA =
new FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>();

readonly FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>
_entityComponentsToAddBufferB =
new FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>();

readonly FasterDictionary<ExclusiveGroupStruct, uint> _entitiesCreatedPerGroupA = new FasterDictionary<ExclusiveGroupStruct, uint>();
readonly FasterDictionary<ExclusiveGroupStruct, uint> _entitiesCreatedPerGroupB = new FasterDictionary<ExclusiveGroupStruct, uint>();

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<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> fasterDictionary1)
void PreallocateDictionaries
(FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> dic)
{
FasterDictionary<RefWrapperType, ITypeSafeDictionary> group =
fasterDictionary1.GetOrCreate((uint) groupID, () => new FasterDictionary<RefWrapperType, ITypeSafeDictionary>());
var group = dic.GetOrCreate(groupID, () => new FasterDictionary<RefWrapperType,
ITypeSafeDictionary>());

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<T>(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<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> current;
internal FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> other;

readonly FasterDictionary<ExclusiveGroupStruct, uint> _entitiesCreatedPerGroupA =
new FasterDictionary<ExclusiveGroupStruct, uint>();

readonly FasterDictionary<ExclusiveGroupStruct, uint> _entitiesCreatedPerGroupB =
new FasterDictionary<ExclusiveGroupStruct, uint>();

readonly FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> _entityComponentsToAddBufferA =
new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>();

readonly FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> _entityComponentsToAddBufferB =
new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>();

/// <summary>
/// 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
/// </summary>
FasterDictionary<ExclusiveGroupStruct, uint> _currentEntitiesCreatedPerGroup;
FasterDictionary<ExclusiveGroupStruct, uint> _otherEntitiesCreatedPerGroup;
}
}
}

+ 1
- 1
com.sebaslab.svelto.ecs/Core/EnginesRoot.Engines.cs View File

@@ -101,7 +101,7 @@ namespace Svelto.ECS
try
{
entityList.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemoveOnDispose, profiler
, new ExclusiveGroupStruct(groups.Key));
, groups.Key);
}
catch (Exception e)
{


+ 11
- 12
com.sebaslab.svelto.ecs/Core/EnginesRoot.Entities.cs View File

@@ -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<RefWrapperType, ITypeSafeDictionary> fromGroup = GetDBGroup(fromIdGroupId);
FasterDictionary<RefWrapperType, ITypeSafeDictionary> 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<RefWrapperType, ITypeSafeDictionary>
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<RefWrapperType, ITypeSafeDictionary> @group, RefWrapperType refWrapper)
(ExclusiveGroupStruct groupID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> @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];


+ 5
- 5
com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFunctions.cs View File

@@ -28,7 +28,7 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntity<T>(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<T>.descriptor.componentsToBuild;
_enginesRoot.Target.CheckRemoveEntityID(entityEGID, TypeCache<T>.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<T>(EGID fromID, ExclusiveBuildGroup toGroupID)
where T : IEntityDescriptor, new()
{
SwapEntityGroup<T>(fromID, new EGID(fromID.entityID, (uint) toGroupID));
SwapEntityGroup<T>(fromID, new EGID(fromID.entityID, toGroupID));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -147,8 +147,8 @@ namespace Svelto.ECS
public void SwapEntityGroup<T>(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<T>.descriptor.componentsToBuild;


+ 4
- 5
com.sebaslab.svelto.ecs/Core/EnginesRoot.Submission.cs View File

@@ -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;



+ 1
- 1
com.sebaslab.svelto.ecs/Core/EntitiesDB.FindGroups.cs View File

@@ -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;
}
}


+ 7
- 5
com.sebaslab.svelto.ecs/Core/EntityFactory.cs View File

@@ -28,11 +28,11 @@ namespace Svelto.ECS.Internal
static FasterDictionary<RefWrapperType, ITypeSafeDictionary> 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<RefWrapperType, ITypeSafeDictionary>();

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<object> 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);


+ 1
- 1
com.sebaslab.svelto.ecs/Core/EntityReference/EnginesRoot.LocatorMap.cs View File

@@ -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;


+ 38
- 28
com.sebaslab.svelto.ecs/Core/GroupHashMap.cs View File

@@ -11,7 +11,6 @@ namespace Svelto.ECS
/// <summary>
/// c# Static constructors are guaranteed to be thread safe
/// </summary>
public static void Init()
{
List<Assembly> 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));
}
}
}

/// <summary>
/// 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
/// </summary>
/// <param name="exclusiveGroupStruct"></param>
/// <param name="name"></param>
/// <exception cref="ECSException"></exception>
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];
}



+ 6
- 6
com.sebaslab.svelto.ecs/Core/GroupNamesMap.cs View File

@@ -4,23 +4,23 @@ using Svelto.ECS;
static class GroupNamesMap
{
#if DEBUG
static GroupNamesMap() { idToName = new Dictionary<uint, string>(); }
static GroupNamesMap() { idToName = new Dictionary<ExclusiveGroupStruct, string>(); }

internal static readonly Dictionary<uint, string> idToName;
internal static readonly Dictionary<ExclusiveGroupStruct, string> 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 = $"<undefined:{((uint)group).ToString()}>";
Dictionary<ExclusiveGroupStruct, string> idToName = GroupNamesMap.idToName;
if (idToName.TryGetValue(@group, out var name) == false)
name = $"<undefined:{(group.id).ToString()}>";

return name;
}
#else
public static string ToName(this in ExclusiveGroupStruct group)
{
return ((uint)group).ToString();
return ((uint)group.ToIDAndBitmask()).ToString();
}
#endif
}

+ 3
- 7
com.sebaslab.svelto.ecs/Core/Groups/ExclusiveBuildGroup.cs View File

@@ -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;
}
}

+ 20
- 30
com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroup.cs View File

@@ -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;
}
}

+ 84
- 47
com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroupStruct.cs View File

@@ -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<ExclusiveGroupStruct>, IComparable<ExclusiveGroupStruct>
public readonly struct ExclusiveGroupStruct : IEquatable<ExclusiveGroupStruct>, IComparable<ExclusiveGroupStruct>
{
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;
}

/// <summary>
/// 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.
/// </summary>
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;
}

/// <summary>
/// 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
/// </summary>
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;
}
}

+ 123
- 106
com.sebaslab.svelto.ecs/Core/Groups/GroupCompound.cs View File

@@ -6,10 +6,10 @@ using Svelto.DataStructures;
namespace Svelto.ECS
{
/// <summary>
/// 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)
/// </summary>
static class GroupCompoundInitializer
{
@@ -23,34 +23,26 @@ namespace Svelto.ECS
where G3 : GroupTag<G3>
where G4 : GroupTag<G4>
{
static readonly FasterList<ExclusiveGroupStruct> _Groups;
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;

public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
new FasterReadOnlyList<ExclusiveGroupStruct>(_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<ExclusiveGroupStruct>(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<ExclusiveGroupStruct>(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<G1, G2, G3, G4>).FullName);

_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));

GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = true;
@@ -80,7 +72,7 @@ namespace Svelto.ECS
GroupCompound<G4, G2, G3, G1>._Groups = _Groups;
GroupCompound<G4, G3, G1, G2>._Groups = _Groups;
GroupCompound<G4, G3, G2, G1>._Groups = _Groups;
//all the permutations are warmed up now
GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = false;

@@ -107,7 +99,7 @@ namespace Svelto.ECS
GroupCompound<G4, G2, G3, G1>._GroupsHashSet = _GroupsHashSet;
GroupCompound<G4, G3, G1, G2>._GroupsHashSet = _GroupsHashSet;
GroupCompound<G4, G3, G2, G1>._GroupsHashSet = _GroupsHashSet;
GroupCompound<G1, G2, G3>.Add(group);
GroupCompound<G1, G2, G4>.Add(group);
GroupCompound<G1, G3, G4>.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<G1, G2, G3>
where G1 : GroupTag<G1> where G2 : GroupTag<G2> where G3 : GroupTag<G3>
{
static readonly FasterList<ExclusiveGroupStruct> _Groups;
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;

public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
new FasterReadOnlyList<ExclusiveGroupStruct>(_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<ExclusiveGroupStruct> _Groups;
static readonly HashSet<ExclusiveGroupStruct> _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<G1, G2, G3>
where G1 : GroupTag<G1> where G2 : GroupTag<G2> where G3 : GroupTag<G3>
{
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<ExclusiveGroupStruct>(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<ExclusiveGroupStruct>(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<G1, G2, G3>).FullName);

_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));

GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = true;
@@ -208,7 +190,7 @@ namespace Svelto.ECS
GroupCompound<G3, G2, G1>._GroupsHashSet = _GroupsHashSet;
GroupCompound<G1, G3, G2>._GroupsHashSet = _GroupsHashSet;
GroupCompound<G2, G1, G3>._GroupsHashSet = _GroupsHashSet;
GroupCompound<G1, G2>.Add(group); //<G1/G2> and <G2/G1> must share the same array
GroupCompound<G1, G3>.Add(group);
GroupCompound<G2, G3>.Add(group);
@@ -220,122 +202,157 @@ namespace Svelto.ECS
GroupTag<G3>.Add(group);
}
}
}

public abstract class GroupCompound<G1, G2> where G1 : GroupTag<G1> where G2 : GroupTag<G2>
{
static readonly FasterList<ExclusiveGroupStruct> _Groups;
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;

public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
new FasterReadOnlyList<ExclusiveGroupStruct>(_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<ExclusiveGroupStruct> _Groups;
static readonly HashSet<ExclusiveGroupStruct> _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<G1, G2> where G1 : GroupTag<G1> where G2 : GroupTag<G2>
{
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<ExclusiveGroupStruct>(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<G1, G2>).FullName);

_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));

GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = true;
GroupCompound<G2, G1>._Groups = _Groups;
GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = false;
GroupCompound<G2, G1>._GroupsHashSet = _GroupsHashSet;
//every abstract group preemptively adds this group, it may or may not be empty in future
GroupTag<G1>.Add(group);
GroupTag<G2>.Add(group);
}
}
}

/// <summary>
///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.
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class GroupTag<T> where T : GroupTag<T>
{
static readonly FasterList<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1);
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;

public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
new FasterReadOnlyList<ExclusiveGroupStruct>(_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<ExclusiveGroupStruct> _Groups;
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;

static int isInitializing;
}

/// <summary>
/// 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.
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class GroupTag<T> where T : GroupTag<T>
{
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<GROUP_NAME> {} 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<T>).FullName);

_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
}
}

public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
new FasterReadOnlyList<ExclusiveGroupStruct>(_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<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1);
static readonly HashSet<ExclusiveGroupStruct> _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;
}
}

+ 3
- 1
com.sebaslab.svelto.ecs/Core/Groups/NamedExclusiveGroup.cs View File

@@ -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) {}


+ 2
- 1
com.sebaslab.svelto.ecs/DataStructures/ITypeSafeDictionary.cs View File

@@ -28,7 +28,8 @@ namespace Svelto.ECS.Internal
void ExecuteEnginesRemoveCallbacks(FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer>> 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);


+ 55
- 54
com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionary.cs View File

@@ -8,29 +8,24 @@ namespace Svelto.ECS.Internal
{
sealed class TypeSafeDictionary<TValue> : ITypeSafeDictionary<TValue> 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<uint, TValue, ManagedStrategy<SveltoDictionaryNode<uint>>, ManagedStrategy<TValue>,
ManagedStrategy<int>> implMgd;

//used directly by native methods
internal SveltoDictionary<uint, TValue, NativeStrategy<SveltoDictionaryNode<uint>>, NativeStrategy<TValue>,
NativeStrategy<int>> implUnmgd;

public TypeSafeDictionary(uint size)
{
if (isUnmanaged)
implUnmgd = new SveltoDictionary<uint, TValue, NativeStrategy<SveltoDictionaryNode<uint>>,
NativeStrategy<TValue>, NativeStrategy<int>>(size, Allocator.Persistent);
implUnmgd =
new SveltoDictionary<uint, TValue, NativeStrategy<SveltoDictionaryNode<uint>>,
NativeStrategy<TValue>, NativeStrategy<int>>(size, Allocator.Persistent);
else
{
implMgd = new SveltoDictionary<uint, TValue, ManagedStrategy<SveltoDictionaryNode<uint>>,
ManagedStrategy<TValue>, ManagedStrategy<int>>(size, Allocator.Managed);
implMgd =
new SveltoDictionary<uint, TValue, ManagedStrategy<SveltoDictionaryNode<uint>>,
ManagedStrategy<TValue>, ManagedStrategy<int>>(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
/// <param name="enginesRoot"></param>
/// <exception cref="TypeSafeDictionaryException"></exception>
public void AddEntitiesFromDictionary
(ITypeSafeDictionary entitiesToSubmit, uint groupId, EnginesRoot enginesRoot)
(ITypeSafeDictionary entitiesToSubmit, ExclusiveGroupStruct groupId, EnginesRoot enginesRoot)
{
var safeDictionary = (entitiesToSubmit as TypeSafeDictionary<TValue>);
if (isUnmanaged)
@@ -100,9 +95,8 @@ namespace Svelto.ECS.Internal
{
var egid = new EGID(tuple.Key, groupId);
if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(
ref tuple.Value, egid);
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref tuple.Value, egid);

if (_hasReference)
SetEGIDWithoutBoxing<TValue>.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<TValue>.Create(1); }
public ITypeSafeDictionary Create()
{
return TypeSafeDictionaryFactory<TValue>.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<RefWrapperType, FasterList<ReactEngineContainer>> 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<TValue>).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<RefWrapperType, FasterList<ReactEngineContainer>> 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<TValue>).Remove(ref entity, egid);
}
}
catch
{
Console.LogError("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()));

throw;
}
}

internal SveltoDictionary<uint, TValue, ManagedStrategy<SveltoDictionaryNode<uint>>, ManagedStrategy<TValue>,
ManagedStrategy<int>> implMgd;

//used directly by native methods
internal SveltoDictionary<uint, TValue, NativeStrategy<SveltoDictionaryNode<uint>>, NativeStrategy<TValue>,
NativeStrategy<int>> implUnmgd;
}
}

+ 5
- 2
com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeDynamicArray.cs View File

@@ -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


+ 7
- 7
com.sebaslab.svelto.ecs/Dispatcher/ReactiveValue.cs View File

@@ -39,8 +39,7 @@ namespace Svelto.ECS
{
set
{
if (_notifyOnChange == ReactiveType.ReactOnSet ||
EqualityComparer<T>.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<EntityReference, T> _subscriber;
T _value;
readonly ReactiveType _notifyOnChange;
readonly EntityReference _senderID;
bool _paused;
Action<EntityReference, T> _subscriber;
T _value;
static readonly EqualityComparer<T> _comp = EqualityComparer<T>.Default;
}

public enum ReactiveType


+ 1
- 1
com.sebaslab.svelto.ecs/Extensions/Svelto/AllGroupsEnumerable.cs View File

@@ -47,7 +47,7 @@ namespace Svelto.ECS
continue;

_array.collection = new EntityCollection<T1>(typeSafeDictionary.GetValues(out var count), count);
_array.@group = new ExclusiveGroupStruct(group.Key);
_array.@group = group.Key;

return true;
}


+ 1
- 1
com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/Native/EnginesRoot.NativeOperation.cs View File

@@ -105,7 +105,7 @@ namespace Svelto.ECS
var reference = buffer.Dequeue<EntityReference>();
var componentCounts = buffer.Dequeue<uint>();

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


+ 3
- 3
com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/UECSSveltoEGID.cs View File

@@ -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;
}
}



+ 1
- 1
com.sebaslab.svelto.ecs/Serialization/EnginesRoot.SerializableEntityHeader.cs View File

@@ -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);


+ 2
- 2
com.sebaslab.svelto.ecs/Svelto.ECS.csproj View File

@@ -30,8 +30,8 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.2" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.6.0" />
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0" />
<None Remove="**\*.meta" />
<ProjectReference Include="..\com.sebaslab.svelto.common\Svelto.Common.csproj" /> <!-- Do not delete. Used for nuget packing -->
</ItemGroup>

+ 2
- 2
com.sebaslab.svelto.ecs/package.json View File

@@ -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"
}

+ 1
- 1
com.sebaslab.svelto.ecs/version.json View File

@@ -1,3 +1,3 @@
{
"version": "3.2.2"
"version": "3.2.3"
}

Loading…
Cancel
Save