Browse Source

update svelto to 3.5.0

pull/116/head
Sebastiano Mandala 7 months ago
parent
commit
2179bb1ae8
65 changed files with 1538 additions and 995 deletions
  1. +34
    -2
      com.sebaslab.svelto.ecs/CHANGELOG.md
  2. +166
    -45
      com.sebaslab.svelto.ecs/Core/CheckEntityUtilities.cs
  3. +3
    -4
      com.sebaslab.svelto.ecs/Core/ComponentBuilder.CheckFields.cs
  4. +1
    -1
      com.sebaslab.svelto.ecs/Core/ComponentBuilder.cs
  5. +6
    -1
      com.sebaslab.svelto.ecs/Core/ComponentID.cs
  6. +4
    -2
      com.sebaslab.svelto.ecs/Core/ComponentTypeID.cs
  7. +2
    -2
      com.sebaslab.svelto.ecs/Core/EGID.cs
  8. +11
    -3
      com.sebaslab.svelto.ecs/Core/EnginesGroup/IStepEngine.cs
  9. +40
    -12
      com.sebaslab.svelto.ecs/Core/EnginesGroup/SortedEnginesGroup.cs
  10. +36
    -12
      com.sebaslab.svelto.ecs/Core/EnginesGroup/UnsortedEnginesGroup.cs
  11. +16
    -8
      com.sebaslab.svelto.ecs/Core/EnginesRoot.Engines.cs
  12. +42
    -15
      com.sebaslab.svelto.ecs/Core/EnginesRoot.Entities.cs
  13. +1
    -1
      com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFactory.cs
  14. +3
    -6
      com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFunctions.cs
  15. +78
    -116
      com.sebaslab.svelto.ecs/Core/EnginesRoot.Submission.cs
  16. +113
    -80
      com.sebaslab.svelto.ecs/Core/EntitiesDB.cs
  17. +120
    -67
      com.sebaslab.svelto.ecs/Core/EntitiesOperations.cs
  18. +4
    -4
      com.sebaslab.svelto.ecs/Core/EntityCollection.cs
  19. +6
    -13
      com.sebaslab.svelto.ecs/Core/EntityInitializer.cs
  20. +9
    -7
      com.sebaslab.svelto.ecs/Core/EntityReference/EnginesRoot.LocatorMap.cs
  21. +1
    -0
      com.sebaslab.svelto.ecs/Core/EntityReference/EntitiesDB.References.cs
  22. +7
    -2
      com.sebaslab.svelto.ecs/Core/EntityReference/EntityReference.cs
  23. +5
    -0
      com.sebaslab.svelto.ecs/Core/Filters/CombinedFilterID.cs
  24. +26
    -59
      com.sebaslab.svelto.ecs/Core/Filters/EnginesRoot.Filters.cs
  25. +1
    -1
      com.sebaslab.svelto.ecs/Core/Filters/EntitiesDB.Filters.cs
  26. +69
    -15
      com.sebaslab.svelto.ecs/Core/Filters/EntityFilterCollection.cs
  27. +5
    -3
      com.sebaslab.svelto.ecs/Core/Filters/EntityFilterIndices.cs
  28. +16
    -7
      com.sebaslab.svelto.ecs/Core/Filters/EntityFilterIterator.cs
  29. +1
    -1
      com.sebaslab.svelto.ecs/Core/Groups/EntityDescriptorsWarmup.cs
  30. +24
    -3
      com.sebaslab.svelto.ecs/Core/Groups/ExclusiveBuildGroup.cs
  31. +10
    -2
      com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroup.cs
  32. +12
    -1
      com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroupStruct.cs
  33. +198
    -87
      com.sebaslab.svelto.ecs/Core/Groups/GroupCompound.cs
  34. +68
    -53
      com.sebaslab.svelto.ecs/Core/Groups/GroupHashMap.cs
  35. +1
    -2
      com.sebaslab.svelto.ecs/Core/Groups/GroupNamesMap.cs
  36. +0
    -1
      com.sebaslab.svelto.ecs/Core/Groups/QueryGroups.cs
  37. +1
    -1
      com.sebaslab.svelto.ecs/Core/IDisposingEngine.cs
  38. +12
    -9
      com.sebaslab.svelto.ecs/Core/IEngine.cs
  39. +24
    -13
      com.sebaslab.svelto.ecs/Core/IEntityFunctions.cs
  40. +15
    -5
      com.sebaslab.svelto.ecs/DataStructures/EntityIDs/ManagedEntityIDs.cs
  41. +15
    -5
      com.sebaslab.svelto.ecs/DataStructures/EntityIDs/NativeEntityIDs.cs
  42. +6
    -7
      com.sebaslab.svelto.ecs/DataStructures/ITypeSafeDictionary.cs
  43. +17
    -17
      com.sebaslab.svelto.ecs/DataStructures/ManagedTypeSafeDictionary.cs
  44. +5
    -0
      com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionaryException.cs
  45. +93
    -71
      com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionaryMethods.cs
  46. +16
    -15
      com.sebaslab.svelto.ecs/DataStructures/UnmanagedTypeSafeDictionary.cs
  47. +1
    -3
      com.sebaslab.svelto.ecs/Extensions/Native/EnginesRoot.NativeOperation.cs
  48. +3
    -3
      com.sebaslab.svelto.ecs/Extensions/Native/EntityNativeDBExtensions.cs
  49. +9
    -2
      com.sebaslab.svelto.ecs/Extensions/Native/NativeEGIDMultiMapper.cs
  50. +59
    -109
      com.sebaslab.svelto.ecs/Extensions/Svelto/EGIDMultiMapper.cs
  51. +78
    -78
      com.sebaslab.svelto.ecs/Extensions/Svelto/EntityCollectionExtension.cs
  52. +3
    -3
      com.sebaslab.svelto.ecs/Extensions/Svelto/EntityManagedDBExtensions.cs
  53. +4
    -4
      com.sebaslab.svelto.ecs/Extensions/Svelto/GroupsEnumerable.cs
  54. +9
    -0
      com.sebaslab.svelto.ecs/Extensions/Svelto/IEGIDMultiMapper.cs
  55. +1
    -1
      com.sebaslab.svelto.ecs/Extensions/Svelto/Legacy/FilterGroupExtensions.cs
  56. +16
    -0
      com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/SveltoOnDOTSEnginesGroup.cs
  57. +1
    -2
      com.sebaslab.svelto.ecs/Serialization/EnginesRoot.GenericEntitySerialization.cs
  58. +2
    -0
      com.sebaslab.svelto.ecs/Serialization/EnginesRoot.SerializableEntityHeader.cs
  59. +1
    -1
      com.sebaslab.svelto.ecs/Serialization/ISerializationData.cs
  60. +1
    -1
      com.sebaslab.svelto.ecs/Serialization/SerializableEntityComponent.cs
  61. +1
    -1
      com.sebaslab.svelto.ecs/Serialization/SerializingEnginesRoot.cs
  62. +1
    -1
      com.sebaslab.svelto.ecs/Serialization/SimpleSerializationData.cs
  63. +2
    -2
      com.sebaslab.svelto.ecs/Svelto.ECS.csproj
  64. +2
    -2
      com.sebaslab.svelto.ecs/package.json
  65. +1
    -1
      com.sebaslab.svelto.ecs/version.json

+ 34
- 2
com.sebaslab.svelto.ecs/CHANGELOG.md View File

@@ -1,11 +1,29 @@
# Svelto.ECS Changelog
All notable changes to this project will be documented in this file. Changes are listed in random order of importance.

## [3.5.0] - 09-2023

* Introduced Serialization namespace for the serialization code
* Unity: dropped 2020 support, minimum requirement is no 2021.3
* Unity DOTS: added CreateDOTSToSveltoSyncEngine method in SveltoOnDOTSEnginesGroup
* Refactor: split NB/MB struct from their internal logic that must be used only by the framework. Eventually NB and MB structs must be ref, as they are not supposed to be held (they may become invalid over the time). However due to the current DOTS patterns this is not possible. In future a sentinel pattern will allow to lease these buffers with the assumption that they can't be modified while held (and if a modification happens an exception will throw)
* Improved managed EGIDMultiMapper. A MultiMapper can improve components fetching performance
* Renamed IDisposingEngine interface to IDisposableEngine
* added EntityReference Exists method to validate it against a given entity database
* BUG FIXED: IReactOnDisposeEx callbacks were not correctly called
* BUG FIXED: fixed serious bug that would pass wrong entities indices to the moveTO callback under specific conditions
* Added Group Range functionality to GroupCompound
* Added Offset to GroupCompound to know the index of a given group compared to the starting group ID of the compound range (check MiniExample 9, Groupsonly for example)
* range and bitmask can be now set only in GroupTag and be inherited by GroupCompounds. GroupCompound bitmasks will be the OR of the group tags bitmasks, while the range will be the larger of the group tags ranges.
* entity filters enumerator do not iterate anymore filters belonging to disabled groups
* remove operations can now be executed in the same frame of a swap. a Remove will always supersed as Swap operation
* engines added to a GreoupEngine are automatically added to the enginesgroup

## [3.4.6] - 05-2023

* SveltoOnDOTS bug fixes/improvements
* Comments and code cleanup
* update Svelto.Common to 3.4.3

## [3.4.4] - 04-2023

@@ -17,7 +35,21 @@ All notable changes to this project will be documented in this file. Changes are

## [3.4.2] - 03-2023

* update Svelto.Common to 3.4.0
* removed static caches used in performance critical paths as they were causing unexpected performance issues (the fetching of static data is slower than i imagined)
* add Native prefix in front of the native memory utilities method names
* largely improved the console logger system
* minor improvements to the platform profiler structs
* improvements to the ThreadSafeObjectPool class (some refactoring too)
* added several datastructures previously belonging to Svelto.ECS
* all the FastClear methods are gone. The standard clear method now is aware of the type used and will clear it in the fastest way possible
* MemClear is added in case memory needs to be cleared explicitly
* added new SveltoStream, Unmanaged and Managed stream classes, their use case will be documented one day
* renamed the Svelto.Common.DataStructures namespace to Svelto.DataStructures
* added FixedTypedArray* methods. Fixed size arrays embedded in structs are now possible
* FasterList extension to convert to Span and ByteSpan
* Fix reported bugs
* Minor Svelto Dictionary improvements
* Added ValueContainer, a simple int, Tvalue dictionary based on sparse set. It has very specific use cases at the moment. Mainly to be used for the new ECS OOP Abstraction resoruce manager
* Added IReactOnSubmissionStarted interface

### SveltoOnDOTS changes


+ 166
- 45
com.sebaslab.svelto.ecs/Core/CheckEntityUtilities.cs View File

@@ -1,77 +1,135 @@
#if !DEBUG || PROFILE_SVELTO
//#define VERBOSE
#if !DEBUG || PROFILE_SVELTO
#define DONT_USE
using System.Diagnostics;
#endif
using System;
using System.Collections.Generic;
using Svelto.DataStructures;

namespace Svelto.ECS
{
/// <summary>
/// Note: this check doesn't catch the case when an add and remove is done on the same entity before the nextI am
/// Note: this check doesn't catch the case when an add and remove is done on the same entity before the next
/// submission. Two operations on the same entity are not allowed between submissions.
/// </summary>
public partial class EnginesRoot
{
#if DONT_USE
enum OperationType
{
Add,
Remove,
SwapFrom,
SwapTo
}
#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
void CheckRemoveEntityID(EGID egid, Type entityDescriptorType, string caller)
void InitDebugChecks()
{
_multipleOperationOnSameEGIDChecker = new FasterDictionary<EGID, OperationType>();
_idChecker = new FasterDictionary<ExclusiveGroupStruct, HashSet<uint>>();
}
#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
void CheckSwapEntityID(EGID fromEgid, EGID toEgid, Type entityDescriptorType, string caller)
{
if (_multipleOperationOnSameEGIDChecker.ContainsKey(egid) == true)
if (_multipleOperationOnSameEGIDChecker.TryGetValue(fromEgid, out var fromOperationType) == true)
{
var operationName = OperationName(fromOperationType);
throw new ECSException(
"Executing multiple structural changes (remove) in one submission on the same entity is not supported "
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
.FastConcat(" previous operation was: ")
.FastConcat(_multipleOperationOnSameEGIDChecker[egid] == 1 ? "add" : "remove"));

if (_idChecker.TryGetValue(egid.groupID, out var hash))
"Executing multiple structural changes (swapFrom) on the same entity is not supported "
.FastConcat(" caller: ", caller, " ").FastConcat(fromEgid.entityID).FastConcat(" groupid: ")
.FastConcat(fromEgid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
.FastConcat(" previous operation was: ")
.FastConcat(operationName));
}

if (_multipleOperationOnSameEGIDChecker.TryGetValue(toEgid, out var toOperationType) == true)
{
if (hash.Contains(egid.entityID) == false)
throw new ECSException("Trying to remove an Entity not present in the database "
.FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available"));
var operationName = OperationName(toOperationType);
throw new ECSException(
"Executing multiple structural changes (swapTo) on the same entity is not supported "
.FastConcat(" caller: ", caller, " ").FastConcat(toEgid.entityID).FastConcat(" groupid: ")
.FastConcat(toEgid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
.FastConcat(" previous operation was: ")
.FastConcat(operationName));
}
else

HashRemove(fromEgid, entityDescriptorType, false, caller);
HashAdd(toEgid, entityDescriptorType, caller);

_multipleOperationOnSameEGIDChecker.Add(fromEgid, OperationType.SwapFrom);
_multipleOperationOnSameEGIDChecker.Add(toEgid, OperationType.SwapTo);
}

#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
void CheckRemoveEntityID(EGID egid, Type entityDescriptorType, string caller)
{
bool isAllowed = false;
if (_multipleOperationOnSameEGIDChecker.TryGetValue(egid, out var operationType) == true)
{
throw new ECSException("Trying to remove an Entity with a group never used so far "
.FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available"));
isAllowed = operationType == OperationType.Remove || operationType == OperationType.SwapFrom;

if (isAllowed)
{
#if VERBOSE
var operationName = OperationName(operationType);
Console.LogDebugWarning(
"Executing multiple structural changes (remove) in one submission on the same entity. Remove supersedes swap and remove operations "
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
.FastConcat(" previous operation was: ")
.FastConcat(operationName));
#endif
}
else
throw new ECSException(
"Executing multiple structural changes (remove) in one submission on the same entity is not supported "
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
.FastConcat(" previous operation was: ")
.FastConcat("add"));
}

hash.Remove(egid.entityID);
HashRemove(egid, entityDescriptorType, isAllowed, caller);

_multipleOperationOnSameEGIDChecker.Add(egid, 0);
if (isAllowed == false)
_multipleOperationOnSameEGIDChecker.Add(egid, OperationType.Remove);
else
_multipleOperationOnSameEGIDChecker[egid] = OperationType.Remove;
}

#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
void CheckAddEntityID(EGID egid, Type entityDescriptorType, string caller)
{
if (_multipleOperationOnSameEGIDChecker.ContainsKey(egid) == true)
if (_multipleOperationOnSameEGIDChecker.TryGetValue(egid, out var operationType) == true)
{
var operationName = OperationName(operationType);

throw new ECSException(
"Executing multiple structural changes (build) on the same entity is not supported "
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
.FastConcat(" previous operation was: ")
.FastConcat(_multipleOperationOnSameEGIDChecker[egid] == 1 ? "add" : "remove"));
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
.FastConcat(" previous operation was: ")
.FastConcat(operationName));
}

var hash = _idChecker.GetOrAdd(egid.groupID, () => new HashSet<uint>());
if (hash.Contains(egid.entityID) == true)
throw new ECSException("Trying to add an Entity already present in the database "
.FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID)
.FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null
? entityDescriptorType.Name
: "not available"));
hash.Add(egid.entityID);
_multipleOperationOnSameEGIDChecker.Add(egid, 1);
HashAdd(egid, entityDescriptorType, caller);

_multipleOperationOnSameEGIDChecker.Add(egid, OperationType.Add);
}

#if DONT_USE
@@ -85,9 +143,72 @@ namespace Svelto.ECS
#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
void ClearChecksForMultipleOperationsOnTheSameEgid() { _multipleOperationOnSameEGIDChecker.Clear(); }
void ClearChecksForMultipleOperationsOnTheSameEgid()
{
_multipleOperationOnSameEGIDChecker.Clear();
}

void HashRemove(EGID egid, Type entityDescriptorType, bool isAllowed, string caller)
{
if (_idChecker.TryGetValue(egid.groupID, out HashSet<uint> hash))
{
if (hash.Contains(egid.entityID) == false && isAllowed == false)
throw new ECSException(
"Trying to remove an Entity not present in the database "
.FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available"));
}
else
{
throw new ECSException(
"Trying to remove an Entity with a group never used so far "
.FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available"));
}

hash.Remove(egid.entityID);
}

void HashAdd(EGID egid, Type entityDescriptorType, string caller)
{
var hash = _idChecker.GetOrAdd(egid.groupID, () => new HashSet<uint>());
if (hash.Contains(egid.entityID) == true)
throw new ECSException(
"Trying to add an Entity already present in the database "
.FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID)
.FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(
entityDescriptorType != null
? entityDescriptorType.Name
: "not available"));
hash.Add(egid.entityID);
}

string OperationName(OperationType operationType)
{
string operationName;
switch (operationType)
{
case OperationType.Remove:
operationName = "remove";
break;
case OperationType.Add:
operationName = "add";
break;
case OperationType.SwapFrom:
operationName = "swapFrom";
break;
default:
operationName = "swapTo";
break;
}

return operationName;
}

readonly DataStructures.FasterDictionary<EGID, uint> _multipleOperationOnSameEGIDChecker;
readonly DataStructures.FasterDictionary<ExclusiveGroupStruct, HashSet<uint>> _idChecker;
DataStructures.FasterDictionary<EGID, OperationType> _multipleOperationOnSameEGIDChecker;
DataStructures.FasterDictionary<ExclusiveGroupStruct, HashSet<uint>> _idChecker;
}
}

+ 3
- 4
com.sebaslab.svelto.ecs/Core/ComponentBuilder.CheckFields.cs View File

@@ -6,6 +6,7 @@ using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using Svelto.ECS.Hybrid;
using Svelto.ECS.Serialization;

namespace Svelto.ECS
{
@@ -154,13 +155,11 @@ namespace Svelto.ECS
static readonly Type STRINGBUILDERTYPE = typeof(System.Text.StringBuilder);

internal static readonly Type ENTITY_INFO_COMPONENT = typeof(EntityInfoComponent);
public static ComponentID ENTITY_INFO_COMPONENT_ID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return ComponentTypeID<EntityInfoComponent>.id;
}
get => ComponentTypeID<EntityInfoComponent>.id;
}
}
}

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

@@ -52,7 +52,7 @@ namespace Svelto.ECS
if (IS_UNMANAGED)
EntityComponentIDMap.Register<T>(new Filler<T>());
#endif
ComponentTypeID<T>.Init();
ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT);

if (IS_ENTITY_VIEW_COMPONENT)


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

@@ -17,7 +17,7 @@ namespace Svelto.ECS


[DebuggerTypeProxy(typeof(ComponentIDDebugProxy))]
public struct ComponentID: IEquatable<ComponentID>
public struct ComponentID: IEquatable<ComponentID>, IComparable<ComponentID>
{
public static implicit operator int(ComponentID id)
{
@@ -46,6 +46,11 @@ namespace Svelto.ECS
{
return _id;
}
public int CompareTo(ComponentID other)
{
return _id.CompareTo(other._id);
}

int _id;
}

+ 4
- 2
com.sebaslab.svelto.ecs/Core/ComponentTypeID.cs View File

@@ -5,7 +5,7 @@ using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public static class BurstCompatibleCounter
static class BurstCompatibleCounter
{
public static int counter;
}
@@ -34,8 +34,10 @@ namespace Svelto.ECS
[Unity.Burst.BurstDiscard]
//SharedStatic values must be initialized from not burstified code
#endif
static void Init()
internal static void Init()
{
if (_id.Data != 0)
return;
_id.Data = Interlocked.Increment(ref BurstCompatibleCounter.counter);
ComponentTypeMap.Add(typeof(T), id);
}


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

@@ -6,8 +6,8 @@ using System.Runtime.InteropServices;

namespace Svelto.ECS
{
[Serialization.DoNotSerialize]
[Serializable]
[Serialization.DoNotSerialize] //EGID cannot be serialised with Svelto serialization code
[Serializable] //I do not remember why we marked this a Serializable though
[StructLayout(LayoutKind.Explicit)]
public struct EGID : IEquatable<EGID>, IComparable<EGID>
{


+ 11
- 3
com.sebaslab.svelto.ecs/Core/EnginesGroup/IStepEngine.cs View File

@@ -1,3 +1,6 @@
using System.Collections.Generic;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public interface IStepEngine : IEngine
@@ -9,17 +12,22 @@ namespace Svelto.ECS
public interface IStepEngine<T> : IEngine
{
void Step(in T _param);
void Step(in T param);
string name { get; }
}

public interface IGroupEngine
{
public IEnumerable<IEngine> engines { get; }
}
//this must stay IStepEngine as it may be part of a group itself
public interface IStepGroupEngine : IStepEngine
public interface IStepGroupEngine : IStepEngine, IGroupEngine
{
}
public interface IStepGroupEngine<T> : IStepEngine<T>
public interface IStepGroupEngine<T> : IStepEngine<T>, IGroupEngine
{
}
}

+ 40
- 12
com.sebaslab.svelto.ecs/Core/EnginesGroup/SortedEnginesGroup.cs View File

@@ -1,12 +1,13 @@
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.Common;

namespace Svelto.ECS
{
/// <summary>
/// SortedEnginesGroup is a practical way to group engines that can be ticked together. The class requires a
/// SequenceOrder struct that define the order of execution. The pattern to use is the following:
/// First define as many enums as you want with the ID of the engines to use. E.G.:
/// SortedEnginesGroup is a practical way to group engines that need to be ticked in order. The class requires a
/// SequenceOrder struct that defines the order of execution. The pattern to use is the following:
/// First define as many enums as you want with the IDs of the engines to use. E.G.:
/// public enum WiresCompositionEngineNames
///{
/// WiresTimeRunningGroup,
@@ -17,11 +18,11 @@ namespace Svelto.ECS
/// then link these ID to the actual engines, using the attribute Sequenced:
///
/// [Sequenced(nameof(WiresCompositionEngineNames.WiresTimeRunningGroup))]
/// class WiresTimeRunningGroup : UnsortedDeterministicEnginesGroup<IDeterministicTimeRunning>g {}
/// class WiresTimeRunningGroup : UnsortedDeterministicEnginesGroup<IDeterministicTimeRunning> {}
///
/// Note that the engine can be another group itself (like in this example).
/// Note that the engine can be another engines group itself.
///
/// then define the ISequenceOrder struct. E.G.:
/// then define the ISequenceOrder struct to define the order of execution. E.G.:
/// public struct DeterministicTimeRunningEnginesOrder: ISequenceOrder
/// {
/// private static readonly string[] order =
@@ -35,7 +36,18 @@ namespace Svelto.ECS
/// }
///
/// Now you can use the Type you just created (i.e.: DeterministicTimeRunningEnginesOrder) as generic parameter
/// of the SortedEnginesGroup.
/// of the SortedEnginesGroup. like this:
///
/// public class SortedDoofusesEnginesExecutionGroup : SortedEnginesGroup<IUpdateEngine, DeterministicTimeRunningEnginesOrder>
/// {
/// public SortedDoofusesEnginesExecutionGroup(FasterList<IUpdateEngine> engines) : base(engines)
/// {
/// }
/// }
///
/// This will then Tick the engines passed by constructor with the order defined in the DeterministicTimeRunningEnginesOrder
/// calling _enginesGroup.Step();
///
/// While the system may look convoluted, is an effective way to keep the engines assemblies decoupled from
/// each other
/// The class is abstract and it requires a user defined interface to push the user to use recognisable names meaningful
@@ -43,7 +55,7 @@ namespace Svelto.ECS
/// </summary>
/// <typeparam name="Interface">user defined interface that implements IStepEngine</typeparam>
public abstract class SortedEnginesGroup<Interface, SequenceOrder> : IStepGroupEngine
where SequenceOrder : struct, ISequenceOrder where Interface : IStepEngine
where SequenceOrder : struct, ISequenceOrder where Interface : class, IStepEngine
{
protected SortedEnginesGroup(FasterList<Interface> engines)
{
@@ -65,7 +77,15 @@ namespace Svelto.ECS
}

public string name => _name;
public IEnumerable<IEngine> engines
{
get
{
for (int i = 0; i < _instancedSequence.items.count; i++)
yield return _instancedSequence.items[i];
}
}

readonly string _name;
readonly Sequence<Interface, SequenceOrder> _instancedSequence;
}
@@ -76,7 +96,7 @@ namespace Svelto.ECS
/// <typeparam name="Interface"></typeparam>
/// <typeparam name="Parameter">Specialised Parameter that can be passed to all the engines in the group</typeparam>
public abstract class SortedEnginesGroup<Interface, Parameter, SequenceOrder>: IStepGroupEngine<Parameter>
where SequenceOrder : struct, ISequenceOrder where Interface : IStepEngine<Parameter>
where SequenceOrder : struct, ISequenceOrder where Interface : class, IStepEngine<Parameter>
{
protected SortedEnginesGroup(FasterList<Interface> engines)
{
@@ -84,7 +104,7 @@ namespace Svelto.ECS
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines);
}

public void Step(in Parameter param)
public void Step(in Parameter time)
{
var sequenceItems = _instancedSequence.items;
using (var profiler = new PlatformProfiler(_name))
@@ -92,12 +112,20 @@ namespace Svelto.ECS
for (var index = 0; index < sequenceItems.count; index++)
{
var engine = sequenceItems[index];
using (profiler.Sample(engine.name)) engine.Step(param);
using (profiler.Sample(engine.name)) engine.Step(time);
}
}
}

public string name => _name;
public IEnumerable<IEngine> engines
{
get
{
for (int i = 0; i < _instancedSequence.items.count; i++)
yield return _instancedSequence.items[i];
}
}
readonly string _name;
readonly Sequence<Interface, SequenceOrder> _instancedSequence;


+ 36
- 12
com.sebaslab.svelto.ecs/Core/EnginesGroup/UnsortedEnginesGroup.cs View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using Svelto.Common;
using Svelto.DataStructures;

@@ -6,12 +7,16 @@ namespace Svelto.ECS
/// <summary>
/// UnsortedEnginesGroup is a practical way to group engines that can be ticked together. As the name suggest
/// there is no way to defines an order, although the engines will run in the same order they are added.
/// It is abstract and it requires a user defined interface to push the user to use recognisable names meaningful
/// to the context where they are used.
/// It is abstract and it requires a user defined class to push the user to use recognisable names meaningful
/// to the context where they are used. like this:
/// public class SirensSequentialEngines: UnsortedEnginesGroup<IStepEngine>
/// {
///
/// }
/// </summary>
/// <typeparam name="Interface">user defined interface that implements IStepEngine</typeparam>
public abstract class UnsortedEnginesGroup<Interface> : IStepGroupEngine
where Interface : IStepEngine
where Interface : class, IStepEngine
{
protected UnsortedEnginesGroup()
{
@@ -27,12 +32,12 @@ namespace Svelto.ECS

public void Step()
{
var sequenceItems = _instancedSequence;
using (var profiler = new PlatformProfiler(_name))
{
for (var index = 0; index < sequenceItems.count; index++)
var instancedSequenceCount = _instancedSequence.count;
for (var index = 0; index < instancedSequenceCount; index++)
{
var engine = sequenceItems[index];
var engine = _instancedSequence[index];
using (profiler.Sample(engine.name)) engine.Step();
}
}
@@ -45,8 +50,18 @@ namespace Svelto.ECS

public string name => _name;
public IEnumerable<IEngine> engines
{
get
{
for (int i = 0; i < _instancedSequence.count; i++)
yield return _instancedSequence[i];
}
}
readonly string _name;
readonly FasterList<Interface> _instancedSequence;
}
/// <summary>
@@ -55,7 +70,7 @@ namespace Svelto.ECS
/// <typeparam name="Interface"></typeparam>
/// <typeparam name="Parameter">Specialised Parameter that can be passed to all the engines in the group</typeparam>
public abstract class UnsortedEnginesGroup<Interface, Parameter> : IStepGroupEngine<Parameter>
where Interface : IStepEngine<Parameter>
where Interface : class, IStepEngine<Parameter>
{
protected UnsortedEnginesGroup()
{
@@ -69,15 +84,15 @@ namespace Svelto.ECS
_instancedSequence = engines;
}

public void Step(in Parameter param)
public void Step(in Parameter time)
{
var sequenceItems = _instancedSequence;
using (var profiler = new PlatformProfiler(_name))
{
for (var index = 0; index < sequenceItems.count; index++)
var instancedSequenceCount = _instancedSequence.count;
for (var index = 0; index < instancedSequenceCount; index++)
{
var engine = sequenceItems[index];
using (profiler.Sample(engine.name)) engine.Step(param);
var engine = _instancedSequence[index];
using (profiler.Sample(engine.name)) engine.Step(time);
}
}
}
@@ -86,6 +101,15 @@ namespace Svelto.ECS
{
_instancedSequence.Add(engine);
}
public IEnumerable<IEngine> engines
{
get
{
for (int i = 0; i < _instancedSequence.count; i++)
yield return _instancedSequence[i];
}
}

public string name => _name;


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

@@ -18,7 +18,6 @@ namespace Svelto.ECS
{
EntityDescriptorsWarmup.WarmUp();
GroupHashMap.WarmUp();
//SharedDictonary.Init();
SerializationDescriptorMap.Init();

_swapEntities = SwapEntities;
@@ -38,13 +37,11 @@ namespace Svelto.ECS
public EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler)
{
_entitiesOperations = new EntitiesOperations();
_idChecker = new FasterDictionary<ExclusiveGroupStruct, HashSet<uint>>();

_cachedRangeOfSubmittedIndices = new FasterList<(uint, uint)>();
_transientEntityIDsLeftAndAffectedByRemoval = new FasterList<uint>();
_transientEntityIDsLeftWithoutDuplicates = new FasterDictionary<uint, int>();

_multipleOperationOnSameEGIDChecker = new FasterDictionary<EGID, uint>();
_transientEntityIDsAffectedByRemoveAtSwapBack = new FasterDictionary<uint, uint>();
InitDebugChecks();
#if UNITY_NATIVE //because of the thread count, ATM this is only for unity
_nativeSwapOperationQueue = new AtomicNativeBags(Allocator.Persistent);
_nativeRemoveOperationQueue = new AtomicNativeBags(Allocator.Persistent);
@@ -152,7 +149,9 @@ namespace Svelto.ECS

if (engine is IReactOnDispose viewEngineDispose)
CheckReactEngineComponents(
#pragma warning disable CS0618
typeof(IReactOnDispose<>), viewEngineDispose, _reactiveEnginesDispose, type.Name);
#pragma warning restore CS0618
if (engine is IReactOnDisposeEx viewEngineDisposeEx)
CheckReactEngineComponents(
@@ -160,7 +159,9 @@ namespace Svelto.ECS

if (engine is IReactOnSwap viewEngineSwap)
#pragma warning disable CS0612
#pragma warning disable CS0618
CheckReactEngineComponents(typeof(IReactOnSwap<>), viewEngineSwap, _reactiveEnginesSwap, type.Name);
#pragma warning restore CS0618
#pragma warning restore CS0612

if (engine is IReactOnSwapEx viewEngineSwapEx)
@@ -172,7 +173,11 @@ namespace Svelto.ECS
if (engine is IReactOnSubmissionStarted submissionEngineStarted)
_reactiveEnginesSubmissionStarted.Add(submissionEngineStarted);

if (engine is IGroupEngine stepGroupEngine)
foreach (var stepEngine in stepGroupEngine.engines)
AddEngine(stepEngine);
_enginesTypeSet.Add(refWrapper);
_enginesSet.Add(engine);

@@ -243,6 +248,9 @@ namespace Svelto.ECS

void Dispose(bool disposing)
{
if (_isDisposed)
return;
using (var profiler = new PlatformProfiler("Final Dispose"))
{
//Note: The engines are disposed before the the remove callback to give the chance to behave
@@ -252,7 +260,7 @@ namespace Svelto.ECS
foreach (var engine in _disposableEngines)
try
{
if (engine is IDisposingEngine dengine)
if (engine is IDisposableEngine dengine)
dengine.isDisposing = true;
engine.Dispose();


+ 42
- 15
com.sebaslab.svelto.ecs/Core/EnginesRoot.Entities.cs View File

@@ -8,7 +8,26 @@ using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public partial class EnginesRoot : IDisposable, IUnitTestingInterface
public static class EGIDMultiMapperNBExtension
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EGIDMultiMapper<T> QueryMappedEntities<T>(this EntitiesDB entitiesDb, LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T : struct, _IInternalEntityComponent
{
var dictionary = new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary<T>>((uint) groups.count);
foreach (var group in groups)
{
entitiesDb.QueryOrCreateEntityDictionary<T>(group, out var typeSafeDictionary);
//if (typeSafeDictionary.count > 0) avoiding this allows these egidmappers to be precreated and stored
dictionary.Add(group, typeSafeDictionary as ITypeSafeDictionary<T>);
}
return new EGIDMultiMapper<T>(dictionary);
}
}

public partial class EnginesRoot: IDisposable, IUnitTestingInterface
{
///--------------------------------------------
///
@@ -34,13 +53,15 @@ namespace Svelto.ECS
{
CheckAddEntityID(entityID, descriptorType, caller);

DBC.ECS.Check.Require(entityID.groupID.isInvalid == false,
DBC.ECS.Check.Require(
entityID.groupID.isInvalid == false,
"invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?");

var reference = _entityLocator.ClaimReference();
_entityLocator.SetReference(reference, entityID);

var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, componentsToBuild, implementors
var dic = EntityFactory.BuildGroupedEntities(
entityID, _groupedEntityToAdd, componentsToBuild, implementors
#if DEBUG && !PROFILE_SVELTO
, descriptorType
#endif
@@ -52,7 +73,7 @@ namespace Svelto.ECS
/// <summary>
/// Preallocate memory to avoid the impact to resize arrays when many entities are submitted at once
/// </summary>
void Preallocate(ExclusiveGroupStruct groupID, uint size, IComponentBuilder[] entityComponentsToBuild)
internal void Preallocate(ExclusiveGroupStruct groupID, uint size, IComponentBuilder[] entityComponentsToBuild)
{
void PreallocateEntitiesToAdd()
{
@@ -67,14 +88,14 @@ namespace Svelto.ECS
for (var index = 0; index < numberOfEntityComponents; index++)
{
var entityComponentBuilder = entityComponentsToBuild[index];
var entityComponentType = entityComponentBuilder.getComponentID;
var entityComponentType = entityComponentBuilder.getComponentID;

var dbList = group.GetOrAdd(entityComponentType, () => entityComponentBuilder.CreateDictionary(size));
var dbList = group.GetOrAdd(entityComponentType, () => entityComponentBuilder.CreateDictionary(size));
entityComponentBuilder.Preallocate(dbList, size);

if (_groupsPerEntity.TryGetValue(entityComponentType, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[entityComponentType] =
new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();
new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();

groupedGroup[groupID] = dbList;
}
@@ -88,7 +109,8 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
FasterDictionary<ComponentID, ITypeSafeDictionary> GetDBGroup(ExclusiveGroupStruct fromIdGroupId)
{
if (_groupEntityComponentsDB.TryGetValue(fromIdGroupId,
if (_groupEntityComponentsDB.TryGetValue(
fromIdGroupId,
out FasterDictionary<ComponentID, ITypeSafeDictionary> fromGroup) == false)
throw new ECSException("Group doesn't exist: ".FastConcat(fromIdGroupId.ToName()));

@@ -98,7 +120,8 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
FasterDictionary<ComponentID, ITypeSafeDictionary> GetOrAddDBGroup(ExclusiveGroupStruct toGroupId)
{
return _groupEntityComponentsDB.GetOrAdd(toGroupId,
return _groupEntityComponentsDB.GetOrAdd(
toGroupId,
() => new FasterDictionary<ComponentID, ITypeSafeDictionary>());
}

@@ -106,9 +129,11 @@ namespace Svelto.ECS
{
var fromGroup = GetDBGroup(fromEntityGID.groupID);

if (fromGroup.TryGetValue(ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID,
if (fromGroup.TryGetValue(
ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID,
out var entityInfoDic) //<entity ID, EntityInfoComponent>
&& ((ITypeSafeDictionary<EntityInfoComponent>)entityInfoDic).TryGetValue(fromEntityGID.entityID,
&& ((ITypeSafeDictionary<EntityInfoComponent>)entityInfoDic).TryGetValue(
fromEntityGID.entityID,
out var entityInfo)) //there could be multiple entity descriptors registered in the same group, so it's necessary to check if the entity registered in the group has entityInfoComponent
{
#if PARANOID_CHECK
@@ -135,9 +160,11 @@ namespace Svelto.ECS
{
var fromGroup = GetDBGroup(fromEntityGID.groupID);

if (fromGroup.TryGetValue(ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID,
if (fromGroup.TryGetValue(
ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID,
out var entityInfoDic) //<entity ID, EntityInfoComponent>
&& ((ITypeSafeDictionary<EntityInfoComponent>)entityInfoDic).TryGetValue(fromEntityGID.entityID,
&& ((ITypeSafeDictionary<EntityInfoComponent>)entityInfoDic).TryGetValue(
fromEntityGID.entityID,
out var entityInfo)) //there could be multiple entity descriptors registered in the same group, so it's necessary to check if the entity registered in the group has entityInfoComponent
{
#if PARANOID_CHECK
@@ -168,14 +195,14 @@ namespace Svelto.ECS
//for each group id, save a dictionary indexed by entity type of entities indexed by id
// group EntityComponentType entityID, EntityComponent
internal readonly FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>
_groupEntityComponentsDB;
_groupEntityComponentsDB;

//for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are
//found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold
//by _groupEntityComponentsDB
// <EntityComponentType <groupID <entityID, EntityComponent>>>
internal readonly FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>
_groupsPerEntity;
_groupsPerEntity;
#if SVELTO_LEGACY_FILTERS
//The filters stored for each component and group
internal readonly FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>>


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

@@ -77,7 +77,7 @@ namespace Svelto.ECS
}
#endif

//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside
//NOTE: enginesRoot is a weakreference ONLY because GenericEntityStreamConsumerFactory can be injected inside
//engines of other enginesRoot
readonly Svelto.DataStructures.WeakReference<EnginesRoot> _enginesRoot;
}


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

@@ -55,8 +55,7 @@ namespace Svelto.ECS

dictionary.KeysEvaluator((key) =>
{
_enginesRoot.Target.CheckRemoveEntityID(new EGID(key, fromGroupID), null, caller);
_enginesRoot.Target.CheckAddEntityID(new EGID(key, toGroupID), null, caller);
_enginesRoot.Target.CheckSwapEntityID(new EGID(key, fromGroupID), new EGID(key, toGroupID), null, caller);
});
#endif
_enginesRoot.Target.QueueSwapGroupOperation(fromGroupID, toGroupID, caller);
@@ -100,8 +99,7 @@ namespace Svelto.ECS

var enginesRootTarget = _enginesRoot.Target;

enginesRootTarget.CheckRemoveEntityID(fromEGID, TypeCache<T>.type, caller);
enginesRootTarget.CheckAddEntityID(toEGID, TypeCache<T>.type, caller);
enginesRootTarget.CheckSwapEntityID(fromEGID, toEGID, TypeCache<T>.type, caller);

enginesRootTarget.QueueSwapEntityOperation(fromEGID, toEGID
, _enginesRoot.Target.FindRealComponents<T>(fromEGID)
@@ -137,8 +135,7 @@ namespace Svelto.ECS
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void QueueSwapEntityOperation
(EGID fromID, EGID toID, IComponentBuilder[] componentBuilders, string caller)
void QueueSwapEntityOperation(EGID fromID, EGID toID, IComponentBuilder[] componentBuilders, string caller)
{
_entitiesOperations.QueueSwapOperation(fromID, toID, componentBuilders, caller);
}


+ 78
- 116
com.sebaslab.svelto.ecs/Core/EnginesRoot.Submission.cs View File

@@ -2,6 +2,7 @@
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.DataStructures.Native;
using Svelto.ECS.Internal;

namespace Svelto.ECS
@@ -13,7 +14,7 @@ namespace Svelto.ECS
{
//clear the data checks before the submission. We want to allow structural changes inside the callbacks
ClearChecksForMultipleOperationsOnTheSameEgid();
_entitiesOperations.ExecuteRemoveAndSwappingOperations(
_swapEntities,
_removeEntities,
@@ -22,7 +23,7 @@ namespace Svelto.ECS
this);

AddEntities(profiler);
//clear the data checks after the submission, so if structural changes happened inside the callback, the debug structure is reset for the next frame operations
ClearChecksForMultipleOperationsOnTheSameEgid();
}
@@ -45,8 +46,7 @@ namespace Svelto.ECS
}
}

static void RemoveEntities(
FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, FasterList<(uint, string)>>>
static void RemoveEntities(FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, FasterList<(uint, string)>>>
removeOperations, FasterList<EGID> entitiesRemoved, EnginesRoot enginesRoot)
{
using (var sampler = new PlatformProfiler("remove Entities"))
@@ -99,11 +99,11 @@ namespace Svelto.ECS

FasterList<(uint, string)> entityIDsToRemove = groupedEntitiesToRemove.value;

enginesRoot._transientEntityIDsLeftAndAffectedByRemoval.Clear();
enginesRoot._transientEntityIDsAffectedByRemoveAtSwapBack.Clear();

fromComponentsDictionary.RemoveEntitiesFromDictionary(
entityIDsToRemove,
enginesRoot._transientEntityIDsLeftAndAffectedByRemoval);
enginesRoot._transientEntityIDsAffectedByRemoveAtSwapBack);

//important: remove from the filter must happen after remove from the dictionary
//as we need to read the new indices linked to entities after the removal
@@ -111,8 +111,7 @@ namespace Svelto.ECS
entityIDsToRemove,
fromGroup,
componentType,
fromComponentsDictionary,
enginesRoot._transientEntityIDsLeftAndAffectedByRemoval);
enginesRoot._transientEntityIDsAffectedByRemoveAtSwapBack);

//store new database count after the entities are removed from the datatabase, plus the number of entities removed
enginesRoot._cachedRangeOfSubmittedIndices.Add(
@@ -162,89 +161,96 @@ namespace Svelto.ECS
}

static void SwapEntities(FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID,
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>> swapEntitiesOperations,
FasterList<(EGID, EGID)> entitiesIDSwaps, EnginesRoot enginesRoot)
FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>>> swapEntitiesOperations,
FasterDictionary<EGID, (EGID, EGID)> entitiesIDSwaps, EnginesRoot enginesRoot)
{
using (var sampler = new PlatformProfiler("Swap entities between groups"))
{
using (sampler.Sample("Update Entity References"))
{
var count = entitiesIDSwaps.count;
var entitiesIDSwapsValues = entitiesIDSwaps.unsafeValues;
for (int i = 0; i < count; i++)
{
var (fromEntityGid, toEntityGid) = entitiesIDSwaps[i];
var (fromEntityGid, toEntityGid) = entitiesIDSwapsValues[i];

enginesRoot._entityLocator.UpdateEntityReference(fromEntityGid, toEntityGid);
}
}
using (sampler.Sample("Swap Entities"))
{
enginesRoot._cachedRangeOfSubmittedIndices.Clear();

//Entities to swap are organised in order to minimise the amount of dictionary lookups.
//swapEntitiesOperations iterations happen in the following order:
//for each fromGroup, get all the entities to swap for each component type.
//then get the list of IDs for each ToGroup.
//now swap the set of FromGroup -> ToGroup entities per ID.
foreach (var entitiesToSwap in swapEntitiesOperations)
foreach (var entitiesToSwap in swapEntitiesOperations) //each operation is a component to swap
{
enginesRoot._cachedRangeOfSubmittedIndices.Clear();
ExclusiveGroupStruct fromGroup = entitiesToSwap.key;
var fromGroupDictionary = enginesRoot.GetDBGroup(fromGroup);
FasterDictionary<ComponentID, ITypeSafeDictionary> fromGroupDictionary = enginesRoot.GetDBGroup(fromGroup);

//iterate all the fromgroups
foreach (var groupedEntitiesToSwap in entitiesToSwap.value)
{
var componentType = groupedEntitiesToSwap.key;
ComponentID componentType = groupedEntitiesToSwap.key;
ITypeSafeDictionary fromComponentsDictionaryDB = fromGroupDictionary[componentType];

//get the subset of togroups that come from from group
//for each fromgroup get the subset of togroups (entities that move from fromgroup to any togroup)
foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
{
ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
ITypeSafeDictionary toComponentsDictionaryDB = enginesRoot.GetOrAddTypeSafeDictionary(
toGroup,
enginesRoot.GetOrAddDBGroup(toGroup),
componentType,
fromComponentsDictionaryDB);
toGroup, enginesRoot.GetOrAddDBGroup(toGroup), componentType, fromComponentsDictionaryDB);

DBC.ECS.Check.Assert(
toComponentsDictionaryDB != null,
"something went wrong with the creation of dictionaries");

//this list represents the set of entities that come from fromGroup and need
//to be swapped to toGroup. Most of the times will be 1 of few units.
FasterList<(uint, uint, string)> fromEntityToEntityIDs = entitiesInfoToSwap.value;
DBC.ECS.Check.Assert(toComponentsDictionaryDB != null, "something went wrong with the creation of dictionaries");

//entities moving from fromgroup to this togroup
FasterDictionary<uint, SwapInfo> fromEntityToEntityIDs = entitiesInfoToSwap.value;
DBC.ECS.Check.Assert(fromEntityToEntityIDs.count > 0, "something went wrong, no entities to swap");
//ensure that to dictionary has enough room to store the new entities
toComponentsDictionaryDB.EnsureCapacity(
(uint)(toComponentsDictionaryDB.count + (uint)fromEntityToEntityIDs.count));

//fortunately swap means that entities are added at the end of each destination
//dictionary list, so we can just iterate the list using the indices ranges added in the
//_cachedIndices
enginesRoot._cachedRangeOfSubmittedIndices.Add(
((uint, uint))(toComponentsDictionaryDB.count,
toComponentsDictionaryDB.count + fromEntityToEntityIDs.count));
var newBufferSize = (uint)(toComponentsDictionaryDB.count + fromEntityToEntityIDs.count);
toComponentsDictionaryDB.EnsureCapacity(newBufferSize);

enginesRoot._transientEntityIDsLeftAndAffectedByRemoval.Clear();
//fortunately swap adds the swapped entities at the end of the buffer
//so we can just iterate the list using the indices ranges added in the cachedIndices
enginesRoot._cachedRangeOfSubmittedIndices.Add(((uint, uint))(toComponentsDictionaryDB.count, newBufferSize));
enginesRoot._transientEntityIDsAffectedByRemoveAtSwapBack.Clear();

fromComponentsDictionaryDB.SwapEntitiesBetweenDictionaries(
fromEntityToEntityIDs,
fromGroup,
toGroup,
toComponentsDictionaryDB,
enginesRoot._transientEntityIDsLeftAndAffectedByRemoval);
fromEntityToEntityIDs, fromGroup, toGroup, toComponentsDictionaryDB,
enginesRoot._transientEntityIDsAffectedByRemoveAtSwapBack);

//important: this must happen after the entities are swapped in the database
enginesRoot.SwapEntityBetweenPersistentFilters(
fromEntityToEntityIDs,
fromComponentsDictionaryDB,
toComponentsDictionaryDB,
fromGroup,
toGroup,
componentType,
enginesRoot._transientEntityIDsLeftAndAffectedByRemoval);
fromEntityToEntityIDs, fromGroup, toGroup, componentType,
enginesRoot._transientEntityIDsAffectedByRemoveAtSwapBack);
}
}
var rangeEnumerator = enginesRoot._cachedRangeOfSubmittedIndices.GetEnumerator();
using (sampler.Sample("Execute Swap Callbacks Fast"))
{
foreach (var groupedEntitiesToSwap in entitiesToSwap.value)
{
var componentType = groupedEntitiesToSwap.key;

foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
{
rangeEnumerator.MoveNext();

//get all the engines linked to TValue
if (!enginesRoot._reactiveEnginesSwapEx.TryGetValue(componentType, out var entityComponentsEngines))
continue;

ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
ITypeSafeDictionary toComponentsDictionary = GetTypeSafeDictionary(
toGroup, enginesRoot.GetDBGroup(toGroup), componentType);

toComponentsDictionary.ExecuteEnginesSwapCallbacksFast(
entityComponentsEngines, fromGroup, toGroup, rangeEnumerator.Current, in sampler);
}
}
}
}
@@ -261,64 +267,20 @@ namespace Svelto.ECS
var componentType = groupedEntitiesToSwap.key;

//get all the engines linked to TValue
if (!enginesRoot._reactiveEnginesSwap.TryGetValue(
componentType,
out var entityComponentsEngines))
if (enginesRoot._reactiveEnginesSwap.TryGetValue(componentType, out var entityComponentsEngines) == false)
continue;

foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
{
ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
ITypeSafeDictionary toComponentsDictionary = GetTypeSafeDictionary(
toGroup,
enginesRoot.GetDBGroup(toGroup),
componentType);
toGroup, enginesRoot.GetDBGroup(toGroup), componentType);

var infosToProcess = entitiesInfoToSwap.value;

toComponentsDictionary.ExecuteEnginesSwapCallbacks(
infosToProcess,
entityComponentsEngines,
fromGroup,
toGroup,
in sampler);
}
}
}
}

var rangeEnumerator = enginesRoot._cachedRangeOfSubmittedIndices.GetEnumerator();
using (sampler.Sample("Execute Swap Callbacks Fast"))
{
foreach (var entitiesToSwap in swapEntitiesOperations)
{
ExclusiveGroupStruct fromGroup = entitiesToSwap.key;

foreach (var groupedEntitiesToSwap in entitiesToSwap.value)
{
var componentType = groupedEntitiesToSwap.key;

foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
{
rangeEnumerator.MoveNext();

//get all the engines linked to TValue
if (!enginesRoot._reactiveEnginesSwapEx.TryGetValue(
componentType, out var entityComponentsEngines))
continue;

ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
ITypeSafeDictionary toComponentsDictionary = GetTypeSafeDictionary(
toGroup,
enginesRoot.GetDBGroup(toGroup),
componentType);

toComponentsDictionary.ExecuteEnginesSwapCallbacksFast(
entityComponentsEngines,
fromGroup,
toGroup,
rangeEnumerator.Current,
in sampler);
infosToProcess, entityComponentsEngines,
fromGroup, toGroup, in sampler);
}
}
}
@@ -353,17 +315,17 @@ namespace Svelto.ECS
{
var type = entityComponentsToSubmit.key;
var fromDictionary = entityComponentsToSubmit.value;
var toDictionary = GetOrAddTypeSafeDictionary(groupID, groupDB, type, fromDictionary);

//all the new entities are added at the end of each dictionary list, so we can
//just iterate the list using the indices ranges added in the _cachedIndices
_cachedRangeOfSubmittedIndices.Add(
((uint, uint))(toDictionary.count, toDictionary.count + fromDictionary.count));
_cachedRangeOfSubmittedIndices.Add(((uint, uint))(toDictionary.count, toDictionary.count + fromDictionary.count));
//Fill the DB with the entity components generated this frame.
fromDictionary.AddEntitiesToDictionary(toDictionary, groupID
fromDictionary.AddEntitiesToDictionary(
toDictionary, groupID
#if SLOW_SVELTO_SUBMISSION
, entityLocator
, entityLocator
#endif
);
}
@@ -383,7 +345,7 @@ namespace Svelto.ECS
foreach (var entityComponentsToSubmit in groupToSubmit.components)
{
var type = entityComponentsToSubmit.key;
var toDictionary = GetTypeSafeDictionary(groupID, groupDB, type);
enumerator.MoveNext();
toDictionary.ExecuteEnginesAddEntityCallbacksFast(_reactiveEnginesAddEx, groupID, enumerator.Current, in sampler);
@@ -413,8 +375,7 @@ namespace Svelto.ECS
//this contains the total number of components ever submitted in the DB
ITypeSafeDictionary toDictionary = GetTypeSafeDictionary(groupID, groupDB, type);

fromDictionary.ExecuteEnginesAddCallbacks(
_reactiveEnginesAdd, toDictionary, groupID, in sampler);
fromDictionary.ExecuteEnginesAddCallbacks(_reactiveEnginesAdd, toDictionary, groupID, in sampler);
}
}
}
@@ -475,9 +436,10 @@ namespace Svelto.ECS
ITypeSafeDictionary fromDictionary = dictionaryOfEntities.value;
ITypeSafeDictionary toDictionary = GetOrAddTypeSafeDictionary(toGroupId, toGroup, refWrapperType, fromDictionary);

fromDictionary.AddEntitiesToDictionary(toDictionary, toGroupId
fromDictionary.AddEntitiesToDictionary(
toDictionary, toGroupId
#if SLOW_SVELTO_SUBMISSION
, entityLocator
, entityLocator
#endif
);
}
@@ -491,7 +453,8 @@ namespace Svelto.ECS
ITypeSafeDictionary toDictionary = GetTypeSafeDictionary(toGroupId, toGroup, refWrapperType);

//SwapEX happens inside
fromDictionary.ExecuteEnginesSwapCallbacks_Group(_reactiveEnginesSwap, _reactiveEnginesSwapEx, toDictionary,
fromDictionary.ExecuteEnginesSwapCallbacks_Group(
_reactiveEnginesSwap, _reactiveEnginesSwapEx, toDictionary,
fromGroupId, toGroupId, platformProfiler);
}

@@ -520,7 +483,7 @@ namespace Svelto.ECS
//update GroupsPerEntity
if (_groupsPerEntity.TryGetValue(typeID, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[typeID] =
new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();
new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();

groupedGroup[groupId] = toEntitiesDictionary;
}
@@ -545,14 +508,13 @@ namespace Svelto.ECS

//transient caches>>>>>>>>>>>>>>>>>>>>>
readonly FasterList<(uint, uint)> _cachedRangeOfSubmittedIndices;
readonly FasterDictionary<uint, int> _transientEntityIDsLeftWithoutDuplicates;
readonly FasterList<uint> _transientEntityIDsLeftAndAffectedByRemoval;
readonly FasterDictionary<uint, uint> _transientEntityIDsAffectedByRemoveAtSwapBack;
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

static readonly
Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID,
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>>, FasterList<(EGID, EGID)>
, EnginesRoot> _swapEntities;
Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID,
FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>>>, FasterDictionary<EGID, (EGID, EGID)>,
EnginesRoot> _swapEntities;

static readonly Action<
FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, FasterList<(uint, string)>>>,


+ 113
- 80
com.sebaslab.svelto.ecs/Core/EntitiesDB.cs View File

@@ -2,9 +2,7 @@
#define ENABLE_DEBUG_FUNC
#endif

using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

@@ -14,27 +12,14 @@ namespace Svelto.ECS
{
internal EntitiesDB(EnginesRoot enginesRoot, EnginesRoot.EntityReferenceMap entityReferencesMap)
{
_enginesRoot = enginesRoot;
_enginesRoot = enginesRoot;
_entityReferencesMap = entityReferencesMap;
}

EntityCollection<T> InternalQueryEntities<T>
(FasterDictionary<ComponentID, ITypeSafeDictionary> entitiesInGroupPerType)
where T : struct, _IInternalEntityComponent
public void PreallocateEntitySpace<T>(ExclusiveGroupStruct groupStructId, uint numberOfEntities)
where T : IEntityDescriptor, new()
{
uint count = 0;
IBuffer<T> buffer;
IEntityIDs ids = default;
if (SafeQueryEntityDictionary<T>(out var typeSafeDictionary, entitiesInGroupPerType) == false)
buffer = default;
else
{
ITypeSafeDictionary<T> safeDictionary = (typeSafeDictionary as ITypeSafeDictionary<T>);
buffer = safeDictionary.GetValues(out count);
ids = safeDictionary.entityIDs;
}

return new EntityCollection<T>(buffer, ids, count);
_enginesRoot.Preallocate(groupStructId, numberOfEntities, EntityDescriptorTemplate<T>.realDescriptor.componentsToBuild);
}

/// <summary>
@@ -46,7 +31,7 @@ namespace Svelto.ECS
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public EntityCollection<T> QueryEntities<T>(ExclusiveGroupStruct groupStructId)
where T : struct, _IInternalEntityComponent
where T : struct, _IInternalEntityComponent
{
if (groupEntityComponentsDB.TryGetValue(groupStructId, out var entitiesInGroupPerType) == false)
{
@@ -57,7 +42,7 @@ namespace Svelto.ECS
}

public EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroupStruct groupStruct)
where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent
where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent
{
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
{
@@ -70,21 +55,23 @@ namespace Svelto.ECS
var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType);
#if DEBUG && !PROFILE_SVELTO
if (T1entities.count != T2entities.count)
throw new ECSException("Entity components count do not match in group. Entity 1: ' count: "
.FastConcat(T1entities.count).FastConcat(" ", typeof(T1).ToString())
.FastConcat("'. Entity 2: ' count: ".FastConcat(T2entities.count)
.FastConcat(" ", typeof(T2).ToString())
.FastConcat(
"' group: ", groupStruct.ToName())).FastConcat(" this means that you are mixing descriptors in the same group that do not share the components that you are querying"));
throw new ECSException(
"Entity components count do not match in group. Entity 1: ' count: "
.FastConcat(T1entities.count).FastConcat(" ", typeof(T1).ToString())
.FastConcat(
"'. Entity 2: ' count: ".FastConcat(T2entities.count)
.FastConcat(" ", typeof(T2).ToString())
.FastConcat("' group: ", groupStruct.ToName())).FastConcat(
" this means that you are mixing descriptors in the same group that do not share the components that you are querying"));
#endif

return new EntityCollection<T1, T2>(T1entities, T2entities);
}

public EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroupStruct groupStruct)
where T1 : struct, _IInternalEntityComponent
where T2 : struct, _IInternalEntityComponent
where T3 : struct, _IInternalEntityComponent
where T1 : struct, _IInternalEntityComponent
where T2 : struct, _IInternalEntityComponent
where T3 : struct, _IInternalEntityComponent
{
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
{
@@ -99,23 +86,25 @@ namespace Svelto.ECS
var T3entities = InternalQueryEntities<T3>(entitiesInGroupPerType);
#if DEBUG && !PROFILE_SVELTO
if (T1entities.count != T2entities.count || T2entities.count != T3entities.count)
throw new ECSException("Entity components count do not match in group. Entity 1: "
.FastConcat(typeof(T1).ToString()).FastConcat(" count: ")
.FastConcat(T1entities.count).FastConcat(
" Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ")
.FastConcat(T2entities.count)
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString()))
.FastConcat(" count: ").FastConcat(T3entities.count)).FastConcat(" this means that you are mixing descriptors in the same group that do not share the components that you are querying"));
throw new ECSException(
"Entity components count do not match in group. Entity 1: "
.FastConcat(typeof(T1).ToString()).FastConcat(" count: ")
.FastConcat(T1entities.count).FastConcat(
" Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ")
.FastConcat(T2entities.count)
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString()))
.FastConcat(" count: ").FastConcat(T3entities.count)).FastConcat(
" this means that you are mixing descriptors in the same group that do not share the components that you are querying"));
#endif

return new EntityCollection<T1, T2, T3>(T1entities, T2entities, T3entities);
}

public EntityCollection<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>(ExclusiveGroupStruct groupStruct)
where T1 : struct, _IInternalEntityComponent
where T2 : struct, _IInternalEntityComponent
where T3 : struct, _IInternalEntityComponent
where T4 : struct, _IInternalEntityComponent
where T1 : struct, _IInternalEntityComponent
where T2 : struct, _IInternalEntityComponent
where T3 : struct, _IInternalEntityComponent
where T4 : struct, _IInternalEntityComponent
{
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
{
@@ -132,23 +121,25 @@ namespace Svelto.ECS
var T4entities = InternalQueryEntities<T4>(entitiesInGroupPerType);
#if DEBUG && !PROFILE_SVELTO
if (T1entities.count != T2entities.count || T2entities.count != T3entities.count
|| T3entities.count != T4entities.count)
throw new ECSException("Entity components count do not match in group. Entity 1: "
.FastConcat(typeof(T1).ToString()).FastConcat(" count: ")
.FastConcat(T1entities.count).FastConcat(
" Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ")
.FastConcat(T2entities.count)
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString()))
.FastConcat(" count: ").FastConcat(T3entities.count)
.FastConcat(" Entity 4: ".FastConcat(typeof(T4).ToString()))
.FastConcat(" count: ").FastConcat(T4entities.count)).FastConcat(" this means that you are mixing descriptors in the same group that do not share the components that you are querying"));
|| T3entities.count != T4entities.count)
throw new ECSException(
"Entity components count do not match in group. Entity 1: "
.FastConcat(typeof(T1).ToString()).FastConcat(" count: ")
.FastConcat(T1entities.count).FastConcat(
" Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ")
.FastConcat(T2entities.count)
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString()))
.FastConcat(" count: ").FastConcat(T3entities.count)
.FastConcat(" Entity 4: ".FastConcat(typeof(T4).ToString()))
.FastConcat(" count: ").FastConcat(T4entities.count)).FastConcat(
" this means that you are mixing descriptors in the same group that do not share the components that you are querying"));
#endif

return new EntityCollection<T1, T2, T3, T4>(T1entities, T2entities, T3entities, T4entities);
}

public GroupsEnumerable<T> QueryEntities<T>
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, _IInternalEntityComponent
public GroupsEnumerable<T> QueryEntities<T>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T : struct, _IInternalEntityComponent
{
return new GroupsEnumerable<T>(this, groups);
}
@@ -159,31 +150,31 @@ namespace Svelto.ECS
/// </summary>
/// <returns></returns>
public GroupsEnumerable<T1, T2> QueryEntities<T1, T2>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent
where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent
{
return new GroupsEnumerable<T1, T2>(this, groups);
}

public GroupsEnumerable<T1, T2, T3> QueryEntities<T1, T2, T3>
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T1 : struct, _IInternalEntityComponent
where T2 : struct, _IInternalEntityComponent
where T3 : struct, _IInternalEntityComponent
public GroupsEnumerable<T1, T2, T3> QueryEntities<T1, T2, T3>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T1 : struct, _IInternalEntityComponent
where T2 : struct, _IInternalEntityComponent
where T3 : struct, _IInternalEntityComponent
{
return new GroupsEnumerable<T1, T2, T3>(this, groups);
}

public GroupsEnumerable<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T1 : struct, _IInternalEntityComponent
where T2 : struct, _IInternalEntityComponent
where T3 : struct, _IInternalEntityComponent
where T4 : struct, _IInternalEntityComponent
public GroupsEnumerable<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T1 : struct, _IInternalEntityComponent
where T2 : struct, _IInternalEntityComponent
where T3 : struct, _IInternalEntityComponent
where T4 : struct, _IInternalEntityComponent
{
return new GroupsEnumerable<T1, T2, T3, T4>(this, groups);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroupStruct groupStructId)
where T : struct, _IInternalEntityComponent
where T : struct, _IInternalEntityComponent
{
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
throw new EntityGroupNotFoundException(typeof(T), groupStructId.ToName());
@@ -192,8 +183,8 @@ namespace Svelto.ECS
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryMappedEntities<T>
(ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper) where T : struct, _IInternalEntityComponent
public bool TryQueryMappedEntities<T>(ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper)
where T : struct, _IInternalEntityComponent
{
mapper = default;
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false
@@ -235,8 +226,7 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ExistsAndIsNotEmpty(ExclusiveGroupStruct gid)
{
if (groupEntityComponentsDB.TryGetValue(
gid, out FasterDictionary<ComponentID, ITypeSafeDictionary> group) == true)
if (groupEntityComponentsDB.TryGetValue(gid, out FasterDictionary<ComponentID, ITypeSafeDictionary> group) == true)
{
return group.count > 0;
}
@@ -271,9 +261,8 @@ namespace Svelto.ECS
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
bool SafeQueryEntityDictionary<T>
(out ITypeSafeDictionary typeSafeDictionary
, FasterDictionary<ComponentID, ITypeSafeDictionary> entitiesInGroupPerType) where T : struct, _IInternalEntityComponent
bool SafeQueryEntityDictionary<T>(FasterDictionary<ComponentID, ITypeSafeDictionary> entitiesInGroupPerType,
out ITypeSafeDictionary typeSafeDictionary) where T : struct, _IInternalEntityComponent
{
if (entitiesInGroupPerType.TryGetValue(ComponentTypeID<T>.id, out var safeDictionary)
== false)
@@ -289,10 +278,24 @@ namespace Svelto.ECS
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool SafeQueryEntityDictionary<T>
(ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary) where T : struct, _IInternalEntityComponent
internal bool SafeQueryEntityDictionary<T>(ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary)
where T : struct, _IInternalEntityComponent
{
if (UnsafeQueryEntityDictionary(group, ComponentTypeID<T>.id, out var safeDictionary) == false)
ITypeSafeDictionary safeDictionary;
bool ret;
//search for the group
if (groupEntityComponentsDB.TryGetValue(group, out FasterDictionary<ComponentID, ITypeSafeDictionary> entitiesInGroupPerType) == false)
{
safeDictionary = null;
ret = false;
}
else
{
ret = entitiesInGroupPerType.TryGetValue(ComponentTypeID<T>.id, out safeDictionary);
}

//search for the indexed entities in the group
if (ret == false)
{
typeSafeDictionary = default;
return false;
@@ -305,11 +308,21 @@ namespace Svelto.ECS
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool UnsafeQueryEntityDictionary
(ExclusiveGroupStruct group, ComponentID id, out ITypeSafeDictionary typeSafeDictionary)
internal void QueryOrCreateEntityDictionary<T>(ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary)
where T : struct, _IInternalEntityComponent
{
//search for the group
if (groupEntityComponentsDB.TryGetValue(group, out var entitiesInGroupPerType) == false)
FasterDictionary<ComponentID, ITypeSafeDictionary> entitiesInGroupPerType =
groupEntityComponentsDB.GetOrAdd(group, () => new FasterDictionary<ComponentID, ITypeSafeDictionary>());

typeSafeDictionary = entitiesInGroupPerType.GetOrAdd(ComponentTypeID<T>.id, () => TypeSafeDictionaryFactory<T>.Create(0));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool UnsafeQueryEntityDictionary(ExclusiveGroupStruct groupID, ComponentID id, out ITypeSafeDictionary typeSafeDictionary)
{
//search for the group
if (groupEntityComponentsDB.TryGetValue(groupID, out FasterDictionary<ComponentID, ITypeSafeDictionary> entitiesInGroupPerType) == false)
{
typeSafeDictionary = null;
return false;
@@ -319,8 +332,28 @@ namespace Svelto.ECS
return entitiesInGroupPerType.TryGetValue(id, out typeSafeDictionary);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
EntityCollection<T> InternalQueryEntities<T>(FasterDictionary<ComponentID, ITypeSafeDictionary> entitiesInGroupPerType)
where T : struct, _IInternalEntityComponent
{
uint count = 0;
IBuffer<T> buffer;
IEntityIDs ids = default;

if (SafeQueryEntityDictionary<T>(entitiesInGroupPerType, out var typeSafeDictionary) == false)
buffer = default;
else
{
ITypeSafeDictionary<T> safeDictionary = (typeSafeDictionary as ITypeSafeDictionary<T>);
buffer = safeDictionary.GetValues(out count);
ids = safeDictionary.entityIDs;
}

return new EntityCollection<T>(buffer, ids, count);
}

static readonly FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> _emptyDictionary =
new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();
new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();

readonly EnginesRoot _enginesRoot;

@@ -331,14 +364,14 @@ namespace Svelto.ECS
//values directly, that can be iterated over, so that is possible to iterate over all the entity components of
//a specific type inside a specific group.
FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>
groupEntityComponentsDB => _enginesRoot._groupEntityComponentsDB;
groupEntityComponentsDB => _enginesRoot._groupEntityComponentsDB;

//for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are
//found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold
//by _groupEntityComponentsDB
// <EntityComponentType <groupID <entityID, EntityComponent>>>
FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>> groupsPerComponent =>
_enginesRoot._groupsPerEntity;
_enginesRoot._groupsPerEntity;

EnginesRoot.EntityReferenceMap _entityReferencesMap;
}

+ 120
- 67
com.sebaslab.svelto.ecs/Core/EntitiesOperations.cs View File

@@ -31,21 +31,40 @@ namespace Svelto.ECS
_thisSubmissionInfo._groupsToRemove.Add((groupID, caller));
}

public void QueueRemoveOperation(EGID entityEgid, IComponentBuilder[] componentBuilders, string caller)
public void QueueRemoveOperation(EGID fromEgid, IComponentBuilder[] componentBuilders, string caller)
{
_thisSubmissionInfo._entitiesRemoved.Add(entityEgid);
_thisSubmissionInfo._entitiesRemoved.Add(fromEgid);
RevertSwapOperation(fromEgid);

//todo: limit the number of dictionaries that can be cached
//recycle or create dictionaries of components per group
var removedComponentsPerType = _thisSubmissionInfo._currentRemoveEntitiesOperations.RecycleOrAdd(
entityEgid.groupID, _newGroupsDictionary, _recycleDictionary);
fromEgid.groupID, _newGroupsDictionary, _recycleDictionary);

foreach (var operation in componentBuilders)
{
removedComponentsPerType //recycle or create dictionaries per component type
.RecycleOrAdd(operation.getComponentID, _newList, _clearList)
//add entity to remove
.Add((entityEgid.entityID, caller));
.Add((fromEgid.entityID, caller));
}

void RevertSwapOperation(EGID fromEgid)
{
if (_thisSubmissionInfo._entitiesSwapped.Remove(fromEgid, out (EGID fromEgid, EGID toEgid) val)) //Remove supersedes swap, check comment in IEntityFunctions.cs
{
var swappedComponentsPerType = _thisSubmissionInfo._currentSwapEntitiesOperations[fromEgid.groupID];

var componentBuildersLength = componentBuilders.Length - 1;

for (var index = componentBuildersLength; index >= 0; index--)
{
var operation = componentBuilders[index];

//todo: maybe the order of swappedComponentsPerType should be fromID, toGroupID, componentID
swappedComponentsPerType[operation.getComponentID][val.toEgid.groupID].Remove(fromEgid.entityID);
}
}
}
}

@@ -54,42 +73,42 @@ namespace Svelto.ECS
_thisSubmissionInfo._groupsToSwap.Add((fromGroupID, toGroupID, caller));
}

public void QueueSwapOperation(EGID fromID, EGID toID, IComponentBuilder[] componentBuilders, string caller)
public void QueueSwapOperation(EGID fromEGID, EGID toEGID, IComponentBuilder[] componentBuilders, string caller)
{
_thisSubmissionInfo._entitiesSwapped.Add((fromID, toID));
_thisSubmissionInfo._entitiesSwapped.Add(fromEGID, (fromEGID, toEGID));

//todo: limit the number of dictionaries that can be cached
//recycle or create dictionaries of components per group
//Get the dictionary that holds the entities that are swapping from fromID

//Get (or create) the dictionary that holds the entities that are swapping from fromEGID group
var swappedComponentsPerType = _thisSubmissionInfo._currentSwapEntitiesOperations.RecycleOrAdd(
fromID.groupID, _newGroupsDictionaryWithCaller, _recycleGroupDictionaryWithCaller);
fromEGID.groupID, _newGroupsDictionaryWithCaller, _recycleGroupDictionaryWithCaller);

var componentBuildersLength = componentBuilders.Length - 1;
//for each component of the entity that is swapping
for (var index = componentBuildersLength; index >= 0; index--)
{
var operation = componentBuilders[index];
//Get the dictionary for each component that holds the list of entities to swap
swappedComponentsPerType //recycle or create dictionaries per component type
var operation = componentBuilders[index];

//Get the dictionary for each component that holds the list of entities to swap
swappedComponentsPerType
//recycle or create dictionaries per component type
.RecycleOrAdd(operation.getComponentID, _newGroupDictionary, _recycleDicitionaryWithCaller)
//recycle or create list of entities to swap
.RecycleOrAdd(toID.groupID, _newListWithCaller, _clearListWithCaller)
.RecycleOrAdd(toEGID.groupID, _newListWithCaller, _clearListWithCaller)
//add entity to swap
.Add((fromID.entityID, toID.entityID, caller));
.Add(fromEGID.entityID, new SwapInfo(fromEGID.entityID, toEGID.entityID, caller));
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
public bool AnyOperationQueued()
{
return _thisSubmissionInfo.AnyOperationQueued();
}

public void ExecuteRemoveAndSwappingOperations(Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID,
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>>, FasterList<(EGID, EGID)>,
EnginesRoot> swapEntities, Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, FasterList<(uint, string)>>>,
FasterList<EGID>, EnginesRoot> removeEntities, Action<ExclusiveGroupStruct, EnginesRoot> removeGroup,
public void ExecuteRemoveAndSwappingOperations(
Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>>>, FasterDictionary<EGID, (EGID, EGID)>, EnginesRoot> swapEntities, Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, FasterList<(uint, string)>>>,
FasterList<EGID>, EnginesRoot> removeEntities, Action<ExclusiveGroupStruct, EnginesRoot> removeGroup,
Action<ExclusiveGroupStruct, ExclusiveGroupStruct, EnginesRoot> swapGroup, EnginesRoot enginesRoot)
{
(_thisSubmissionInfo, _lastSubmittedInfo) = (_lastSubmittedInfo, _thisSubmissionInfo);
@@ -104,7 +123,7 @@ namespace Svelto.ECS
catch
{
var str = "Crash while removing a whole group on ".FastConcat(group.ToString())
.FastConcat(" from : ", caller);
.FastConcat(" from : ", caller);

Console.LogError(str);

@@ -119,7 +138,7 @@ namespace Svelto.ECS
catch
{
var str = "Crash while swapping a whole group on "
.FastConcat(fromGroup.ToString(), " ", toGroup.ToString()).FastConcat(" from : ", caller);
.FastConcat(fromGroup.ToString(), " ", toGroup.ToString()).FastConcat(" from : ", caller);

Console.LogError(str);

@@ -127,88 +146,86 @@ namespace Svelto.ECS
}

if (_lastSubmittedInfo._entitiesSwapped.count > 0)
swapEntities(_lastSubmittedInfo._currentSwapEntitiesOperations, _lastSubmittedInfo._entitiesSwapped
, enginesRoot);
swapEntities(_lastSubmittedInfo._currentSwapEntitiesOperations, _lastSubmittedInfo._entitiesSwapped, enginesRoot);

if (_lastSubmittedInfo._entitiesRemoved.count > 0)
removeEntities(_lastSubmittedInfo._currentRemoveEntitiesOperations, _lastSubmittedInfo._entitiesRemoved
, enginesRoot);
removeEntities(
_lastSubmittedInfo._currentRemoveEntitiesOperations, _lastSubmittedInfo._entitiesRemoved
, enginesRoot);

_lastSubmittedInfo.Clear();
}
FasterDictionary<ComponentID, FasterList<(uint, string)>> NewGroupsDictionary()
static FasterDictionary<ComponentID, FasterList<(uint, string)>> NewGroupsDictionary()
{
return new FasterDictionary<ComponentID, FasterList<(uint, string)>>();
}
void RecycleDictionary(ref FasterDictionary<ComponentID, FasterList<(uint, string)>> recycled)
static void RecycleDictionary(ref FasterDictionary<ComponentID, FasterList<(uint, string)>> recycled)
{
recycled.Recycle();
}

FasterList<(uint, string)> NewList()
static FasterList<(uint, string)> NewList()
{
return new FasterList<(uint, string)>();
}

void ClearList(ref FasterList<(uint, string)> target)
static void ClearList(ref FasterList<(uint, string)> target)
{
target.Clear();
}

void RecycleDicitionaryWithCaller(ref FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>> target)
static void RecycleDicitionaryWithCaller(ref FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>> target)
{
target.Recycle();
}

void ClearListWithCaller(ref FasterList<(uint, uint, string)> target)
static void ClearListWithCaller(ref FasterDictionary<uint, SwapInfo> target)
{
target.Clear();
}

FasterList<(uint, uint, string)> NewListWithCaller()
static FasterDictionary<uint, SwapInfo> NewListWithCaller()
{
return new FasterList<(uint, uint, string)>();
return new FasterDictionary<uint, SwapInfo>();
}

FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>> NewGroupsDictionaryWithCaller()
static FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>>
NewGroupsDictionaryWithCaller()
{
return new FasterDictionary<ComponentID, //add case
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>();
return new FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>>();
}

void RecycleGroupDictionaryWithCaller(ref FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>> recycled)
static void RecycleGroupDictionaryWithCaller(
ref FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>> recycled)
{
recycled.Recycle();
}

FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>> NewGroupDictionary()
static FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>> NewGroupDictionary()
{
return new FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>();
return new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>();
}

struct Info
{
//from group //actual component type
internal FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID,
// to group ID //entityIDs , debugInfo
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>>
_currentSwapEntitiesOperations;
//from group //actual component type
internal FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>>> _currentSwapEntitiesOperations;

internal FasterDictionary<ExclusiveGroupStruct,
FasterDictionary<ComponentID, FasterList<(uint, string)>>> _currentRemoveEntitiesOperations;

internal FasterList<(EGID, EGID)> _entitiesSwapped;
internal FasterList<EGID> _entitiesRemoved;
public FasterList<(ExclusiveBuildGroup, ExclusiveBuildGroup, string)> _groupsToSwap;
public FasterList<(ExclusiveBuildGroup, string)> _groupsToRemove;
internal FasterDictionary<EGID, (EGID fromEgid, EGID toEgid)> _entitiesSwapped;
internal FasterList<EGID> _entitiesRemoved;
public FasterList<(ExclusiveBuildGroup, ExclusiveBuildGroup, string)> _groupsToSwap;
public FasterList<(ExclusiveBuildGroup, string)> _groupsToRemove;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool AnyOperationQueued()
{
return _entitiesSwapped.count > 0 || _entitiesRemoved.count > 0 || _groupsToSwap.count > 0
|| _groupsToRemove.count > 0;
|| _groupsToRemove.count > 0;
}

internal void Clear()
@@ -223,32 +240,68 @@ namespace Svelto.ECS

internal void Init()
{
_entitiesSwapped = new FasterList<(EGID, EGID)>();
_entitiesSwapped = new FasterDictionary<EGID, (EGID fromEgid, EGID toEgid)>();
_entitiesRemoved = new FasterList<EGID>();
_groupsToRemove = new FasterList<(ExclusiveBuildGroup, string)>();
_groupsToSwap = new FasterList<(ExclusiveBuildGroup, ExclusiveBuildGroup, string)>();
_groupsToRemove = new FasterList<(ExclusiveBuildGroup, string)>();
_groupsToSwap = new FasterList<(ExclusiveBuildGroup, ExclusiveBuildGroup, string)>();

_currentSwapEntitiesOperations =
new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID,
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>>();
new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID,
FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>>>();
_currentRemoveEntitiesOperations =
new FasterDictionary<ExclusiveGroupStruct,
FasterDictionary<ComponentID, FasterList<(uint, string)>>>();
new FasterDictionary<ExclusiveGroupStruct,
FasterDictionary<ComponentID, FasterList<(uint, string)>>>();
}
}

Info _lastSubmittedInfo;
Info _thisSubmissionInfo;

readonly Func<FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>> _newGroupDictionary;
readonly Func<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>> _newGroupDictionary;
readonly Func<FasterDictionary<ComponentID, FasterList<(uint, string)>>> _newGroupsDictionary;
readonly ActionRef<FasterDictionary<ComponentID, FasterList<(uint, string)>>> _recycleDictionary;
readonly Func<FasterList<(uint, string)>> _newList;
readonly ActionRef<FasterList<(uint, string)>> _clearList;
readonly Func<FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>> _newGroupsDictionaryWithCaller;
readonly ActionRef<FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>> _recycleGroupDictionaryWithCaller;
readonly ActionRef<FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>> _recycleDicitionaryWithCaller;
readonly Func<FasterList<(uint, uint, string)>> _newListWithCaller;
readonly ActionRef<FasterList<(uint, uint, string)>> _clearListWithCaller;

readonly Func<FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>>>
_newGroupsDictionaryWithCaller;

readonly ActionRef<FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>>>
_recycleGroupDictionaryWithCaller;

readonly ActionRef<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<uint, SwapInfo>>> _recycleDicitionaryWithCaller;
readonly Func<FasterDictionary<uint, SwapInfo>> _newListWithCaller;
readonly ActionRef<FasterDictionary<uint, SwapInfo>> _clearListWithCaller;
}

public struct SwapInfo
{
public uint fromID; //to do this information should be redundant, try to remove it
public uint toID;
public uint toIndex;
public string trace;

public SwapInfo(uint fromEgidEntityId, uint toEgidEntityId, string s)
{
fromID = fromEgidEntityId;
toID = toEgidEntityId;
toIndex = 0;
trace = s;
}

public void Deconstruct(out uint fromID, out uint toID, out uint toIndex, out string caller)
{
fromID = this.fromID;
toID = this.toID;
toIndex = this.toIndex;
caller = this.trace;
}
public void Deconstruct(out uint fromID, out uint toID, out string caller)
{
fromID = this.fromID;
toID = this.toID;
caller = this.trace;
}
}
}

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

@@ -10,15 +10,15 @@ namespace Svelto.ECS
{
DBC.ECS.Check.Require(count == 0 || buffer.isValid, "Buffer is found in impossible state");

_buffer = buffer;
_entityIDs = entityIDs;
this.buffer = buffer;
this.entityIDs = entityIDs;
this.count = count;
}

public uint count { get; }

public readonly IBufferBase _buffer;
public readonly IEntityIDs _entityIDs;
public readonly IBufferBase buffer;
public readonly IEntityIDs entityIDs;
}

public readonly ref struct EntityCollection<T1, T2> where T1 : struct, _IInternalEntityComponent


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

@@ -19,9 +19,7 @@ namespace Svelto.ECS

public void Init<T>(T initializer) where T : struct, _IInternalEntityComponent
{
if (_group.TryGetValue(
ComponentTypeID<T>.id,
out var typeSafeDictionary) == false)
if (_group.TryGetValue(ComponentTypeID<T>.id, out var typeSafeDictionary) == false)
return;

var dictionary = (ITypeSafeDictionary<T>)typeSafeDictionary;
@@ -29,16 +27,14 @@ namespace Svelto.ECS
if (ComponentBuilder<T>.HAS_EGID)
SetEGIDWithoutBoxing<T>.SetIDWithoutBoxing(ref initializer, _ID);
#endif

if (dictionary.TryFindIndex(_ID.entityID, out var findElementIndex))
dictionary.GetDirectValueByRef(findElementIndex) = initializer;
}

internal ref T GetOrAdd<T>() where T : struct, _IInternalEntityComponent
internal ref T GetOrAdd<T>() where T : unmanaged, _IInternalEntityComponent
{
ref var entityDictionary = ref _group.GetOrAdd(
ComponentTypeID<T>.id,
() => new UnmanagedTypeSafeDictionary<T>(1));
ref var entityDictionary = ref _group.GetOrAdd(ComponentTypeID<T>.id, () => new UnmanagedTypeSafeDictionary<T>(1));
var dictionary = (ITypeSafeDictionary<T>)entityDictionary;

return ref dictionary.GetOrAdd(_ID.entityID);
@@ -46,15 +42,12 @@ namespace Svelto.ECS

public ref T Get<T>() where T : struct, _IInternalEntityComponent
{
return ref (_group[ComponentTypeID<T>.id] as ITypeSafeDictionary<T>)
.GetValueByRef(_ID.entityID);
return ref (_group[ComponentTypeID<T>.id] as ITypeSafeDictionary<T>).GetValueByRef(_ID.entityID);
}

public bool Has<T>() where T : struct, _IInternalEntityComponent
{
if (_group.TryGetValue(
ComponentTypeID<T>.id,
out var typeSafeDictionary))
if (_group.TryGetValue(ComponentTypeID<T>.id, out var typeSafeDictionary))
{
var dictionary = (ITypeSafeDictionary<T>)typeSafeDictionary;



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

@@ -146,6 +146,7 @@ namespace Svelto.ECS
_egidToReferenceMap.Remove(fromGroupId);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityReference GetEntityReference(EGID egid)
{
if (_egidToReferenceMap.TryGetValue(egid.groupID, out var groupMap))
@@ -153,15 +154,16 @@ namespace Svelto.ECS
if (groupMap.TryGetValue(egid.entityID, out var locator))
return locator;
#if DEBUG && !PROFILE_SVELTO
else
throw new ECSException(
$"Entity {egid} does not exist. Are you creating it? Try getting it from initializer.reference.");
throw new ECSException(
$"Entity {egid} does not exist. If you just created it, get it from initializer.reference.");
#endif
}

return EntityReference.Invalid;
throw new ECSException(
$"Entity {egid} does not exist. If you just created it, get it from initializer.reference.");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SharedSveltoDictionaryNative<uint, EntityReference> GetEntityReferenceMap(ExclusiveGroupStruct groupID)
{
if (_egidToReferenceMap.TryGetValue(groupID, out var groupMap) == false)
@@ -170,6 +172,7 @@ namespace Svelto.ECS
return groupMap;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetEGID(EntityReference reference, out EGID egid)
{
egid = default;
@@ -189,6 +192,7 @@ namespace Svelto.ECS
return false;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EGID GetEGID(EntityReference reference)
{
#if DEBUG && !PROFILE_SVELTO
@@ -202,7 +206,6 @@ namespace Svelto.ECS
if (entityReferenceMapElement.version != reference.version)
throw new ECSException("outdated Reference");
#endif

return entityReferenceMapElement.egid;
}

@@ -244,8 +247,7 @@ namespace Svelto.ECS
//alternatively since the groups are guaranteed to be sequential an array should be used instead
//than a dictionary for groups. It could be a good case to implement a 4k chunk based sparseset
SharedSveltoDictionaryNative<ExclusiveGroupStruct, SharedSveltoDictionaryNative<uint, EntityReference>>
_egidToReferenceMap;
SharedSveltoDictionaryNative<ExclusiveGroupStruct, SharedSveltoDictionaryNative<uint, EntityReference>> _egidToReferenceMap;
}

EntityReferenceMap entityLocator => _entityLocator;


+ 1
- 0
com.sebaslab.svelto.ecs/Core/EntityReference/EntitiesDB.References.cs View File

@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using Svelto.DataStructures;
using Svelto.DataStructures.Native;

namespace Svelto.ECS


+ 7
- 2
com.sebaslab.svelto.ecs/Core/EntityReference/EntityReference.cs View File

@@ -67,11 +67,16 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ToEGID(EntitiesDB entitiesDB, out EGID egid)
{
DBC.ECS.Check.Require(this != Invalid, "Invalid Reference Used");

return entitiesDB.TryGetEGID(this, out egid);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists(EntitiesDB entitiesDB)
{
return this != Invalid && entitiesDB.TryGetEGID(this, out _);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong ToULong()
{
return _GID;


+ 5
- 0
com.sebaslab.svelto.ecs/Core/Filters/CombinedFilterID.cs View File

@@ -26,6 +26,11 @@ namespace Svelto.ECS
{
id = (long)filterID << 32 | (long)contextID.id << 16;
}
public CombinedFilterID(uint filterID, FilterContextID contextID)
{
id = (long)filterID << 32 | (long)contextID.id << 16;
}

public static implicit operator CombinedFilterID((int filterID, FilterContextID contextID) data)
{


+ 26
- 59
com.sebaslab.svelto.ecs/Core/Filters/EnginesRoot.Filters.cs View File

@@ -1,6 +1,5 @@
using Svelto.DataStructures;
using Svelto.DataStructures.Native;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
@@ -59,11 +58,9 @@ namespace Svelto.ECS
/// <param name="entityIDsRemoved"></param>
/// <param name="fromGroup"></param>
/// <param name="refWrapperType"></param>
/// <param name="fromDic"></param>
/// <param name="entityIDsLeftAndAffectedByRemoval"></param>
void RemoveEntitiesFromPersistentFilters
(FasterList<(uint entityID, string)> entityIDsRemoved, ExclusiveGroupStruct fromGroup, ComponentID refWrapperType
, ITypeSafeDictionary fromDic, FasterList<uint> entityIDsLeftAndAffectedByRemoval)
(FasterList<(uint entityID, string)> entityIDsRemoved, ExclusiveGroupStruct fromGroup, ComponentID refWrapperType, FasterDictionary<uint, uint> entityIDsAffectedByRemoveAtSwapBack)
{
//is there any filter used by this component?
if (_indicesOfPersistentFiltersUsedByThisComponent.TryGetValue(
@@ -72,14 +69,6 @@ namespace Svelto.ECS
var numberOfFilters = listOfFilters.count;
var filters = _persistentEntityFilters.unsafeValues;
//remove duplicates
_transientEntityIDsLeftWithoutDuplicates.Clear();
var entityAffectedCount = entityIDsLeftAndAffectedByRemoval.count;
for (int i = 0; i < entityAffectedCount; i++)
{
_transientEntityIDsLeftWithoutDuplicates[entityIDsLeftAndAffectedByRemoval[i]] = -1;
}

for (int filterIndex = 0; filterIndex < numberOfFilters; ++filterIndex)
{
//foreach filter linked to this component
@@ -105,16 +94,11 @@ namespace Svelto.ECS
//of the deleted component
//entityIDsAffectedByRemoval tracks all the entitiesID of the components that need to be updated
//in the filters because their indices in the array changed.
foreach (var entity in _transientEntityIDsLeftWithoutDuplicates)
foreach (var entity in entityIDsAffectedByRemoveAtSwapBack)
{
var entityId = entity.key;
if (fromGroupFilter.Exists(entityId)) //does the entityID that has been swapped exist in the filter?
{
if (entity.value == -1)
entity.value = (int)fromDic.GetIndex(entityId); //let's find the index of the entityID in the dictionary only once
fromGroupFilter._entityIDToDenseIndex[entityId] = (uint) entity.value; //update the index in the filter of the component that has been swapped
}
var resultEntityIdToDenseIndex = fromGroupFilter._entityIDToDenseIndex;
if (resultEntityIdToDenseIndex.TryFindIndex(entity.key, out var index)) //does the entityID that has been swapped exist in the filter?
resultEntityIdToDenseIndex.unsafeValues[index] = entity.value; //update the index in the filter of the component that has been swapped
}
}
}
@@ -123,66 +107,49 @@ namespace Svelto.ECS

//this method is called by the framework only if listOfFilters.count > 0
void SwapEntityBetweenPersistentFilters
(FasterList<(uint, uint, string)> fromEntityToEntityIDs, ITypeSafeDictionary fromDic
, ITypeSafeDictionary toDic, ExclusiveGroupStruct fromGroup, ExclusiveGroupStruct toGroup
, ComponentID refWrapperType, FasterList<uint> entityIDsLeftAndAffectedByRemoval)
(FasterDictionary<uint, SwapInfo> fromEntityToEntityIDs, ExclusiveGroupStruct fromGroup, ExclusiveGroupStruct toGroup
, ComponentID refWrapperType, FasterDictionary<uint, uint> entityIDsAffectedByRemoveAtSwapBack)
{
//is there any filter used by this component?
if (_indicesOfPersistentFiltersUsedByThisComponent.TryGetValue(
refWrapperType, out NativeDynamicArrayCast<int> listOfFilters) == true)
if (_indicesOfPersistentFiltersUsedByThisComponent.TryGetValue(refWrapperType, out NativeDynamicArrayCast<int> listOfFilters) == true)
{
DBC.ECS.Check.Require(listOfFilters.count > 0, "why are you calling this with an empty list?");
var numberOfFilters = listOfFilters.count;
//remove duplicates
_transientEntityIDsLeftWithoutDuplicates.Clear();
var entityAffectedCount = entityIDsLeftAndAffectedByRemoval.count;
for (int i = 0; i < entityAffectedCount; i++)
{
_transientEntityIDsLeftWithoutDuplicates[entityIDsLeftAndAffectedByRemoval[i]] = -1;
}

/// fromEntityToEntityIDs are the IDs of the entities to swap from the from group to the to group.
/// for this component type. for each component type, there is only one set of fromEntityToEntityIDs
/// per from/to group.
//foreach filter linked to this component
for (int filterIndex = 0; filterIndex < numberOfFilters; ++filterIndex)
{
//if the group has a filter linked:
EntityFilterCollection persistentFilter =
_persistentEntityFilters.unsafeValues[listOfFilters[filterIndex]];
EntityFilterCollection persistentFilter = _persistentEntityFilters.unsafeValues[listOfFilters[filterIndex]];
//if the group has a filter linked:
if (persistentFilter._filtersPerGroup.TryGetValue(fromGroup, out var fromGroupFilter))
{
EntityFilterCollection.GroupFilters groupFilterTo = default;

foreach (var (fromEntityID, toEntityID, _) in fromEntityToEntityIDs)
/// fromEntityToEntityIDs are the IDs of the entities to swap from the fromgroup to the togroup.
/// for this component type. for each component type, there is only one set of fromEntityToEntityIDs
/// per from/to group.
var countOfEntitiesToSwap = fromEntityToEntityIDs.count;
SwapInfo[] idOfEntitiesToSwap = fromEntityToEntityIDs.unsafeValues;

for (var index = 0; index < countOfEntitiesToSwap; index++)
{
var toIndex = toDic.GetIndex(toEntityID); //todo: optimize this should be calculated only once and not once per filter
(uint fromEntityID, uint toEntityID, uint toIndex, _) = idOfEntitiesToSwap[index];
//if there is an entity, it must be moved to the to filter
if (fromGroupFilter.Exists(fromEntityID) == true)
if (fromGroupFilter.Remove(fromEntityID) == true)
{
if (groupFilterTo.isValid == false)
groupFilterTo = persistentFilter.GetOrCreateGroupFilter(toGroup);
groupFilterTo = persistentFilter.GetOrCreateGroupFilter(toGroup); //should I call this outside and avoid the if? Do I want to create a group if there is no entity to swap?

groupFilterTo.Add(toEntityID, toIndex);
groupFilterTo.Add(toEntityID, toIndex); //add the entity with the index in the current buffer
}
}

foreach (var (fromEntityID, _, _) in fromEntityToEntityIDs)
foreach (var entity in entityIDsAffectedByRemoveAtSwapBack)
{
fromGroupFilter.Remove(fromEntityID); //Remove works even if the ID is not found (just returns false)
}

foreach (var entity in _transientEntityIDsLeftWithoutDuplicates)
{
var entityId = entity.key;
if (fromGroupFilter.Exists(entityId))
{
if (entity.value == -1)
entity.value = (int)fromDic.GetIndex(entityId);
fromGroupFilter._entityIDToDenseIndex[entityId] = (uint) entity.value;
}
var resultEntityIdToDenseIndex = fromGroupFilter._entityIDToDenseIndex;
if (resultEntityIdToDenseIndex.TryFindIndex(entity.key, out var index))
resultEntityIdToDenseIndex.unsafeValues[index] = (uint)entity.value;
}
}
}


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

@@ -99,10 +99,10 @@ namespace Svelto.ECS
/// Create a persistent filter. Persistent filters are not deleted after each submission,
/// however they have a maintenance cost that must be taken into account and will affect
/// entities submission performance.
/// Persistent filters keep track of the entities group swaps and they are automatically updated accordingly.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>

#if UNITY_BURST && UNITY_COLLECTIONS
[Unity.Burst.BurstDiscard] //not burst compatible because of ComponentTypeID<T>.id and GetOrAdd callback;
#endif


+ 69
- 15
com.sebaslab.svelto.ecs/Core/Filters/EntityFilterCollection.cs View File

@@ -7,6 +7,13 @@ using Svelto.ECS.Native;

namespace Svelto.ECS
{
/// <summary>
/// note: the whole Svelto ECS framework is based on the assumption that the NB and MB structures are ref
/// since we are holding them inside jobs, we decided to not mark them as ref but the pointers inside must be constant!
/// This works because NB and MB data wrapped can be changed only inside a submission which is not jobifiable
/// however filters can be modified inside jobs which means that data can change asynchronously. For this reason filters should be
/// always queries inside jobs when these are used.
/// </summary>
public readonly struct EntityFilterCollection
{
internal EntityFilterCollection(CombinedFilterID combinedFilterId,
@@ -22,25 +29,72 @@ namespace Svelto.ECS
public EntityFilterIterator GetEnumerator() => new EntityFilterIterator(this);

/// <summary>
/// Add a new entity to this filter
/// This method assumes that the entity has already been submitted in the database, so it can be found through the EGIDMapper
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Add<T>(EGID egid, NativeEGIDMapper<T> mmap) where T : unmanaged, _IInternalEntityComponent
public void Add<T>(EGID egid, NativeEGIDMapper<T> mmap) where T : unmanaged, _IInternalEntityComponent
{
DBC.ECS.Check.Require(mmap.groupID == egid.groupID, "not compatible NativeEgidMapper used");

return Add(egid, mmap.GetIndex(egid.entityID));
Add(egid, mmap.GetIndex(egid.entityID));
}

public bool Add<T>(EGID egid, NativeEGIDMultiMapper<T> mmap) where T : unmanaged, _IInternalEntityComponent
/// <summary>
/// Add a new entity to this filter
/// This method assumes that the entity has already been submitted in the database, so it can be found through the EGIDMapper
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add<T>(EGID egid, NativeEGIDMultiMapper<T> mmap) where T : unmanaged, _IInternalEntityComponent
{
Add(egid, mmap.GetIndex(egid));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add<T>(EGID egid, EGIDMapper<T> mmap) where T : unmanaged, _IInternalEntityComponent
{
return Add(egid, mmap.GetIndex(egid));
DBC.ECS.Check.Require(mmap.groupID == egid.groupID, "not compatible NativeEgidMapper used");

Add(egid, mmap.GetIndex(egid.entityID));
}

/// <summary>
/// Add a new entity to this filter
/// This method assumes that the entity has already been submitted in the database, so it can be found through the EGIDMapper
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Add(EGID egid, uint indexInComponentArray)
public void Add<T>(EGID egid, EGIDMultiMapper<T> mmap) where T : unmanaged, _IInternalEntityComponent
{
return GetOrCreateGroupFilter(egid.groupID).Add(egid.entityID, indexInComponentArray);
Add(egid, mmap.GetIndex(egid));
}

/// <summary>
/// Add a new entity to this filter
/// If the user knows at which position the entity is stored in the database, it can be passed directly to the filter
/// This position can be assumed if the user knows how Svelto stores components internally, but it's only guaranteed when passed back from the
/// Svelto callbacks, like the IReactOnAddEx.Add callbacks. Exploting IReactOnAddEx is the only reccomended pattern to use together with this
/// method. If the wrong index is passed, the filter will have undefined behaviour
/// </summary>
/// <param name="egid"> the entity EGID </param>
/// <param name="indexInComponentArray"> the position in the Svelto database array where the component is stored</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(EGID egid, uint indexInComponentArray)
{
GetOrCreateGroupFilter(egid.groupID).Add(egid.entityID, indexInComponentArray);
}

/// <summary>
/// Add a new entity to this filter
/// If the user knows at which position the entity is stored in the database, it can be passed directly to the filter
/// This position can be assumed if the user knows how Svelto stores components internally, but it's only guaranteed when passed back from the
/// Svelto callbacks, like the IReactOnAddEx.Add callbacks. Exploting IReactOnAddEx is the only reccomended pattern to use together with this
/// method. If the wrong index is passed, the filter will have undefined behaviour
/// </summary>
/// <param name="egid"> the entity EGID </param>
/// <param name="indexInComponentArray"> the position in the Svelto database array where the component is stored</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(uint entityID, ExclusiveGroupStruct groupId, uint indexInComponentArray)
{
@@ -122,14 +176,16 @@ namespace Svelto.ECS

internal int groupCount => _filtersPerGroup.count;
public void ComputeFinalCount(out int count)
public int ComputeFinalCount()
{
count = 0;
int count = 0;
for (int i = 0; i < _filtersPerGroup.count; i++)
{
count += (int)GetGroup(i).count;
}

return count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -161,21 +217,19 @@ namespace Svelto.ECS
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Add(uint entityId, uint indexInComponentArray)
public void Add(uint entityId, uint indexInComponentArray)
{
//TODO: check what happens (unit test) if adding the same entityID twice
//TODO: when sentinels are finished, we need to add AsWriter here
//cannot write in parallel
return _entityIDToDenseIndex.TryAdd(entityId, indexInComponentArray, out _);
_entityIDToDenseIndex.Add(entityId, indexInComponentArray);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists(uint entityId) => _entityIDToDenseIndex.ContainsKey(entityId);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Remove(uint entityId)
{
_entityIDToDenseIndex.Remove(entityId);
}
public bool Remove(uint entityId) => _entityIDToDenseIndex.Remove(entityId);

public EntityFilterIndices indices
{


+ 5
- 3
com.sebaslab.svelto.ecs/Core/Filters/EntityFilterIndices.cs View File

@@ -4,6 +4,8 @@ using Svelto.DataStructures;

namespace Svelto.ECS
{
//these are not meant to be held or passed by parameter.
//however cannot be ref struct because of DOTS lambda design for entity queries Entities.ForEach
public struct EntityFilterIndices
{
public EntityFilterIndices(NB<uint> indices, uint count)
@@ -13,10 +15,10 @@ namespace Svelto.ECS
_index = 0;
}

public uint count
public int count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _count;
get => (int)_count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -40,7 +42,7 @@ namespace Svelto.ECS
return _indices[Interlocked.Increment(ref _index) - 1];
}

readonly NB<uint> _indices;
readonly NBInternal<uint> _indices;
readonly uint _count;
int _index;
}

+ 16
- 7
com.sebaslab.svelto.ecs/Core/Filters/EntityFilterIterator.cs View File

@@ -2,11 +2,11 @@
{
public ref struct EntityFilterIterator
{
internal EntityFilterIterator(EntityFilterCollection filter)
internal EntityFilterIterator(EntityFilterCollection filter)
{
_filter = filter;
_filter = filter;
_indexGroup = -1;
_current = default;
_current = default;
}

public bool MoveNext()
@@ -15,7 +15,8 @@
{
_current = _filter.GetGroup(_indexGroup);

if (_current.count > 0) break;
if (_current.count > 0 && _current.group.IsEnabled() == true)
break;
}

return _indexGroup < _filter.groupCount;
@@ -28,8 +29,8 @@

public RefCurrent Current => new RefCurrent(_current);

int _indexGroup;
readonly EntityFilterCollection _filter;
int _indexGroup;
readonly EntityFilterCollection _filter;
EntityFilterCollection.GroupFilters _current;

public readonly ref struct RefCurrent
@@ -42,7 +43,15 @@
public void Deconstruct(out EntityFilterIndices indices, out ExclusiveGroupStruct group)
{
indices = _filter.indices;
group = _filter.group;
group = _filter.group;
}

public void Deconstruct(out EntityFilterIndices indices, out ExclusiveGroupStruct group,
out EntityFilterCollection.GroupFilters groupFilter)
{
indices = _filter.indices;
group = _filter.group;
groupFilter = _filter;
}

readonly EntityFilterCollection.GroupFilters _filter;


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

@@ -20,7 +20,7 @@ namespace Svelto.ECS

foreach (Type type in AssemblyUtility.GetTypesSafe(assembly))
{
if (type.IsInterface == false && typeOfEntityDescriptors.IsAssignableFrom(type)) //IsClass and IsSealed and IsAbstract means only static classes
if (type.IsInterface == false && type.IsAbstract == false && type.IsGenericType == false && typeOfEntityDescriptors.IsAssignableFrom(type))
{
try
{


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

@@ -1,20 +1,23 @@
using System.Runtime.CompilerServices;

namespace Svelto.ECS
{
public readonly struct ExclusiveBuildGroup
{
internal ExclusiveBuildGroup(ExclusiveGroupStruct group)
internal ExclusiveBuildGroup(ExclusiveGroupStruct group, ushort range)
{
_range = range;
this.group = group;
}

public static implicit operator ExclusiveBuildGroup(ExclusiveGroupStruct group)
{
return new ExclusiveBuildGroup(group);
return new ExclusiveBuildGroup(group, 0);
}

public static implicit operator ExclusiveBuildGroup(ExclusiveGroup group)
{
return new ExclusiveBuildGroup(group);
return new ExclusiveBuildGroup(group, 0);
}

public static implicit operator ExclusiveGroupStruct(ExclusiveBuildGroup group)
@@ -22,12 +25,30 @@ namespace Svelto.ECS
return group.group;
}
public static ExclusiveGroupStruct operator +(ExclusiveBuildGroup c1, uint c2)
{
DBC.ECS.Check.Require(c2 < c1._range, $"group out of range, {c2} max range is {c1._range}");
return c1.group + c2;
}
public override string ToString()
{
return group.ToName();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal uint Offset(ExclusiveGroupStruct exclusiveGroupStruct)
{
DBC.ECS.Check.Require((exclusiveGroupStruct.id - group.id) < _range, "group out of range");
var offset = (uint)exclusiveGroupStruct.id - (uint)group.id;
return offset;
}

internal ExclusiveGroupStruct @group { get; }

readonly ushort _range;

public bool isInvalid => group == ExclusiveGroupStruct.Invalid;
}
}

+ 10
- 2
com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroup.cs View File

@@ -46,6 +46,14 @@ namespace Svelto.ECS
_range = range;
#endif
}
public ExclusiveGroup(ushort range, ExclusiveGroupBitmask bitmask)
{
_group = ExclusiveGroupStruct.GenerateWithRange(range, (byte)bitmask);
#if DEBUG && !PROFILE_SVELTO
_range = range;
#endif
}

public static implicit operator ExclusiveGroupStruct(ExclusiveGroup group)
{
@@ -62,10 +70,10 @@ namespace Svelto.ECS
#endif
return group._group + b;
}
public uint id => _group.id;

//todo document the use case for this method
//todo document the use case for this method. I may honestly set this as a deprecated as it's original scenario is probably not valid anymore
public static ExclusiveGroupStruct Search(string holderGroupName)
{
if (_knownGroups.ContainsKey(holderGroupName) == false)


+ 12
- 1
com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroupStruct.cs View File

@@ -121,7 +121,18 @@ namespace Svelto.ECS
{
var newValue = Interlocked.Add(ref _staticGlobalID, (int)range);
ExclusiveGroupStruct groupStruct = new ExclusiveGroupStruct((uint) newValue - (uint)range);
ExclusiveGroupStruct groupStruct = new ExclusiveGroupStruct((uint)newValue - (uint)range);
DBC.ECS.Check.Require(_globalId < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created");

return groupStruct;
}
public static ExclusiveGroupStruct GenerateWithRange(ushort range, byte bitmask)
{
var newValue = Interlocked.Add(ref _staticGlobalID, (int)range);
ExclusiveGroupStruct groupStruct = new ExclusiveGroupStruct((uint)newValue - (uint)range, bitmask);
DBC.ECS.Check.Require(_globalId < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created");



+ 198
- 87
com.sebaslab.svelto.ecs/Core/Groups/GroupCompound.cs View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using Svelto.DataStructures;

@@ -9,6 +11,9 @@ namespace Svelto.ECS
/// 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)
/// each permutation of the same groups of tag is actually a different class with a different static constructor and we
/// don't want to call it more than once for the same set of tags
/// it's thread local because since it's not linked to a specific group, it must work synchronously
/// </summary>
static class GroupCompoundInitializer
{
@@ -27,22 +32,16 @@ namespace Svelto.ECS
{
static GroupCompound()
{
//avoid race conditions if compounds are using on multiple thread. This shouldn't be necessary though since c# static constructors are guaranteed to be thread safe!
/// c# Static constructors are guaranteed to be thread safe
/// The runtime guarantees that a static constructor is only called once. So even if a type is called by multiple threads at the same time,
/// the static constructor is always executed one time. To get a better understanding how this works, it helps to know what purpose it serves.
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value == false)
/// c# Static constructors are guaranteed to be thread safe and not called more than once
if (Interlocked.CompareExchange(ref isInitialised, 1, 0) != 0)
throw new Exception("GroupCompound static constructor called twice - impossible");
if (GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value == false)
{
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(
typeof(GroupCompound<G1, G2, G3, G4>).TypeHandle);
var group = new ExclusiveGroup(
GroupTag<G1>.bitmask | GroupTag<G2>.bitmask | GroupTag<G3>.bitmask | GroupTag<G4>.bitmask
| bitmask);
var group = new ExclusiveGroup(GroupTag<G1>.bitmask | GroupTag<G2>.bitmask | GroupTag<G3>.bitmask | GroupTag<G4>.bitmask);

_Groups = new FasterList<ExclusiveGroupStruct>(1);
_Groups.Add(group);

#if DEBUG
var name =
$"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint)group.id}";
@@ -54,7 +53,13 @@ namespace Svelto.ECS

//ToArrayFast is theoretically not correct, but since multiple 0s are ignored and we don't care if we
//add one, we avoid an allocation
_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
var exclusiveGroupStructs = _Groups.ToArrayFast(out var count);
_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(count);
for (var index = 0; index < count; ++index)
{
var exclusiveGroupStruct = exclusiveGroupStructs[index];
_GroupsHashSet.Add(exclusiveGroupStruct);
}

GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = true;

@@ -84,7 +89,7 @@ namespace Svelto.ECS
GroupCompound<G4, G3, G1, G2>._Groups = _Groups;
GroupCompound<G4, G3, G2, G1>._Groups = _Groups;

//all the permutations are warmed up now
//all the constructor have been called now
GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = false;

GroupCompound<G1, G2, G4, G3>._GroupsHashSet = _GroupsHashSet;
@@ -110,7 +115,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);
@@ -132,11 +137,19 @@ namespace Svelto.ECS
}
}

public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(_Groups);
}

public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
public static ExclusiveBuildGroup BuildGroup
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(_Groups[0], 1);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Includes(ExclusiveGroupStruct group)
{
DBC.ECS.Check.Require(group != ExclusiveGroupStruct.Invalid, "invalid group passed");
@@ -160,8 +173,7 @@ namespace Svelto.ECS
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;

//we are changing this with Interlocked, so it cannot be readonly
static int isInitializing;
protected internal static ExclusiveGroupBitmask bitmask;
static int isInitialised;
}

public abstract class GroupCompound<G1, G2, G3>: ITouchedByReflection
@@ -171,14 +183,13 @@ namespace Svelto.ECS
{
static GroupCompound()
{
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value == false)
/// c# Static constructors are guaranteed to be thread safe and not called more than once
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) != 0)
throw new Exception("GroupCompound static constructor called twice - impossible");
if (GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value == false)
{
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(
typeof(GroupCompound<G1, G2, G3>).TypeHandle);

var group = new ExclusiveGroup(
GroupTag<G1>.bitmask | GroupTag<G2>.bitmask | GroupTag<G3>.bitmask | bitmask);
var group = new ExclusiveGroup(GroupTag<G1>.bitmask | GroupTag<G2>.bitmask | GroupTag<G3>.bitmask);

_Groups = new FasterList<ExclusiveGroupStruct>(1);
_Groups.Add(group);
@@ -191,7 +202,13 @@ namespace Svelto.ECS
//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 _));
var exclusiveGroupStructs = _Groups.ToArrayFast(out var count);
_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(count);
for (var index = 0; index < count; ++index)
{
var exclusiveGroupStruct = exclusiveGroupStructs[index];
_GroupsHashSet.Add(exclusiveGroupStruct);
}

GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = true;

@@ -210,7 +227,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);
@@ -223,11 +240,19 @@ namespace Svelto.ECS
}
}

public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(_Groups);
}

public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
public static ExclusiveBuildGroup BuildGroup
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(_Groups[0], 1);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Includes(ExclusiveGroupStruct group)
{
DBC.ECS.Check.Require(group != ExclusiveGroupStruct.Invalid, "invalid group passed");
@@ -252,7 +277,6 @@ namespace Svelto.ECS

//we are changing this with Interlocked, so it cannot be readonly
static int isInitializing;
protected internal static ExclusiveGroupBitmask bitmask;
}

public abstract class GroupCompound<G1, G2>: ITouchedByReflection
@@ -261,44 +285,75 @@ namespace Svelto.ECS
{
static GroupCompound()
{
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value == false)
/// c# Static constructors are guaranteed to be thread safe and not called more than once
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) != 0)
throw new Exception($"{typeof(GroupCompound<G1, G2>).FullName} GroupCompound static constructor called twice - impossible");
if (GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value == false)
{
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(
typeof(GroupCompound<G1, G2>).TypeHandle);

var group = new ExclusiveGroup(GroupTag<G1>.bitmask | GroupTag<G2>.bitmask | bitmask);
range = GroupTag<G1>.range > GroupTag<G2>.range ? GroupTag<G1>.range : GroupTag<G2>.range;

_Groups = new FasterList<ExclusiveGroupStruct>(1);
_Groups.Add(group);
var initialSize = range;
_Groups = new FasterList<ExclusiveGroupStruct>(initialSize);

var group = new ExclusiveGroup(initialSize, GroupTag<G1>.bitmask | GroupTag<G2>.bitmask);
#if DEBUG
GroupNamesMap.idToName[group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {group.id}";
for (uint i = 0; i < initialSize; ++i)
{
var groupID = group.id + i;
var name = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {groupID}";

GroupNamesMap.idToName[group + i] = name;
}
#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, typeof(GroupCompound<G1, G2>).FullName);

_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
for (uint i = 0; i < initialSize; ++i)
{
var exclusiveGroupStruct = group + i;
//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(exclusiveGroupStruct, typeof(GroupCompound<G1, G2>).FullName + i);
_Groups.Add(exclusiveGroupStruct);
//every abstract group preemptively adds this group, it may or may not be empty in future
GroupTag<G1>.Add(exclusiveGroupStruct);
GroupTag<G2>.Add(exclusiveGroupStruct);
}
var exclusiveGroupStructs = _Groups.ToArrayFast(out var count);
_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(count);
for (var index = 0; index < count; index++)
{
var exclusiveGroupStruct = exclusiveGroupStructs[index];
_GroupsHashSet.Add(exclusiveGroupStruct);
}

GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = true;
GroupCompound<G2, G1>._Groups = _Groups;
//all the constructor have been called now
GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = false;

GroupCompound<G2, G1>.range = initialSize;
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);
}
}

public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(_Groups);
}

public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
public static ExclusiveBuildGroup BuildGroup
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(_Groups[0], range);
}

//TODO there is an overlap between this method and ExclusiveGroupExtensions FoundIn
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Includes(ExclusiveGroupStruct group)
{
DBC.ECS.Check.Require(group != ExclusiveGroupStruct.Invalid, "invalid group passed");
@@ -318,65 +373,92 @@ namespace Svelto.ECS
_GroupsHashSet.Add(group);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Offset(ExclusiveGroupStruct group)
{
return BuildGroup.Offset(group);
}

static readonly FasterList<ExclusiveGroupStruct> _Groups;
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;

static ushort range;

static int isInitializing;
protected internal static ExclusiveGroupBitmask bitmask;
}

/// <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.
/// can use the same adjective together with other ones. Albeit since I need to be able to iterate over all the
/// groups with the same adjective, a group tag needs to hold all the group compounds using it.
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class GroupTag<T>: ITouchedByReflection
where T : GroupTag<T>
public abstract class GroupTag<T>: ITouchedByReflection where T : GroupTag<T>
{
static GroupTag()
{
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0)
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) != 0)
throw new Exception("GroupTag static constructor called twice - impossible");
//GroupTag can set values for ranges and bitmasks in their static constructors so they must be called first
//there is no other way around this, as the base static constructor will be called once base fields are touched
//RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);
typeof(T).TypeInitializer?.Invoke(null, null); //must use this because if a specialised GroupTag is called first will call the this constructor before having the chance to initialise the protected values. This will force to initialise the values no matter what (and won't call the base constructor again because already executing)
var initialRange = range; //range may be overriden by the constructor previously called

if (initialRange == 0) //means never initialised by a inherited static constructor
{
//Allow to call GroupTag static constructors like
// public class Dead: GroupTag<Dead>
// {
// static Dead()
// {
// bitmask = ExclusiveGroupBitmask.DISABLED_BIT;
// }
// };
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);

var group = new ExclusiveGroup(bitmask);
_Groups.Add(group);
initialRange = 1;
range = 1;
}

var group = new ExclusiveGroup(initialRange, bitmask);
for (uint i = 0; i < initialRange; ++i)
{
_Groups.Add(group + i);
//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 + i, typeof(GroupTag<T>).FullName + i);
}
#if DEBUG
var typeInfo = typeof(T);
var name = $"Compound: {typeInfo.Name} ID {(uint)group.id}";
#if !PROFILE_SVELTO
var typeInfo = typeof(T);
for (uint i = 0; i < initialRange; ++i)
{
var groupID = group.id + i;
var name = $"Compound: {typeInfo.Name} ID {groupID}";

var typeInfoBaseType = typeInfo.BaseType;
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
//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
if (typeInfoBaseType.GenericTypeArguments[0] != typeInfo)
throw new ECSException("Invalid Group Tag declared");
#endif

GroupNamesMap.idToName[group] = name;
GroupNamesMap.idToName[group + i] = name;
}
#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, typeof(GroupTag<T>).FullName);

_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
var exclusiveGroupStructs = _Groups.ToArrayFast(out var count);
_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(count);
for (var index = 0; index < count; index++)
{
var exclusiveGroupStruct = exclusiveGroupStructs[index];
_GroupsHashSet.Add(exclusiveGroupStruct);
}
}

public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(_Groups);
}

public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
public static ExclusiveBuildGroup BuildGroup
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(_Groups[0], range);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Includes(ExclusiveGroupStruct group)
{
DBC.ECS.Check.Require(group != ExclusiveGroupStruct.Invalid, "invalid group passed");
@@ -397,11 +479,40 @@ namespace Svelto.ECS
_GroupsHashSet.Add(group);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Offset(ExclusiveGroupStruct group)
{
return BuildGroup.Offset(group);
}

static readonly FasterList<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1);
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;

//we are changing this with Interlocked, so it cannot be readonly
static int isInitializing;

//special group attributes, at the moment of writing this comment, only the disabled group has a special attribute

//Allow to call GroupTag static constructors like
// public class Dead: GroupTag<Dead>
// {
// static Dead()
// {
// bitmask = ExclusiveGroupBitmask.DISABLED_BIT;
// }
// };

protected internal static ExclusiveGroupBitmask bitmask;
//set a number different than 0 to create a range of groups instead of a single group
//example of usage:
// public class VehicleGroup:GroupTag<VehicleGroup>
// {
// static VehicleGroup()
// {
// range = (ushort)Data.MaxTeamCount;
// }
// }

protected internal static ushort range;
}
}

+ 68
- 53
com.sebaslab.svelto.ecs/Core/Groups/GroupHashMap.cs View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using Svelto.ECS.Serialization;

@@ -20,78 +22,69 @@ namespace Svelto.ECS
List<Assembly> assemblies = AssemblyUtility.GetCompatibleAssemblies();
foreach (Assembly assembly in assemblies)
{
try
var typeOfExclusiveGroup = typeof(ExclusiveGroup);
var typeOfExclusiveGroupStruct = typeof(ExclusiveGroupStruct);
var typeOfExclusiveBuildGroup = typeof(ExclusiveBuildGroup);

var typesSafe = AssemblyUtility.GetTypesSafe(assembly);
foreach (Type type in typesSafe)
{
var typeOfExclusiveGroup = typeof(ExclusiveGroup);
var typeOfExclusiveGroupStruct = typeof(ExclusiveGroupStruct);
var typeOfExclusiveBuildGroup = typeof(ExclusiveBuildGroup);
CheckForGroupCompounds(type);

foreach (Type type in AssemblyUtility.GetTypesSafe(assembly))
//Search inside static types
if (type != null && type.IsClass && type.IsSealed
&& type.IsAbstract) //IsClass and IsSealed and IsAbstract means only static classes
{
CheckForGroupCompounds(type);
var subClasses = type.GetNestedTypes();

//Search inside static types
if (type != null && type.IsClass && type.IsSealed && type.IsAbstract) //IsClass and IsSealed and IsAbstract means only static classes
foreach (var subclass in subClasses)
{
var subClasses = type.GetNestedTypes();
CheckForGroupCompounds(subclass);
}

var fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

foreach (var subclass in subClasses)
foreach (var field in fields)
{
if ((typeOfExclusiveGroup.IsAssignableFrom(field.FieldType)
|| typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType)
|| typeOfExclusiveBuildGroup.IsAssignableFrom(field.FieldType)))
{
CheckForGroupCompounds(subclass);
}
uint groupIDAndBitMask;

var fields = type.GetFields();
if (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType))
{
var group = (ExclusiveGroup)field.GetValue(null);
groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask();
}
else if (typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType))
{
var group = (ExclusiveGroupStruct)field.GetValue(null);
groupIDAndBitMask = @group.ToIDAndBitmask();
}
else
{
var group = (ExclusiveBuildGroup)field.GetValue(null);
groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask();
}

foreach (var field in fields)
{
if (field.IsStatic
&& (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType)
|| typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType)
|| typeOfExclusiveBuildGroup.IsAssignableFrom(field.FieldType)))
{
uint groupIDAndBitMask;

if (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType))
{
var group = (ExclusiveGroup)field.GetValue(null);
groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask();
}
else
if (typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType))
{
var group = (ExclusiveGroupStruct)field.GetValue(null);
groupIDAndBitMask = @group.ToIDAndBitmask();
}
else
{
var group = (ExclusiveBuildGroup)field.GetValue(null);
groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask();
}

{
var bitMask = (byte)(groupIDAndBitMask >> 24);
var groupID = groupIDAndBitMask & 0xFFFFFF;
ExclusiveGroupStruct group = new ExclusiveGroupStruct(groupID, bitMask);
var bitMask = (byte)(groupIDAndBitMask >> 24);
var groupID = groupIDAndBitMask & 0xFFFFFF;
ExclusiveGroupStruct group = new ExclusiveGroupStruct(groupID, bitMask);
#if DEBUG && !PROFILE_SVELTO
if (GroupNamesMap.idToName.ContainsKey(@group) == false)
GroupNamesMap.idToName[@group] =
$"{type.FullName}.{field.Name} {@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
RegisterGroup(@group, $"{type.FullName}.{field.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}");
}
}
}
}
}
catch
{
Console.LogDebugWarning(
"something went wrong while gathering group names on the assembly: ".FastConcat(
assembly.FullName));
}
}
}

@@ -101,7 +94,29 @@ namespace Svelto.ECS
{
//this calls the static constructor, but only once. Static constructors won't be called
//more than once with this
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(type.BaseType.TypeHandle);
CallStaticConstructorsRecursively(type);
}

static void CallStaticConstructorsRecursively(Type type)
{
// Check if the current type has a static constructor
// type.TypeInitializer.Invoke(null, null); //calling Invoke will force the static constructor to be called even if already called, this is a problem because GroupTag and Compound throw an exception if called multiple times
RuntimeHelpers.RunClassConstructor(type.TypeHandle); //this will call the static constructor only once
#if DEBUG && !PROFILE_SVELTO
if (type.GetInterfaces().Contains(type) == false)
{
if (type.IsSealed == false)
Svelto.Console.LogWarning(
$"Group compound/tag {type} is not sealed. GroupCompounds and Tags cannot be inherited, consider marking it sealed");
}
#endif
// Recursively check the base types
Type baseType = type.BaseType;
if (baseType != null && baseType != typeof(object)) //second if means we got the the end of the hierarchy
{
CallStaticConstructorsRecursively(baseType);
}
}
}



+ 1
- 2
com.sebaslab.svelto.ecs/Core/Groups/GroupNamesMap.cs View File

@@ -7,8 +7,7 @@ static class GroupNamesMap
static GroupNamesMap() { idToName = new Dictionary<ExclusiveGroupStruct, string>(); }

internal static readonly Dictionary<ExclusiveGroupStruct, string> idToName;
#endif
#if DEBUG

public static string ToName(this in ExclusiveGroupStruct group)
{
Dictionary<ExclusiveGroupStruct, string> idToName = GroupNamesMap.idToName;


+ 0
- 1
com.sebaslab.svelto.ecs/Core/Groups/QueryGroups.cs View File

@@ -61,7 +61,6 @@ namespace Svelto.ECS.Experimental
HashSet<ExclusiveGroupStruct> _sets;
}

//I am not 100% sure why I made this thread-safe since it cannot be used inside jobs.
public ref struct QueryGroups
{
static readonly ThreadLocal<GroupsList> groups;


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

@@ -2,7 +2,7 @@ using System;

namespace Svelto.ECS
{
public interface IDisposingEngine: IDisposable
public interface IDisposableEngine: IDisposable
{
bool isDisposing { set; }
}

+ 12
- 9
com.sebaslab.svelto.ecs/Core/IEngine.cs View File

@@ -1,12 +1,12 @@
using System;
using Svelto.ECS.Internal;

namespace Svelto.ECS.Internal
namespace Svelto.ECS
{
public interface IReactEngine : IEngine
{
}
#region legacy interfaces
/// <summary>
/// This is now considered legacy and it will be deprecated in future
/// </summary>
@@ -27,7 +27,13 @@ namespace Svelto.ECS.Internal
public interface IReactOnSwap : IReactEngine
{
}
#endregion
/// <summary>
/// This is now considered legacy and it will be deprecated in future
/// </summary>
public interface IReactOnDispose : IReactEngine
{
}

public interface IReactOnAddEx : IReactEngine
{
@@ -44,10 +50,6 @@ namespace Svelto.ECS.Internal
public interface IReactOnDisposeEx : IReactEngine
{
}

public interface IReactOnDispose : IReactEngine
{
}
}

namespace Svelto.ECS
@@ -102,7 +104,7 @@ namespace Svelto.ECS
ExclusiveGroupStruct groupID);
}

[Obsolete]
[Obsolete("Use IReactOnAddEx<T> and IReactOnRemoveEx<T> or IReactOnAddAndRemoveEx<T> instead")]
public interface IReactOnAddAndRemove<T> : IReactOnAdd<T>, IReactOnRemove<T> where T : _IInternalEntityComponent
{
}
@@ -112,6 +114,7 @@ namespace Svelto.ECS
/// It can work together with IReactOnRemove which normally is not called on enginesroot disposed
/// </summary>
/// <typeparam name="T"></typeparam>
[Obsolete("Use IReactOnDisposeEx<T> instead")]
public interface IReactOnDispose<T> : IReactOnDispose where T : _IInternalEntityComponent
{
void Remove(ref T entityComponent, EGID egid);
@@ -121,7 +124,7 @@ namespace Svelto.ECS
/// Interface to mark an Engine as reacting to entities swapping group
/// </summary>
/// <typeparam name="T"></typeparam>
[Obsolete]
[Obsolete("Use IReactOnSwapEx<T> instead")]
public interface IReactOnSwap<T> : IReactOnSwap where T : _IInternalEntityComponent
{
void MovedTo(ref T entityComponent, ExclusiveGroupStruct previousGroup, EGID egid);


+ 24
- 13
com.sebaslab.svelto.ecs/Core/IEntityFunctions.cs View File

@@ -4,22 +4,33 @@ namespace Svelto.ECS
{
public interface IEntityFunctions
{
//being entity ID globally not unique, the group must be specified when
//an entity is removed. Not specifying the group will attempt to remove
//the entity from the special standard group.
void RemoveEntity<T>(uint entityID, ExclusiveBuildGroup groupID , [CallerMemberName] string caller = null) where T : IEntityDescriptor, new();
void RemoveEntity<T>(EGID entityegid , [CallerMemberName] string caller = null) where T : IEntityDescriptor, new();
/// <summary>
/// Remove an entity from the database. Since Svelto.ECS 3.5 Removal operation behaves like this:
/// * Remove supersedes a previous Remove operation on the same submission frame
/// * Remove supersedes a previous Swap operation on the same submission frame if the egid is used a origin (fromEGID) - similar to the remove case
/// * Remove throws an exception if a Build operation with the same egid is done on the same submission frame
/// * Remove throws an exception if called on an EGID used as destination (toEGID) for a swap - similare to the build case
/// </summary>
void RemoveEntity<T>(uint entityID, ExclusiveBuildGroup groupID, [CallerMemberName] string caller = null) where T : IEntityDescriptor, new();
void RemoveEntity<T>(EGID entityegid, [CallerMemberName] string caller = null) where T : IEntityDescriptor, new();

/// <summary>
/// Swap an entity between groups (subset of entities). Only one structural operation per submission frame is allowed.
/// </summary>
void SwapEntityGroup<T>(uint entityID, ExclusiveBuildGroup fromGroupID, ExclusiveBuildGroup toGroupID,
[CallerMemberName] string caller = null) where T : IEntityDescriptor, new();
void SwapEntityGroup<T>(EGID fromEGID, ExclusiveBuildGroup toGroupID, [CallerMemberName] string caller = null)
where T : IEntityDescriptor, new();
void SwapEntityGroup<T>(EGID fromEGID, EGID toEGID, [CallerMemberName] string caller = null) where T : IEntityDescriptor, new();
void SwapEntityGroup<T>(EGID fromEGID, EGID toEGID, ExclusiveBuildGroup mustBeFromGroup, [CallerMemberName] string caller = null)
where T : IEntityDescriptor, new();
void RemoveEntitiesFromGroup(ExclusiveBuildGroup groupID , [CallerMemberName] string caller = null);
void RemoveEntitiesFromGroup(ExclusiveBuildGroup groupID, [CallerMemberName] string caller = null);
void SwapEntitiesInGroup(ExclusiveBuildGroup fromGroupID, ExclusiveBuildGroup toGroupID, [CallerMemberName] string caller = null);

void SwapEntityGroup<T>(uint entityID, ExclusiveBuildGroup fromGroupID, ExclusiveBuildGroup toGroupID, [CallerMemberName] string caller = null) where T : IEntityDescriptor, new();
void SwapEntityGroup<T>(EGID fromEGID, ExclusiveBuildGroup toGroupID, [CallerMemberName] string caller = null) where T : IEntityDescriptor, new();
void SwapEntityGroup<T>(EGID fromEGID, EGID toEGID, [CallerMemberName] string caller = null)where T : IEntityDescriptor, new();
void SwapEntityGroup<T>(EGID fromEGID, EGID toEGID, ExclusiveBuildGroup mustBeFromGroup, [CallerMemberName] string caller = null) where T : IEntityDescriptor, new();
#if UNITY_NATIVE
Svelto.ECS.Native.NativeEntityRemove ToNativeRemove<T>(string memberName) where T : IEntityDescriptor, new();
Svelto.ECS.Native.NativeEntitySwap ToNativeSwap<T>(string memberName) where T : IEntityDescriptor, new();
#endif
Svelto.ECS.Native.NativeEntityRemove ToNativeRemove<T>(string memberName) where T : IEntityDescriptor, new();
Svelto.ECS.Native.NativeEntitySwap ToNativeSwap<T>(string memberName) where T : IEntityDescriptor, new();
#endif
}
}

+ 15
- 5
com.sebaslab.svelto.ecs/DataStructures/EntityIDs/ManagedEntityIDs.cs View File

@@ -1,22 +1,32 @@
using System.Runtime.CompilerServices;
using Svelto.DataStructures;

namespace Svelto.ECS.Internal
{
public struct ManagedEntityIDs: IEntityIDs
{
public ManagedEntityIDs(MB<SveltoDictionaryNode<uint>> managed)
internal ManagedEntityIDs(MB<SveltoDictionaryNode<uint>> managed)
{
_managed = managed;
}
public void Update(MB<SveltoDictionaryNode<uint>> managed)
internal void Update(MB<SveltoDictionaryNode<uint>> managed)
{
_managed = managed;
}

public uint this[uint index] => _managed[index].key;
public uint this[int index] => _managed[index].key;
public uint this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _managed[index].key;
}

public uint this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _managed[index].key;
}

MB<SveltoDictionaryNode<uint>> _managed;
MBInternal<SveltoDictionaryNode<uint>> _managed;
}
}

+ 15
- 5
com.sebaslab.svelto.ecs/DataStructures/EntityIDs/NativeEntityIDs.cs View File

@@ -1,22 +1,32 @@
using System.Runtime.CompilerServices;
using Svelto.DataStructures;

namespace Svelto.ECS.Internal
{
public struct NativeEntityIDs: IEntityIDs
{
public NativeEntityIDs(NB<SveltoDictionaryNode<uint>> native)
internal NativeEntityIDs(NB<SveltoDictionaryNode<uint>> native)
{
_native = native;
}
public void Update(in NB<SveltoDictionaryNode<uint>> unsafeKeys)
{
_native = unsafeKeys;
}

public uint this[uint index] => _native[index].key;
public uint this[int index] => _native[index].key;
public uint this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _native[index].key;
}

public uint this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _native[index].key;
}

NB<SveltoDictionaryNode<uint>> _native;
NBInternal<SveltoDictionaryNode<uint>> _native;
}
}

+ 6
- 7
com.sebaslab.svelto.ecs/DataStructures/ITypeSafeDictionary.cs View File

@@ -6,7 +6,7 @@ namespace Svelto.ECS.Internal
{
public interface ITypeSafeDictionary<TValue> : ITypeSafeDictionary where TValue : _IInternalEntityComponent
{
void Add(uint egidEntityId, in TValue entityComponent);
uint Add(uint egidEntityId, in TValue entityComponent);
bool TryGetValue(uint entityId, out TValue item);
ref TValue GetOrAdd(uint idEntityId);
@@ -29,11 +29,10 @@ namespace Svelto.ECS.Internal
, in EnginesRoot.EntityReferenceMap entityLocator
#endif
);
void RemoveEntitiesFromDictionary
(FasterList<(uint, string)> infosToProcess, FasterList<uint> entityIDsAffectedByRemoval);
void SwapEntitiesBetweenDictionaries
(FasterList<(uint, uint, string)> infosToProcess, ExclusiveGroupStruct fromGroup, ExclusiveGroupStruct toGroup
, ITypeSafeDictionary toComponentsDictionary, FasterList<uint> entityIDsAffectedByRemoval);
void RemoveEntitiesFromDictionary(FasterList<(uint, string)> infosToProcess, FasterDictionary<uint, uint> entityIDsAffectedByRemoveAtSwapBack);
void SwapEntitiesBetweenDictionaries(in FasterDictionary<uint, SwapInfo> infosToProcess, ExclusiveGroupStruct fromGroup,
ExclusiveGroupStruct toGroup
, ITypeSafeDictionary toComponentsDictionary, FasterDictionary<uint, uint> entityIDsAffectedByRemoveAtSwapBack);
//------------

@@ -49,7 +48,7 @@ namespace Svelto.ECS.Internal
//------------
//This is now obsolete, but I cannot mark it as such because it's heavily used by legacy projects
void ExecuteEnginesSwapCallbacks(FasterList<(uint, uint, string)> infosToProcess,
void ExecuteEnginesSwapCallbacks(FasterDictionary<uint, SwapInfo> infosToProcess,
FasterList<ReactEngineContainer<IReactOnSwap>> reactiveEnginesSwap, ExclusiveGroupStruct fromGroup,
ExclusiveGroupStruct toGroup, in PlatformProfiler sampler);
//Version to use


+ 17
- 17
com.sebaslab.svelto.ecs/DataStructures/ManagedTypeSafeDictionary.cs View File

@@ -127,9 +127,12 @@ namespace Svelto.ECS.Internal
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(uint egidEntityId, in TValue entityComponent)
public uint Add(uint egidEntityId, in TValue entityComponent)
{
implMgd.Add(egidEntityId, entityComponent);
if (implMgd.TryAdd(egidEntityId, entityComponent, out uint index) == false)
throw new TypeSafeDictionaryException("Key already present");

return index;
}

public void Dispose()
@@ -158,22 +161,20 @@ namespace Svelto.ECS.Internal
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntitiesFromDictionary
(FasterList<(uint, string)> infosToProcess, FasterList<uint> entityIDsAffectedByRemoval)
public void RemoveEntitiesFromDictionary(FasterList<(uint, string)> infosToProcess, FasterDictionary<uint, uint> entityIDsAffectedByRemoveAtSwapBack)
{
TypeSafeDictionaryMethods.RemoveEntitiesFromDictionary(infosToProcess, ref implMgd, entityIDsAffectedByRemoval);
TypeSafeDictionaryMethods.RemoveEntitiesFromDictionary(infosToProcess, ref implMgd, entityIDsAffectedByRemoveAtSwapBack);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntitiesBetweenDictionaries
(FasterList<(uint, uint, string)> infosToProcess, ExclusiveGroupStruct fromGroup
, ExclusiveGroupStruct toGroup, ITypeSafeDictionary toComponentsDictionary
, FasterList<uint> entityIDsAffectedByRemoval)
public void SwapEntitiesBetweenDictionaries(in FasterDictionary<uint, SwapInfo> infosToProcess,
ExclusiveGroupStruct fromGroup
, ExclusiveGroupStruct toGroup, ITypeSafeDictionary toComponentsDictionary
, FasterDictionary<uint, uint> entityIDsAffectedByRemoveAtSwapBack)
{
TypeSafeDictionaryMethods.SwapEntitiesBetweenDictionaries(infosToProcess, ref implMgd
, toComponentsDictionary as
ITypeSafeDictionary<TValue>, fromGroup
, toGroup, entityIDsAffectedByRemoval);
, toComponentsDictionary as ITypeSafeDictionary<TValue>, fromGroup
, toGroup, entityIDsAffectedByRemoveAtSwapBack);
}

/// <summary>
@@ -192,10 +193,9 @@ namespace Svelto.ECS.Internal
/// Execute all the engine IReactOnSwap callbacks linked to components swapped this submit
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExecuteEnginesSwapCallbacks
(FasterList<(uint, uint, string)> infosToProcess
, FasterList<ReactEngineContainer<IReactOnSwap>> reactiveEnginesSwap, ExclusiveGroupStruct fromGroup
, ExclusiveGroupStruct toGroup, in PlatformProfiler profiler)
public void ExecuteEnginesSwapCallbacks(FasterDictionary<uint, SwapInfo> infosToProcess
, FasterList<ReactEngineContainer<IReactOnSwap>> reactiveEnginesSwap, ExclusiveGroupStruct fromGroup
, ExclusiveGroupStruct toGroup, in PlatformProfiler profiler)
{
TypeSafeDictionaryMethods.ExecuteEnginesSwapCallbacks(infosToProcess, ref implMgd, reactiveEnginesSwap
, toGroup, fromGroup, in profiler);
@@ -295,7 +295,7 @@ namespace Svelto.ECS.Internal
ref implMgd, reactiveEnginesDispose, reactiveEnginesDisposeEx, entityIDs, group, in profiler);
}

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

+ 5
- 0
com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionaryException.cs View File

@@ -8,5 +8,10 @@ namespace Svelto.ECS
base(message, exception)
{
}
public TypeSafeDictionaryException(string message) :
base(message)
{
}
}
}

+ 93
- 71
com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionaryMethods.cs View File

@@ -34,9 +34,12 @@ namespace Svelto.ECS.Internal
ref tuple.value,
entityLocator.GetEntityReference(egid));
#endif
#if DEBUG && !PROFILE_SVELTO
try
{
#endif
toDic.Add(tuple.key, tuple.value);
#if DEBUG && !PROFILE_SVELTO
}
catch (Exception e)
{
@@ -47,6 +50,7 @@ namespace Svelto.ECS.Internal

throw;
}
#endif
#if PARANOID_CHECK && SLOW_SVELTO_SUBMISSION
DBC.ECS.Check.Ensure(_hasEgid == false || ((INeedEGID)fromDictionary[egid.entityID]).ID == egid, "impossible situation happened during swap");
#endif
@@ -109,58 +113,59 @@ namespace Svelto.ECS.Internal
where Strategy3 : struct, IBufferStrategy<int>
where TValue : struct, _IInternalEntityComponent
{
if (reactiveEnginesDispose.TryGetValue(ComponentTypeID<TValue>.id, out var entityComponentsEngines)
== false)
return;

for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (sampler.Sample(entityComponentsEngines[i].name))
if (reactiveEnginesDispose.TryGetValue(ComponentTypeID<TValue>.id, out var entityComponentsEngines) == true)
{
var resultCount = entityComponentsEngines.count;
for (var i = 0; i < resultCount; i++)
try
{
foreach (var value in fromDictionary)
using (sampler.Sample(entityComponentsEngines[i].name))
{
ref var entity = ref value.value;
var egid = new EGID(value.key, group);
var reactOnRemove = (IReactOnDispose<TValue>)entityComponentsEngines[i].engine;
reactOnRemove.Remove(ref entity, egid);
foreach (var value in fromDictionary)
{
ref var entity = ref value.value;
var egid = new EGID(value.key, group);
#pragma warning disable CS0618
var reactOnRemove = (IReactOnDispose<TValue>)entityComponentsEngines[i].engine;
#pragma warning restore CS0618
reactOnRemove.Remove(ref entity, egid);
}
}
}
}
catch
{
Console.LogError("Code crashed inside Remove callback ".FastConcat(entityComponentsEngines[i].name));
catch
{
Console.LogError("Code crashed inside Remove callback ".FastConcat(entityComponentsEngines[i].name));

throw;
}
var count = fromDictionary.count;
if (reactiveEnginesDisposeEx.TryGetValue(
ComponentTypeID<TValue>.id
, out var reactiveEnginesDisposeExPerType))
throw;
}
}

if (reactiveEnginesDisposeEx.TryGetValue(ComponentTypeID<TValue>.id, out var reactiveEnginesDisposeExPerType))
{
var count = fromDictionary.count;
var enginesCount = reactiveEnginesDisposeExPerType.count;

for (var i = 0; i < enginesCount; i++)
try
if (count > 0)
{
for (var i = 0; i < enginesCount; i++)
{
using (sampler.Sample(reactiveEnginesDisposeExPerType[i].name))
try
{
((IReactOnDisposeEx<TValue>)reactiveEnginesDisposeExPerType[i].engine).Remove(
(0, (uint)count)
, new EntityCollection<TValue>(
fromDictionary.UnsafeGetValues(out _), entityids
, (uint)count), group);
using (sampler.Sample(reactiveEnginesDisposeExPerType[i].name))
{
((IReactOnDisposeEx<TValue>)reactiveEnginesDisposeExPerType[i].engine).Remove(
(0, (uint)count)
, new EntityCollection<TValue>(fromDictionary.UnsafeGetValues(out _), entityids, (uint)count), group);
}
}
}
catch
{
Console.LogError("Code crashed inside Remove callback ".FastConcat(reactiveEnginesDisposeExPerType[i].name));
catch
{
Console.LogError("Code crashed inside Remove callback ".FastConcat(reactiveEnginesDisposeExPerType[i].name));

throw;
throw;
}
}
}
}
}

@@ -283,7 +288,7 @@ namespace Svelto.ECS.Internal
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ExecuteEnginesSwapCallbacks<Strategy1, Strategy2, Strategy3, TValue>(FasterList<(uint, uint, string)> infostoprocess
public static void ExecuteEnginesSwapCallbacks<Strategy1, Strategy2, Strategy3, TValue>(FasterDictionary<uint, SwapInfo> infostoprocess
, ref SveltoDictionary<uint, TValue, Strategy1, Strategy2, Strategy3> fromDictionary
, FasterList<ReactEngineContainer<IReactOnSwap>> reactiveenginesswap, ExclusiveGroupStruct togroup
, ExclusiveGroupStruct fromgroup, in PlatformProfiler sampler)
@@ -297,19 +302,23 @@ namespace Svelto.ECS.Internal

var iterations = infostoprocess.count;

var infostoprocessUnsafeValues = infostoprocess.unsafeValues;

for (var i = 0; i < iterations; i++)
{
var (fromEntityID, toEntityID, trace) = infostoprocess[i];
var (fromEntityID, toEntityID, trace) = infostoprocessUnsafeValues[i];

try
{
ref var entityComponent = ref fromDictionary.GetValueByRef(fromEntityID);
ref var entityComponent = ref fromDictionary.GetValueByRef(toEntityID);
var newEgid = new EGID(toEntityID, togroup);
for (var j = 0; j < reactiveenginesswap.count; j++)
using (sampler.Sample(reactiveenginesswap[j].name))
{
#pragma warning disable CS0612
#pragma warning disable CS0618
((IReactOnSwap<TValue>)reactiveenginesswap[j].engine).MovedTo(
#pragma warning restore CS0618
#pragma warning restore CS0612
ref entityComponent, fromgroup, newEgid);
}
@@ -357,7 +366,9 @@ namespace Svelto.ECS.Internal
using (sampler.Sample(reactiveEnginesSwapPerType[i].name))
{
#pragma warning disable CS0612
#pragma warning disable CS0618
((IReactOnSwap<TValue>)reactiveEnginesSwapPerType[i].engine).MovedTo(
#pragma warning restore CS0618
#pragma warning restore CS0612
ref entityComponent, fromgroup, newEgid);
}
@@ -401,7 +412,7 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemoveEntitiesFromDictionary<Strategy1, Strategy2, Strategy3, TValue>(FasterList<(uint, string)> infostoprocess
, ref SveltoDictionary<uint, TValue, Strategy1, Strategy2, Strategy3> fromDictionary
, FasterList<uint> entityIDsAffectedByRemoval)
, FasterDictionary<uint, uint> entityIDsAffectedByRemoveAtSwapBack)
where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>>
where Strategy2 : struct, IBufferStrategy<TValue>
where Strategy3 : struct, IBufferStrategy<int>
@@ -412,25 +423,31 @@ namespace Svelto.ECS.Internal
for (var i = 0; i < iterations; i++)
{
var (id, trace) = infostoprocess[i];
#if DEBUG && !PROFILE_SVELTO
try
{
#endif
if (fromDictionary.Remove(id, out var index, out var value))
{
//Note I am doing this to be able to use a range of values even with the
//remove Ex callbacks. Basically I am copying back the deleted value
//at the end of the array, so I can use as range count, count + number of deleted entities
//I need to swap the keys too to have matching EntityIDs
fromDictionary.unsafeValues[(uint)fromDictionary.count] = value;
fromDictionary.unsafeKeys[(uint)fromDictionary.count] = new SveltoDictionaryNode<uint>(ref id, 0);
if (index != fromDictionary.count)
{
fromDictionary.unsafeValues[(uint)fromDictionary.count] = value;
fromDictionary.unsafeKeys[(uint)fromDictionary.count] = new SveltoDictionaryNode<uint>(id, 0);
}

//when a component is removed from a component array, a remove swap back happens. This means
//that not only we have to remove the index of the component of the entity deleted from the array
//but we need also to update the index of the component that has been swapped in the cell
//of the deleted component
//entityIDsAffectedByRemoval tracks all the entitiesID of the components that need to be updated
//in the filters because their indices in the array changed.
entityIDsAffectedByRemoval.Add(fromDictionary.unsafeKeys[index].key);
entityIDsAffectedByRemoveAtSwapBack[fromDictionary.unsafeKeys[index].key] = index;
}
#if DEBUG && !PROFILE_SVELTO
}
catch
{
@@ -441,34 +458,39 @@ namespace Svelto.ECS.Internal

throw;
}
#endif
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SwapEntitiesBetweenDictionaries<Strategy1, Strategy2, Strategy3, TValue>(FasterList<(uint, uint, string)> infostoprocess
public static void SwapEntitiesBetweenDictionaries<Strategy1, Strategy2, Strategy3, TValue>(in FasterDictionary<uint, SwapInfo> entitiesIDsToSwap
, ref SveltoDictionary<uint, TValue, Strategy1, Strategy2, Strategy3> fromDictionary
, ITypeSafeDictionary<TValue> toDictionary, ExclusiveGroupStruct fromgroup, ExclusiveGroupStruct togroup
, FasterList<uint> entityIDsAffectedByRemoval)
, FasterDictionary<uint, uint> entityIDsAffectedByRemoveAtSwapBack)
where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>>
where Strategy2 : struct, IBufferStrategy<TValue>
where Strategy3 : struct, IBufferStrategy<int>
where TValue : struct, _IInternalEntityComponent
{
var iterations = infostoprocess.count;
var iterations = entitiesIDsToSwap.count;
var entitiesToSwapInfo = entitiesIDsToSwap.unsafeValues;
var fromDictionaryUnsafeKeys = fromDictionary.unsafeKeys;

for (var i = 0; i < iterations; i++)
{
var (fromID, toID, trace) = infostoprocess[i];
ref SwapInfo swapInfo = ref entitiesToSwapInfo[i];

#if DEBUG && !PROFILE_SVELTO
try
{
var fromEntityGid = new EGID(fromID, fromgroup);
var toEntityEgid = new EGID(toID, togroup);
#endif
var fromEntityGid = new EGID(swapInfo.fromID, fromgroup);
var toEntityEgid = new EGID(swapInfo.toID, togroup);

Check.Require(togroup.isInvalid == false, "Invalid To Group");

if (fromDictionary.Remove(fromEntityGid.entityID, out var index, out var value))
entityIDsAffectedByRemoval.Add(fromDictionary.unsafeKeys[index].key);
entityIDsAffectedByRemoveAtSwapBack[fromDictionaryUnsafeKeys[index].key] = index; //after the removal, the entity ad index is the entity that was at the end of the buffer (swapped back).
else
Check.Assert(false, "Swapping an entity that doesn't exist");

@@ -477,21 +499,23 @@ namespace Svelto.ECS.Internal
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref value, toEntityEgid);
#endif

toDictionary.Add(toEntityEgid.entityID, value);
swapInfo.toIndex = toDictionary.Add(toEntityEgid.entityID, value);

#if PARANOID_CHECK
DBC.ECS.Check.Ensure(_hasEgid == false || ((INeedEGID)toGroupCasted[toEntityEGID.entityID]).ID == toEntityEGID, "impossible situation happened during swap");
DBC.ECS.Check.Ensure(_hasEgid == false || ((INeedEGID)toGroupCasted[toEntityEGID.entityID]).ID == toEntityEGID, "impossible situation happened during swap");
#endif
#if DEBUG && !PROFILE_SVELTO
}
catch
{
var str = "Crash while executing Swap Entity operation on ".FastConcat(TypeCache<TValue>.name)
.FastConcat(" from : ", trace);
.FastConcat(" from : ", swapInfo.trace);

Console.LogError(str);

throw;
}
#endif
}
}

@@ -503,9 +527,7 @@ namespace Svelto.ECS.Internal
where TValue : struct, _IInternalEntityComponent
{
//get all the engines linked to TValue
if (!fasterDictionary.TryGetValue(
ComponentTypeID<TValue>.id
, out var entityComponentsEngines))
if (!fasterDictionary.TryGetValue(ComponentTypeID<TValue>.id, out var entityComponentsEngines))
return;

for (var i = 0; i < entityComponentsEngines.count; i++)
@@ -528,26 +550,26 @@ namespace Svelto.ECS.Internal
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ExecuteEnginesSwapCallbacksFast<TValue>(FasterList<ReactEngineContainer<IReactOnSwapEx>> fasterList,
ExclusiveGroupStruct fromGroup
, ExclusiveGroupStruct toGroup, IEntityIDs entityids, ITypeSafeDictionary<TValue> typeSafeDictionary
, (uint, uint) rangeofsubmittedentitiesindicies, PlatformProfiler sampler)
public static void ExecuteEnginesSwapCallbacksFast<TValue>(
FasterList<ReactEngineContainer<IReactOnSwapEx>> callbackEngines,
ExclusiveGroupStruct fromGroup, ExclusiveGroupStruct toGroup, IEntityIDs entityids, ITypeSafeDictionary<TValue> typeSafeDictionary
, (uint, uint) rangeOfSubmittedEntitiesIndicies, PlatformProfiler sampler)
where TValue : struct, _IInternalEntityComponent
{
for (var i = 0; i < fasterList.count; i++)
for (var i = 0; i < callbackEngines.count; i++)
try
{
using (sampler.Sample(fasterList[i].name))
using (sampler.Sample(callbackEngines[i].name))
{
((IReactOnSwapEx<TValue>)fasterList[i].engine).MovedTo(
rangeofsubmittedentitiesindicies
, new EntityCollection<TValue>(typeSafeDictionary.GetValues(out var count), entityids, count)
, fromGroup, toGroup);
var values = typeSafeDictionary.GetValues(out var count);
((IReactOnSwapEx<TValue>)callbackEngines[i].engine).MovedTo(
rangeOfSubmittedEntitiesIndicies, new EntityCollection<TValue>(values, entityids, count), fromGroup, toGroup);
}
}
catch (Exception e)
{
Console.LogException(e, "Code crashed inside Add callback ".FastConcat(fasterList[i].name));
Console.LogException(e, "Code crashed inside Add callback ".FastConcat(callbackEngines[i].name));

throw;
}


+ 16
- 15
com.sebaslab.svelto.ecs/DataStructures/UnmanagedTypeSafeDictionary.cs View File

@@ -139,9 +139,12 @@ namespace Svelto.ECS.Internal
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(uint egidEntityId, in TValue entityComponent)
public uint Add(uint egidEntityId, in TValue entityComponent)
{
implUnmgd.dictionary.Add(egidEntityId, entityComponent);
if (implUnmgd.dictionary.TryAdd(egidEntityId, entityComponent, out var index) == false)
throw new TypeSafeDictionaryException("Key already present");

return index;
}

public void Dispose()
@@ -172,20 +175,19 @@ namespace Svelto.ECS.Internal
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntitiesFromDictionary
(FasterList<(uint, string)> infosToProcess, FasterList<uint> entityIDsAffectedByRemoval)
public void RemoveEntitiesFromDictionary(FasterList<(uint, string)> infosToProcess, FasterDictionary<uint, uint> entityIDsAffectedByRemoveAtSwapBack)
{
TypeSafeDictionaryMethods.RemoveEntitiesFromDictionary(infosToProcess, ref implUnmgd.dictionary
, entityIDsAffectedByRemoval);
TypeSafeDictionaryMethods.RemoveEntitiesFromDictionary(infosToProcess, ref implUnmgd.dictionary, entityIDsAffectedByRemoveAtSwapBack);
}

public void SwapEntitiesBetweenDictionaries
(FasterList<(uint, uint, string)> infosToProcess, ExclusiveGroupStruct fromGroup
, ExclusiveGroupStruct toGroup, ITypeSafeDictionary toComponentsDictionary
, FasterList<uint> entityIDsAffectedByRemoval)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntitiesBetweenDictionaries(in FasterDictionary<uint, SwapInfo> infosToProcess,
ExclusiveGroupStruct fromGroup
, ExclusiveGroupStruct toGroup, ITypeSafeDictionary toComponentsDictionary
, FasterDictionary<uint, uint> entityIDsAffectedByRemoveAtSwapBack)
{
TypeSafeDictionaryMethods.SwapEntitiesBetweenDictionaries(infosToProcess, ref implUnmgd.dictionary
,toComponentsDictionary as ITypeSafeDictionary<TValue>, fromGroup, toGroup, entityIDsAffectedByRemoval);
,toComponentsDictionary as ITypeSafeDictionary<TValue>, fromGroup, toGroup, entityIDsAffectedByRemoveAtSwapBack);
}

/// <summary>
@@ -204,10 +206,9 @@ namespace Svelto.ECS.Internal
/// Execute all the engine IReactOnSwap callbacks linked to components swapped this submit
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExecuteEnginesSwapCallbacks
(FasterList<(uint, uint, string)> infosToProcess
, FasterList<ReactEngineContainer<IReactOnSwap>> reactiveEnginesSwap, ExclusiveGroupStruct fromGroup
, ExclusiveGroupStruct toGroup, in PlatformProfiler profiler)
public void ExecuteEnginesSwapCallbacks(FasterDictionary<uint, SwapInfo> infosToProcess
, FasterList<ReactEngineContainer<IReactOnSwap>> reactiveEnginesSwap, ExclusiveGroupStruct fromGroup
, ExclusiveGroupStruct toGroup, in PlatformProfiler profiler)
{
TypeSafeDictionaryMethods.ExecuteEnginesSwapCallbacks(infosToProcess, ref implUnmgd.dictionary
, reactiveEnginesSwap, toGroup, fromGroup, in profiler);


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

@@ -88,10 +88,8 @@ namespace Svelto.ECS

ref var nativeSwapOperation = ref _nativeSwapOperations[componentsIndex];

CheckRemoveEntityID(entityEGID.@from, nativeSwapOperation.entityDescriptorType
CheckSwapEntityID(entityEGID.@from, entityEGID.to, nativeSwapOperation.entityDescriptorType
, nativeSwapOperation.caller);
CheckAddEntityID(entityEGID.to, nativeSwapOperation.entityDescriptorType
, nativeSwapOperation.caller);

QueueSwapEntityOperation(entityEGID.@from, entityEGID.to
, FindRealComponents(entityEGID.@from, nativeSwapOperation.components)


+ 3
- 3
com.sebaslab.svelto.ecs/Extensions/Native/EntityNativeDBExtensions.cs View File

@@ -83,7 +83,7 @@ namespace Svelto.ECS
if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false)
return false;

buffer = (NB<T>) (safeDictionary as ITypeSafeDictionary<T>).GetValues(out _);
buffer = (NBInternal<T>) (safeDictionary as ITypeSafeDictionary<T>).GetValues(out _);

return true;
}
@@ -129,7 +129,7 @@ namespace Svelto.ECS
{
if (mapper._map.TryFindIndex(entityID, out index))
{
return (NB<T>) mapper._map.GetValues(out _);
return (NBInternal<T>) mapper._map.GetValues(out _);
}

throw new ECSException("Entity not found");
@@ -143,7 +143,7 @@ namespace Svelto.ECS
index = default;
if (mapper._map != null && mapper._map.TryFindIndex(entityID, out index))
{
array = (NB<T>) mapper._map.GetValues(out _);
array = (NBInternal<T>) mapper._map.GetValues(out _);
return true;
}



+ 9
- 2
com.sebaslab.svelto.ecs/Extensions/Native/NativeEGIDMultiMapper.cs View File

@@ -1,4 +1,5 @@
using System;
using Svelto.Common;
using Svelto.DataStructures.Native;
using Svelto.ECS.Internal;

@@ -11,11 +12,15 @@ namespace Svelto.ECS.Native
/// is disposed right after the use.
///
///WARNING: REMEMBER THIS MUST BE DISPOSED OF, AS IT USES NATIVE MEMORY. IT WILL LEAK MEMORY OTHERWISE
///
/// to retrieve a NativeEGIDMultiMapper use entitiesDB.QueryNativeMappedEntities<T>(groups, Svelto.Common.Allocator.TempJob);
///
/// TODO: this could be extended to support all the query interfaces so that NB can become ref and this used to query entities inside jobs
///
/// </summary>
public struct NativeEGIDMultiMapper<T> : IDisposable where T : unmanaged, _IInternalEntityComponent
public struct NativeEGIDMultiMapper<T> : IEGIDMultiMapper, IDisposable where T : unmanaged, _IInternalEntityComponent
{
public NativeEGIDMultiMapper(in SveltoDictionaryNative<ExclusiveGroupStruct, SharedSveltoDictionaryNative<uint, T>> dictionary)
internal NativeEGIDMultiMapper(in SveltoDictionaryNative<ExclusiveGroupStruct, SharedSveltoDictionaryNative<uint, T>> dictionary)
{
_dic = dictionary;
}
@@ -61,5 +66,7 @@ namespace Svelto.ECS.Native
}

SveltoDictionaryNative<ExclusiveGroupStruct, SharedSveltoDictionaryNative<uint, T>> _dic;

public Type entityType => TypeCache<T>.type;
}
}

+ 59
- 109
com.sebaslab.svelto.ecs/Extensions/Svelto/EGIDMultiMapper.cs View File

@@ -1,111 +1,18 @@
using System;
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.DataStructures.Native;
using Svelto.ECS.Hybrid;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
namespace Native
/// <summary>
/// to retrieve an EGIDMultiMapper use entitiesDB.QueryMappedEntities<T>(groups);
/// </summary>
/// <typeparam name="T"></typeparam>
public readonly struct EGIDMultiMapper<T>: IEGIDMultiMapper where T : struct, _IInternalEntityComponent
{
public struct EGIDMultiMapper<T> where T : unmanaged, _IInternalEntityComponent
{
public EGIDMultiMapper
(SveltoDictionary<ExclusiveGroupStruct,
SveltoDictionary<uint, T, NativeStrategy<
SveltoDictionaryNode<uint>>, NativeStrategy<T>, NativeStrategy<int>>,
ManagedStrategy<SveltoDictionaryNode<ExclusiveGroupStruct>>,
ManagedStrategy<SveltoDictionary<uint, T, NativeStrategy<SveltoDictionaryNode<uint>>, NativeStrategy<T>
, NativeStrategy<int>>>, NativeStrategy<int>> dictionary)
{
_dic = dictionary;
}

public int count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _dic.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Entity(EGID entity)
{
#if DEBUG && !PROFILE_SVELTO
if (Exists(entity) == false)
throw new Exception("NativeEGIDMultiMapper: Entity not found");
#endif
ref var sveltoDictionary = ref _dic.GetValueByRef(entity.groupID);
return ref sveltoDictionary.GetValueByRef(entity.entityID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists(EGID entity)
{
return _dic.TryFindIndex(entity.groupID, out var index)
&& _dic.GetDirectValueByRef(index).ContainsKey(entity.entityID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetEntity(EGID entity, out T component)
{
component = default;
return _dic.TryFindIndex(entity.groupID, out var index)
&& _dic.GetDirectValueByRef(index).TryGetValue(entity.entityID, out component);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool FindIndex(ExclusiveGroupStruct group, uint entityID, out uint index)
{
index = 0;
return _dic.TryFindIndex(group, out var groupIndex) &&
_dic.GetDirectValueByRef(groupIndex).TryFindIndex(entityID, out index);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetIndex(ExclusiveGroupStruct group, uint entityID)
{
uint groupIndex = _dic.GetIndex(group);
return _dic.GetDirectValueByRef(groupIndex).GetIndex(entityID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists(ExclusiveGroupStruct group, uint entityID)
{
return _dic.TryFindIndex(group, out var groupIndex) &&
_dic.GetDirectValueByRef(groupIndex).ContainsKey(entityID);
}

public Type entityType => TypeCache<T>.type;

SveltoDictionary<ExclusiveGroupStruct,
SveltoDictionary<uint, T, NativeStrategy<SveltoDictionaryNode<uint>>, NativeStrategy<T>,
NativeStrategy<int>>, ManagedStrategy<SveltoDictionaryNode<ExclusiveGroupStruct>>,
ManagedStrategy<SveltoDictionary<uint, T, NativeStrategy<SveltoDictionaryNode<uint>>, NativeStrategy<T>,
NativeStrategy<int>>>, NativeStrategy<int>> _dic;
}

public interface IEGIDMultiMapper
{
bool FindIndex(ExclusiveGroupStruct group, uint entityID, out uint index);

uint GetIndex(ExclusiveGroupStruct group, uint entityID);

bool Exists(ExclusiveGroupStruct group, uint entityID);

Type entityType { get; }
}
}

public struct EGIDMultiMapper<T> where T : struct, IEntityViewComponent
{
public EGIDMultiMapper
(SveltoDictionary<ExclusiveGroupStruct,
SveltoDictionary<uint, T, ManagedStrategy<SveltoDictionaryNode<uint>>, ManagedStrategy<T>,
ManagedStrategy<int>>, ManagedStrategy<SveltoDictionaryNode<ExclusiveGroupStruct>>,
ManagedStrategy<SveltoDictionary<uint, T, ManagedStrategy<SveltoDictionaryNode<uint>>, ManagedStrategy<T>,
ManagedStrategy<int>>>, ManagedStrategy<int>> dictionary)
internal EGIDMultiMapper(FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary<T>> dictionary)
{
_dic = dictionary;
}
@@ -121,17 +28,34 @@ namespace Svelto.ECS
{
#if DEBUG && !PROFILE_SVELTO
if (Exists(entity) == false)
throw new Exception("NativeEGIDMultiMapper: Entity not found");
throw new Exception("EGIDMultiMapper: Entity not found");
#endif
ref var sveltoDictionary = ref _dic.GetValueByRef(entity.groupID);
return ref sveltoDictionary.GetValueByRef(entity.entityID);
}
public EntityCollection<T> Entities(ExclusiveGroupStruct targetEgidGroupID)
{
uint count = 0;
IBuffer<T> buffer;
IEntityIDs ids = default;

if (_dic.TryGetValue(targetEgidGroupID, out var typeSafeDictionary) == false)
buffer = default;
else
{
ITypeSafeDictionary<T> safeDictionary = typeSafeDictionary;
buffer = safeDictionary.GetValues(out count);
ids = safeDictionary.entityIDs;
}

return new EntityCollection<T>(buffer, ids, count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists(EGID entity)
{
return _dic.TryFindIndex(entity.groupID, out var index)
&& _dic.GetDirectValueByRef(index).ContainsKey(entity.entityID);
return _dic.TryFindIndex(entity.groupID, out var index) && _dic.GetDirectValueByRef(index).ContainsKey(entity.entityID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -139,13 +63,39 @@ namespace Svelto.ECS
{
component = default;
return _dic.TryFindIndex(entity.groupID, out var index)
&& _dic.GetDirectValueByRef(index).TryGetValue(entity.entityID, out component);
&& _dic.GetDirectValueByRef(index).TryGetValue(entity.entityID, out component);
}

SveltoDictionary<ExclusiveGroupStruct,
SveltoDictionary<uint, T, ManagedStrategy<SveltoDictionaryNode<uint>>, ManagedStrategy<T>,
ManagedStrategy<int>>, ManagedStrategy<SveltoDictionaryNode<ExclusiveGroupStruct>>,
ManagedStrategy<SveltoDictionary<uint, T, ManagedStrategy<SveltoDictionaryNode<uint>>, ManagedStrategy<T>,
ManagedStrategy<int>>>, ManagedStrategy<int>> _dic;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool FindIndex(ExclusiveGroupStruct group, uint entityID, out uint index)
{
index = 0;
return _dic.TryFindIndex(group, out var groupIndex) &&
_dic.GetDirectValueByRef(groupIndex).TryFindIndex(entityID, out index);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetIndex(ExclusiveGroupStruct group, uint entityID)
{
uint groupIndex = _dic.GetIndex(group);
return _dic.GetDirectValueByRef(groupIndex).GetIndex(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetIndex(EGID egid)
{
return GetIndex(egid.groupID, egid.entityID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists(ExclusiveGroupStruct group, uint entityID)
{
return _dic.TryFindIndex(group, out var groupIndex) &&
_dic.GetDirectValueByRef(groupIndex).ContainsKey(entityID);
}

public Type entityType => TypeCache<T>.type;

readonly FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary<T>> _dic;
}
}

+ 78
- 78
com.sebaslab.svelto.ecs/Extensions/Svelto/EntityCollectionExtension.cs View File

@@ -11,14 +11,14 @@ namespace Svelto.ECS
public static void Deconstruct<T1>(in this EntityCollection<T1> ec, out NB<T1> buffer, out int count)
where T1 : unmanaged, IEntityComponent
{
if (ec._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
if (ec.buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer = default;
count = 0;
return;
}

buffer = (NB<T1>)ec._buffer;
buffer = (NBInternal<T1>)ec.buffer;
count = (int)ec.count;
}

@@ -27,7 +27,7 @@ namespace Svelto.ECS
out NativeEntityIDs entityIDs, out int count)
where T1 : unmanaged, IEntityComponent
{
if (ec._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
if (ec.buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer = default;
count = 0;
@@ -35,9 +35,9 @@ namespace Svelto.ECS
return;
}

buffer = (NB<T1>)ec._buffer;
buffer = (NBInternal<T1>)ec.buffer;
count = (int)ec.count;
entityIDs = (NativeEntityIDs)ec._entityIDs;
entityIDs = (NativeEntityIDs)ec.entityIDs;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -46,7 +46,7 @@ namespace Svelto.ECS
where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -56,10 +56,10 @@ namespace Svelto.ECS
return;
}

buffer1 = (NB<T1>)ec.buffer1._buffer;
buffer2 = (NB<T2>)ec.buffer2._buffer;
buffer1 = (NBInternal<T1>)ec.buffer1.buffer;
buffer2 = (NBInternal<T2>)ec.buffer2.buffer;
count = ec.count;
entityIDs = (NativeEntityIDs)ec.buffer1._entityIDs;
entityIDs = (NativeEntityIDs)ec.buffer1.entityIDs;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -68,7 +68,7 @@ namespace Svelto.ECS
where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -77,8 +77,8 @@ namespace Svelto.ECS
return;
}

buffer1 = (NB<T1>)ec.buffer1._buffer;
buffer2 = (NB<T2>)ec.buffer2._buffer;
buffer1 = (NBInternal<T1>)ec.buffer1.buffer;
buffer2 = (NBInternal<T2>)ec.buffer2.buffer;
count = (int)ec.count;
}

@@ -89,7 +89,7 @@ namespace Svelto.ECS
where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -99,9 +99,9 @@ namespace Svelto.ECS
return;
}

buffer1 = (NB<T1>)ec.buffer1._buffer;
buffer2 = (NB<T2>)ec.buffer2._buffer;
buffer3 = (NB<T3>)ec.buffer3._buffer;
buffer1 = (NBInternal<T1>)ec.buffer1.buffer;
buffer2 = (NBInternal<T2>)ec.buffer2.buffer;
buffer3 = (NBInternal<T3>)ec.buffer3.buffer;
count = (int)ec.count;
}

@@ -112,7 +112,7 @@ namespace Svelto.ECS
where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -123,11 +123,11 @@ namespace Svelto.ECS
return;
}

buffer1 = (NB<T1>)ec.buffer1._buffer;
buffer2 = (NB<T2>)ec.buffer2._buffer;
buffer3 = (NB<T3>)ec.buffer3._buffer;
buffer1 = (NBInternal<T1>)ec.buffer1.buffer;
buffer2 = (NBInternal<T2>)ec.buffer2.buffer;
buffer3 = (NBInternal<T3>)ec.buffer3.buffer;
count = (int)ec.count;
entityIDs = (NativeEntityIDs)ec.buffer1._entityIDs;
entityIDs = (NativeEntityIDs)ec.buffer1.entityIDs;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -138,7 +138,7 @@ namespace Svelto.ECS
where T3 : unmanaged, IEntityComponent
where T4 : unmanaged, IEntityComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -149,10 +149,10 @@ namespace Svelto.ECS
return;
}

buffer1 = (NB<T1>)ec.buffer1._buffer;
buffer2 = (NB<T2>)ec.buffer2._buffer;
buffer3 = (NB<T3>)ec.buffer3._buffer;
buffer4 = (NB<T4>)ec.buffer4._buffer;
buffer1 = (NBInternal<T1>)ec.buffer1.buffer;
buffer2 = (NBInternal<T2>)ec.buffer2.buffer;
buffer3 = (NBInternal<T3>)ec.buffer3.buffer;
buffer4 = (NBInternal<T4>)ec.buffer4.buffer;
count = (int)ec.count;
}

@@ -164,7 +164,7 @@ namespace Svelto.ECS
where T3 : unmanaged, IEntityComponent
where T4 : unmanaged, IEntityComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -176,11 +176,11 @@ namespace Svelto.ECS
return;
}

buffer1 = (NB<T1>)ec.buffer1._buffer;
buffer2 = (NB<T2>)ec.buffer2._buffer;
buffer3 = (NB<T3>)ec.buffer3._buffer;
buffer4 = (NB<T4>)ec.buffer4._buffer;
entityIDs = (NativeEntityIDs)ec.buffer1._entityIDs;
buffer1 = (NBInternal<T1>)ec.buffer1.buffer;
buffer2 = (NBInternal<T2>)ec.buffer2.buffer;
buffer3 = (NBInternal<T3>)ec.buffer3.buffer;
buffer4 = (NBInternal<T4>)ec.buffer4.buffer;
entityIDs = (NativeEntityIDs)ec.buffer1.entityIDs;
count = (int)ec.count;
}
}
@@ -191,14 +191,14 @@ namespace Svelto.ECS
public static void Deconstruct<T1>(in this EntityCollection<T1> ec, out MB<T1> buffer, out int count)
where T1 : struct, IEntityViewComponent
{
if (ec._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
if (ec.buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer = default;
count = 0;
return;
}

buffer = (MB<T1>)ec._buffer;
buffer = (MBInternal<T1>)ec.buffer;
count = (int)ec.count;
}

@@ -207,7 +207,7 @@ namespace Svelto.ECS
out ManagedEntityIDs entityIDs, out int count)
where T1 : struct, IEntityViewComponent
{
if (ec._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
if (ec.buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer = default;
count = 0;
@@ -215,9 +215,9 @@ namespace Svelto.ECS
return;
}

buffer = (MB<T1>)ec._buffer;
buffer = (MBInternal<T1>)ec.buffer;
count = (int)ec.count;
entityIDs = (ManagedEntityIDs)ec._entityIDs;
entityIDs = (ManagedEntityIDs)ec.entityIDs;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -226,7 +226,7 @@ namespace Svelto.ECS
where T1 : struct, IEntityViewComponent
where T2 : struct, IEntityViewComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -235,8 +235,8 @@ namespace Svelto.ECS
return;
}

buffer1 = (MB<T1>)ec.buffer1._buffer;
buffer2 = (MB<T2>)ec.buffer2._buffer;
buffer1 = (MBInternal<T1>)ec.buffer1.buffer;
buffer2 = (MBInternal<T2>)ec.buffer2.buffer;
count = (int)ec.count;
}

@@ -246,7 +246,7 @@ namespace Svelto.ECS
where T1 : struct, IEntityViewComponent
where T2 : struct, IEntityViewComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -256,10 +256,10 @@ namespace Svelto.ECS
return;
}

buffer1 = (MB<T1>)ec.buffer1._buffer;
buffer2 = (MB<T2>)ec.buffer2._buffer;
buffer1 = (MBInternal<T1>)ec.buffer1.buffer;
buffer2 = (MBInternal<T2>)ec.buffer2.buffer;
count = (int)ec.count;
entityIDs = (ManagedEntityIDs)ec.buffer1._entityIDs;
entityIDs = (ManagedEntityIDs)ec.buffer1.entityIDs;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -269,7 +269,7 @@ namespace Svelto.ECS
where T2 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -279,9 +279,9 @@ namespace Svelto.ECS
return;
}

buffer1 = (MB<T1>)ec.buffer1._buffer;
buffer2 = (MB<T2>)ec.buffer2._buffer;
buffer3 = (MB<T3>)ec.buffer3._buffer;
buffer1 = (MBInternal<T1>)ec.buffer1.buffer;
buffer2 = (MBInternal<T2>)ec.buffer2.buffer;
buffer3 = (MBInternal<T3>)ec.buffer3.buffer;
count = (int)ec.count;
}

@@ -292,7 +292,7 @@ namespace Svelto.ECS
where T2 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -303,11 +303,11 @@ namespace Svelto.ECS
return;
}

buffer1 = (MB<T1>)ec.buffer1._buffer;
buffer2 = (MB<T2>)ec.buffer2._buffer;
buffer3 = (MB<T3>)ec.buffer3._buffer;
buffer1 = (MBInternal<T1>)ec.buffer1.buffer;
buffer2 = (MBInternal<T2>)ec.buffer2.buffer;
buffer3 = (MBInternal<T3>)ec.buffer3.buffer;
count = (int)ec.count;
entityIDs = (ManagedEntityIDs)ec.buffer1._entityIDs;
entityIDs = (ManagedEntityIDs)ec.buffer1.entityIDs;
}
}

@@ -319,7 +319,7 @@ namespace Svelto.ECS
where T1 : unmanaged, IEntityComponent
where T2 : struct, IEntityViewComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -328,10 +328,10 @@ namespace Svelto.ECS
return;
}

buffer1 = (NB<T1>)ec.buffer1._buffer;
buffer2 = (MB<T2>)ec.buffer2._buffer;
buffer1 = (NBInternal<T1>)ec.buffer1.buffer;
buffer2 = (MBInternal<T2>)ec.buffer2.buffer;
count = (int)ec.count;
entityIDs = (ManagedEntityIDs)ec.buffer2._entityIDs;
entityIDs = (ManagedEntityIDs)ec.buffer2.entityIDs;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -340,7 +340,7 @@ namespace Svelto.ECS
where T1 : unmanaged, IEntityComponent
where T2 : struct, IEntityViewComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -349,8 +349,8 @@ namespace Svelto.ECS
return;
}

buffer1 = (NB<T1>)ec.buffer1._buffer;
buffer2 = (MB<T2>)ec.buffer2._buffer;
buffer1 = (NBInternal<T1>)ec.buffer1.buffer;
buffer2 = (MBInternal<T2>)ec.buffer2.buffer;
count = (int)ec.count;
}

@@ -361,7 +361,7 @@ namespace Svelto.ECS
where T2 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -371,9 +371,9 @@ namespace Svelto.ECS
return;
}

buffer1 = (NB<T1>)ec.buffer1._buffer;
buffer2 = (MB<T2>)ec.buffer2._buffer;
buffer3 = (MB<T3>)ec.buffer3._buffer;
buffer1 = (NBInternal<T1>)ec.buffer1.buffer;
buffer2 = (MBInternal<T2>)ec.buffer2.buffer;
buffer3 = (MBInternal<T3>)ec.buffer3.buffer;
count = (int)ec.count;
}

@@ -385,7 +385,7 @@ namespace Svelto.ECS
where T3 : unmanaged, IEntityComponent
where T4 : struct, IEntityViewComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -396,10 +396,10 @@ namespace Svelto.ECS
return;
}

buffer1 = (NB<T1>)ec.buffer1._buffer;
buffer2 = (NB<T2>)ec.buffer2._buffer;
buffer3 = (NB<T3>)ec.buffer3._buffer;
buffer4 = (MB<T4>)ec.buffer4._buffer;
buffer1 = (NBInternal<T1>)ec.buffer1.buffer;
buffer2 = (NBInternal<T2>)ec.buffer2.buffer;
buffer3 = (NBInternal<T3>)ec.buffer3.buffer;
buffer4 = (MBInternal<T4>)ec.buffer4.buffer;
count = (int)ec.count;
}
}
@@ -413,7 +413,7 @@ namespace Svelto.ECS
where T2 : unmanaged, IEntityComponent
where T3 : struct, IEntityViewComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -423,9 +423,9 @@ namespace Svelto.ECS
return;
}

buffer1 = (NB<T1>)ec.buffer1._buffer;
buffer2 = (NB<T2>)ec.buffer2._buffer;
buffer3 = (MB<T3>)ec.buffer3._buffer;
buffer1 = (NBInternal<T1>)ec.buffer1.buffer;
buffer2 = (NBInternal<T2>)ec.buffer2.buffer;
buffer3 = (MBInternal<T3>)ec.buffer3.buffer;
count = (int)ec.count;
}

@@ -437,7 +437,7 @@ namespace Svelto.ECS
where T3 : struct, IEntityViewComponent
where T4 : struct, IEntityViewComponent
{
if (ec.buffer1._buffer
if (ec.buffer1.buffer
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0
{
buffer1 = default;
@@ -448,10 +448,10 @@ namespace Svelto.ECS
return;
}

buffer1 = (NB<T1>)ec.buffer1._buffer;
buffer2 = (NB<T2>)ec.buffer2._buffer;
buffer3 = (MB<T3>)ec.buffer3._buffer;
buffer4 = (MB<T4>)ec.buffer4._buffer;
buffer1 = (NBInternal<T1>)ec.buffer1.buffer;
buffer2 = (NBInternal<T2>)ec.buffer2.buffer;
buffer3 = (MBInternal<T3>)ec.buffer3.buffer;
buffer4 = (MBInternal<T4>)ec.buffer4.buffer;
count = (int)ec.count;
}
}

+ 3
- 3
com.sebaslab.svelto.ecs/Extensions/Svelto/EntityManagedDBExtensions.cs View File

@@ -52,7 +52,7 @@ namespace Svelto.ECS
if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false)
return false;

buffer = (MB<T>) (safeDictionary as ITypeSafeDictionary<T>).GetValues(out _);
buffer = (MBInternal<T>) (safeDictionary as ITypeSafeDictionary<T>).GetValues(out _);

return true;
}
@@ -98,7 +98,7 @@ namespace Svelto.ECS
{
if (mapper._map.TryFindIndex(entityID, out index))
{
return (MB<T>) mapper._map.GetValues(out _);
return (MBInternal<T>) mapper._map.GetValues(out _);
}

throw new ECSException("Entity not found");
@@ -112,7 +112,7 @@ namespace Svelto.ECS
index = default;
if (mapper._map != null && mapper._map.TryFindIndex(entityID, out index))
{
array = (MB<T>) mapper._map.GetValues(out _);
array = (MBInternal<T>) mapper._map.GetValues(out _);
return true;
}



+ 4
- 4
com.sebaslab.svelto.ecs/Extensions/Svelto/GroupsEnumerable.cs View File

@@ -37,7 +37,7 @@ namespace Svelto.ECS
while (++_indexGroup < _groups.count)
{
var exclusiveGroupStruct = _groups[_indexGroup];
if (!exclusiveGroupStruct.IsEnabled())
if (exclusiveGroupStruct.IsEnabled() == false)
continue;

var entityCollection = _entitiesDB.QueryEntities<T1, T2, T3, T4>(exclusiveGroupStruct);
@@ -117,7 +117,7 @@ namespace Svelto.ECS
while (++_indexGroup < _groups.count)
{
var exclusiveGroupStruct = _groups[_indexGroup];
if (!exclusiveGroupStruct.IsEnabled())
if (exclusiveGroupStruct.IsEnabled() == false)
continue;

EntityCollection<T1, T2, T3> entityCollection = _entitiesDB.QueryEntities<T1, T2, T3>(exclusiveGroupStruct);
@@ -198,7 +198,7 @@ namespace Svelto.ECS
while (++_indexGroup < _groups.count)
{
var exclusiveGroupStruct = _groups[_indexGroup];
if (!exclusiveGroupStruct.IsEnabled())
if (exclusiveGroupStruct.IsEnabled() == false)
continue;

var entityCollection = _db.QueryEntities<T1, T2>(exclusiveGroupStruct);
@@ -277,7 +277,7 @@ namespace Svelto.ECS
{
var exclusiveGroupStruct = _groups[_indexGroup];
if (!exclusiveGroupStruct.IsEnabled())
if (exclusiveGroupStruct.IsEnabled() == false)
continue;

var entityCollection = _db.QueryEntities<T1>(exclusiveGroupStruct);


+ 9
- 0
com.sebaslab.svelto.ecs/Extensions/Svelto/IEGIDMultiMapper.cs View File

@@ -0,0 +1,9 @@
using System;

namespace Svelto.ECS
{
public interface IEGIDMultiMapper
{
uint GetIndex(EGID entity);
}
}

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

@@ -13,7 +13,7 @@ namespace Svelto.ECS
$"trying adding an entity {entityID} to filter {mapper.entityType} - {legacyFilter._ID} with group {legacyFilter._exclusiveGroupStruct}, but entity is not found! ");
#endif

return legacyFilter.InternalAdd(entityID, mapper.GetIndex(legacyFilter._exclusiveGroupStruct, entityID));
return legacyFilter.InternalAdd(entityID, mapper.GetIndex(new EGID(entityID, legacyFilter._exclusiveGroupStruct)));
}

}


+ 16
- 0
com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/SveltoOnDOTSEnginesGroup.cs View File

@@ -1,4 +1,5 @@
#if UNITY_ECS
using System;
using Svelto.Common;
using Svelto.ECS.Schedulers;
using Unity.Entities;
@@ -78,6 +79,21 @@ namespace Svelto.ECS.SveltoOnDOTS

_syncSveltoToDotsGroup.Add(engine);
}
public void CreateDOTSToSveltoSyncEngine<T>() where T:SyncSveltoToDOTSEngine, new()
{
//it's a Svelto Engine/DOTS ECS SystemBase so it must be added in the DOTS ECS world AND svelto enginesRoot
#if UNITY_ECS_100
T engine = world.GetOrCreateSystemManaged<T>();
_enginesRoot.AddEngine(engine);

_syncSveltoToDotsGroup.Add(engine);
#else
throw new NotImplementedException();
#endif
}

public void AddDOTSToSveltoSyncEngine(SyncDOTSToSveltoEngine engine)
{


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

@@ -119,8 +119,7 @@ namespace Svelto.ECS
uint descriptorHash = serializableEntityComponent.descriptorHash;
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);

_enginesRoot.CheckRemoveEntityID(fromEGID, entityDescriptor.realType, caller);
_enginesRoot.CheckAddEntityID(toEGID, entityDescriptor.realType, caller);
_enginesRoot.CheckSwapEntityID(fromEGID, toEGID, entityDescriptor.realType, caller);

/// Serializable Entity Descriptors can be extended so we need to use FindRealComponents
_enginesRoot.QueueSwapEntityOperation(fromEGID, toEGID,


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

@@ -1,3 +1,5 @@
using Svelto.ECS.Serialization;

namespace Svelto.ECS
{
public partial class EnginesRoot


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

@@ -1,6 +1,6 @@
using Svelto.DataStructures;

namespace Svelto.ECS
namespace Svelto.ECS.Serialization
{
public interface ISerializationData
{


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

@@ -1,4 +1,4 @@
namespace Svelto.ECS
namespace Svelto.ECS.Serialization
{
struct SerializableEntityComponent : IEntityComponent
{


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

@@ -1,6 +1,6 @@
using Svelto.ECS.Schedulers;

namespace Svelto.ECS
namespace Svelto.ECS.Serialization
{
public class SerializingEnginesRoot : EnginesRoot
{


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

@@ -1,6 +1,6 @@
using Svelto.DataStructures;

namespace Svelto.ECS
namespace Svelto.ECS.Serialization
{
public class SimpleSerializationData : ISerializationData
{


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

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>Svelto.ECS</AssemblyName>
<LangVersion>9</LangVersion>
<LangVersion>10</LangVersion>
<TargetFramework>netstandard2.1</TargetFramework>
<Company>Svelto</Company>
<AssemblyVersion>3.4</AssemblyVersion>


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

@@ -11,7 +11,7 @@
"url": "https://github.com/sebas77/Svelto.ECS.git"
},
"dependencies": {
"com.sebaslab.svelto.common": "3.4.3"
"com.sebaslab.svelto.common": "3.5.0"
},
"keywords": [
"svelto",
@@ -19,7 +19,7 @@
"svelto.ecs"
],
"name": "com.sebaslab.svelto.ecs",
"version": "3.4.6",
"version": "3.5.0",
"type": "library",
"unity": "2020.3"
}

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

@@ -1,3 +1,3 @@
{
"version": "3.4.6"
"version": "3.5.0-pre"
}

Loading…
Cancel
Save