Преглед на файлове

UPM package version 3.0.0

tags/3.0.0
GitHub преди 3 години
родител
ревизия
2197fcce2e
променени са 100 файла, в които са добавени 5639 реда и са изтрити 2091 реда
  1. +66
    -62
      CheckEntityUtilities.cs
  2. +142
    -0
      ComponentBuilder.CheckFields.cs
  3. +1
    -1
      ComponentBuilder.CheckFields.cs.meta
  4. +155
    -0
      ComponentBuilder.cs
  5. +1
    -1
      ComponentBuilder.cs.meta
  6. +2
    -1
      Components.meta
  7. +7
    -0
      Components/EGIDComponent.cs
  8. +1
    -1
      Components/EGIDComponent.cs.meta
  9. +11
    -0
      Components/EntityHierarchyComponent.cs
  10. +11
    -0
      Components/EntityHierarchyComponent.cs.meta
  11. +7
    -0
      Components/LinkedEntityComponent.cs
  12. +11
    -0
      Components/LinkedEntityComponent.cs.meta
  13. +1
    -1
      DBC.cs
  14. +293
    -0
      DataStructures/FastTypeSafeDictionary.cs
  15. +11
    -0
      DataStructures/FastTypeSafeDictionary.cs.meta
  16. +47
    -0
      DataStructures/ITypeSafeDictionary.cs
  17. +11
    -0
      DataStructures/ITypeSafeDictionary.cs.meta
  18. +0
    -46
      DataStructures/SetEGIDWithoutBoxing.cs
  19. +74
    -0
      DataStructures/ThreadSafeNativeBagTest.cs
  20. +11
    -0
      DataStructures/ThreadSafeNativeBagTest.cs.meta
  21. +444
    -130
      DataStructures/TypeSafeDictionary.cs
  22. +13
    -0
      DataStructures/TypeSafeDictionaryUtilities.cs
  23. +11
    -0
      DataStructures/TypeSafeDictionaryUtilities.cs.meta
  24. +8
    -0
      DataStructures/Unmanaged.meta
  25. +76
    -0
      DataStructures/Unmanaged/AtomicNativeBags.cs
  26. +11
    -0
      DataStructures/Unmanaged/AtomicNativeBags.cs.meta
  27. +281
    -0
      DataStructures/Unmanaged/NativeBag.cs
  28. +11
    -0
      DataStructures/Unmanaged/NativeBag.cs.meta
  29. +370
    -0
      DataStructures/Unmanaged/NativeDynamicArray.cs
  30. +11
    -0
      DataStructures/Unmanaged/NativeDynamicArray.cs.meta
  31. +48
    -0
      DataStructures/Unmanaged/NativeDynamicArrayCast.cs
  32. +11
    -0
      DataStructures/Unmanaged/NativeDynamicArrayCast.cs.meta
  33. +23
    -0
      DataStructures/Unmanaged/NativeDynamicArrayUnityExtension.cs
  34. +11
    -0
      DataStructures/Unmanaged/NativeDynamicArrayUnityExtension.cs.meta
  35. +115
    -0
      DataStructures/Unmanaged/SharedNativeInt.cs
  36. +11
    -0
      DataStructures/Unmanaged/SharedNativeInt.cs.meta
  37. +194
    -0
      DataStructures/Unmanaged/ThreadSafeNativeBag.cs
  38. +11
    -0
      DataStructures/Unmanaged/ThreadSafeNativeBag.cs.meta
  39. +138
    -0
      DataStructures/Unmanaged/UnsafeArray.cs
  40. +11
    -0
      DataStructures/Unmanaged/UnsafeArray.cs.meta
  41. +302
    -0
      DataStructures/Unmanaged/UnsafeBlob.cs
  42. +11
    -0
      DataStructures/Unmanaged/UnsafeBlob.cs.meta
  43. +8
    -0
      Debugger.meta
  44. +70
    -0
      Debugger/ExclusiveGroupDebugger.cs
  45. +11
    -0
      Debugger/ExclusiveGroupDebugger.cs.meta
  46. +4
    -2
      Dispatcher/DispatchOnChange.cs
  47. +10
    -5
      Dispatcher/DispatchOnSet.cs
  48. +36
    -38
      DynamicEntityDescriptor.cs
  49. +10
    -0
      ECSException.cs
  50. +6
    -2
      ECSResources/ECSResources.cs
  51. +69
    -11
      ECSResources/ECSString.cs
  52. +22
    -11
      EGID.cs
  53. +57
    -12
      EGIDMapper.cs
  54. +148
    -0
      EnginesRoot.DoubleBufferedEntitiesToAdd.cs
  55. +11
    -0
      EnginesRoot.DoubleBufferedEntitiesToAdd.cs.meta
  56. +0
    -86
      EnginesRoot.DoubleBufferedEntityViews.cs
  57. +166
    -50
      EnginesRoot.Engines.cs
  58. +222
    -195
      EnginesRoot.Entities.cs
  59. +33
    -23
      EnginesRoot.GenericEntityFactory.cs
  60. +102
    -30
      EnginesRoot.GenericEntityFunctions.cs
  61. +67
    -51
      EnginesRoot.Submission.cs
  62. +154
    -0
      EntitiesDB.FindGroups.cs
  63. +11
    -0
      EntitiesDB.FindGroups.cs.meta
  64. +245
    -180
      EntitiesDB.cs
  65. +0
    -138
      EntityBuilder.CheckFields.cs
  66. +0
    -146
      EntityBuilder.cs
  67. +0
    -8
      EntityBuilder.cs.rej
  68. +154
    -259
      EntityCollection.cs
  69. +63
    -0
      EntityComponentInitializer.cs
  70. +11
    -0
      EntityComponentInitializer.cs.meta
  71. +12
    -3
      EntityDescriptorTemplate.cs
  72. +33
    -48
      EntityFactory.cs
  73. +2
    -2
      EntityGroupNotFoundException.cs
  74. +0
    -11
      EntityHierarchyStruct.cs
  75. +0
    -11
      EntityHierarchyStruct.cs.meta
  76. +2
    -2
      EntityInfoView.cs
  77. +1
    -1
      EntityNotFoundException.cs
  78. +0
    -163
      EntityStream.cs
  79. +0
    -75
      EntityStructInitializer.cs
  80. +0
    -11
      EntityStructInitializer.cs.meta
  81. +14
    -2
      EntitySubmissionScheduler.cs
  82. +20
    -8
      EntitySubmitOperation.cs
  83. +66
    -68
      EntityViewUtility.cs
  84. +23
    -120
      ExclusiveGroup.cs
  85. +139
    -0
      ExclusiveGroupStruct.cs
  86. +11
    -0
      ExclusiveGroupStruct.cs.meta
  87. +0
    -61
      ExecuteOnEntitiesDB.cs
  88. +0
    -11
      ExecuteOnEntitiesDB.cs.meta
  89. +4
    -4
      ExtendibleEntityDescriptor.cs
  90. +19
    -0
      Extensions/ProcessorCount.cs
  91. +11
    -0
      Extensions/ProcessorCount.cs.meta
  92. +8
    -0
      Extensions/Svelto.meta
  93. +70
    -0
      Extensions/Svelto/AllGroupsEnumerable.cs
  94. +11
    -0
      Extensions/Svelto/AllGroupsEnumerable.cs.meta
  95. +251
    -0
      Extensions/Svelto/EntityCollectionExtension.cs
  96. +11
    -0
      Extensions/Svelto/EntityCollectionExtension.cs.meta
  97. +109
    -0
      Extensions/Svelto/EntityManagedDBExtensions.cs
  98. +11
    -0
      Extensions/Svelto/EntityManagedDBExtensions.cs.meta
  99. +125
    -0
      Extensions/Svelto/EntityNativeDBExtensions.cs
  100. +11
    -0
      Extensions/Svelto/EntityNativeDBExtensions.cs.meta

+ 66
- 62
CheckEntityUtilities.cs Целия файл

@@ -1,81 +1,85 @@
#if DEBUG && !PROFILER
#if !DEBUG || PROFILE_SVELTO
#define DONT_USE
#endif
using System;
using System.Collections.Generic;
using Svelto.DataStructures;
#else
using System.Diagnostics;
#endif
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
/// submission. Two operations on the same entity are not allowed between submissions.
/// </summary>
public partial class EnginesRoot
{
#if DEBUG && !PROFILER
void CheckRemoveEntityID(EGID egid)
#if DONT_USE
[Conditional("CHECK_ALL")]
#endif
void CheckRemoveEntityID(EGID egid, Type entityDescriptorType, string caller = "")
{
// Console.LogError("<color=orange>removed</color>".FastConcat(egid.ToString()));
if (_idCheckers.TryGetValue(egid.groupID, out var hash))
{
if (hash.Contains(egid.entityID) == false)
throw new ECSException("Entity with not found ID is about to be removed: id: "
.FastConcat(egid.entityID)
.FastConcat(" groupid: ")
.FastConcat(egid.groupID));
if (_multipleOperationOnSameEGIDChecker.ContainsKey(egid) == true)
throw new ECSException(
"Executing multiple structural changes 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(" operation was: ")
.FastConcat(_multipleOperationOnSameEGIDChecker[egid] == 1 ? "add" : "remove"));

hash.Remove(egid.entityID);
if (_idChecker.TryGetValue(egid.groupID, out var hash))
if (hash.Contains(egid.entityID) == false)
throw new ECSException("Trying to remove an Entity never submitted in the database "
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID)
.FastConcat(" groupid: ").FastConcat(egid.groupID.ToName())
.FastConcat(" type: ")
.FastConcat(entityDescriptorType != null
? entityDescriptorType.Name
: "not available"));
else
hash.Remove(egid.entityID);

if (hash.Count == 0)
_idCheckers.Remove(egid.groupID);
}
else
{
throw new ECSException("Entity with not found ID is about to be removed: id: "
.FastConcat(egid.entityID)
.FastConcat(" groupid: ")
.FastConcat(egid.groupID));
}
_multipleOperationOnSameEGIDChecker.Add(egid, 0);
}

void CheckAddEntityID(EGID egid)
#if DONT_USE
[Conditional("CHECK_ALL")]
#endif
void CheckAddEntityID(EGID egid, Type entityDescriptorType, string caller = "")
{
// Console.LogError("<color=orange>added</color> ".FastConcat(egid.ToString()));
if (_idCheckers.TryGetValue(egid.groupID, out var hash) == false)
hash = _idCheckers[egid.groupID] = new HashSet<uint>();
else
{
if (hash.Contains(egid.entityID))
throw new ECSException("Entity with used ID is about to be built: '"
.FastConcat("' id: '")
.FastConcat(egid.entityID)
.FastConcat("' groupid: '")
.FastConcat(egid.groupID)
.FastConcat("'"));
}
if (_multipleOperationOnSameEGIDChecker.ContainsKey(egid) == true)
throw new ECSException(
"Executing multiple structural changes 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(" operation was: ")
.FastConcat(_multipleOperationOnSameEGIDChecker[egid] == 1 ? "add" : "remove"));

var hash = _idChecker.GetOrCreate(egid.groupID, () => new HashSet<uint>());
if (hash.Contains(egid.entityID) == true)
throw new ECSException("Trying to add an Entity already submitted to the database "
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID)
.FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null
? entityDescriptorType.Name
: "not available"));
hash.Add(egid.entityID);
}
void RemoveGroupID(ExclusiveGroup.ExclusiveGroupStruct groupID)
{
_idCheckers.Remove(groupID);
_multipleOperationOnSameEGIDChecker.Add(egid, 1);
}

readonly FasterDictionary<uint, HashSet<uint>> _idCheckers = new FasterDictionary<uint, HashSet<uint>>();
#else
[Conditional("_CHECKS_DISABLED")]
void CheckRemoveEntityID(EGID egid)
{
}
#if DONT_USE
[Conditional("CHECK_ALL")]
#endif
void RemoveGroupID(BuildGroup groupID) { _idChecker.Remove(groupID); }

[Conditional("_CHECKS_DISABLED")]
void CheckAddEntityID(EGID egid)
{
}
[Conditional("_CHECKS_DISABLED")]
void RemoveGroupID(ExclusiveGroup.ExclusiveGroupStruct groupID)
{
}
#if DONT_USE
[Conditional("CHECK_ALL")]
#endif
void ClearChecks() { _multipleOperationOnSameEGIDChecker.FastClear(); }

readonly FasterDictionary<EGID, uint> _multipleOperationOnSameEGIDChecker = new FasterDictionary<EGID, uint>();
readonly FasterDictionary<uint, HashSet<uint>> _idChecker = new FasterDictionary<uint, HashSet<uint>>();
}
}
}

+ 142
- 0
ComponentBuilder.CheckFields.cs Целия файл

@@ -0,0 +1,142 @@
#if !DEBUG || PROFILE_SVELTO
#define DISABLE_CHECKS
using System.Diagnostics;
#endif
using System;
using System.Reflection;
using Svelto.Common;

namespace Svelto.ECS
{
static class ComponentBuilderUtilities
{
const string MSG = "Entity Components and Entity View Components fields cannot hold managed fields outside the Svelto rules.";

#if DISABLE_CHECKS
[Conditional("_CHECKS_DISABLED")]
#endif
public static void CheckFields(Type entityComponentType, bool needsReflection, bool isStringAllowed = false)
{
if (entityComponentType == ENTITY_INFO_COMPONENT || entityComponentType == EGIDType ||
entityComponentType == EXCLUSIVEGROUPSTRUCTTYPE || entityComponentType == SERIALIZABLE_ENTITY_STRUCT)
{
return;
}

if (needsReflection == false)
{
if (entityComponentType.IsClass)
{
throw new ECSException("EntityComponents must be structs.", entityComponentType);
}

FieldInfo[] fields = entityComponentType.GetFields(BindingFlags.Public | BindingFlags.Instance);

for (var i = fields.Length - 1; i >= 0; --i)
{
FieldInfo fieldInfo = fields[i];
Type fieldType = fieldInfo.FieldType;

SubCheckFields(fieldType, entityComponentType, isStringAllowed);
}
}
else
{
FieldInfo[] fields = entityComponentType.GetFields(BindingFlags.Public | BindingFlags.Instance);

if (fields.Length < 1)
{
ProcessError("No valid fields found in Entity View Components", entityComponentType);
}

for (int i = fields.Length - 1; i >= 0; --i)
{
FieldInfo fieldInfo = fields[i];

if (fieldInfo.FieldType.IsInterfaceEx() == true)
{
PropertyInfo[] properties = fieldInfo.FieldType.GetProperties(
BindingFlags.Public | BindingFlags.Instance
| BindingFlags.DeclaredOnly);

for (int j = properties.Length - 1; j >= 0; --j)
{
if (properties[j].PropertyType.IsGenericType)
{
Type genericTypeDefinition = properties[j].PropertyType.GetGenericTypeDefinition();
if (genericTypeDefinition == DISPATCHONSETTYPE
|| genericTypeDefinition == DISPATCHONCHANGETYPE)
{
continue;
}
}

Type propertyType = properties[j].PropertyType;

//for EntityComponentStructs, component fields that are structs that hold strings
//are allowed
SubCheckFields(propertyType, entityComponentType, isStringAllowed: true);
}
}
else
if (fieldInfo.FieldType.IsUnmanagedEx() == false)
{
ProcessError("Entity View Components must hold only public interfaces, strings or unmanaged type fields.",
entityComponentType);

}
}
}
}

static bool IsString(Type type)
{
return type == STRINGTYPE || type == STRINGBUILDERTYPE;
}

/// <summary>
/// This method checks the fields if it's an IEntityComponent, but checks all the properties if it's
/// IEntityViewComponent
/// </summary>
/// <param name="fieldType"></param>
/// <param name="entityComponentType"></param>
/// <param name="isStringAllowed"></param>
static void SubCheckFields(Type fieldType, Type entityComponentType, bool isStringAllowed = false)
{
//pass if it's Primitive or C# 8 unmanaged, or it's a string and string are allowed
//this check must allow pointers are they are unmanaged types
if ((isStringAllowed == true && IsString(fieldType) == true) || fieldType.IsValueTypeEx() == true)
{
//if it's a struct we have to check the fields recursively
if (IsString(fieldType) == false)
{
CheckFields(fieldType, false, isStringAllowed);
}

return;
}
ProcessError(MSG, entityComponentType, fieldType);
}

static void ProcessError(string message, Type entityComponentType, Type fieldType = null)
{
if (fieldType != null)
{
throw new ECSException(message, entityComponentType, fieldType);
}

throw new ECSException(message, entityComponentType);
}

static readonly Type DISPATCHONCHANGETYPE = typeof(DispatchOnChange<>);
static readonly Type DISPATCHONSETTYPE = typeof(DispatchOnSet<>);
static readonly Type EGIDType = typeof(EGID);
static readonly Type EXCLUSIVEGROUPSTRUCTTYPE = typeof(ExclusiveGroupStruct);
static readonly Type SERIALIZABLE_ENTITY_STRUCT = typeof(SerializableEntityComponent);
static readonly Type STRINGTYPE = typeof(string);
static readonly Type STRINGBUILDERTYPE = typeof(System.Text.StringBuilder);

internal static readonly Type ENTITY_INFO_COMPONENT = typeof(EntityInfoComponent);
}
}

EntityBuilder.CheckFields.cs.meta → ComponentBuilder.CheckFields.cs.meta Целия файл

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: ea4e6d9818ba3189beab2cf40d7e8e15
guid: b8801fb2bdee37a6aa48c7ab61badd55
MonoImporter:
externalObjects: {}
serializedVersion: 2

+ 155
- 0
ComponentBuilder.cs Целия файл

@@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Hybrid;
using Svelto.ECS.Internal;
using Svelto.Utilities;

namespace Svelto.ECS
{
public class ComponentBuilder<T> : IComponentBuilder where T : struct, IEntityComponent
{
public ComponentBuilder()
{
_initializer = DEFAULT_IT;
}

public ComponentBuilder(in T initializer) : this()
{
_initializer = initializer;
}

public void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID egid,
IEnumerable<object> implementors)
{
if (dictionary == null)
dictionary = TypeSafeDictionaryFactory<T>.Create();

var castedDic = dictionary as ITypeSafeDictionary<T>;

T entityComponent = default;
if (IS_ENTITY_VIEW_COMPONENT)
{
DBC.ECS.Check.Require(castedDic.ContainsKey(egid.entityID) == false,
$"building an entity with already used entity id! id: '{(ulong) egid}', {ENTITY_COMPONENT_NAME}");

this.FillEntityComponent(ref entityComponent, EntityViewComponentCache.cachedFields, implementors,
EntityViewComponentCache.implementorsByType, EntityViewComponentCache.cachedTypes);

castedDic.Add(egid.entityID, entityComponent);
}
else
{
DBC.ECS.Check.Require(!castedDic.ContainsKey(egid.entityID),
$"building an entity with already used entity id! id: '{egid.entityID}'");

castedDic.Add(egid.entityID, _initializer);
}
}

ITypeSafeDictionary IComponentBuilder.Preallocate(ref ITypeSafeDictionary dictionary, uint size)
{
return Preallocate(ref dictionary, size);
}

static ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size)
{
if (dictionary == null)
dictionary = TypeSafeDictionaryFactory<T>.Create(size);
else
dictionary.SetCapacity(size);

return dictionary;
}

public Type GetEntityComponentType()
{
return ENTITY_COMPONENT_TYPE;
}

static ComponentBuilder()
{
ENTITY_COMPONENT_TYPE = typeof(T);
DEFAULT_IT = default;
IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString();
var IS_UNMANAGED = ENTITY_COMPONENT_TYPE.IsUnmanagedEx();

if (IS_UNMANAGED)
EntityComponentIDMap.Register<T>(new Filler<T>());
SetEGIDWithoutBoxing<T>.Warmup();
ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT);

if (IS_ENTITY_VIEW_COMPONENT)
EntityViewComponentCache.InitCache();
else
{
if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_INFO_COMPONENT && ENTITY_COMPONENT_TYPE.IsUnmanagedEx() == false)
throw new Exception($"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}");
}
}


readonly T _initializer;

internal static readonly Type ENTITY_COMPONENT_TYPE;
public static readonly bool HAS_EGID;
internal static readonly bool IS_ENTITY_VIEW_COMPONENT;

static readonly T DEFAULT_IT;
static readonly string ENTITY_COMPONENT_NAME;
/// <summary>
/// Note: this static class will hold forever the references of the entities implementors. These references
/// are not even cleared when the engines root is destroyed, as they are static references.
/// It must be seen as an application-wide cache system. Honestly, I am not sure if this may cause leaking
/// issues in some kind of applications. To remember.
/// </summary>
static class EntityViewComponentCache
{
internal static readonly FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> cachedFields;
internal static readonly Dictionary<Type, Type[]> cachedTypes;
#if DEBUG && !PROFILE_SVELTO
internal static readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType;
#else
internal static readonly Dictionary<Type, object> implementorsByType;
#endif
static EntityViewComponentCache()
{
cachedFields = new FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>>();

var type = typeof(T);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
for (var i = fields.Length - 1; i >= 0; --i)
{
var field = fields[i];
if (field.FieldType.IsInterface == true)
{
var setter = FastInvoke<T>.MakeSetter(field);

//for each interface, cache the setter for this type
cachedFields.Add(new KeyValuePair<Type, FastInvokeActionCast<T>>(field.FieldType, setter));
}
}

cachedTypes = new Dictionary<Type, Type[]>();

#if DEBUG && !PROFILE_SVELTO
implementorsByType = new Dictionary<Type, ECSTuple<object, int>>();
#else
implementorsByType = new Dictionary<Type, object>();
#endif
}

internal static void InitCache()
{}
}
}
}

EntityBuilder.cs.meta → ComponentBuilder.cs.meta Целия файл

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: cc541a5ea51e37898f2e56854ab8c4fb
guid: cf16c7aee929396a99cb63c9d8242a91
MonoImporter:
externalObjects: {}
serializedVersion: 2

EntityBuilder.cs.rej.meta → Components.meta Целия файл

@@ -1,5 +1,6 @@
fileFormatVersion: 2
guid: 7f991729576f3f0fa1771f61c9f77c15
guid: 7163f266434d335e810da967a4c4b3ce
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:

+ 7
- 0
Components/EGIDComponent.cs Целия файл

@@ -0,0 +1,7 @@
namespace Svelto.ECS
{
public struct EGIDComponent:IEntityComponent, INeedEGID
{
public EGID ID { get; set; }
}
}

EnginesRoot.DoubleBufferedEntityViews.cs.meta → Components/EGIDComponent.cs.meta Целия файл

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 31949720b3a734b4a94de73465ef307f
guid: 486ed173f6753a56b9f8b9ec44c7bfc3
MonoImporter:
externalObjects: {}
serializedVersion: 2

+ 11
- 0
Components/EntityHierarchyComponent.cs Целия файл

@@ -0,0 +1,11 @@
namespace Svelto.ECS
{
public struct EntityHierarchyComponent: IEntityComponent, INeedEGID
{
public readonly ExclusiveGroupStruct parentGroup;
public EntityHierarchyComponent(ExclusiveGroup group): this() { parentGroup = group; }
public EGID ID { get; set; }
}
}

+ 11
- 0
Components/EntityHierarchyComponent.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b278b4ce4e7f34f0a2434b7de79b7cbb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 7
- 0
Components/LinkedEntityComponent.cs Целия файл

@@ -0,0 +1,7 @@
namespace Svelto.ECS
{
public struct LinkedEntityComponent : IEntityComponent
{
public EGID linkedEntity;
}
}

+ 11
- 0
Components/LinkedEntityComponent.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b2536af786e0381aa68a66f6569358bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 1
- 1
DBC.cs Целия файл

@@ -1,4 +1,4 @@
#if DISABLE_DBC || !DEBUG || PROFILER
#if DISABLE_DBC || !DEBUG || PROFILE_SVELTO
#define DISABLE_CHECKS
using System.Diagnostics;
#endif


+ 293
- 0
DataStructures/FastTypeSafeDictionary.cs Целия файл

@@ -0,0 +1,293 @@
#if EXPERIMENTAL
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;

namespace Svelto.ECS.Internal
{
sealed class FastTypeSafeDictionary<TValue> : ITypeSafeDictionary<TValue> where TValue : struct, IEntityComponent
{
static readonly Type _type = typeof(TValue);
static readonly string _typeName = _type.Name;
static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type);

public FastTypeSafeDictionary(uint size) { _implementation = new SetDictionary<TValue>(size); }

public FastTypeSafeDictionary() { _implementation = new SetDictionary<TValue>(1); }

/// <summary>
/// Add entities from external typeSafeDictionary
/// </summary>
/// <param name="entitiesToSubmit"></param>
/// <param name="groupId"></param>
/// <exception cref="TypeSafeDictionaryException"></exception>
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId)
{
var typeSafeDictionary = entitiesToSubmit as FastTypeSafeDictionary<TValue>;

foreach (var tuple in typeSafeDictionary)
{
try
{
if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref typeSafeDictionary.unsafeValues[tuple.Key],
new EGID(tuple.Key, groupId));

_implementation.Add(tuple.Value);
}
catch (Exception e)
{
throw new
TypeSafeDictionaryException("trying to add an EntityComponent with the same ID more than once Entity: ".
FastConcat(typeof(TValue).ToString()).FastConcat(", group ").
FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key), e);
}
}
}

public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup)
{
var valueIndex = _implementation.GetIndex(fromEntityGid.entityID);

if (toGroup != null)
{
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
ref var entity = ref _implementation.unsafeValues[(int) valueIndex];

if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);

toGroupCasted.Add(fromEntityGid.entityID, entity);
}
}

public void AddEntitiesToEngines(FasterDictionary<RefWrapperType, FasterList<IEngine>> entityComponentEnginesDB,
ITypeSafeDictionary realDic,
ExclusiveGroupStruct @group,
in PlatformProfiler profiler)
{
var typeSafeDictionary = realDic as ITypeSafeDictionary<TValue>;

//this can be optimized, should pass all the entities and not restart the process for each one
foreach (var value in _implementation)
AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary.GetValueByRef(value.Key), null,
in profiler, new EGID(value.Key, group));
}

public void RemoveEntitiesFromEngines(
FasterDictionary<RefWrapperType, FasterList<IEngine>> entityComponentEnginesDB, in PlatformProfiler profiler,
ExclusiveGroupStruct @group)
{
foreach (var value in _implementation)
RemoveEntityComponentFromEngines(entityComponentEnginesDB, ref _implementation.GetValueByRef(value.Key), null,
in profiler, new EGID(value.Key, group));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FastClear() { _implementation.FastClear(); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(uint key) { return _implementation.ContainsKey(key); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntityFromDictionary(EGID fromEntityGid)
{
_implementation.Remove(fromEntityGid.entityID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetCapacity(uint size) { throw new NotImplementedException(); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Trim() { _implementation.Trim(); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() { _implementation.Clear(); }

public void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup,
FasterDictionary<RefWrapperType, FasterList<IEngine>> engines,
in PlatformProfiler profiler)
{
var valueIndex = _implementation.GetIndex(fromEntityGid.entityID);

ref var entity = ref _implementation.unsafeValues[(int) valueIndex];

if (toGroup != null)
{
RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler, fromEntityGid);

var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
var previousGroup = fromEntityGid.groupID;

if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value);

var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);

AddEntityComponentToEngines(engines, ref toGroupCasted.unsafeValues[(int) index], previousGroup, in profiler,
toEntityID.Value);
}
else
RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid);
}

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ITypeSafeDictionary Create() { return new FastTypeSafeDictionary<TValue>(); }

void AddEntityComponentToEngines(FasterDictionary<RefWrapperType, FasterList<IEngine>> entityComponentEnginesDB,
ref TValue entity,
ExclusiveGroupStruct? previousGroup,
in PlatformProfiler profiler,
EGID egid)
{
//get all the engines linked to TValue
if (!entityComponentEnginesDB.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines)) return;

if (previousGroup == null)
{
for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityComponentsEngines[i], _typeName))
{
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity, egid);
}
}
catch (Exception e)
{
throw new
ECSException("Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e);
}
}
else
{
for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityComponentsEngines[i], _typeName))
{
(entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedTo(ref entity, previousGroup.Value,
egid);
}
}
catch (Exception e)
{
throw new
ECSException("Code crashed inside MovedTo callback ".FastConcat(typeof(TValue).ToString()),
e);
}
}
}

static void RemoveEntityComponentFromEngines(FasterDictionary<RefWrapperType, FasterList<IEngine>> @group,
ref TValue entity,
ExclusiveGroupStruct? previousGroup,
in PlatformProfiler profiler,
EGID egid)
{
if (!@group.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines)) return;

if (previousGroup == null)
{
for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityComponentsEngines[i], _typeName))
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid);
}
catch (Exception e)
{
throw new
ECSException("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()),
e);
}
}
#if SEEMS_UNNECESSARY
else
{
for (var i = 0; i < entityComponentsEngines.Count; i++)
try
{
using (profiler.Sample(entityComponentsEngines[i], _typeName))
(entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedFrom(ref entity, egid);
}
catch (Exception e)
{
throw new ECSException(
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e);
}
}
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TValue[] GetValuesArray(out uint count)
{
var managedBuffer = _implementation.GetValuesArray(out count);
return managedBuffer;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(uint egidEntityId) { return _implementation.ContainsKey(egidEntityId); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(uint egidEntityId, in TValue entityComponent) { _implementation.Add(entityComponent); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SetDictionary<TValue>.SetDictionaryKeyValueEnumerator GetEnumerator()
{
return _implementation.GetEnumerator();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref TValue GetValueByRef(uint key) { return ref _implementation.GetValueByRef(key); }

public TValue this[uint idEntityId]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _implementation[idEntityId];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => _implementation[idEntityId] = value;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetIndex(uint valueEntityId) { return _implementation.GetIndex(valueEntityId); }

public TValue[] unsafeValues
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _implementation.unsafeValues;
}

public SetDictionary<TValue> implementation => _implementation;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(uint entityId, out TValue item)
{
return _implementation.TryGetValue(entityId, out item);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref TValue GetOrCreate(uint idEntityId) { throw new NotImplementedException(); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryFindIndex(uint entityId, out uint index)
{
return _implementation.TryFindIndex(entityId, out index);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref TValue GetDirectValue(uint findElementIndex)
{
return ref _implementation.GetDirectValue(findElementIndex);
}

readonly SetDictionary<TValue> _implementation;
}
}
#endif

+ 11
- 0
DataStructures/FastTypeSafeDictionary.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5aab8c4d657d3850b22b7a8d7b0038b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 47
- 0
DataStructures/ITypeSafeDictionary.cs Целия файл

@@ -0,0 +1,47 @@
using System;
using Svelto.Common;
using Svelto.DataStructures;

namespace Svelto.ECS.Internal
{
public interface ITypeSafeDictionary<TValue> : ITypeSafeDictionary where TValue : IEntityComponent
{
void Add(uint egidEntityId, in TValue entityComponent);
ref TValue this[uint idEntityId] { get; }
bool TryGetValue(uint entityId, out TValue item);
ref TValue GetOrCreate(uint idEntityId);

IBuffer<TValue> GetValues(out uint count);
ref TValue GetDirectValueByRef(uint key);
}

public interface ITypeSafeDictionary:IDisposable
{
uint count { get; }
ITypeSafeDictionary Create();

//todo: there is something wrong in the design of the execute callback methods. Something to cleanup
void ExecuteEnginesAddOrSwapCallbacks(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> entityComponentEnginesDb,
ITypeSafeDictionary realDic, ExclusiveGroupStruct? fromGroup, ExclusiveGroupStruct toGroup, in PlatformProfiler profiler);
void ExecuteEnginesSwapOrRemoveCallbacks(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup,
FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, in PlatformProfiler profiler);
void ExecuteEnginesRemoveCallbacks(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> entityComponentEnginesDB,
in PlatformProfiler profiler, ExclusiveGroupStruct @group);
void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId);
void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup);
void RemoveEntityFromDictionary(EGID fromEntityGid);

void SetCapacity(uint size);
void Trim();
void Clear();
void FastClear();
bool Has(uint key);
bool ContainsKey(uint egidEntityId);
uint GetIndex(uint valueEntityId);
bool TryFindIndex(uint entityGidEntityId, out uint index);

void KeysEvaluator(System.Action<uint> action);
}
}

+ 11
- 0
DataStructures/ITypeSafeDictionary.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 186bc7d192ae3700b0cbd86c710b6630
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 0
- 46
DataStructures/SetEGIDWithoutBoxing.cs Целия файл

@@ -1,46 +0,0 @@
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Svelto.ECS.Internal
{
static class SetEGIDWithoutBoxing<T> where T : struct, IEntityStruct
{
internal delegate void ActionCast(ref T target, EGID egid);

public static readonly ActionCast SetIDWithoutBoxing = MakeSetter();

static ActionCast MakeSetter()
{
if (EntityBuilder<T>.HAS_EGID)
{
#if !ENABLE_IL2CPP
Type myTypeA = typeof(T);
PropertyInfo myFieldInfo = myTypeA.GetProperty("ID");

ParameterExpression targetExp = Expression.Parameter(typeof(T).MakeByRefType(), "target");
ParameterExpression valueExp = Expression.Parameter(typeof(EGID), "value");
MemberExpression fieldExp = Expression.Property(targetExp, myFieldInfo);
BinaryExpression assignExp = Expression.Assign(fieldExp, valueExp);

var setter = Expression.Lambda<ActionCast>(assignExp, targetExp, valueExp).Compile();

return setter;
#else
return (ref T target, EGID value) =>
{
var needEgid = (target as INeedEGID);
needEgid.ID = value;
target = (T) needEgid;
};
#endif
}

return null;
}

public static void Warmup()
{
}
}
}

+ 74
- 0
DataStructures/ThreadSafeNativeBagTest.cs Целия файл

@@ -0,0 +1,74 @@
// using System.Threading.Tasks;
// using NUnit.Framework;
// using Svelto.Common;
// using Svelto.ECS.DataStructures;
//
// namespace Svelto.ECS.Tests.Common.DataStructures
// {
// [TestFixture]
// public class ThreadSafeNativeBagTest
// {
// [Test]
// public void TestByteReallocWorks()
// {
// var threadNativeBag = new ThreadSafeNativeBag(Allocator.Persistent);
//
// Parallel.Invoke(() =>
// {
// for (int i = 0; i < 100; i++)
// {
// threadNativeBag.Enqueue((int)1);
// }
// }
// , // close first Action
// () =>
// {
// for (int i = 0; i < 100; i++)
// {
// threadNativeBag.Enqueue((int)2);
// }
// }
// , //close second Action
//
// () =>
// {
// for (int i = 0; i < 100; i++)
// {
// threadNativeBag.Enqueue(3);
// }
// } //close third Action
// ); //close parallel.invoke
//
// // for (int i = 0; i < 100; i++)
// // {
// // threadNativeBag.Enqueue(1);
// // }
//
// int oneCount = 0, twoCount = 0, threeCount = 0;
//
// while (threadNativeBag.count > 0)
// {
// var value = threadNativeBag.Dequeue<int>();
//
// switch (value)
// {
// case 1:
// oneCount++;
// break;
// case 2:
// twoCount++;
// break;
// case 3:
// threeCount++;
// break;
// }
// }
//
// Assert.That(oneCount, Is.EqualTo(100));
// Assert.That(twoCount, Is.EqualTo(100));
// Assert.That(threeCount, Is.EqualTo(100));
//
// threadNativeBag.Dispose();
// }
// }
// }

+ 11
- 0
DataStructures/ThreadSafeNativeBagTest.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6783b8d49c5935fd8f863f6d78e003ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 444
- 130
DataStructures/TypeSafeDictionary.cs Целия файл

@@ -1,222 +1,536 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Hybrid;

namespace Svelto.ECS.Internal
{
public interface ITypeSafeDictionary
sealed class TypeSafeDictionary<TValue> : ITypeSafeDictionary<TValue> where TValue : struct, IEntityComponent
{
int Count { get; }
ITypeSafeDictionary Create();
static readonly Type _type = typeof(TValue);
static readonly string _typeName = _type.Name;
static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type);

void AddEntitiesToEngines(
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDb,
ITypeSafeDictionary realDic, in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group);
internal static readonly bool IsUnmanaged =
_type.IsUnmanagedEx() && (typeof(IEntityViewComponent).IsAssignableFrom(_type) == false);

void RemoveEntitiesFromEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB,
in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group);
SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, ManagedStrategy<TValue>,
ManagedStrategy<int>> implMgd;

void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId);
//used directly by native methods
internal SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<TValue>,
NativeStrategy<int>> implUnmgd;

void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup,
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, in PlatformProfiler profiler);
public TypeSafeDictionary(uint size)
{
if (IsUnmanaged)
implUnmgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>,
NativeStrategy<TValue>, NativeStrategy<int>>(size);
else
{
implMgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>,
ManagedStrategy<TValue>, ManagedStrategy<int>>(size);
}
}

void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup);
void RemoveEntityFromDictionary(EGID fromEntityGid, in PlatformProfiler profiler);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(uint egidEntityId, in TValue entityComponent)
{
if (IsUnmanaged)
implUnmgd.Add(egidEntityId, entityComponent);
else
implMgd.Add(egidEntityId, entityComponent);
}

void SetCapacity(uint size);
void Trim();
void Clear();
void FastClear();
bool Has(uint entityIdEntityId);
}
/// <summary>
/// Add entities from external typeSafeDictionary
/// </summary>
/// <param name="entitiesToSubmit"></param>
/// <param name="groupId"></param>
/// <exception cref="TypeSafeDictionaryException"></exception>
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId)
{
if (IsUnmanaged)
{
var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary<TValue>).implUnmgd;

class TypeSafeDictionary<TValue> : FasterDictionary<uint, TValue>,
ITypeSafeDictionary where TValue : struct, IEntityStruct
{
static readonly Type _type = typeof(TValue);
static readonly string _typeName = _type.Name;
static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type);
foreach (var tuple in typeSafeDictionary)
try
{
if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(
ref tuple.Value, new EGID(tuple.Key, groupId));

public TypeSafeDictionary(uint size) : base(size) {}
public TypeSafeDictionary() {}
implUnmgd.Add(tuple.Key, tuple.Value);
}
catch (Exception e)
{
Console.LogException(
e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key));

public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId)
throw;
}
}
else
{
var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary<TValue>).implMgd;

foreach (var tuple in typeSafeDictionary)
try
{
if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(
ref tuple.Value, new EGID(tuple.Key, groupId));

implMgd.Add(tuple.Key, tuple.Value);
}
catch (Exception e)
{
Console.LogException(
e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key));

throw;
}
}
}

public void ExecuteEnginesAddOrSwapCallbacks
(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> entityComponentEnginesDB
, ITypeSafeDictionary realDic, ExclusiveGroupStruct? fromGroup, ExclusiveGroupStruct toGroup
, in PlatformProfiler profiler)
{
var typeSafeDictionary = entitiesToSubmit as TypeSafeDictionary<TValue>;
if (IsUnmanaged)
{
var typeSafeDictionary = realDic as ITypeSafeDictionary<TValue>;

foreach (var tuple in typeSafeDictionary)
//this can be optimized, should pass all the entities and not restart the process for each one
foreach (var value in implUnmgd)
ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(entityComponentEnginesDB, ref typeSafeDictionary[value.Key]
, fromGroup, in profiler, new EGID(value.Key, toGroup));
}
else
{
try
var typeSafeDictionary = realDic as ITypeSafeDictionary<TValue>;

//this can be optimized, should pass all the entities and not restart the process for each one
foreach (var value in implMgd)
ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(entityComponentEnginesDB, ref typeSafeDictionary[value.Key]
, fromGroup, in profiler, new EGID(value.Key, toGroup));
}
}

public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup)
{
if (IsUnmanaged)
{
var valueIndex = implUnmgd.GetIndex(fromEntityGid.entityID);

DBC.ECS.Check.Require(toGroup != null
, "Invalid To Group"); //todo check this, if it's right merge GetIndex
{
if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref tuple.Value, new EGID(tuple.Key, groupId));
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
ref var entity = ref implUnmgd.GetDirectValueByRef(valueIndex);

Add(tuple.Key, tuple.Value);
if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);

toGroupCasted.Add(toEntityID.entityID, entity);
}
catch (Exception e)
}
else
{
var valueIndex = implMgd.GetIndex(fromEntityGid.entityID);

DBC.ECS.Check.Require(toGroup != null
, "Invalid To Group"); //todo check this, if it's right merge GetIndex
{
throw new TypeSafeDictionaryException(
"trying to add an EntityView with the same ID more than once Entity: "
.FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key), e);
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
ref var entity = ref implMgd.GetDirectValueByRef(valueIndex);

if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);

toGroupCasted.Add(toEntityID.entityID, entity);
}
}
}

public void AddEntitiesToEngines(
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB,
ITypeSafeDictionary realDic, in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
var typeSafeDictionary = realDic as TypeSafeDictionary<TValue>;

//this can be optimized, should pass all the entities and not restart the process for each one
foreach (var value in this)
AddEntityViewToEngines(entityViewEnginesDB, ref typeSafeDictionary.GetValueByRef(value.Key), null,
in profiler, new EGID(value.Key, group));
if (IsUnmanaged)
{
implUnmgd.Clear();
}
else
{
implMgd.Clear();
}
}

public void RemoveEntitiesFromEngines(
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB,
in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FastClear()
{
foreach (var value in this)
RemoveEntityViewFromEngines(entityViewEnginesDB, ref GetValueByRef(value.Key), null, in profiler,
new EGID(value.Key, group));
if (IsUnmanaged)
{
implUnmgd.FastClear();
}
else
{
implMgd.FastClear();
}
}

public bool Has(uint entityIdEntityId)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(uint egidEntityId)
{
return ContainsKey(entityIdEntityId);
if (IsUnmanaged)
{
return implUnmgd.ContainsKey(egidEntityId);
}
else
{
return implMgd.ContainsKey(egidEntityId);
}
}

public void RemoveEntityFromDictionary(EGID fromEntityGid, in PlatformProfiler profiler)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ITypeSafeDictionary Create() { return TypeSafeDictionaryFactory<TValue>.Create(1); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetIndex(uint valueEntityId)
{
Remove(fromEntityGid.entityID);
if (IsUnmanaged)
{
return this.implUnmgd.GetIndex(valueEntityId);
}
else
{
return this.implMgd.GetIndex(valueEntityId);
}
}

public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref TValue GetOrCreate(uint idEntityId)
{
var valueIndex = GetIndex(fromEntityGid.entityID);
if (IsUnmanaged)
{
return ref this.implUnmgd.GetOrCreate(idEntityId);
}
else
{
return ref this.implMgd.GetOrCreate(idEntityId);
}
}

if (toGroup != null)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IBuffer<TValue> GetValues(out uint count)
{
if (IsUnmanaged)
{
var toGroupCasted = toGroup as TypeSafeDictionary<TValue>;
ref var entity = ref valuesArray[valueIndex];
return this.implUnmgd.GetValues(out count);
}
else
{
return this.implMgd.GetValues(out count);
}
}

if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref TValue GetDirectValueByRef(uint key)
{
if (IsUnmanaged)
{
return ref this.implUnmgd.GetDirectValueByRef(key);
}
else
{
return ref this.implMgd.GetDirectValueByRef(key);
}
}

toGroupCasted.Add(fromEntityGid.entityID, entity);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(uint key)
{
if (IsUnmanaged)
{
return this.implUnmgd.ContainsKey(key);
}
else
{
return this.implMgd.ContainsKey(key);
}
}

public void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup,
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, in PlatformProfiler profiler)
public void ExecuteEnginesSwapOrRemoveCallbacks
(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup
, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, in PlatformProfiler profiler)
{
var valueIndex = GetIndex(fromEntityGid.entityID);
ref var entity = ref valuesArray[valueIndex];
if (IsUnmanaged)
{
var valueIndex = this.implUnmgd.GetIndex(fromEntityGid.entityID);

if (toGroup != null)
ref var entity = ref this.implUnmgd.GetDirectValueByRef(valueIndex);

//move
if (toGroup != null)
{
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
var previousGroup = fromEntityGid.groupID;

if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value);

var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);

ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(engines, ref toGroupCasted.GetDirectValueByRef(index)
, previousGroup, in profiler, toEntityID.Value);
}
//remove
else
{
ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref entity, in profiler, fromEntityGid);
}
}
else
{
RemoveEntityViewFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler,
fromEntityGid);
var valueIndex = this.implMgd.GetIndex(fromEntityGid.entityID);

ref var entity = ref this.implMgd.GetDirectValueByRef(valueIndex);

var toGroupCasted = toGroup as TypeSafeDictionary<TValue>;
var previousGroup = fromEntityGid.groupID;
if (toGroup != null)
{
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
var previousGroup = fromEntityGid.groupID;

if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value);
if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value);

var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);
var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);

AddEntityViewToEngines(engines, ref toGroupCasted.valuesArray[index], previousGroup,
in profiler, toEntityID.Value);
ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(engines, ref toGroupCasted.GetDirectValueByRef(index)
, previousGroup, in profiler, toEntityID.Value);
}
else
{
ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref entity, in profiler, fromEntityGid);
}
}
}

public void ExecuteEnginesRemoveCallbacks
(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, in PlatformProfiler profiler
, ExclusiveGroupStruct group)
{
if (IsUnmanaged)
{
foreach (var value in implUnmgd)
ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref implUnmgd.GetValueByRef(value.Key)
, in profiler, new EGID(value.Key, group));
}
else
RemoveEntityViewFromEngines(engines, ref entity, null, in profiler, fromEntityGid);
{
foreach (var value in implMgd)
ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref implMgd.GetValueByRef(value.Key)
, in profiler, new EGID(value.Key, group));
}
}

public ITypeSafeDictionary Create()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntityFromDictionary(EGID fromEntityGid)
{
return new TypeSafeDictionary<TValue>();
if (IsUnmanaged)
{
this.implUnmgd.Remove(fromEntityGid.entityID);
}
else
{
this.implMgd.Remove(fromEntityGid.entityID);
}
}

void AddEntityViewToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB,
ref TValue entity, ExclusiveGroup.ExclusiveGroupStruct? previousGroup,
in PlatformProfiler profiler, EGID egid)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetCapacity(uint size)
{
//get all the engines linked to TValue
if (!entityViewEnginesDB.TryGetValue(new RefWrapper<Type>(_type), out var entityViewsEngines)) return;
if (IsUnmanaged)
{
this.implUnmgd.SetCapacity(size);
}
else
{
this.implMgd.SetCapacity(size);
}
}

if (previousGroup == null)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Trim()
{
if (IsUnmanaged)
{
for (var i = 0; i < entityViewsEngines.Count; i++)
try
{
using (profiler.Sample(entityViewsEngines[i], _typeName))
{
(entityViewsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity, egid);
}
}
catch (Exception e)
{
throw new ECSException(
"Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e);
}
this.implUnmgd.Trim();
}
else
{
for (var i = 0; i < entityViewsEngines.Count; i++)
try
{
using (profiler.Sample(entityViewsEngines[i], _typeName))
{
(entityViewsEngines[i] as IReactOnSwap<TValue>).MovedTo(ref entity, previousGroup.Value,
egid);
}
}
catch (Exception e)
this.implMgd.Trim();
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryFindIndex(uint entityId, out uint index)
{
if (IsUnmanaged)
{
return implUnmgd.TryFindIndex(entityId, out index);
}
else
{
return implMgd.TryFindIndex(entityId, out index);
}
}

public void KeysEvaluator(Action<uint> action)
{
if (IsUnmanaged)
{
foreach (var key in implUnmgd.keys)
{
action(key);
}
}
else
{
foreach (var key in implMgd.keys)
{
action(key);
}
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(uint entityId, out TValue item)
{
if (IsUnmanaged)
{
return this.implUnmgd.TryGetValue(entityId, out item);
}
else
{
return this.implMgd.TryGetValue(entityId, out item);
}
}

public uint count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (IsUnmanaged)
{
return (uint) this.implUnmgd.count;
}
else
{
return (uint) this.implMgd.count;
}
}
}

public ref TValue this[uint idEntityId]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (IsUnmanaged)
{
return ref this.implUnmgd.GetValueByRef(idEntityId);
}
else
{
return ref this.implMgd.GetValueByRef(idEntityId);
}
}
}

static void ExecuteEnginesRemoveCallbackOnSingleEntity
(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, ref TValue entity
, in PlatformProfiler profiler, EGID egid)
{
if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines))
return;

for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityComponentsEngines[i], _typeName))
{
throw new ECSException(
"Code crashed inside MovedTo callback ".FastConcat(typeof(TValue).ToString()), e);
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid);
}
}
}
catch
{
Svelto.Console.LogError(
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()));

throw;
}
}

static void RemoveEntityViewFromEngines(
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> @group, ref TValue entity,
ExclusiveGroup.ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid)
void ExecuteEnginesAddOrSwapCallbacksOnSingleEntity
(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, ref TValue entity
, ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid)
{
if (!@group.TryGetValue(new RefWrapper<Type>(_type), out var entityViewsEngines)) return;
//get all the engines linked to TValue
if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines))
return;

if (previousGroup == null)
{
for (var i = 0; i < entityViewsEngines.Count; i++)
for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityViewsEngines[i], _typeName))
(entityViewsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid);
using (profiler.Sample(entityComponentsEngines[i], _typeName))
{
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity, egid);
}
}
catch (Exception e)
catch
{
throw new ECSException(
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e);
Svelto.Console.LogError(
"Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()));

throw;
}
}
#if SEEMS_UNNECESSARY
else
{
for (var i = 0; i < entityViewsEngines.Count; i++)
for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityViewsEngines[i], _typeName))
(entityViewsEngines[i] as IReactOnSwap<TValue>).MovedFrom(ref entity, egid);
using (profiler.Sample(entityComponentsEngines[i], _typeName))
{
(entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedTo(
ref entity, previousGroup.Value, egid);
}
}
catch (Exception e)
catch (Exception)
{
throw new ECSException(
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e);
Svelto.Console.LogError(
"Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString()));

throw;
}
}
#endif
}

public void Dispose()
{
if (IsUnmanaged)
implUnmgd.Dispose();
else
implMgd.Dispose();

GC.SuppressFinalize(this);
}
}
}

+ 13
- 0
DataStructures/TypeSafeDictionaryUtilities.cs Целия файл

@@ -0,0 +1,13 @@
namespace Svelto.ECS.Internal
{
static class TypeSafeDictionaryUtilities
{
internal static EGIDMapper<T> ToEGIDMapper<T>(this ITypeSafeDictionary<T> dic,
ExclusiveGroupStruct groupStructId) where T:struct, IEntityComponent
{
var mapper = new EGIDMapper<T>(groupStructId, dic);

return mapper;
}
}
}

+ 11
- 0
DataStructures/TypeSafeDictionaryUtilities.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a2196c9108d435e6ac91b30c0aed8644
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 8
- 0
DataStructures/Unmanaged.meta Целия файл

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c887c28f847537e58b00adf544344895
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

+ 76
- 0
DataStructures/Unmanaged/AtomicNativeBags.cs Целия файл

@@ -0,0 +1,76 @@
#if UNITY_NATIVE //because of the thread count, ATM this is only for unity
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Unity.Jobs.LowLevel.Unsafe;
using Allocator = Svelto.Common.Allocator;

namespace Svelto.ECS.DataStructures
{
public unsafe struct AtomicNativeBags:IDisposable
{
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
NativeBag* _data;
readonly Allocator _allocator;
readonly uint _threadsCount;

public uint count => _threadsCount;

public AtomicNativeBags(Allocator allocator)
{
_allocator = allocator;
_threadsCount = JobsUtility.MaxJobThreadCount + 1;

var bufferSize = MemoryUtilities.SizeOf<NativeBag>();
var bufferCount = _threadsCount;
var allocationSize = bufferSize * bufferCount;

var ptr = (byte*)MemoryUtilities.Alloc((uint) allocationSize, allocator);
// MemoryUtilities.MemClear((IntPtr) ptr, (uint) allocationSize);

for (int i = 0; i < bufferCount; i++)
{
var bufferPtr = (NativeBag*)(ptr + bufferSize * i);
var buffer = new NativeBag(allocator);
MemoryUtilities.CopyStructureToPtr(ref buffer, (IntPtr) bufferPtr);
}

_data = (NativeBag*)ptr;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref NativeBag GetBuffer(int index)
{
if (_data == null)
throw new Exception("using invalid AtomicNativeBags");
return ref MemoryUtilities.ArrayElementAsRef<NativeBag>((IntPtr) _data, index);
}

public void Dispose()
{
if (_data == null)
throw new Exception("using invalid AtomicNativeBags");
for (int i = 0; i < _threadsCount; i++)
{
GetBuffer(i).Dispose();
}
MemoryUtilities.Free((IntPtr) _data, _allocator);
_data = null;
}

public void Clear()
{
if (_data == null)
throw new Exception("using invalid AtomicNativeBags");
for (int i = 0; i < _threadsCount; i++)
{
GetBuffer(i).Clear();
}
}
}
}
#endif

+ 11
- 0
DataStructures/Unmanaged/AtomicNativeBags.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9903f36f7416334c99d89982bf84cf2a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 281
- 0
DataStructures/Unmanaged/NativeBag.cs Целия файл

@@ -0,0 +1,281 @@
#if DEBUG && !PROFILE_SVELTO
#define ENABLE_DEBUG_CHECKS
#endif

#if DEBUG && !PROFILE_SVELTO
//#define ENABLE_THREAD_SAFE_CHECKS
#endif

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using Svelto.Common;

namespace Svelto.ECS.DataStructures
{
/// <summary>
/// Burst friendly RingBuffer on steroid:
/// it can: Enqueue/Dequeue, it wraps if there is enough space after dequeuing
/// It resizes if there isn't enough space left.
/// It's a "bag", you can queue and dequeue any T. Just be sure that you dequeue what you queue! No check on type
/// is done.
/// You can reserve a position in the queue to update it later.
/// The datastructure is a struct and it's "copyable"
/// I eventually decided to call it NativeBag and not NativeBag because it can also be used as
/// a preallocated memory pool where any kind of T can be stored as long as T is unmanaged
/// </summary>
public struct NativeBag : IDisposable
{
public uint count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
unsafe
{
BasicTests();
#if ENABLE_THREAD_SAFE_CHECKS
try
{
#endif
return _queue->size;
#if ENABLE_THREAD_SAFE_CHECKS
}
finally
{
Volatile.Write(ref _threadSentinel, 0);
}
#endif
}
}
}

public uint capacity
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
unsafe
{
BasicTests();
#if ENABLE_THREAD_SAFE_CHECKS
try
{
#endif
return _queue->capacity;
#if ENABLE_THREAD_SAFE_CHECKS
}
finally
{
Volatile.Write(ref _threadSentinel, 0);
}
#endif
}
}
}

public NativeBag(Allocator allocator)
{
unsafe
{
var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>();
var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator);

//clear to nullify the pointers
//MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf);
listData->allocator = allocator;
_queue = listData;
#if ENABLE_THREAD_SAFE_CHECKS
_threadSentinel = 0;
#endif
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsEmpty()
{
unsafe
{
BasicTests();
#if ENABLE_THREAD_SAFE_CHECKS
try
{
#endif
if (_queue == null || _queue->ptr == null)
return true;
#if ENABLE_THREAD_SAFE_CHECKS
}
finally
{
Volatile.Write(ref _threadSentinel, 0);
}
#endif
}

return count == 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void Dispose()
{
if (_queue != null)
{
#if ENABLE_THREAD_SAFE_CHECKS
//todo: this must be unit tested
if (Interlocked.CompareExchange(ref _threadSentinel, 1, 0) != 0)
throw new Exception("NativeBag is not thread safe, reading and writing operations can happen" +
"on different threads, but not simultaneously");

try
{
#endif
_queue->Dispose();
MemoryUtilities.Free((IntPtr) _queue, _queue->allocator);
_queue = null;
#if ENABLE_THREAD_SAFE_CHECKS
}
finally
{
Volatile.Write(ref _threadSentinel, 0);
}
#endif
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T ReserveEnqueue<T>(out UnsafeArrayIndex index) where T : struct
{
unsafe
{
BasicTests();

var sizeOf = MemoryUtilities.SizeOf<T>();
if (_queue->space - sizeOf < 0)
_queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f));

#if ENABLE_THREAD_SAFE_CHECKS
try
{
#endif

return ref _queue->Reserve<T>(out index);
#if ENABLE_THREAD_SAFE_CHECKS
}
finally
{
Volatile.Write(ref _threadSentinel, 0);
}
#endif
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enqueue<T>(in T item) where T : struct
{
unsafe
{
BasicTests();

#if ENABLE_THREAD_SAFE_CHECKS
try
{
#endif
var sizeOf = MemoryUtilities.SizeOf<T>();
if (_queue->space - sizeOf < 0)
_queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f));

_queue->Write(item);
#if ENABLE_THREAD_SAFE_CHECKS
}
finally
{
Volatile.Write(ref _threadSentinel, 0);
}
#endif
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
unsafe
{
BasicTests();
#if ENABLE_THREAD_SAFE_CHECKS
try
{
#endif
_queue->Clear();
#if ENABLE_THREAD_SAFE_CHECKS
}
finally
{
Volatile.Write(ref _threadSentinel, 0);
}
#endif
}
}

public T Dequeue<T>() where T : struct
{
unsafe
{
BasicTests();
#if ENABLE_THREAD_SAFE_CHECKS
try
{
#endif
return _queue->Read<T>();
#if ENABLE_THREAD_SAFE_CHECKS
}
finally
{
Volatile.Write(ref _threadSentinel, 0);
}
#endif
}
}

internal ref T AccessReserved<T>(UnsafeArrayIndex reserverIndex) where T : struct
{
unsafe
{
BasicTests();
#if ENABLE_THREAD_SAFE_CHECKS
try
{
#endif
return ref _queue->AccessReserved<T>(reserverIndex);
#if ENABLE_THREAD_SAFE_CHECKS
}
finally
{
Volatile.Write(ref _threadSentinel, 0);
}
#endif
}
}

[Conditional("ENABLE_DEBUG_CHECKS")]
unsafe void BasicTests()
{
if (_queue == null)
throw new Exception("SimpleNativeArray: null-access");
#if ENABLE_THREAD_SAFE_CHECKS
todo: this must be unit tested
if (Interlocked.CompareExchange(ref _threadSentinel, 1, 0) != 0)
throw new Exception("NativeBag is not thread safe, reading and writing operations can happen"
+ "on different threads, but not simultaneously");
#endif
}

#if ENABLE_THREAD_SAFE_CHECKS
int _threadSentinel;
#endif
#if UNITY_NATIVE
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe UnsafeBlob* _queue;
}
}

+ 11
- 0
DataStructures/Unmanaged/NativeBag.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5775773e479e3b1db95b31e996594fb8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 370
- 0
DataStructures/Unmanaged/NativeDynamicArray.cs Целия файл

@@ -0,0 +1,370 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Allocator = Svelto.Common.Allocator;

namespace Svelto.ECS.DataStructures
{
public struct NativeDynamicArray : IDisposable
{
public bool isValid
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
unsafe
{
return _list != null;
}
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Count<T>() where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception($"NativeDynamicArray: not expected type used");

#endif
return (_list->count / MemoryUtilities.SizeOf<T>());
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Capacity<T>() where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");

#endif
return (_list->capacity / MemoryUtilities.SizeOf<T>());
}
}

public static NativeDynamicArray Alloc<T>(Allocator allocator, uint newLength = 0) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
var rtnStruc = new NativeDynamicArray {_hashType = TypeHash<T>.hash};
#else
NativeDynamicArray rtnStruc = default;
#endif
var sizeOf = MemoryUtilities.SizeOf<T>();

uint structSize = (uint) MemoryUtilities.SizeOf<UnsafeArray>();
UnsafeArray* listData = (UnsafeArray*) MemoryUtilities.Alloc(structSize, allocator);

//clear to nullify the pointers
//MemoryUtilities.MemClear((IntPtr) listData, structSize);

rtnStruc._allocator = allocator;
listData->Realloc((uint) (newLength * sizeOf), allocator);

rtnStruc._list = listData;

return rtnStruc;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Get<T>(uint index) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
if (index >= Count<T>())
throw new Exception($"NativeDynamicArray: out of bound access, index {index} count {Count<T>()}");
#endif
return ref _list->Get<T>(index);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set<T>(uint index, in T value) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
if (index >= Capacity<T>())
throw new Exception($"NativeDynamicArray: out of bound access, index {index} capacity {Capacity<T>()}");
#endif
_list->Set(index, value);
}
}

public unsafe void Dispose()
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
#endif
_list->Dispose(_allocator);
_list = null;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add<T>(in T item) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
#endif
var structSize = (uint) MemoryUtilities.SizeOf<T>();

if (_list->space - (int) structSize < 0)
_list->Realloc((uint) (((uint) ((Count<T>() + 1) * 1.5f) * (float) structSize)), _allocator);

_list->Add(item);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T AddAt<T>(uint index) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
#endif
var structSize = (uint) MemoryUtilities.SizeOf<T>();

if (index >= Capacity<T>())
_list->Realloc((uint) (((index + 1) * 1.5f) * structSize), _allocator);

var writeIndex = (index + 1) * structSize;
if (_list->count < writeIndex)
_list->SetCountTo(writeIndex);

return ref _list->Get<T>(index);
}
}
public void Grow<T>(uint newCapacity) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
if (newCapacity <= Capacity<T>())
throw new Exception("New capacity must be greater than current one");
#endif
uint structSize = (uint) MemoryUtilities.SizeOf<T>();

uint size = (uint) (newCapacity * structSize);
_list->Realloc((uint) size, _allocator);
}
}

public void SetCount<T>(uint count) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
#endif
uint structSize = (uint) MemoryUtilities.SizeOf<T>();
uint size = (uint) (count * structSize);

_list->SetCountTo((uint) size);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddWithoutGrow<T>(in T item) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");

var structSize = (uint) MemoryUtilities.SizeOf<T>();
if (_list->space - (int)structSize < 0)
throw new Exception("NativeDynamicArray: no writing authorized");
#endif
_list->Add(item);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void UnorderedRemoveAt<T>(uint index) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
if (Count<T>() == 0)
throw new Exception("NativeDynamicArray: empty array invalid operation");
#endif
var indexToMove = Count<T>() - 1;
if (index < indexToMove)
{
Set<T>(index, Get<T>((uint) indexToMove));
}

_list->Pop<T>();
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FastClear()
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
#endif
_list->Clear();
}
}

public unsafe T* ToPTR<T>() where T : unmanaged
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");

#endif
return (T*) _list->ptr;
}

public IntPtr ToIntPTR<T>() where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");

#endif
return (IntPtr) _list->ptr;
}
}

public T[] ToManagedArray<T>() where T : unmanaged
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");

#endif
var count = Count<T>();
var ret = new T[count];
var lengthToCopyInBytes = count * MemoryUtilities.SizeOf<T>();

fixed (void* handle = ret)
{
Unsafe.CopyBlock(handle, _list->ptr, (uint) lengthToCopyInBytes);
}

return ret;
}
}

public T[] ToManagedArrayUntrimmed<T>() where T : unmanaged
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
#endif
var capacity = Capacity<T>();
var lengthToCopyInBytes = capacity * MemoryUtilities.SizeOf<T>();
var ret = new T[capacity];

fixed (void* handle = ret)
{
Unsafe.CopyBlock(handle, _list->ptr, (uint) lengthToCopyInBytes);
}

return ret;
}
}

public void RemoveAt<T>(uint index) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
#endif

var sizeOf = MemoryUtilities.SizeOf<T>();
//Unsafe.CopyBlock may not be memory overlapping safe (memcpy vs memmove)
Buffer.MemoryCopy(_list->ptr + (index + 1) * sizeOf, _list->ptr + index * sizeOf, _list->count
, (uint) ((Count<T>() - (index + 1)) * sizeOf));
_list->Pop<T>();
}
}

public void MemClear()
{
unsafe
{
MemoryUtilities.MemClear((IntPtr) _list->ptr, (uint) _list->capacity);
}
}
#if UNITY_NATIVE
[global::Unity.Burst.NoAlias] [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe UnsafeArray* _list;
#if DEBUG && !PROFILE_SVELTO
int _hashType;
#endif
Allocator _allocator;
}
}

+ 11
- 0
DataStructures/Unmanaged/NativeDynamicArray.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 711d4a28132031fca4870da7c8dae575
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 48
- 0
DataStructures/Unmanaged/NativeDynamicArrayCast.cs Целия файл

@@ -0,0 +1,48 @@
using System.Runtime.CompilerServices;

namespace Svelto.ECS.DataStructures
{
public struct NativeDynamicArrayCast<T> where T : struct
{
public NativeDynamicArrayCast(NativeDynamicArray array) : this() { _array = array; }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Count() => _array.Count<T>();

public int count => _array.Count<T>();

public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _array.Get<T>((uint) index);
}

public ref T this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _array.Get<T>(index);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(in T id) { _array.Add(id); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void UnorderedRemoveAt(uint index) { _array.UnorderedRemoveAt<T>(index); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(uint index) { _array.RemoveAt<T>(index); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() { _array.FastClear(); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose() { _array.Dispose(); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T AddAt(uint lastIndex) { return ref _array.AddAt<T>(lastIndex); }

public bool isValid => _array.isValid;

NativeDynamicArray _array;
}
}

+ 11
- 0
DataStructures/Unmanaged/NativeDynamicArrayCast.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 60ca1e40bef23e8db37576d0f3dbc631
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 23
- 0
DataStructures/Unmanaged/NativeDynamicArrayUnityExtension.cs Целия файл

@@ -0,0 +1,23 @@
#if UNITY_NATIVE
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;

namespace Svelto.ECS.DataStructures
{
public static class NativeDynamicArrayUnityExtension
{
public static NativeArray<T> ToNativeArray<T>(this NativeDynamicArray array) where T : struct
{
unsafe
{
var nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>(
(void*) array.ToIntPTR<T>(), (int) array.Count<T>(), Allocator.None);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, AtomicSafetyHandle.Create());
#endif
return nativeArray;
}
}
}
}
#endif

+ 11
- 0
DataStructures/Unmanaged/NativeDynamicArrayUnityExtension.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 668917ff718433479e84c1f270e88377
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 115
- 0
DataStructures/Unmanaged/SharedNativeInt.cs Целия файл

@@ -0,0 +1,115 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Svelto.Common;

namespace Svelto.ECS.DataStructures
{
public struct SharedNativeInt: IDisposable
{
#if UNITY_NATIVE
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe int* data;

Allocator _allocator;

public SharedNativeInt(Allocator allocator)
{
unsafe
{
_allocator = allocator;
data = (int*) MemoryUtilities.Alloc(sizeof(int), allocator);
}
}

public static SharedNativeInt Create(int t, Allocator allocator)
{
unsafe
{
var current = new SharedNativeInt();
current._allocator = allocator;
current.data = (int*) MemoryUtilities.Alloc(sizeof(int), allocator);
*current.data = t;

return current;
}
}
public static implicit operator int(SharedNativeInt t)
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (t.data == null)
throw new Exception("using disposed SharedInt");
#endif
return *t.data;
}
}

public void Dispose()
{
unsafe
{
if (data != null)
{
MemoryUtilities.Free((IntPtr) data, _allocator);
data = null;
}
}
}

public int Decrement()
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (data == null)
throw new Exception("null-access");
#endif
return Interlocked.Decrement(ref *data);
}
}
public int Increment()
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (data == null)
throw new Exception("null-access");
#endif
return Interlocked.Increment(ref *data);
}
}
public int Add(int val)
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (data == null)
throw new Exception("null-access");
#endif
return Interlocked.Add(ref *data, val);
}
}
public void Set(int val)
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (data == null)
throw new Exception("null-access");
#endif
Volatile.Write(ref *data, val);
}
}
}
}

+ 11
- 0
DataStructures/Unmanaged/SharedNativeInt.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 109ecc8b65fa3a55a287c25fe70ef472
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 194
- 0
DataStructures/Unmanaged/ThreadSafeNativeBag.cs Целия файл

@@ -0,0 +1,194 @@
#if later
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using Svelto.Common;
using Svelto.Utilities;

namespace Svelto.ECS.DataStructures
{
/// <summary>
/// Burst friendly Ring Buffer on steroid:
/// it can: Enqueue/Dequeue, it wraps if there is enough space after dequeuing
/// It resizes if there isn't enough space left.
/// It's a "bag", you can queue and dequeue any T. Just be sure that you dequeue what you queue! No check on type
/// is done.
/// You can reserve a position in the queue to update it later.
/// The datastructure is a struct and it's "copyable"
/// I eventually decided to call it NativeBag and not NativeBag because it can also be used as
/// a preallocated memory pool where any kind of T can be stored as long as T is unmanaged
/// </summary>
public struct ThreadSafeNativeBag : IDisposable
{
public uint count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_queue == null)
throw new Exception("SimpleNativeArray: null-access");
#endif

return _queue->size;
}
}
}

public uint capacity
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_queue == null)
throw new Exception("SimpleNativeArray: null-access");
#endif

return _queue->capacity;
}
}
}

public ThreadSafeNativeBag(Allocator allocator)
{
unsafe
{
var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>();
var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator);

//clear to nullify the pointers
//MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf);
listData->allocator = allocator;
_queue = listData;
}

_writingGuard = 0;
}
public ThreadSafeNativeBag(Allocator allocator, uint capacity)
{
unsafe
{
var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>();
var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator);

//clear to nullify the pointers
//MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf);
listData->allocator = allocator;
_queue = listData;
_queue->Realloc(capacity);
}

_writingGuard = 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsEmpty()
{
unsafe
{
if (_queue == null || _queue->ptr == null)
return true;
}

return count == 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void Dispose()
{
if (_queue != null)
{
_queue->Dispose();
MemoryUtilities.Free((IntPtr) _queue, _queue->allocator);
_queue = null;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enqueue<T>(in T item) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_queue == null)
throw new Exception("SimpleNativeArray: null-access");
#endif
var sizeOf = MemoryUtilities.SizeOf<T>();
var alignedSize = (uint) MemoryUtilities.SizeOfAligned<T>();

Interlocked.MemoryBarrier();
Reset:
var oldCapacity = _queue->capacity;
var spaceleft = oldCapacity - (_queue->_writeIndex - _queue->_readIndex) - sizeOf;

while (spaceleft < 0)
{
//if _writingGuard is not equal to 0, it means that another thread increased the
//value so it's possible the reallocing is already happening OR it means that
//writing are still in progress and we must be sure that are all flushed first
if (Interlocked.CompareExchange(ref _writingGuard, 1, 0) != 0)
{
ThreadUtility.Yield();
goto Reset;
}
var newCapacity = (uint) ((oldCapacity + alignedSize) * 2.0f);
Svelto.Console.Log($"realloc {newCapacity}");
_queue->Realloc(newCapacity);
Volatile.Write(ref _writingGuard, 0);
}
int writeIndex;
//look for the first available slot to write in
writeIndex = _queue->_writeIndex;
if (Interlocked.CompareExchange(ref _queue->_writeIndex, (int) (writeIndex + alignedSize)
, writeIndex) != writeIndex)
{
ThreadUtility.Yield();
goto Reset;
}

Interlocked.Increment(ref _writingGuard);
_queue->Write(item, (uint) writeIndex);
Interlocked.Decrement(ref _writingGuard);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_queue == null)
throw new Exception("SimpleNativeArray: null-access");
#endif
_queue->Clear();
}
}

public T Dequeue<T>() where T : struct
{
unsafe
{
return _queue->Read<T>();
}
}
#if UNITY_NATIVE
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe UnsafeBlob* _queue;

int _writingGuard;
}
}
#endif

+ 11
- 0
DataStructures/Unmanaged/ThreadSafeNativeBag.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ecdfbc4967aa30eebd81d08e327f9857
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 138
- 0
DataStructures/Unmanaged/UnsafeArray.cs Целия файл

@@ -0,0 +1,138 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;

namespace Svelto.ECS.DataStructures
{
struct UnsafeArray
{
internal unsafe byte* ptr => _ptr;

//expressed in bytes
internal int capacity => (int) _capacity;

//expressed in bytes
internal int count => (int) _writeIndex;

//expressed in bytes
internal int space => capacity - count;

#if DEBUG && !PROFILE_SVELTO
#pragma warning disable 649
internal uint id;
#pragma warning restore 649
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Get<T>(uint index) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
uint sizeOf = (uint) MemoryUtilities.SizeOf<T>();
if (index + sizeOf > _writeIndex)
throw new Exception("no reading authorized");
#endif
return ref Unsafe.AsRef<T>(Unsafe.Add<T>(ptr, (int) index));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set<T>(uint index, in T value) where T : struct
{
unsafe
{
uint sizeOf = (uint) MemoryUtilities.SizeOf<T>();
uint writeIndex = (uint) (index * sizeOf);
#if DEBUG && !PROFILE_SVELTO
if (_capacity < writeIndex + sizeOf)
throw new Exception("no writing authorized");
#endif
Unsafe.AsRef<T>(Unsafe.Add<T>(_ptr, (int) index)) = value;

if (_writeIndex < writeIndex + sizeOf)
_writeIndex = (uint) (writeIndex + sizeOf);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add<T>(in T value) where T : struct
{
unsafe
{
var structSize = MemoryUtilities.SizeOf<T>();
#if DEBUG && !PROFILE_SVELTO
if (space - structSize < 0)
throw new Exception("no writing authorized");
#endif
Unsafe.Write(ptr + _writeIndex, value);

_writeIndex += (uint)structSize;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Pop<T>() where T : struct
{
unsafe
{
var structSize = MemoryUtilities.SizeOf<T>();
_writeIndex -= (uint)structSize;
return ref Unsafe.AsRef<T>(ptr + _writeIndex);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Realloc(uint newCapacity, Allocator allocator)
{
unsafe
{
if (_ptr == null)
_ptr = (byte*) MemoryUtilities.Alloc(newCapacity, allocator);
else
_ptr = (byte*) MemoryUtilities.Realloc((IntPtr) _ptr, (uint) count, newCapacity, allocator);

_capacity = newCapacity;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose(Allocator allocator)
{
unsafe
{
if (ptr != null)
MemoryUtilities.Free((IntPtr) ptr, allocator);

_ptr = null;
_writeIndex = 0;
_capacity = 0;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
_writeIndex = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetCountTo(uint count)
{
_writeIndex = count;
}
#if UNITY_NATIVE
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe byte* _ptr;
uint _writeIndex;
uint _capacity;
}
}

+ 11
- 0
DataStructures/Unmanaged/UnsafeArray.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0acd30efabd337da835fcfda3b966aee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 302
- 0
DataStructures/Unmanaged/UnsafeBlob.cs Целия файл

@@ -0,0 +1,302 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;

namespace Svelto.ECS.DataStructures
{
//ToDO to complete in future version of svelto, maybe removed
public struct UnsafeArrayIndex
{
internal uint index;
internal uint capacity;
}

/// <summary>
/// Note: this must work inside burst, so it must follow burst restrictions
/// Note: All the svelto native structures
/// </summary>
struct UnsafeBlob : IDisposable
{
internal unsafe byte* ptr { get; set; }

//expressed in bytes
internal uint capacity { get; private set; }

//expressed in bytes
internal uint size => (uint)_writeIndex - _readIndex;

//expressed in bytes
internal uint space => capacity - size;

/// <summary>
/// </summary>
internal Allocator allocator;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Write<T>(in T item) where T : struct
{
unsafe
{
var structSize = (uint) MemoryUtilities.SizeOf<T>();

//the idea is, considering the wrap, a read pointer must always be behind a writer pointer
#if DEBUG && !PROFILE_SVELTO
if (space - (int) structSize < 0)
throw new Exception("no writing authorized");
#endif
var writeHead = _writeIndex % capacity;

if (writeHead + structSize <= capacity)
{
Unsafe.Write(ptr + writeHead, item);
}
else
//copy with wrap, will start to copy and wrap for the reminder
{
var byteCountToEnd = capacity - writeHead;

var localCopyToAvoidGcIssues = item;
//read and copy the first portion of Item until the end of the stream
Unsafe.CopyBlock(ptr + writeHead, Unsafe.AsPointer(ref localCopyToAvoidGcIssues), (uint)byteCountToEnd);

var restCount = structSize - byteCountToEnd;

//read and copy the remainder
Unsafe.CopyBlock(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGcIssues) + byteCountToEnd
, (uint)restCount);
}

//this is may seems a waste if you are going to use an unsafeBlob just for bytes, but it's necessary for mixed types.
//it's still possible to use WriteUnaligned though
int paddedStructSize = (int) MemoryUtilities.Align4(structSize);

_writeIndex += paddedStructSize;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Write<T>(in T item, uint writeIndex) where T : struct
{
unsafe
{
var structSize = (uint) MemoryUtilities.SizeOf<T>();

//the idea is, considering the wrap, a read pointer must always be behind a writer pointer
var writeHead = writeIndex % capacity;

if (writeHead + structSize <= capacity)
{
Unsafe.Write(ptr + writeHead, item);
}
else //copy with wrap, will start to copy and wrap for the reminder
{
var byteCountToEnd = capacity - writeHead;

var localCopyToAvoidGcIssues = item;
//read and copy the first portion of Item until the end of the stream
Unsafe.CopyBlock(ptr + writeHead, Unsafe.AsPointer(ref localCopyToAvoidGcIssues), byteCountToEnd);

var restCount = structSize - byteCountToEnd;

//read and copy the remainder
Unsafe.CopyBlock(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGcIssues) + byteCountToEnd
, restCount);
}
}
}

// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// //ToDo: remove this and create an UnsafeBlobUnaligned, used on NativeRingBuffer where T cannot change
// internal void WriteUnaligned<T>(in T item) where T : struct
// {
// unsafe
// {
// var structSize = (uint) MemoryUtilities.SizeOf<T>();
//
// //the idea is, considering the wrap, a read pointer must always be behind a writer pointer
// #if DEBUG && !PROFILE_SVELTO
// if (space - (int) structSize < 0)
// throw new Exception("no writing authorized");
// #endif
// var pointer = _writeIndex % capacity;
//
// if (pointer + structSize <= capacity)
// {
// Unsafe.Write(ptr + pointer, item);
// }
// else
// {
// var byteCount = capacity - pointer;
//
// var localCopyToAvoidGCIssues = item;
//
// Unsafe.CopyBlockUnaligned(ptr + pointer, Unsafe.AsPointer(ref localCopyToAvoidGCIssues), byteCount);
//
// var restCount = structSize - byteCount;
// Unsafe.CopyBlockUnaligned(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGCIssues) + byteCount
// , restCount);
// }
//
// _writeIndex += structSize;
// }
// }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal T Read<T>() where T : struct
{
unsafe
{
var structSize = (uint) MemoryUtilities.SizeOf<T>();

#if DEBUG && !PROFILE_SVELTO
if (size < structSize) //are there enough bytes to read?
throw new Exception("dequeuing empty queue or unexpected type dequeued");
if (_readIndex > _writeIndex)
throw new Exception("unexpected read");
#endif
var head = _readIndex % capacity;
var paddedStructSize = MemoryUtilities.Align4(structSize);
_readIndex += paddedStructSize;

if (_readIndex == _writeIndex)
{
//resetting the Indices has the benefit to let the Reserve work in more occasions and
//the rapping happening less often. If the _readIndex reached the _writeIndex, it means
//that there is no data left to read, so we can start to write again from the begin of the memory
_writeIndex = 0;
_readIndex = 0;
}

if (head + paddedStructSize <= capacity)
return Unsafe.Read<T>(ptr + head);

T item = default;
var byteCountToEnd = capacity - head;
Unsafe.CopyBlock(Unsafe.AsPointer(ref item), ptr + head, byteCountToEnd);

var restCount = structSize - byteCountToEnd;
Unsafe.CopyBlock((byte*) Unsafe.AsPointer(ref item) + byteCountToEnd, ptr, restCount);

return item;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref T Reserve<T>(out UnsafeArrayIndex index) where T : struct
{
unsafe
{
var sizeOf = (uint) MemoryUtilities.SizeOf<T>();

ref var buffer = ref Unsafe.AsRef<T>(ptr + _writeIndex);

#if DEBUG && !PROFILE_SVELTO
if (_writeIndex > capacity)
throw new Exception(
$"can't reserve if the writeIndex wrapped around the capacity, writeIndex {_writeIndex} capacity {capacity}");
if (_writeIndex + sizeOf > capacity)
throw new Exception("out of bound reserving");
#endif
index = new UnsafeArrayIndex
{
capacity = capacity
, index = (uint)_writeIndex
};

int align4 = (int) MemoryUtilities.Align4(sizeOf);
_writeIndex += align4;

return ref buffer;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref T AccessReserved<T>(UnsafeArrayIndex index) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
var size = MemoryUtilities.SizeOf<T>();
if (index.index + size > capacity)
throw new Exception($"out of bound access, index {index.index} size {size} capacity {capacity}");
#endif
return ref Unsafe.AsRef<T>(ptr + index.index);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Realloc(uint newCapacity)
{
unsafe
{
//be sure it's multiple of 4. Assuming that what we write is aligned to 4, then we will always have aligned wrapped heads
newCapacity = MemoryUtilities.Align4(newCapacity);

byte* newPointer = null;
#if DEBUG && !PROFILE_SVELTO
if (newCapacity <= capacity)
throw new Exception("new capacity must be bigger than current");
#endif
if (newCapacity > 0)
{
newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, allocator);
if (size > 0)
{
var readerHead = _readIndex % capacity;
var writerHead = _writeIndex % capacity;

if (readerHead < writerHead)
{
//copy to the new pointer, from th reader position
var currentSize = _writeIndex - _readIndex;
Unsafe.CopyBlock(newPointer, ptr + readerHead, (uint)currentSize);
}
//the assumption is that if size > 0 (so readerPointer and writerPointer are not the same)
//writerHead wrapped and reached readerHead. so I have to copy from readerHead to the end
//and from the start to writerHead (which is the same position of readerHead)
else
{
var byteCountToEnd = capacity - readerHead;

Unsafe.CopyBlock(newPointer, ptr + readerHead, byteCountToEnd);
Unsafe.CopyBlock(newPointer + byteCountToEnd, ptr, (uint)writerHead);
}
}
}

if (ptr != null)
MemoryUtilities.Free((IntPtr) ptr, allocator);

ptr = newPointer;
capacity = newCapacity;
_readIndex = 0;
_writeIndex = (int)size;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
unsafe
{
if (ptr != null)
MemoryUtilities.Free((IntPtr) ptr, allocator);

ptr = null;
_writeIndex = 0;
capacity = 0;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
_writeIndex = 0;
_readIndex = 0;
}

internal int _writeIndex;
internal uint _readIndex;
}
}

+ 11
- 0
DataStructures/Unmanaged/UnsafeBlob.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 62fedd45acd134729d5be903fc2d5b26
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 8
- 0
Debugger.meta Целия файл

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d2793f2ae73e357f9773b68721bbe468
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

+ 70
- 0
Debugger/ExclusiveGroupDebugger.cs Целия файл

@@ -0,0 +1,70 @@
using Svelto.ECS;

#if DEBUG
using System;
using System.Collections.Generic;
using System.Reflection;

public static class ExclusiveGroupDebugger
{
static ExclusiveGroupDebugger()
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
Type[] types = assembly.GetTypes();

foreach (Type type in types)
{
if (type != null && type.IsClass && type.IsSealed && type.IsAbstract) //this means only static classes
{
var fields = type.GetFields();
foreach (var field in fields)
{
if (field.IsStatic && typeof(ExclusiveGroup).IsAssignableFrom(field.FieldType))
{
var group = (ExclusiveGroup) field.GetValue(null);
string name = $"{type.FullName}.{field.Name} ({(uint)group})";
GroupMap.idToName[(ExclusiveGroupStruct) group] = name;
}

if (field.IsStatic && typeof(ExclusiveGroupStruct).IsAssignableFrom(field.FieldType))
{
var group = (ExclusiveGroupStruct) field.GetValue(null);

string name = $"{type.FullName}.{field.Name} ({(uint)group})";
GroupMap.idToName[@group] = name;
}
}
}
}
}
}
public static string ToName(this in ExclusiveGroupStruct group)
{
if (GroupMap.idToName.TryGetValue(group, out var name) == false)
name = $"<undefined:{((uint)group).ToString()}>";

return name;
}
}

public static class GroupMap
{
static GroupMap()
{
GroupMap.idToName = new Dictionary<uint, string>();
}

internal static readonly Dictionary<uint, string> idToName;
}
#else
public static class ExclusiveGroupDebugger
{
public static string ToName(this in ExclusiveGroupStruct group)
{
return ((uint)group).ToString();
}
}
#endif

+ 11
- 0
Debugger/ExclusiveGroupDebugger.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fd73f27c31073381b9b694f3f32941f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 4
- 2
Dispatcher/DispatchOnChange.cs Целия файл

@@ -4,8 +4,10 @@ namespace Svelto.ECS
{
public class DispatchOnChange<T> : DispatchOnSet<T> where T:IEquatable<T>
{
public DispatchOnChange(EGID senderID) : base(senderID)
{ }
public DispatchOnChange(EGID senderID, T initialValue = default(T)) : base(senderID)
{
_value = initialValue;
}
public new T value
{


+ 10
- 5
Dispatcher/DispatchOnSet.cs Целия файл

@@ -16,18 +16,23 @@ namespace Svelto.ECS
_value = value;

if (_paused == false)
_subscribers(_senderID, value);
_subscriber(_senderID, value);
}
}
public void NotifyOnValueSet(Action<EGID, T> action)
{
_subscribers += action;
#if DEBUG && !PROFILE_SVELTO
DBC.ECS.Check.Require(_subscriber == null, $"{this.GetType().Name}: listener already registered");
#endif
_subscriber = action;
_paused = false;
}

public void StopNotify(Action<EGID, T> action)
public void StopNotify()
{
_subscribers -= action;
_subscriber = null;
_paused = true;
}

public void PauseNotify() { _paused = true; }
@@ -36,7 +41,7 @@ namespace Svelto.ECS
protected T _value;
readonly EGID _senderID;

Action<EGID, T> _subscribers;
Action<EGID, T> _subscriber;
bool _paused;
}
}

+ 36
- 38
DynamicEntityDescriptor.cs Целия файл

@@ -4,66 +4,66 @@ using Svelto.DataStructures;
namespace Svelto.ECS
{
/// <summary>
/// DynamicEntityDescriptor can be used to add entity views to an existing EntityDescriptor that act as flags,
/// DynamicEntityDescriptor can be used to add entity components to an existing EntityDescriptor that act as flags,
/// at building time.
/// This method allocates, so it shouldn't be abused
/// </summary>
/// <typeparam name="TType"></typeparam>
public struct DynamicEntityDescriptor<TType> : IEntityDescriptor where TType : IEntityDescriptor, new()
public struct DynamicEntityDescriptor<TType> : IDynamicEntityDescriptor where TType : IEntityDescriptor, new()
{
internal DynamicEntityDescriptor(bool isExtendible) : this()
{
var defaultEntities = EntityDescriptorTemplate<TType>.descriptor.entitiesToBuild;
var defaultEntities = EntityDescriptorTemplate<TType>.descriptor.componentsToBuild;
var length = defaultEntities.Length;

_entitiesToBuild = new IEntityBuilder[length + 1];
ComponentsToBuild = new IComponentBuilder[length + 1];

Array.Copy(defaultEntities, 0, _entitiesToBuild, 0, length);
Array.Copy(defaultEntities, 0, ComponentsToBuild, 0, length);

//assign it after otherwise the previous copy will overwrite the value in case the item
//is already present
_entitiesToBuild[length] = new EntityBuilder<EntityStructInfoView>
ComponentsToBuild[length] = new ComponentBuilder<EntityInfoComponent>
(
new EntityStructInfoView
new EntityInfoComponent
{
entitiesToBuild = _entitiesToBuild
componentsToBuild = ComponentsToBuild
}
);
}

public DynamicEntityDescriptor(IEntityBuilder[] extraEntityBuilders) : this()
public DynamicEntityDescriptor(IComponentBuilder[] extraEntityBuilders) : this()
{
var extraEntitiesLength = extraEntityBuilders.Length;

_entitiesToBuild = Construct(extraEntitiesLength, extraEntityBuilders,
EntityDescriptorTemplate<TType>.descriptor.entitiesToBuild);
ComponentsToBuild = Construct(extraEntitiesLength, extraEntityBuilders,
EntityDescriptorTemplate<TType>.descriptor.componentsToBuild);
}

public DynamicEntityDescriptor(FasterList<IEntityBuilder> extraEntityBuilders) : this()
public DynamicEntityDescriptor(FasterList<IComponentBuilder> extraEntityBuilders) : this()
{
var extraEntities = extraEntityBuilders.ToArrayFast();
var extraEntitiesLength = extraEntityBuilders.Count;
var extraEntities = extraEntityBuilders.ToArrayFast(out _);
var extraEntitiesLength = extraEntityBuilders.count;

_entitiesToBuild = Construct(extraEntitiesLength, extraEntities,
EntityDescriptorTemplate<TType>.descriptor.entitiesToBuild);
ComponentsToBuild = Construct((int) extraEntitiesLength, extraEntities,
EntityDescriptorTemplate<TType>.descriptor.componentsToBuild);
}

public void ExtendWith<T>() where T : IEntityDescriptor, new()
{
var newEntitiesToBuild = EntityDescriptorTemplate<T>.descriptor.entitiesToBuild;
var newEntitiesToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;

_entitiesToBuild = Construct(newEntitiesToBuild.Length, newEntitiesToBuild, _entitiesToBuild);
ComponentsToBuild = Construct(newEntitiesToBuild.Length, newEntitiesToBuild, ComponentsToBuild);
}
public void ExtendWith(IEntityBuilder[] extraEntities)
public void ExtendWith(IComponentBuilder[] extraEntities)
{
_entitiesToBuild = Construct(extraEntities.Length, extraEntities, _entitiesToBuild);
ComponentsToBuild = Construct(extraEntities.Length, extraEntities, ComponentsToBuild);
}

static IEntityBuilder[] Construct(int extraEntitiesLength, IEntityBuilder[] extraEntities,
IEntityBuilder[] startingEntities)
static IComponentBuilder[] Construct(int extraEntitiesLength, IComponentBuilder[] extraEntities,
IComponentBuilder[] startingEntities)
{
IEntityBuilder[] localEntitiesToBuild;
IComponentBuilder[] localEntitiesToBuild;

if (extraEntitiesLength == 0)
{
@@ -72,26 +72,25 @@ namespace Svelto.ECS
}

var defaultEntities = startingEntities;
var length = defaultEntities.Length;

var index = SetupSpecialEntityStruct(defaultEntities, out localEntitiesToBuild, extraEntitiesLength);
var index = SetupEntityInfoComponent(defaultEntities, out localEntitiesToBuild, extraEntitiesLength);

Array.Copy(extraEntities, 0, localEntitiesToBuild, length, extraEntitiesLength);
Array.Copy(extraEntities, 0, localEntitiesToBuild, defaultEntities.Length, extraEntitiesLength);

//assign it after otherwise the previous copy will overwrite the value in case the item
//is already present
localEntitiesToBuild[index] = new EntityBuilder<EntityStructInfoView>
localEntitiesToBuild[index] = new ComponentBuilder<EntityInfoComponent>
(
new EntityStructInfoView
new EntityInfoComponent
{
entitiesToBuild = localEntitiesToBuild
componentsToBuild = localEntitiesToBuild
}
);

return localEntitiesToBuild;
}

static int SetupSpecialEntityStruct(IEntityBuilder[] defaultEntities, out IEntityBuilder[] entitiesToBuild,
static int SetupEntityInfoComponent(IComponentBuilder[] defaultEntities, out IComponentBuilder[] componentsToBuild,
int extraLenght)
{
int length = defaultEntities.Length;
@@ -100,7 +99,7 @@ namespace Svelto.ECS
for (var i = 0; i < length; i++)
{
//the special entity already exists
if (defaultEntities[i].GetEntityType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW)
if (defaultEntities[i].GetEntityComponentType() == ComponentBuilderUtilities.ENTITY_INFO_COMPONENT)
{
index = i;
break;
@@ -110,19 +109,18 @@ namespace Svelto.ECS
if (index == -1)
{
index = length + extraLenght;
entitiesToBuild = new IEntityBuilder[index + 1];
componentsToBuild = new IComponentBuilder[index + 1];
}
else
entitiesToBuild = new IEntityBuilder[length + extraLenght];
componentsToBuild = new IComponentBuilder[length + extraLenght];

Array.Copy(defaultEntities, 0, entitiesToBuild, 0, length);
Array.Copy(defaultEntities, 0, componentsToBuild, 0, length);

return index;
}

public IComponentBuilder[] componentsToBuild => ComponentsToBuild;

public IEntityBuilder[] entitiesToBuild => _entitiesToBuild;

IEntityBuilder[] _entitiesToBuild;
IComponentBuilder[] ComponentsToBuild;
}
}

+ 10
- 0
ECSException.cs Целия файл

@@ -9,5 +9,15 @@ namespace Svelto.ECS
public ECSException(string message, Exception innerE):base("<color=red>".FastConcat(message, "</color>"), innerE)
{}
public ECSException(string message, Type entityComponentType, Type type) :
base(message.FastConcat(" entity view: '", entityComponentType.Name, "', field: '", type.Name))
{
}

public ECSException(string message, Type entityComponentType) :
base(message.FastConcat(" entity view: ", entityComponentType.Name))
{
}
}
}

+ 6
- 2
ECSResources/ECSResources.cs Целия файл

@@ -9,6 +9,10 @@ namespace Svelto.ECS.Experimental
public static implicit operator T(ECSResources<T> ecsString) { return ResourcesECSDB<T>.FromECS(ecsString.id); }
}
/// <summary>
/// To do. Or we reuse the ID or we need to clear this
/// </summary>
/// <typeparam name="T"></typeparam>
static class ResourcesECSDB<T>
{
static readonly FasterList<T> _resources = new FasterList<T>();
@@ -22,12 +26,12 @@ namespace Svelto.ECS.Experimental
{
_resources.Add(resource);

return (uint)_resources.Count;
return (uint)_resources.count;
}

public static T FromECS(uint id)
{
if (id - 1 < _resources.Count)
if (id - 1 < _resources.count)
return _resources[(int) id - 1];
return default;


+ 69
- 11
ECSResources/ECSString.cs Целия файл

@@ -1,38 +1,96 @@
using System;
using System.Runtime.InteropServices;

namespace Svelto.ECS.Experimental
{
[Serialization.DoNotSerialize]
[StructLayout(LayoutKind.Explicit)]
///
/// Note: I should extend this to reuse unused id
///
//todo ResourcesECSDB<T> must be used only inside entity components. Same for ECSString.
//what I could do is that if the component is removed from the database, a reference counter to the object
//will be modified. If 0 is reached, the ID should be recycled.
public struct ECSString:IEquatable<ECSString>
{
uint id;
[FieldOffset(0)] uint _id;
[FieldOffset(4)] uint _versioning;
[FieldOffset(0)] long _realID;

public ECSString(string newText)
public ECSString(string newText):this()
{
id = ResourcesECSDB<string>.ToECS(newText);
_id = ResourcesECSDB<string>.ToECS(newText);
}

ECSString(uint id):this()
{
_id = id;
}

public static implicit operator string(ECSString ecsString)
{
return ResourcesECSDB<string>.FromECS(ecsString.id);
return ResourcesECSDB<string>.FromECS(ecsString._id);
}

/// <summary>
/// Note: Setting null String could be a good way to signal a disposing of the ID so that
/// it can be recycled.
/// Zero id must be a null string
/// </summary>
/// <param name="newText"></param>
public void Set(string newText)
{
if (id != 0)
ResourcesECSDB<string>.resources(id) = newText;
if (_id != 0)
{
if (ResourcesECSDB<string>.resources(_id).Equals(newText) == false)
{
ResourcesECSDB<string>.resources(_id) = newText;
_versioning++;
}
}
else
id = ResourcesECSDB<string>.ToECS(newText);
_id = ResourcesECSDB<string>.ToECS(newText);
}

public bool Equals(ECSString other)
public ECSString Copy()
{
return other.id == id;
DBC.ECS.Check.Require(_id != 0, "copying not initialized string");
var id = ResourcesECSDB<string>.ToECS(ResourcesECSDB<string>.resources(_id));
return new ECSString(id);
}

public override string ToString()
{
return ResourcesECSDB<string>.FromECS(id);
return ResourcesECSDB<string>.FromECS(_id);
}

public bool Equals(ECSString other)
{
return _realID == other._realID;
}

public static bool operator==(ECSString options1, ECSString options2)
{
return options1._realID == options2._realID;
}

public static bool operator!=(ECSString options1, ECSString options2)
{
return options1._realID != options2._realID;
}

public override bool Equals(object obj)
{
throw new NotSupportedException(); //this is on purpose
}

public override int GetHashCode()
{
return _realID.GetHashCode();
}
}
}

+ 22
- 11
EGID.cs Целия файл

@@ -1,19 +1,21 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

#pragma warning disable 660,661

namespace Svelto.ECS
{
//todo: add debug map
[Serialization.DoNotSerialize]
[Serializable]
public struct EGID:IEquatable<EGID>,IEqualityComparer<EGID>,IComparable<EGID>
[StructLayout(LayoutKind.Explicit)]
public struct EGID:IEquatable<EGID>,IComparable<EGID>
{
public uint entityID => (uint) (_GID & 0xFFFFFFFF);
public ExclusiveGroup.ExclusiveGroupStruct groupID => new ExclusiveGroup.ExclusiveGroupStruct((uint) (_GID >> 32));
[FieldOffset(0)] public readonly uint entityID;
[FieldOffset(4)] public readonly ExclusiveGroupStruct groupID;
[FieldOffset(0)] readonly ulong _GID;

public static readonly EGID Empty = new EGID();
public static bool operator ==(EGID obj1, EGID obj2)
{
return obj1._GID == obj2._GID;
@@ -24,10 +26,15 @@ namespace Svelto.ECS
return obj1._GID != obj2._GID;
}

public EGID(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) : this()
public EGID(uint entityID, ExclusiveGroupStruct groupID) : this()
{
_GID = MAKE_GLOBAL_ID(entityID, groupID);
}
public EGID(uint entityID, BuildGroup groupID) : this()
{
_GID = MAKE_GLOBAL_ID(entityID, groupID.group);
}

static ulong MAKE_GLOBAL_ID(uint entityId, uint groupId)
{
@@ -52,11 +59,16 @@ namespace Svelto.ECS
return x == y;
}

public int GetHashCode(EGID obj)
public override int GetHashCode()
{
return _GID.GetHashCode();
}

public int GetHashCode(EGID egid)
{
return egid.GetHashCode();
}

public int CompareTo(EGID other)
{
return _GID.CompareTo(other._GID);
@@ -69,9 +81,8 @@ namespace Svelto.ECS

public override string ToString()
{
return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID);
var value = groupID.ToName();
return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(value);
}

readonly ulong _GID;
}
}

+ 57
- 12
EGIDMapper.cs Целия файл

@@ -1,37 +1,82 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public struct EGIDMapper<T> where T : struct, IEntityStruct
/// <summary>
/// Note: does mono devirtualize sealed classes? If so it could be worth to use TypeSafeDictionary instead of
/// the interface
/// </summary>
/// <typeparam name="T"></typeparam>
public readonly struct EGIDMapper<T>: IEGIDMapper where T : struct, IEntityComponent
{
internal FasterDictionary<uint, T> map;
public uint length => _map.count;
public ExclusiveGroupStruct groupID { get; }
public Type entityType => TypeCache<T>.type;

internal EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary<T> dic) : this()
{
groupID = groupStructId;
_map = dic;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Entity(uint entityID)
{
#if DEBUG && !PROFILER
if (map.TryFindIndex(entityID, out var findIndex) == false)
throw new Exception("Entity not found in this group ".FastConcat(typeof(T).ToString()));
#if DEBUG && !PROFILE_SVELTO
if (_map == null)
throw new System.Exception("Not initialized EGIDMapper in this group ".FastConcat(typeof(T).ToString()));
if (_map.TryFindIndex(entityID, out var findIndex) == false)
throw new System.Exception("Entity not found in this group ".FastConcat(typeof(T).ToString()));
#else
map.TryFindIndex(entityID, out var findIndex);
_map.TryFindIndex(entityID, out var findIndex);
#endif
return ref map.valuesArray[findIndex];
return ref _map.GetDirectValueByRef(findIndex);
}
public bool TryGetEntity(uint entityID, out T value)
{
if (map.TryFindIndex(entityID, out var index))
if (_map != null && _map.TryFindIndex(entityID, out var index))
{
value = map.GetDirectValue(index);
value = _map.GetDirectValueByRef(index);
return true;
}

value = default;
return false;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists(uint idEntityId)
{
return _map.count > 0 && _map.TryFindIndex(idEntityId, out _);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetIndex(uint entityID)
{
return _map.GetIndex(entityID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool FindIndex(uint valueKey, out uint index)
{
return _map.TryFindIndex(valueKey, out index);
}

internal readonly ITypeSafeDictionary<T> _map;
}
}

public interface IEGIDMapper
{
bool FindIndex(uint valueKey, out uint index);
uint GetIndex(uint entityID);
bool Exists(uint idEntityId);
ExclusiveGroupStruct groupID { get; }
Type entityType { get; }
}
}

+ 148
- 0
EnginesRoot.DoubleBufferedEntitiesToAdd.cs Целия файл

@@ -0,0 +1,148 @@
using System;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public partial class EnginesRoot
{
internal class DoubleBufferedEntitiesToAdd
{
const int MaximumNumberOfItemsPerFrameBeforeToClear = 100;

internal void Swap()
{
Swap(ref current, ref other);
Swap(ref currentEntitiesCreatedPerGroup, ref otherEntitiesCreatedPerGroup);
}

void Swap<T>(ref T item1, ref T item2)
{
var toSwap = item2;
item2 = item1;
item1 = toSwap;
}

public void ClearOther()
{
//do not clear the groups created so far, they will be reused, unless they are too many!
var otherCount = other.count;
if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear)
{
FasterDictionary<RefWrapperType, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues;
for (int i = 0; i < otherCount; ++i)
{
var safeDictionariesCount = otherValuesArray[i].count;
ITypeSafeDictionary[] safeDictionaries = otherValuesArray[i].unsafeValues;
{
for (int j = 0; j < safeDictionariesCount; ++j)
{
//clear the dictionary of entities create do far (it won't allocate though)
safeDictionaries[j].Dispose();
}
}
}
otherEntitiesCreatedPerGroup.FastClear();
other.FastClear();
return;
}

{
FasterDictionary<RefWrapperType, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues;
for (int i = 0; i < otherCount; ++i)
{
var safeDictionariesCount = otherValuesArray[i].count;
ITypeSafeDictionary[] safeDictionaries = otherValuesArray[i].unsafeValues;
//do not remove the dictionaries of entities per type created so far, they will be reused
if (safeDictionariesCount <= MaximumNumberOfItemsPerFrameBeforeToClear)
{
for (int j = 0; j < safeDictionariesCount; ++j)
{
//clear the dictionary of entities create do far (it won't allocate though)
safeDictionaries[j].FastClear();
}
}
else
{
for (int j = 0; j < safeDictionariesCount; ++j)
{
//clear the dictionary of entities create do far (it won't allocate though)
safeDictionaries[j].Dispose();
}

otherValuesArray[i].FastClear();
}
}

otherEntitiesCreatedPerGroup.FastClear();
}
}

/// <summary>
/// To avoid extra allocation, I don't clear the dictionaries, so I need an extra data structure
/// to keep count of the number of entities submitted this frame
/// </summary>
internal FasterDictionary<ExclusiveGroupStruct, uint> currentEntitiesCreatedPerGroup;
internal FasterDictionary<ExclusiveGroupStruct, uint> otherEntitiesCreatedPerGroup;

//Before I tried for the third time to use a SparseSet instead of FasterDictionary, remember that
//while group indices are sequential, they may not be used in a sequential order. Sparaset needs
//entities to be created sequentially (the index cannot be managed externally)
internal FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> current;
internal FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> other;

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

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

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

public DoubleBufferedEntitiesToAdd()
{
currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA;
otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB;

current = _entityComponentsToAddBufferA;
other = _entityComponentsToAddBufferB;
}

public void Dispose()
{
{
var otherValuesArray = other.unsafeValues;
for (int i = 0; i < other.count; ++i)
{
var safeDictionariesCount = otherValuesArray[i].count;
var safeDictionaries = otherValuesArray[i].unsafeValues;
//do not remove the dictionaries of entities per type created so far, they will be reused
for (int j = 0; j < safeDictionariesCount; ++j)
{
//clear the dictionary of entities create do far (it won't allocate though)
safeDictionaries[j].Dispose();
}
}
}
{
var currentValuesArray = current.unsafeValues;
for (int i = 0; i < current.count; ++i)
{
var safeDictionariesCount = currentValuesArray[i].count;
var safeDictionaries = currentValuesArray[i].unsafeValues;
//do not remove the dictionaries of entities per type created so far, they will be reused
for (int j = 0; j < safeDictionariesCount; ++j)
{
//clear the dictionary of entities create do far (it won't allocate though)
safeDictionaries[j].Dispose();
}
}
}
}
}
}
}

+ 11
- 0
EnginesRoot.DoubleBufferedEntitiesToAdd.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5bf312fc57853c4d8368dcb99141a1e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 0
- 86
EnginesRoot.DoubleBufferedEntityViews.cs Целия файл

@@ -1,86 +0,0 @@
using System;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public partial class EnginesRoot
{
internal class DoubleBufferedEntitiesToAdd
{
const int MaximumNumberOfItemsPerFrameBeforeToClear = 100;

internal void Swap()
{
Swap(ref current, ref other);
Swap(ref currentEntitiesCreatedPerGroup, ref otherEntitiesCreatedPerGroup);
}

void Swap<T>(ref T item1, ref T item2)
{
var toSwap = item2;
item2 = item1;
item1 = toSwap;
}

public void ClearOther()
{
//do not clear the groups created so far, they will be reused, unless they are too many!
var otherCount = other.Count;
if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear)
{
otherEntitiesCreatedPerGroup.FastClear();
other.FastClear();
return;
}
var otherValuesArray = other.valuesArray;
for (int i = 0; i < otherCount; ++i)
{
var safeDictionariesCount = otherValuesArray[i].Count;
var safeDictionaries = otherValuesArray[i].valuesArray;
//do not remove the dictionaries of entities per type created so far, they will be reused
if (safeDictionariesCount <= MaximumNumberOfItemsPerFrameBeforeToClear)
{
for (int j = 0; j < safeDictionariesCount; ++j)
{
//clear the dictionary of entities create do far (it won't allocate though)
safeDictionaries[j].FastClear();
}
}
else
{
otherValuesArray[i].FastClear();
}
}

otherEntitiesCreatedPerGroup.FastClear();
}

internal FasterDictionary<uint, uint> currentEntitiesCreatedPerGroup;
internal FasterDictionary<uint, uint> otherEntitiesCreatedPerGroup;

internal FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> current;
internal FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> other;

readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>
_entityViewsToAddBufferA =
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>();

readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>
_entityViewsToAddBufferB =
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>();

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

public DoubleBufferedEntitiesToAdd()
{
currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA;
otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB;

current = _entityViewsToAddBufferA;
other = _entityViewsToAddBufferB;
}
}
}
}

+ 166
- 50
EnginesRoot.Engines.cs Целия файл

@@ -1,28 +1,35 @@
using System;
using System.Collections.Generic;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using Svelto.ECS.Schedulers;

namespace Svelto.ECS
{
public partial class EnginesRoot
public sealed partial class EnginesRoot
{
public struct EntitiesSubmitter
public readonly struct EntitiesSubmitter
{
public EntitiesSubmitter(EnginesRoot enginesRoot)
{
_weakReference = new DataStructures.WeakReference<EnginesRoot>(enginesRoot);
_weakReference = new Svelto.DataStructures.WeakReference<EnginesRoot>(enginesRoot);
}

public bool IsUnused => _weakReference.IsValid == false;

public void Invoke()
{
if (_weakReference.IsValid)
_weakReference.Target.SubmitEntityViews();
_weakReference.Target.SubmitEntityComponents();
}

readonly DataStructures.WeakReference<EnginesRoot> _weakReference;
readonly Svelto.DataStructures.WeakReference<EnginesRoot> _weakReference;
}

readonly EntitiesSubmissionScheduler _scheduler;
public IEntitiesSubmissionScheduler scheduler => _scheduler;

/// <summary>
/// Engines root contextualize your engines and entities. You don't need to limit yourself to one EngineRoot
/// as multiple engines root could promote separation of scopes. The EntitySubmissionScheduler checks
@@ -31,49 +38,156 @@ namespace Svelto.ECS
/// The EntitySubmissionScheduler cannot hold an EnginesRoot reference, that's why
/// it must receive a weak reference of the EnginesRoot callback.
/// </summary>
public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler)
public EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler)
{
_entitiesOperations = new FasterDictionary<ulong, EntitySubmitOperation>();
serializationDescriptorMap = new SerializationDescriptorMap();
_reactiveEnginesAddRemove = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>();
_reactiveEnginesSwap = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>();
_enginesSet = new FasterList<IEngine>();
_enginesTypeSet = new HashSet<Type>();
_disposableEngines = new FasterList<IDisposable>();
_entitiesOperations = new ThreadSafeDictionary<ulong, EntitySubmitOperation>();
serializationDescriptorMap = new SerializationDescriptorMap();
_reactiveEnginesAddRemove = new FasterDictionary<RefWrapperType, FasterList<IReactEngine>>();
_reactiveEnginesSwap = new FasterDictionary<RefWrapperType, FasterList<IReactEngine>>();
_reactiveEnginesSubmission = new FasterList<IReactOnSubmission>();
_enginesSet = new FasterList<IEngine>();
_enginesTypeSet = new HashSet<Type>();
_disposableEngines = new FasterList<IDisposable>();
_transientEntitiesOperations = new FasterList<EntitySubmitOperation>();

_groupEntityViewsDB = new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>();
_groupsPerEntity = new FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>>();
_groupEntityComponentsDB =
new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>();
_groupsPerEntity =
new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>();
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd();

_entitiesStream = new EntitiesStream();
_entitiesDB = new EntitiesDB(_groupEntityViewsDB, _groupsPerEntity, _entitiesStream);
_entityStreams = EntitiesStreams.Create();
_groupFilters =
new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>();
_entitiesDB = new EntitiesDB(this);

_scheduler = entityViewScheduler;
_scheduler = entitiesComponentScheduler;
_scheduler.onTick = new EntitiesSubmitter(this);
#if UNITY_NATIVE
AllocateNativeOperations();
#endif
}
public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler, bool isDeserializationOnly):this(entityViewScheduler)

public EnginesRoot
(EntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly) :
this(entitiesComponentScheduler)
{
_isDeserializationOnly = isDeserializationOnly;
}

/// <summary>
/// Dispose an EngineRoot once not used anymore, so that all the
/// engines are notified with the entities removed.
/// It's a clean up process.
/// </summary>
public void Dispose()
{
_isDisposing = true;

using (var profiler = new PlatformProfiler("Final Dispose"))
{
//Note: The engines are disposed before the the remove callback to give the chance to behave
//differently if a remove happens as a consequence of a dispose
//The pattern is to implement the IDisposable interface and set a flag in the engine. The
//remove callback will then behave differently according the flag.
foreach (var engine in _disposableEngines)
{
try
{
if (engine is IDisposingEngine dengine)
dengine.isDisposing = true;
engine.Dispose();
}
catch (Exception e)
{
Svelto.Console.LogException(e);
}
}

foreach (FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>.
KeyValuePairFast groups in _groupEntityComponentsDB)
{
foreach (FasterDictionary<RefWrapperType, ITypeSafeDictionary>.KeyValuePairFast entityList in groups
.Value)
try
{
entityList.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemove, profiler
, new ExclusiveGroupStruct(groups.Key));
}
catch (Exception e)
{
Svelto.Console.LogException(e);
}
}

foreach (FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>.
KeyValuePairFast groups in _groupEntityComponentsDB)
{
foreach (FasterDictionary<RefWrapperType, ITypeSafeDictionary>.KeyValuePairFast entityList in groups
.Value)
entityList.Value.Dispose();
}

foreach (FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>.
KeyValuePairFast type in _groupFilters)
foreach (FasterDictionary<ExclusiveGroupStruct, GroupFilters>.KeyValuePairFast group in type.Value)
group.Value.Dispose();

_groupFilters.Clear();

#if UNITY_NATIVE
_addOperationQueue.Dispose();
_removeOperationQueue.Dispose();
_swapOperationQueue.Dispose();
#endif
_groupEntityComponentsDB.Clear();
_groupsPerEntity.Clear();

_disposableEngines.Clear();
_enginesSet.Clear();
_enginesTypeSet.Clear();
_reactiveEnginesSwap.Clear();
_reactiveEnginesAddRemove.Clear();
_reactiveEnginesSubmission.Clear();

_entitiesOperations.Clear();
_transientEntitiesOperations.Clear();

_groupedEntityToAdd.Dispose();
_entityStreams.Dispose();
scheduler.Dispose();
}

GC.SuppressFinalize(this);
}

~EnginesRoot()
{
Console.LogWarning("Engines Root has been garbage collected, don't forget to call Dispose()!");

Dispose();
}

public void AddEngine(IEngine engine)
{
var type = engine.GetType();
var refWrapper = new RefWrapper<Type>(type);
var type = engine.GetType();
var refWrapper = new RefWrapperType(type);
DBC.ECS.Check.Require(engine != null, "Engine to add is invalid or null");
DBC.ECS.Check.Require(
_enginesTypeSet.Contains(refWrapper) == false ||
type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true,
"The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute "
.FastConcat(engine.ToString()));
_enginesTypeSet.Contains(refWrapper) == false
|| type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true
, "The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute "
.FastConcat(engine.ToString()));
try
{
if (engine is IReactOnAddAndRemove viewEngine)
CheckEntityViewsEngine(viewEngine, _reactiveEnginesAddRemove);
CheckReactEngineComponents(viewEngine, _reactiveEnginesAddRemove);

if (engine is IReactOnSwap viewEngineSwap)
CheckEntityViewsEngine(viewEngineSwap, _reactiveEnginesSwap);
CheckReactEngineComponents(viewEngineSwap, _reactiveEnginesSwap);

if (engine is IReactOnSubmission submissionEngine)
_reactiveEnginesSubmission.Add(submissionEngine);

_enginesTypeSet.Add(refWrapper);
_enginesSet.Add(engine);
@@ -81,20 +195,21 @@ namespace Svelto.ECS
if (engine is IDisposable)
_disposableEngines.Add(engine as IDisposable);

if (engine is IQueryingEntitiesEngine queryableEntityViewEngine)
if (engine is IQueryingEntitiesEngine queryableEntityComponentEngine)
{
queryableEntityViewEngine.entitiesDB = _entitiesDB;
queryableEntityViewEngine.Ready();
queryableEntityComponentEngine.entitiesDB = _entitiesDB;
queryableEntityComponentEngine.Ready();
}
}
catch (Exception e)
{
throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " "), e);
throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " ")
, e);
}
}

void CheckEntityViewsEngine<T>(T engine, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines)
where T : class, IEngine
void CheckReactEngineComponents<T>(T engine, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines)
where T : class, IReactEngine
{
var interfaces = engine.GetType().GetInterfaces();

@@ -109,36 +224,37 @@ namespace Svelto.ECS
}
}

static void AddEngine<T>(T engine, Type[] entityViewTypes,
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines)
where T : class, IEngine
static void AddEngine<T>
(T engine, Type[] entityComponentTypes, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines)
where T : class, IReactEngine
{
for (var i = 0; i < entityViewTypes.Length; i++)
for (var i = 0; i < entityComponentTypes.Length; i++)
{
var type = entityViewTypes[i];
var type = entityComponentTypes[i];

AddEngine(engine, engines, type);
}
}

static void AddEngine<T>(T engine, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, Type type)
where T : class, IEngine
static void AddEngine<T>(T engine, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, Type type)
where T : class, IReactEngine
{
if (engines.TryGetValue(new RefWrapper<Type>(type), out var list) == false)
if (engines.TryGetValue(new RefWrapperType(type), out var list) == false)
{
list = new FasterList<IEngine>();
list = new FasterList<IReactEngine>();

engines.Add(new RefWrapper<Type>(type), list);
engines.Add(new RefWrapperType(type), list);
}

list.Add(engine);
}

readonly FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> _reactiveEnginesAddRemove;
readonly FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> _reactiveEnginesSwap;
readonly FasterList<IDisposable> _disposableEngines;
readonly FasterList<IEngine> _enginesSet;
readonly HashSet<Type> _enginesTypeSet;
readonly FasterDictionary<RefWrapperType, FasterList<IReactEngine>> _reactiveEnginesAddRemove;
readonly FasterDictionary<RefWrapperType, FasterList<IReactEngine>> _reactiveEnginesSwap;
readonly FasterList<IReactOnSubmission> _reactiveEnginesSubmission;
readonly FasterList<IDisposable> _disposableEngines;
readonly FasterList<IEngine> _enginesSet;
readonly HashSet<Type> _enginesTypeSet;
internal bool _isDisposing;
}
}

+ 222
- 195
EnginesRoot.Entities.cs Целия файл

@@ -1,65 +1,15 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using DBC.ECS;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public partial class EnginesRoot : IDisposable
public partial class EnginesRoot : IDisposable, IUnitTestingInterface
{
/// <summary>
/// Dispose an EngineRoot once not used anymore, so that all the
/// engines are notified with the entities removed.
/// It's a clean up process.
/// </summary>
public void Dispose()
{
using (var profiler = new PlatformProfiler("Final Dispose"))
{
foreach (var groups in _groupEntityViewsDB)
{
foreach (var entityList in groups.Value)
{
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove,
profiler, new ExclusiveGroup.ExclusiveGroupStruct(groups.Key));
}
}

_groupEntityViewsDB.Clear();
_groupsPerEntity.Clear();

foreach (var engine in _disposableEngines)
engine.Dispose();

_disposableEngines.Clear();
_enginesSet.Clear();
_enginesTypeSet.Clear();
_reactiveEnginesSwap.Clear();
_reactiveEnginesAddRemove.Clear();

_entitiesOperations.Clear();
_transientEntitiesOperations.Clear();
_scheduler.Dispose();
#if DEBUG && !PROFILER
_idCheckers.Clear();
#endif
_groupedEntityToAdd = null;

_entitiesStream.Dispose();
}

GC.SuppressFinalize(this);
}

~EnginesRoot()
{
Console.LogWarning("Engines Root has been garbage collected, don't forget to call Dispose()!");

Dispose();
}

///--------------------------------------------
///
public IEntityStreamConsumerFactory GenerateConsumerFactory()
@@ -79,227 +29,304 @@ namespace Svelto.ECS

///--------------------------------------------
[MethodImpl(MethodImplOptions.AggressiveInlining)]
EntityStructInitializer BuildEntity(EGID entityID, IEntityBuilder[] entitiesToBuild,
EntityComponentInitializer BuildEntity
(EGID entityID, IComponentBuilder[] componentsToBuild, Type descriptorType,
IEnumerable<object> implementors = null)
{
CheckAddEntityID(entityID);
CheckAddEntityID(entityID, descriptorType);
Check.Require(entityID.groupID != 0, "invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?");

var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd,
entitiesToBuild, implementors);
var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, componentsToBuild
, implementors, descriptorType);

return new EntityStructInitializer(entityID, dic);
return new EntityComponentInitializer(entityID, dic);
}

///--------------------------------------------
void Preallocate<T>(uint groupID, uint size) where T : IEntityDescriptor, new()
void Preallocate<T>(ExclusiveGroupStruct groupID, uint size) where T : IEntityDescriptor, new()
{
var entityViewsToBuild = EntityDescriptorTemplate<T>.descriptor.entitiesToBuild;
var numberOfEntityViews = entityViewsToBuild.Length;
using (var profiler = new PlatformProfiler("Preallocate"))
{
var entityComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;
var numberOfEntityComponents = entityComponentsToBuild.Length;

//reserve space in the database
if (_groupEntityViewsDB.TryGetValue(groupID, out var group) == false)
group = _groupEntityViewsDB[groupID] = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();
FasterDictionary<RefWrapperType, ITypeSafeDictionary> group = GetOrCreateGroup(groupID, profiler);

for (var index = 0; index < numberOfEntityViews; index++)
{
var entityViewBuilder = entityViewsToBuild[index];
var entityViewType = entityViewBuilder.GetEntityType();
for (var index = 0; index < numberOfEntityComponents; index++)
{
var entityComponentBuilder = entityComponentsToBuild[index];
var entityComponentType = entityComponentBuilder.GetEntityComponentType();

var refWrapper = new RefWrapper<Type>(entityViewType);
if (group.TryGetValue(refWrapper, out var dbList) == false)
group[refWrapper] = entityViewBuilder.Preallocate(ref dbList, size);
else
dbList.SetCapacity(size);
var refWrapper = new RefWrapperType(entityComponentType);
if (group.TryGetValue(refWrapper, out var dbList) == false)
group[refWrapper] = entityComponentBuilder.Preallocate(ref dbList, size);
else
dbList.SetCapacity(size);

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

groupedGroup[groupID] = dbList;
groupedGroup[groupID] = dbList;
}
}
}

///--------------------------------------------
///
void MoveEntityFromAndToEngines(IEntityBuilder[] entityBuilders, EGID fromEntityGID, EGID? toEntityGID)
void MoveEntityFromAndToEngines(IComponentBuilder[] componentBuilders, EGID fromEntityGID, EGID? toEntityGID)
{
using (var sampler = new PlatformProfiler("Move Entity From Engines"))
{
//for each entity view generated by the entity descriptor
if (_groupEntityViewsDB.TryGetValue(fromEntityGID.groupID, out var fromGroup) == false)
throw new ECSException("from group not found eid: ".FastConcat(fromEntityGID.entityID)
.FastConcat(" group: ").FastConcat(fromEntityGID.groupID));

//Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor!
if (fromGroup.TryGetValue(new RefWrapper<Type>(EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW),
out var entityInfoViewDic) &&
(entityInfoViewDic as TypeSafeDictionary<EntityStructInfoView>).TryGetValue(
fromEntityGID.entityID, out var entityInfoView))
MoveEntities(fromEntityGID, toEntityGID, entityInfoView.entitiesToBuild, fromGroup, sampler);
var fromGroup = GetGroup(fromEntityGID.groupID);

//Check if there is an EntityInfo linked to this entity, if so it's a DynamicEntityDescriptor!
if (fromGroup.TryGetValue(new RefWrapperType(ComponentBuilderUtilities.ENTITY_INFO_COMPONENT)
, out var entityInfoDic)
&& (entityInfoDic as ITypeSafeDictionary<EntityInfoComponent>).TryGetValue(
fromEntityGID.entityID, out var entityInfo))
SwapOrRemoveEntityComponents(fromEntityGID, toEntityGID, entityInfo.componentsToBuild, fromGroup
, sampler);
//otherwise it's a normal static entity descriptor
else
MoveEntities(fromEntityGID, toEntityGID, entityBuilders, fromGroup, sampler);
SwapOrRemoveEntityComponents(fromEntityGID, toEntityGID, componentBuilders, fromGroup, sampler);
}
}

void MoveEntities(EGID fromEntityGID, EGID? toEntityGID, IEntityBuilder[] entitiesToMove,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, PlatformProfiler sampler)
void SwapOrRemoveEntityComponents(EGID fromEntityGID, EGID? toEntityGID, IComponentBuilder[] entitiesToMove
, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup, in PlatformProfiler sampler)
{
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = null;

if (toEntityGID != null)
using (sampler.Sample("MoveEntityComponents"))
{
var toGroupID = toEntityGID.Value.groupID;
var length = entitiesToMove.Length;

if (_groupEntityViewsDB.TryGetValue(toGroupID, out toGroup) == false)
toGroup = _groupEntityViewsDB[toGroupID] = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();
FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup = null;

//Add all the entities to the dictionary
for (var i = 0; i < entitiesToMove.Length; i++)
CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup,
entitiesToMove[i].GetEntityType());
}
//Swap is not like adding a new entity. While adding new entities happen at the end of submission
//Adding an entity to a group due to a swap of groups happens now.
if (toEntityGID != null)
{
var toGroupID = toEntityGID.Value.groupID;

//call all the callbacks
for (var i = 0; i < entitiesToMove.Length; i++)
MoveEntityViewFromAndToEngines(fromEntityGID, toEntityGID, fromGroup, toGroup,
entitiesToMove[i].GetEntityType(), sampler);
toGroup = GetOrCreateGroup(toGroupID, sampler);

//then remove all the entities from the dictionary
for (var i = 0; i < entitiesToMove.Length; i++)
RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityType(), sampler);
}
//Add all the entities to the dictionary
for (var i = 0; i < length; i++)
CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup
, entitiesToMove[i].GetEntityComponentType(), sampler);
}

void CopyEntityToDictionary(EGID entityGID, EGID toEntityGID,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityViewType)
{
var wrapper = new RefWrapper<Type>(entityViewType);
//call all the callbacks
for (var i = 0; i < length; i++)
ExecuteEnginesSwapOrRemoveCallbacks(fromEntityGID, toEntityGID, fromGroup, toGroup
, entitiesToMove[i].GetEntityComponentType(), sampler);

if (fromGroup.TryGetValue(wrapper, out var fromTypeSafeDictionary) == false)
{
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID)
.FastConcat(" group: ").FastConcat(entityGID.groupID));
//then remove all the entities from the dictionary
for (var i = 0; i < length; i++)
RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityComponentType(),
sampler);
}
}

#if DEBUG && !PROFILER
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
{
throw new EntityNotFoundException(entityGID, entityViewType);
}
#endif
if (toGroup.TryGetValue(wrapper, out var toEntitiesDictionary) == false)
void CopyEntityToDictionary
(EGID entityGID, EGID toEntityGID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup
, FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup, Type entityComponentType,
in PlatformProfiler sampler)
{
using (sampler.Sample("CopyEntityToDictionary"))
{
toEntitiesDictionary = fromTypeSafeDictionary.Create();
toGroup.Add(wrapper, toEntitiesDictionary);
}
var wrapper = new RefWrapperType(entityComponentType);

//todo: this must be unit tested properly
if (_groupsPerEntity.TryGetValue(wrapper, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[wrapper] =
new FasterDictionary<uint, ITypeSafeDictionary>();
ITypeSafeDictionary fromTypeSafeDictionary =
GetTypeSafeDictionary(entityGID.groupID, fromGroup, wrapper);

groupedGroup[toEntityGID.groupID] = toEntitiesDictionary;
#if DEBUG && !PROFILE_SVELTO
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
{
throw new EntityNotFoundException(entityGID, entityComponentType);
}
#endif
ITypeSafeDictionary toEntitiesDictionary =
GetOrCreateTypeSafeDictionary(toEntityGID.groupID, toGroup, wrapper, fromTypeSafeDictionary);

fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary);
fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary);
}
}

void MoveEntityViewFromAndToEngines(EGID entityGID, EGID? toEntityGID,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityViewType,
in PlatformProfiler profiler)
void ExecuteEnginesSwapOrRemoveCallbacks
(EGID entityGID, EGID? toEntityGID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup
, FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup, Type entityComponentType
, in PlatformProfiler profiler)
{
//add all the entities
var refWrapper = new RefWrapper<Type>(entityViewType);
if (fromGroup.TryGetValue(refWrapper, out var fromTypeSafeDictionary) == false)
using (profiler.Sample("MoveEntityComponentFromAndToEngines"))
{
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID)
.FastConcat(" group: ").FastConcat(entityGID.groupID));
}
//add all the entities
var refWrapper = new RefWrapperType(entityComponentType);
var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper);

ITypeSafeDictionary toEntitiesDictionary = null;
if (toGroup != null)
toEntitiesDictionary = toGroup[refWrapper]; //this is guaranteed to exist by AddEntityToDictionary
ITypeSafeDictionary toEntitiesDictionary = null;
if (toGroup != null)
toEntitiesDictionary = toGroup[refWrapper]; //this is guaranteed to exist by AddEntityToDictionary

#if DEBUG && !PROFILER
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
throw new EntityNotFoundException(entityGID, entityViewType);
#if DEBUG && !PROFILE_SVELTO
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
throw new EntityNotFoundException(entityGID, entityComponentType);
#endif
fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID,
toEntitiesDictionary, toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap,
in profiler);
fromTypeSafeDictionary.ExecuteEnginesSwapOrRemoveCallbacks(entityGID, toEntityGID, toEntitiesDictionary
, toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap, in profiler);
}
}

void RemoveEntityFromDictionary(EGID entityGID,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, Type entityViewType,
in PlatformProfiler profiler)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void RemoveEntityFromDictionary
(EGID entityGID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup, Type entityComponentType
, in PlatformProfiler sampler)
{
var refWrapper = new RefWrapper<Type>(entityViewType);
if (fromGroup.TryGetValue(refWrapper, out var fromTypeSafeDictionary) == false)
using (sampler.Sample("RemoveEntityFromDictionary"))
{
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID)
.FastConcat(" group: ").FastConcat(entityGID.groupID));
}

fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID, profiler);
var refWrapper = new RefWrapperType(entityComponentType);
var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper);

if (fromTypeSafeDictionary.Count == 0) //clean up
{
//todo: this must be unit tested properly
_groupsPerEntity[refWrapper].Remove(entityGID.groupID);
//I don't remove the group if empty on purpose, in case it needs to be reused
fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID);
}
}

/// <summary>
/// Todo: I should keep the group, but I need to mark the group as deleted for the Exist function to work
/// Swap all the entities from one group to another
///
/// TODO: write unit test that also tests that this calls MoveTo callbacks and not Add or Remove.
/// also that the passing EGID is the same of a component with EGID
/// </summary>
/// <param name="groupID"></param>
/// <param name="fromIdGroupId"></param>
/// <param name="toGroupId"></param>
/// <param name="profiler"></param>
void RemoveGroupAndEntitiesFromDB(uint groupID, in PlatformProfiler profiler)
void SwapEntitiesBetweenGroups(ExclusiveGroupStruct fromIdGroupId, ExclusiveGroupStruct toGroupId, in PlatformProfiler profiler)
{
var dictionariesOfEntities = _groupEntityViewsDB[groupID];
foreach (var dictionaryOfEntities in dictionariesOfEntities)
using (profiler.Sample("SwapEntitiesBetweenGroups"))
{
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler,
new ExclusiveGroup.ExclusiveGroupStruct(groupID));
var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key];
groupedGroupOfEntities.Remove(groupID);
FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId);
FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId, profiler);

foreach (var dictionaryOfEntities in fromGroup)
{
ITypeSafeDictionary toEntitiesDictionary =
GetOrCreateTypeSafeDictionary(toGroupId, toGroup, dictionaryOfEntities.Key
, dictionaryOfEntities.Value);

var groupsOfEntityType = _groupsPerEntity[dictionaryOfEntities.Key];

var groupOfEntitiesToCopyAndClear = groupsOfEntityType[fromIdGroupId];
toEntitiesDictionary.AddEntitiesFromDictionary(groupOfEntitiesToCopyAndClear, toGroupId);
//call all the MoveTo callbacks
dictionaryOfEntities.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesSwap
, dictionaryOfEntities.Value, new ExclusiveGroupStruct(fromIdGroupId), new ExclusiveGroupStruct(toGroupId), profiler);

//todo: if it's unmanaged, I can use fastclear
groupOfEntitiesToCopyAndClear.Clear();
}
}
}

FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetGroup(ExclusiveGroupStruct fromIdGroupId)
{
if (_groupEntityComponentsDB.TryGetValue(fromIdGroupId
, out FasterDictionary<RefWrapperType, ITypeSafeDictionary>
fromGroup) == false)
throw new ECSException("Group doesn't exist: ".FastConcat(fromIdGroupId));

return fromGroup;
}

FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetOrCreateGroup(ExclusiveGroupStruct toGroupId,
in PlatformProfiler profiler)
{
using (profiler.Sample("GetOrCreateGroup"))
{
if (_groupEntityComponentsDB.TryGetValue(
toGroupId, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup) == false)
toGroup = _groupEntityComponentsDB[toGroupId] =
new FasterDictionary<RefWrapperType, ITypeSafeDictionary>();

//careful, in this case I assume you really don't want to use this group anymore
//so I remove it from the database
_groupEntityViewsDB.Remove(groupID);
return toGroup;
}
}

internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct
ITypeSafeDictionary GetOrCreateTypeSafeDictionary
(ExclusiveGroupStruct groupId, FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup, RefWrapperType type
, ITypeSafeDictionary fromTypeSafeDictionary)
{
return _entitiesStream.GenerateConsumer<T>(name, capacity);
//be sure that the TypeSafeDictionary for the entity Type exists
if (toGroup.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) == false)
{
toEntitiesDictionary = fromTypeSafeDictionary.Create();
toGroup.Add(type, toEntitiesDictionary);
}

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

groupedGroup[groupId] = toEntitiesDictionary;
return toEntitiesDictionary;
}

static ITypeSafeDictionary GetTypeSafeDictionary
(uint groupID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> @group, RefWrapperType refWrapper)
{
if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false)
{
throw new ECSException("no group found: ".FastConcat(groupID));
}

return fromTypeSafeDictionary;
}

public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) where T : unmanaged,
IEntityStruct
void RemoveEntitiesFromGroup(ExclusiveGroupStruct groupID, in PlatformProfiler profiler)
{
return _entitiesStream.GenerateConsumer<T>(group, name, capacity);
if (_groupEntityComponentsDB.TryGetValue(groupID, out var dictionariesOfEntities))
{
foreach (FasterDictionary<RefWrapperType, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities
in dictionariesOfEntities)
{
dictionaryOfEntities.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemove, profiler
, new ExclusiveGroupStruct(groupID));
dictionaryOfEntities.Value.FastClear();

var groupsOfEntityType =
_groupsPerEntity[dictionaryOfEntities.Key];
groupsOfEntityType[groupID].FastClear();
}
}
}

//one datastructure rule them all:
//split by group
//split by type per group. It's possible to get all the entities of a give type T per group thanks
//to the FasterDictionary capabilities OR it's possible to get a specific entityView indexed by
//to the FasterDictionary capabilities OR it's possible to get a specific entityComponent indexed by
//ID. This ID doesn't need to be the EGID, it can be just the entityID
//for each group id, save a dictionary indexed by entity type of entities indexed by id
//ITypeSafeDictionary = Key = entityID, Value = EntityStruct
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> _groupEntityViewsDB;
// group EntityComponentType entityID, EntityComponent
internal readonly FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>
_groupEntityComponentsDB;

//for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are
//found indexed by group id
//EntityViewType //groupID //entityID, EntityStruct
readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity;
//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<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>
_groupsPerEntity;

readonly EntitiesDB _entitiesDB;
readonly EntitiesStream _entitiesStream;
//The filters stored for each component and group
internal readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>
_groupFilters;

readonly EntitiesDB _entitiesDB;

EntitiesDB IUnitTestingInterface.entitiesForTesting => _entitiesDB;
}

public interface IUnitTestingInterface
{
EntitiesDB entitiesForTesting { get; }
}
}

+ 33
- 23
EnginesRoot.GenericEntityFactory.cs Целия файл

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

namespace Svelto.ECS
{
@@ -9,49 +10,58 @@ namespace Svelto.ECS
{
public GenericEntityFactory(EnginesRoot weakReference)
{
_enginesRoot = new WeakReference<EnginesRoot>(weakReference);
_enginesRoot = new Svelto.DataStructures.WeakReference<EnginesRoot>(weakReference);
}

public EntityStructInitializer BuildEntity<T>(uint entityID,
ExclusiveGroup.ExclusiveGroupStruct groupStructId, IEnumerable<object> implementors = null)
public EntityComponentInitializer BuildEntity<T>
(uint entityID, BuildGroup groupStructId, IEnumerable<object> implementors = null)
where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId),
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, implementors);
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId)
, EntityDescriptorTemplate<T>.descriptor.componentsToBuild
, TypeCache<T>.type, implementors);
}

public EntityStructInitializer BuildEntity<T>(EGID egid, IEnumerable<object> implementors = null)
public EntityComponentInitializer BuildEntity<T>(EGID egid, IEnumerable<object> implementors = null)
where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.BuildEntity(egid,
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, implementors);
return _enginesRoot.Target.BuildEntity(
egid, EntityDescriptorTemplate<T>.descriptor.componentsToBuild, TypeCache<T>.type, implementors);
}

public EntityStructInitializer BuildEntity<T>(EGID egid, T entityDescriptor,
IEnumerable<object> implementors)
where T : IEntityDescriptor
public EntityComponentInitializer BuildEntity<T>
(EGID egid, T entityDescriptor, IEnumerable<object> implementors) where T : IEntityDescriptor
{
return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.entitiesToBuild, implementors);
return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.componentsToBuild, TypeCache<T>.type, implementors);
}

public EntityStructInitializer BuildEntity<T>(uint entityID,
ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, IEnumerable<object> implementors)
#if UNITY_NATIVE
public NativeEntityFactory ToNative<T>(string memberName) where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.ProvideNativeEntityFactoryQueue<T>(memberName);
}
#endif
public EntityComponentInitializer BuildEntity<T>
(uint entityID, BuildGroup groupStructId, T descriptorEntity, IEnumerable<object> implementors)
where T : IEntityDescriptor
{
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId),
descriptorEntity.entitiesToBuild,
implementors);
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId)
, descriptorEntity.componentsToBuild, TypeCache<T>.type, implementors);
}

public void PreallocateEntitySpace<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size)
public void PreallocateEntitySpace<T>(ExclusiveGroupStruct groupStructId, uint size)
where T : IEntityDescriptor, new()
{
_enginesRoot.Target.Preallocate<T>(groupStructId, size);
}
public EntityComponentInitializer BuildEntity(EGID egid, IComponentBuilder[] componentsToBuild, Type type, IEnumerable<object> implementors = null)
{
return _enginesRoot.Target.BuildEntity(egid, componentsToBuild, type, implementors);
}

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

+ 102
- 30
EnginesRoot.GenericEntityFunctions.cs Целия файл

@@ -1,6 +1,8 @@
using System.Diagnostics;
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
@@ -8,17 +10,17 @@ namespace Svelto.ECS
{
/// <summary>
/// todo: EnginesRoot was a weakreference to give the change to inject
/// entityfunctions from other engines root. It probably should be reverted
/// entity functions from other engines root. It probably should be reverted
/// </summary>
sealed class GenericEntityFunctions : IEntityFunctions
class GenericEntityFunctions : IEntityFunctions
{
internal GenericEntityFunctions(EnginesRoot weakReference)
{
_enginesRoot = new WeakReference<EnginesRoot>(weakReference);
_enginesRoot = new Svelto.DataStructures.WeakReference<EnginesRoot>(weakReference);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T :
public void RemoveEntity<T>(uint entityID, BuildGroup groupID) where T :
IEntityDescriptor, new()
{
RemoveEntity<T>(new EGID(entityID, groupID));
@@ -27,40 +29,92 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntity<T>(EGID entityEGID) where T : IEntityDescriptor, new()
{
_enginesRoot.Target.CheckRemoveEntityID(entityEGID);
DBC.ECS.Check.Require(entityEGID.groupID != 0, "invalid group detected");
var descriptorComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;
_enginesRoot.Target.CheckRemoveEntityID(entityEGID, TypeCache<T>.type);

_enginesRoot.Target.QueueEntitySubmitOperation<T>(
new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID,
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild));
descriptorComponentsToBuild));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID)
public void RemoveEntitiesFromGroup(BuildGroup groupID)
{
DBC.ECS.Check.Require(groupID != 0, "invalid group detected");
_enginesRoot.Target.RemoveGroupID(groupID);

_enginesRoot.Target.QueueEntitySubmitOperation(
new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(0, groupID), new EGID()));
}

// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// void RemoveAllEntities<D, S>(ExclusiveGroup group)
// where D : IEntityDescriptor, new() where S : unmanaged, IEntityComponent
// {
// var targetEntitiesDB = _enginesRoot.Target._entitiesDB;
// var (buffer, count) = targetEntitiesDB.QueryEntities<S>(@group);
// for (uint i = 0; i < count; ++i)
// {
// RemoveEntity<D>(new EGID(i, group));
// }
// }
//
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// void RemoveAllEntities<D, S>()
// where D : IEntityDescriptor, new() where S : unmanaged, IEntityComponent
// {
// var targetEntitiesDB = _enginesRoot.Target._entitiesDB;
// foreach (var ((buffer, count), exclusiveGroupStruct) in targetEntitiesDB.QueryEntities<S>())
// for (uint i = 0; i < count; ++i)
// {
// RemoveEntity<D>(new EGID(i, exclusiveGroupStruct));
// }
// }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID,
ExclusiveGroup.ExclusiveGroupStruct toGroupID)
public void SwapEntitiesInGroup<T>(BuildGroup fromGroupID, BuildGroup toGroupID)
where T : IEntityDescriptor, new()
{
if (_enginesRoot.Target._groupEntityComponentsDB.TryGetValue(
fromGroupID.group, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType)
== true)
{
#if DEBUG && !PROFILE_SVELTO
IComponentBuilder[] components = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;
var dictionary = entitiesInGroupPerType[new RefWrapperType(components[0].GetEntityComponentType())];

dictionary.KeysEvaluator((key) =>
{
_enginesRoot.Target.CheckRemoveEntityID(new EGID(key, fromGroupID), TypeCache<T>.type);
_enginesRoot.Target.CheckAddEntityID(new EGID(key, toGroupID), TypeCache<T>.type);
});

#endif
_enginesRoot.Target.QueueEntitySubmitOperation(
new EntitySubmitOperation(EntitySubmitOperationType.SwapGroup, new EGID(0, fromGroupID)
, new EGID(0, toGroupID)));
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(uint entityID, BuildGroup fromGroupID,
BuildGroup toGroupID)
where T : IEntityDescriptor, new()
{
SwapEntityGroup<T>(new EGID(entityID, fromGroupID), toGroupID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID)
public void SwapEntityGroup<T>(EGID fromID, BuildGroup toGroupID)
where T : IEntityDescriptor, new()
{
SwapEntityGroup<T>(fromID, new EGID(fromID.entityID, (uint) toGroupID));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID
, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup)
public void SwapEntityGroup<T>(EGID fromID, BuildGroup toGroupID
, BuildGroup mustBeFromGroup)
where T : IEntityDescriptor, new()
{
if (fromID.groupID != mustBeFromGroup)
@@ -71,7 +125,7 @@ namespace Svelto.ECS

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(EGID fromID, EGID toID
, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup)
, BuildGroup mustBeFromGroup)
where T : IEntityDescriptor, new()
{
if (fromID.groupID != mustBeFromGroup)
@@ -80,47 +134,65 @@ namespace Svelto.ECS
SwapEntityGroup<T>(fromID, toID);
}

#if UNITY_NATIVE
public NativeEntityRemove ToNativeRemove<T>(string memberName) where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.ProvideNativeEntityRemoveQueue<T>(memberName);
}

public NativeEntitySwap ToNativeSwap<T>(string memberName) where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.ProvideNativeEntitySwapQueue<T>(memberName);
}
#endif

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(EGID fromID, EGID toID)
where T : IEntityDescriptor, new()
{
_enginesRoot.Target.CheckRemoveEntityID(fromID);
_enginesRoot.Target.CheckAddEntityID(toID);
DBC.ECS.Check.Require(fromID.groupID != 0, "invalid group detected");
DBC.ECS.Check.Require(toID.groupID != 0, "invalid group detected");

_enginesRoot.Target.QueueEntitySubmitOperation<T>(
var enginesRootTarget = _enginesRoot.Target;
var descriptorComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;
enginesRootTarget.CheckRemoveEntityID(fromID, TypeCache<T>.type);
enginesRootTarget.CheckAddEntityID(toID, TypeCache<T>.type);

enginesRootTarget.QueueEntitySubmitOperation<T>(
new EntitySubmitOperation(EntitySubmitOperationType.Swap,
fromID, toID, EntityDescriptorTemplate<T>.descriptor.entitiesToBuild));
fromID, toID, descriptorComponentsToBuild));
}
//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside
//engines of other enginesRoot
readonly WeakReference<EnginesRoot> _enginesRoot;
//engines of other enginesRoot
readonly Svelto.DataStructures.WeakReference<EnginesRoot> _enginesRoot;
}

void QueueEntitySubmitOperation(EntitySubmitOperation entitySubmitOperation)
{
#if DEBUG && !PROFILER
entitySubmitOperation.trace = new StackFrame(1, true);
#if DEBUG && !PROFILE_SVELTO
entitySubmitOperation.trace = new System.Diagnostics.StackFrame(1, true);
#endif
_entitiesOperations.Add((ulong) entitySubmitOperation.fromID, entitySubmitOperation);
}

void QueueEntitySubmitOperation<T>(EntitySubmitOperation entitySubmitOperation) where T : IEntityDescriptor
{
#if DEBUG && !PROFILER
entitySubmitOperation.trace = new StackFrame(1, true);
#if DEBUG && !PROFILE_SVELTO
entitySubmitOperation.trace = new System.Diagnostics.StackFrame(1, true);

if (_entitiesOperations.TryGetValue((ulong) entitySubmitOperation.fromID, out var entitySubmitedOperation))
{
if (entitySubmitedOperation != entitySubmitOperation)
throw new ECSException("Only one entity operation per submission is allowed"
.FastConcat(" entityViewType: ")
.FastConcat(typeof(T).Name)
.FastConcat(" submission type ", entitySubmitOperation.type.ToString(),
.FastConcat(" entityComponentType: ")
.FastConcat(typeof(T).Name)
.FastConcat(" submission type ", entitySubmitOperation.type.ToString(),
" from ID: ", entitySubmitOperation.fromID.entityID.ToString())
.FastConcat(" previous operation type: ",
.FastConcat(" previous operation type: ",
_entitiesOperations[(ulong) entitySubmitOperation.fromID].type
.ToString()));
.ToString()));
}
else
#endif


+ 67
- 51
EnginesRoot.Submission.cs Целия файл

@@ -2,7 +2,6 @@
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using Svelto.ECS.Schedulers;

namespace Svelto.ECS
{
@@ -10,7 +9,7 @@ namespace Svelto.ECS
{
readonly FasterList<EntitySubmitOperation> _transientEntitiesOperations;

void SubmitEntityViews()
void SubmitEntityComponents()
{
using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission"))
{
@@ -18,29 +17,40 @@ namespace Svelto.ECS
do
{
SingleSubmission(profiler);
} while ((_groupedEntityToAdd.currentEntitiesCreatedPerGroup.Count > 0 ||
} while ((_groupedEntityToAdd.currentEntitiesCreatedPerGroup.count > 0 ||
_entitiesOperations.Count > 0) && ++iterations < 5);

#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
if (iterations == 5)
throw new ECSException("possible circular submission detected");
#endif
}
}

/// <summary>
/// Todo: it would be probably better to split even further the logic between submission and callbacks
/// Something to do when I will optimize the callbacks
/// </summary>
/// <param name="profiler"></param>
void SingleSubmission(in PlatformProfiler profiler)
{
#if UNITY_NATIVE
NativeOperationSubmission(profiler);
#endif
ClearChecks();

bool entitiesAreSubmitted = false;
if (_entitiesOperations.Count > 0)
{
using (profiler.Sample("Remove and Swap operations"))
{
_transientEntitiesOperations.FastClear();
var entitySubmitOperations = _entitiesOperations.GetValuesArray(out var count);
_transientEntitiesOperations.AddRange(entitySubmitOperations, count);
_entitiesOperations.CopyValuesTo(_transientEntitiesOperations);
_entitiesOperations.FastClear();

var entitiesOperations = _transientEntitiesOperations.ToArrayFast();
for (var i = 0; i < _transientEntitiesOperations.Count; i++)
EntitySubmitOperation[] entitiesOperations = _transientEntitiesOperations.ToArrayFast(out var count);
for (var i = 0; i < count; i++)
{
try
{
@@ -48,57 +58,74 @@ namespace Svelto.ECS
{
case EntitySubmitOperationType.Swap:
MoveEntityFromAndToEngines(entitiesOperations[i].builders,
entitiesOperations[i].fromID,
entitiesOperations[i].toID);
entitiesOperations[i].fromID, entitiesOperations[i].toID);
break;
case EntitySubmitOperationType.Remove:
MoveEntityFromAndToEngines(entitiesOperations[i].builders,
entitiesOperations[i].fromID, null);
break;
case EntitySubmitOperationType.RemoveGroup:
RemoveGroupAndEntitiesFromDB(
RemoveEntitiesFromGroup(
entitiesOperations[i].fromID.groupID, profiler);
break;
case EntitySubmitOperationType.SwapGroup:
SwapEntitiesBetweenGroups(entitiesOperations[i].fromID.groupID,
entitiesOperations[i].toID.groupID, profiler);
break;
}
}
catch (Exception e)
catch
{
var str = "Crash while executing Entity Operation "
.FastConcat(entitiesOperations[i].type.ToString());

throw new ECSException(str.FastConcat(" ")
#if DEBUG && !PROFILER
.FastConcat(entitiesOperations[i].trace.ToString())
Svelto.Console.LogError(str.FastConcat(" ")
#if DEBUG && !PROFILE_SVELTO
.FastConcat(entitiesOperations[i].trace.ToString())
#endif
, e);
);

throw;
}
}
}

entitiesAreSubmitted = true;
}

_groupedEntityToAdd.Swap();

if (_groupedEntityToAdd.otherEntitiesCreatedPerGroup.Count > 0)
if (_groupedEntityToAdd.otherEntitiesCreatedPerGroup.count > 0)
{
using (profiler.Sample("Add operations"))
{
try
{
AddEntityViewsToTheDBAndSuitableEngines(profiler);
AddEntityComponentsToTheDBAndSuitableEngines(profiler);
}
finally
{
using (profiler.Sample("clear operates double buffering"))
using (profiler.Sample("clear 6operates double buffering"))
{
//other can be cleared now, but let's avoid deleting the dictionary every time
_groupedEntityToAdd.ClearOther();
}
}
}
entitiesAreSubmitted = true;
}

if (entitiesAreSubmitted)
{
var enginesCount = _reactiveEnginesSubmission.count;
for (int i = 0; i < enginesCount; i++)
_reactiveEnginesSubmission[i].EntitiesSubmitted();
}
}

void AddEntityViewsToTheDBAndSuitableEngines(in PlatformProfiler profiler)
void AddEntityComponentsToTheDBAndSuitableEngines(in PlatformProfiler profiler)
{
using (profiler.Sample("Add entities to database"))
{
@@ -106,57 +133,46 @@ namespace Svelto.ECS
foreach (var groupOfEntitiesToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup)
{
var groupID = groupOfEntitiesToSubmit.Key;
var groupDB = GetOrCreateGroup(groupID, profiler);

//if the group doesn't exist in the current DB let's create it first
if (_groupEntityViewsDB.TryGetValue(groupID, out var groupDB) == false)
groupDB = _groupEntityViewsDB[groupID] =
new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();

//add the entityViews in the group
foreach (var entityViewsToSubmit in _groupedEntityToAdd.other[groupID])
//add the entityComponents in the group
foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID])
{
var type = entityViewsToSubmit.Key;
var typeSafeDictionary = entityViewsToSubmit.Value;
var type = entityComponentsToSubmit.Key;
var targetTypeSafeDictionary = entityComponentsToSubmit.Value;
var wrapper = new RefWrapperType(type);

var wrapper = new RefWrapper<Type>(type);
if (groupDB.TryGetValue(wrapper, out var dbDic) == false)
dbDic = groupDB[wrapper] = typeSafeDictionary.Create();
ITypeSafeDictionary dbDic = GetOrCreateTypeSafeDictionary(groupID, groupDB, wrapper,
targetTypeSafeDictionary);

//Fill the DB with the entity views generate this frame.
dbDic.AddEntitiesFromDictionary(typeSafeDictionary, groupID);

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

groupedGroup[groupID] = dbDic;
//Fill the DB with the entity components generate this frame.
dbDic.AddEntitiesFromDictionary(targetTypeSafeDictionary, groupID);
}
}
}

//then submit everything in the engines, so that the DB is up to date with all the entity views and struct
//then submit everything in the engines, so that the DB is up to date with all the entity components
//created by the entity built
using (profiler.Sample("Add entities to engines"))
{
foreach (var groupToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup)
{
var groupID = groupToSubmit.Key;
var groupDB = _groupEntityComponentsDB[groupID];

var groupDB = _groupEntityViewsDB[groupID];

foreach (var entityViewsToSubmit in _groupedEntityToAdd.other[groupID])
foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID])
{
var realDic = groupDB[new RefWrapper<Type>(entityViewsToSubmit.Key)];
var realDic = groupDB[new RefWrapperType(entityComponentsToSubmit.Key)];

entityViewsToSubmit.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, realDic, in profiler,
new ExclusiveGroup.ExclusiveGroupStruct(groupToSubmit.Key));
entityComponentsToSubmit.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesAddRemove, realDic,
null, new ExclusiveGroupStruct(groupToSubmit.Key), in profiler);
}
}
}
}

DoubleBufferedEntitiesToAdd _groupedEntityToAdd;
readonly IEntitySubmissionScheduler _scheduler;
readonly FasterDictionary<ulong, EntitySubmitOperation> _entitiesOperations;
readonly DoubleBufferedEntitiesToAdd _groupedEntityToAdd;
readonly ThreadSafeDictionary<ulong, EntitySubmitOperation> _entitiesOperations;
}
}

+ 154
- 0
EntitiesDB.FindGroups.cs Целия файл

@@ -0,0 +1,154 @@
using System;
using System.Threading;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public partial class EntitiesDB
{
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1>() where T1 : IEntityComponent
{
FasterList<ExclusiveGroupStruct> result = groups.Value;
result.FastClear();
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> result1) == false)
return result;
var result1Count = result1.count;
var fasterDictionaryNodes1 = result1.unsafeKeys;
for (int j = 0; j < result1Count; j++)
{
result.Add(new ExclusiveGroupStruct(fasterDictionaryNodes1[j].key));
}
return result;
}
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1, T2>() where T1 : IEntityComponent where T2 : IEntityComponent
{
FasterList<ExclusiveGroupStruct> result = groups.Value;
result.FastClear();
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> result1) == false)
return result;
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T2>.wrapper
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> result2) == false)
return result;
var result1Count = result1.count;
var result2Count = result2.count;
var fasterDictionaryNodes1 = result1.unsafeKeys;
var fasterDictionaryNodes2 = result2.unsafeKeys;

for (int i = 0; i < result1Count; i++)
{
var groupID = fasterDictionaryNodes1[i].key;
for (int j = 0; j < result2Count; j++)
{
//if the same group is found used with both T1 and T2
if (groupID == fasterDictionaryNodes2[j].key)
{
result.Add(new ExclusiveGroupStruct(groupID));
break;
}
}
}

return result;
}

/// <summary>
/// Remember that this operation os O(N*M*P) where N,M,P are the number of groups where each component
/// is found.
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <returns></returns>
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1, T2, T3>()
where T1 : IEntityComponent where T2 : IEntityComponent where T3 : IEntityComponent
{
FasterList<ExclusiveGroupStruct> result = groups.Value;
result.FastClear();
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> groupOfEntities1) == false)
return result;
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T2>.wrapper
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> groupOfEntities2) == false)
return result;
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T3>.wrapper
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> groupOfEntities3) == false)
return result;
var result1Count = groupOfEntities1.count;
var result2Count = groupOfEntities2.count;
var result3Count = groupOfEntities3.count;
var fasterDictionaryNodes1 = groupOfEntities1.unsafeKeys;
var fasterDictionaryNodes2 = groupOfEntities2.unsafeKeys;
var fasterDictionaryNodes3 = groupOfEntities3.unsafeKeys;
//
//TODO: I have to find once for ever a solution to be sure that the entities in the groups match
//Currently this returns group where the entities are found, but the entities may not match in these
//groups.
//Checking the size of the entities is an early check, needed, but not sufficient, as entities components may
//coincidentally match in number but not from which entities they are generated
//foreach group where T1 is found
for (int i = 0; i < result1Count; i++)
{
var groupT1 = fasterDictionaryNodes1[i].key;
//foreach group where T2 is found
for (int j = 0; j < result2Count; ++j)
{
if (groupT1 == fasterDictionaryNodes2[j].key)
{
//foreach group where T3 is found
for (int k = 0; k < result3Count; ++k)
{
if (groupT1 == fasterDictionaryNodes3[k].key)
{
result.Add(new ExclusiveGroupStruct(groupT1));
break;
}
}
break;
}
}
}

return result;
}

internal FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> FindGroups_INTERNAL(Type type)
{
if (groupsPerEntity.ContainsKey(new RefWrapperType(type)) == false)
return _emptyDictionary;

return groupsPerEntity[new RefWrapperType(type)];
}

struct GroupsList
{
static GroupsList()
{
groups = new FasterList<ExclusiveGroupStruct>();
}

static readonly FasterList<ExclusiveGroupStruct> groups;

public static implicit operator FasterList<ExclusiveGroupStruct>(in GroupsList list)
{
return list.reference;
}

FasterList<ExclusiveGroupStruct> reference => groups;
}
static readonly ThreadLocal<GroupsList> groups = new ThreadLocal<GroupsList>();
}
}

+ 11
- 0
EntitiesDB.FindGroups.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 11e0317f53f0374d9924cae6235eacdb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 245
- 180
EntitiesDB.cs Целия файл

@@ -1,254 +1,263 @@
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
#define ENABLE_DEBUG_FUNC
#endif

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

namespace Svelto.ECS.Internal
namespace Svelto.ECS
{
partial class EntitiesDB : IEntitiesDB
public partial class EntitiesDB
{
internal EntitiesDB(
FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> groupEntityViewsDB,
FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> groupsPerEntity,
EntitiesStream entityStream)
internal EntitiesDB(EnginesRoot enginesRoot)
{
_groupEntityViewsDB = groupEntityViewsDB;
_groupsPerEntity = groupsPerEntity;
_entityStream = entityStream;
_enginesRoot = enginesRoot;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T QueryUniqueEntity<T>(ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct
{
var entities = QueryEntities<T>(group, out var count);

if (count == 0)
throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'"));
if (count != 1)
throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString())
.FastConcat("'"));
return ref entities[0];
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T QueryEntity<T>(EGID entityGID) where T : struct, IEntityStruct
EntityCollection<T> InternalQueryEntities<T>(FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType)
where T : struct, IEntityComponent
{
T[] array;
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out var index)) != null)
return ref array[index];
uint count = 0;
IBuffer<T> buffer;
if (SafeQueryEntityDictionary<T>(out var typeSafeDictionary, entitiesInGroupPerType) == false)
buffer = RetrieveEmptyEntityComponentArray<T>();
else
{
var safeDictionary = (typeSafeDictionary as ITypeSafeDictionary<T>);
buffer = safeDictionary.GetValues(out count);
}

throw new EntityNotFoundException(entityGID, typeof(T));
return new EntityCollection<T>(buffer, count);
}

public ref T QueryEntity<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct
/// <summary>
/// The QueryEntities<T> follows the rule that entities could always be iterated regardless if they
/// are 0, 1 or N. In case of 0 it returns an empty array. This allows to use the same for iteration
/// regardless the number of entities built.
/// </summary>
/// <param name="groupStructId"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public EntityCollection<T> QueryEntities<T>(ExclusiveGroupStruct groupStructId)
where T : struct, IEntityComponent
{
return ref QueryEntity<T>(new EGID(id, group));
if (groupEntityComponentsDB.TryGetValue(groupStructId, out var entitiesInGroupPerType) == false)
{
var buffer = RetrieveEmptyEntityComponentArray<T>();
return new EntityCollection<T>(buffer, 0);
}

return InternalQueryEntities<T>(entitiesInGroupPerType);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
where T : struct, IEntityStruct
public EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
uint group = groupStruct;
count = 0;
if (SafeQueryEntityDictionary(group, out TypeSafeDictionary<T> typeSafeDictionary) == false)
return RetrieveEmptyEntityViewArray<T>();
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
{
return new EntityCollection<T1, T2>(new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0),
new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0));
}
var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType);
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())));
#endif

return typeSafeDictionary.GetValuesArray(out count);
return new EntityCollection<T1, T2>(T1entities, T2entities);
}

public EntityCollection<T> QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
where T : struct, IEntityStruct
public EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent
{
return new EntityCollection<T>(QueryEntities<T>(groupStruct, out var count), count);
}
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
{
return new EntityCollection<T1, T2, T3>(
new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0),
new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0),
new EntityCollection<T3>(RetrieveEmptyEntityComponentArray<T3>(), 0));
}
var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType);
var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType);
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)));
#endif

public EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct
{
return new EntityCollection<T1, T2>(QueryEntities<T1, T2>(groupStruct, out var count), count);
return new EntityCollection<T1, T2, T3>(T1entities, T2entities, T3entities);
}

public EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct
public EntityCollection<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>(ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent where T4 : struct, IEntityComponent
{
return new EntityCollection<T1, T2, T3>(QueryEntities<T1, T2, T3>(groupStruct, out var count), count);
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
{
return new EntityCollection<T1, T2, T3, T4>(
new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0),
new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0),
new EntityCollection<T3>(RetrieveEmptyEntityComponentArray<T3>(), 0),
new EntityCollection<T4>(RetrieveEmptyEntityComponentArray<T4>(), 0));
}
var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType);
var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType);
var T3entities = InternalQueryEntities<T3>(entitiesInGroupPerType);
var T4entities = InternalQueryEntities<T4>(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)));
#endif

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

public EntityCollections<T> QueryEntities<T>(ExclusiveGroup[] groups) where T : struct, IEntityStruct
public GroupsEnumerable<T> QueryEntities<T>
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, IEntityComponent
{
return new EntityCollections<T>(this, groups);
return new GroupsEnumerable<T>(this, groups);
}

public EntityCollections<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup[] groups)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct
public GroupsEnumerable<T1, T2> QueryEntities<T1, T2>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
return new EntityCollections<T1, T2>(this, groups);
return new GroupsEnumerable<T1, T2>(this, groups);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public (T1[], T2[]) QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
where T1 : struct, IEntityStruct
where T2 : struct, IEntityStruct
public GroupsEnumerable<T1, T2, T3> QueryEntities<T1, T2, T3>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent
{
var T1entities = QueryEntities<T1>(groupStruct, out var countCheck);
var T2entities = QueryEntities<T2>(groupStruct, out count);

if (count != countCheck)
{
throw new ECSException("Entity views count do not match in group. Entity 1: ' count: "
.FastConcat(countCheck)
.FastConcat(typeof(T1).ToString())
.FastConcat("'. Entity 2: ' count: ".FastConcat(count)
.FastConcat(typeof(T2).ToString())
.FastConcat("'")));
}

return (T1entities, T2entities);
return new GroupsEnumerable<T1, T2, T3>(this, groups);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public (T1[], T2[], T3[]) QueryEntities
<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct
public GroupsEnumerable<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent where T4 : struct, IEntityComponent
{
var T1entities = QueryEntities<T1>(groupStruct, out var countCheck1);
var T2entities = QueryEntities<T2>(groupStruct, out var countCheck2);
var T3entities = QueryEntities<T3>(groupStruct, out count);

if (count != countCheck1 || count != countCheck2)
throw new ECSException("Entity views count do not match in group. Entity 1: "
.FastConcat(typeof(T1).ToString()).FastConcat(" count: ").FastConcat(countCheck1).FastConcat(
" Entity 2: ".FastConcat(typeof(T2).ToString())
.FastConcat(" count: ").FastConcat(countCheck2)
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())).FastConcat(" count: ")
.FastConcat(count)));

return (T1entities, T2entities, T3entities);
return new GroupsEnumerable<T1, T2, T3, T4>(this, groups);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId)
where T : struct, IEntityStruct
public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroupStruct groupStructId)
where T : struct, IEntityComponent
{
if (SafeQueryEntityDictionary(groupStructId, out TypeSafeDictionary<T> typeSafeDictionary) == false)
throw new EntityGroupNotFoundException(groupStructId, typeof(T));
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
throw new EntityGroupNotFoundException(typeof(T) , groupStructId.ToName());

EGIDMapper<T> mapper;
mapper.map = typeSafeDictionary;

return mapper;
return (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId,
out EGIDMapper<T> mapper)
where T : struct, IEntityStruct
public bool TryQueryMappedEntities<T>
(ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper) where T : struct, IEntityComponent
{
mapper = default;
if (SafeQueryEntityDictionary(groupStructId, out TypeSafeDictionary<T> typeSafeDictionary) == false)
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false
|| typeSafeDictionary.count == 0)
return false;

mapper.map = typeSafeDictionary;
mapper = (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);

return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] QueryEntitiesAndIndex<T>(EGID entityGID, out uint index) where T : struct, IEntityStruct
public bool Exists<T>(EGID entityGID) where T : struct, IEntityComponent
{
T[] array;
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out index)) != null)
return array;
if (SafeQueryEntityDictionary<T>(entityGID.groupID, out var casted) == false)
return false;

throw new EntityNotFoundException(entityGID, typeof(T));
return casted != null && casted.ContainsKey(entityGID.entityID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array)
where T : struct, IEntityStruct
public bool Exists<T>(uint id, ExclusiveGroupStruct group) where T : struct, IEntityComponent
{
if ((array = QueryEntitiesAndIndexInternal<T>(entityGid, out index)) != null)
return true;
if (SafeQueryEntityDictionary<T>(group, out var casted) == false)
return false;

return false;
return casted != null && casted.ContainsKey(id);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] QueryEntitiesAndIndex<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index)
where T : struct, IEntityStruct
public bool ExistsAndIsNotEmpty(ExclusiveGroupStruct gid)
{
return QueryEntitiesAndIndex<T>(new EGID(id, group), out index);
}
if (groupEntityComponentsDB.TryGetValue(
gid, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> group) == true)
{
return group.count > 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryEntitiesAndIndex<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index,
out T[] array) where T : struct, IEntityStruct
{
return TryQueryEntitiesAndIndex(new EGID(id, group), out index, out array);
return false;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists<T>(EGID entityGID) where T : struct, IEntityStruct
public bool HasAny<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent
{
if (SafeQueryEntityDictionary(entityGID.groupID, out TypeSafeDictionary<T> casted) == false) return false;

return casted != null && casted.ContainsKey(entityGID.entityID);
return Count<T>(groupStruct) > 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct
public int Count<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent
{
if (SafeQueryEntityDictionary(group, out TypeSafeDictionary<T> casted) == false) return false;
if (SafeQueryEntityDictionary<T>(groupStruct, out var typeSafeDictionary) == false)
return 0;

return casted != null && casted.ContainsKey(id);
return (int) typeSafeDictionary.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid)
public bool FoundInGroups<T1>() where T1 : IEntityComponent
{
return _groupEntityViewsDB.ContainsKey(gid);
return groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasAny<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct
{
QueryEntities<T>(groupStruct, out var count);
return count > 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Count<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct
{
QueryEntities<T>(groupStruct, out var count);
return count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityStruct
{
_entityStream.PublishEntity(ref QueryEntity<T>(egid), egid);
}
public bool IsDisposing => _enginesRoot._isDisposing;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
T[] QueryEntitiesAndIndexInternal<T>(EGID entityGID, out uint index) where T : struct, IEntityStruct
internal bool SafeQueryEntityDictionary<T>(out ITypeSafeDictionary typeSafeDictionary,
FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType)
where T : IEntityComponent
{
index = 0;
if (SafeQueryEntityDictionary(entityGID.groupID, out TypeSafeDictionary<T> safeDictionary) == false)
return null;
if (entitiesInGroupPerType.TryGetValue(new RefWrapperType(TypeCache<T>.type), out var safeDictionary) == false)
{
typeSafeDictionary = default;
return false;
}

if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false)
return null;
//return the indexes entities if they exist
typeSafeDictionary = safeDictionary;

return safeDictionary.GetValuesArray(out _);
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
bool SafeQueryEntityDictionary<T>(uint group, out TypeSafeDictionary<T> typeSafeDictionary)
where T : struct, IEntityStruct
internal bool SafeQueryEntityDictionary<T>(ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary)
where T : IEntityComponent
{
if (UnsafeQueryEntityDictionary(group, TypeCache<T>.type, out var safeDictionary) == false)
{
@@ -257,45 +266,101 @@ namespace Svelto.ECS.Internal
}

//return the indexes entities if they exist
typeSafeDictionary = safeDictionary as TypeSafeDictionary<T>;
typeSafeDictionary = safeDictionary;

return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool UnsafeQueryEntityDictionary(uint group, Type type, out ITypeSafeDictionary typeSafeDictionary)
internal bool UnsafeQueryEntityDictionary(ExclusiveGroupStruct group, Type type, out ITypeSafeDictionary typeSafeDictionary)
{
//search for the group
if (_groupEntityViewsDB.TryGetValue(group, out var entitiesInGroupPerType) == false)
if (groupEntityComponentsDB.TryGetValue(group, out var entitiesInGroupPerType) == false)
{
typeSafeDictionary = null;
return false;
}

//search for the indexed entities in the group
return entitiesInGroupPerType.TryGetValue(new RefWrapper<Type>(type), out typeSafeDictionary);
return entitiesInGroupPerType.TryGetValue(new RefWrapperType(type), out typeSafeDictionary);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static T[] RetrieveEmptyEntityViewArray<T>()
internal bool FindIndex(uint entityID, ExclusiveGroupStruct @group, Type type, out uint index)
{
return EmptyList<T>.emptyArray;
EGID entityGID = new EGID(entityID, @group);

index = default;
if (UnsafeQueryEntityDictionary(@group, type, out var safeDictionary) == false)
return false;

if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false)
return false;

return true;
}

//grouped set of entity views, this is the standard way to handle entity views entity views are grouped per
//group, then indexable per type, then indexable per EGID. however the TypeSafeDictionary can return an array of
//values directly, that can be iterated over, so that is possible to iterate over all the entity views of
//a specific type inside a specific group.
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> _groupEntityViewsDB;
internal uint GetIndex(uint entityID, ExclusiveGroupStruct @group, Type type)
{
EGID entityGID = new EGID(entityID, @group);
if (UnsafeQueryEntityDictionary(@group, type, out var safeDictionary) == false)
{
throw new EntityNotFoundException(entityGID, type);
}

if (safeDictionary.TryFindIndex(entityGID.entityID, out var index) == false)
throw new EntityNotFoundException(entityGID, type);

//needed to be able to iterate over all the entities of the same type regardless the group
//may change in future
readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity;
readonly EntitiesStream _entityStream;
return index;
}

static class EmptyList<T>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static IBuffer<T> RetrieveEmptyEntityComponentArray<T>() where T : struct, IEntityComponent
{
internal static readonly T[] emptyArray = new T[0];
return EmptyList<T>.emptyArray;
}

static class EmptyList<T> where T : struct, IEntityComponent
{
internal static readonly IBuffer<T> emptyArray;

static EmptyList()
{
if (ComponentBuilder<T>.IS_ENTITY_VIEW_COMPONENT)
{
MB<T> b = default;

emptyArray = b;
}
else
{
NB<T> b = default;

emptyArray = b;
}
}
}

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

readonly EnginesRoot _enginesRoot;

EntitiesStreams _entityStream => _enginesRoot._entityStreams;

//grouped set of entity components, this is the standard way to handle entity components are grouped per
//group, then indexable per type, then indexable per EGID. however the TypeSafeDictionary can return an array of
//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<RefWrapperType, ITypeSafeDictionary>>
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<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>> groupsPerEntity =>
_enginesRoot._groupsPerEntity;
}
}

+ 0
- 138
EntityBuilder.CheckFields.cs Целия файл

@@ -1,138 +0,0 @@
#if !DEBUG || PROFILER
#define DISABLE_CHECKS
using System.Diagnostics;
#endif
using System;
using System.Reflection;

namespace Svelto.ECS
{
internal static class EntityBuilderUtilities
{
const string MSG = "Entity Structs field and Entity View Struct components must hold value types.";


#if DISABLE_CHECKS
[Conditional("_CHECKS_DISABLED")]
#endif
public static void CheckFields(Type entityStructType, bool needsReflection, bool isStringAllowed = false)
{
if (entityStructType == ENTITY_STRUCT_INFO_VIEW ||
entityStructType == EGIDType ||
entityStructType == EXCLUSIVEGROUPSTRUCTTYPE ||
entityStructType == SERIALIZABLE_ENTITY_STRUCT)
{
return;
}

if (needsReflection == false)
{
if (entityStructType.IsClass)
{
throw new EntityStructException("EntityStructs must be structs.", entityStructType);
}

FieldInfo[] fields = entityStructType.GetFields(BindingFlags.Public | BindingFlags.Instance);

for (var i = fields.Length - 1; i >= 0; --i)
{
FieldInfo fieldInfo = fields[i];
Type fieldType = fieldInfo.FieldType;

SubCheckFields(fieldType, entityStructType, isStringAllowed);
}
}
else
{
FieldInfo[] fields = entityStructType.GetFields(BindingFlags.Public | BindingFlags.Instance);

if (fields.Length < 1)
{
ProcessError("Entity View Structs must hold only entity components interfaces.", entityStructType);
}

for (int i = fields.Length - 1; i >= 0; --i)
{
FieldInfo fieldInfo = fields[i];

if (fieldInfo.FieldType.IsInterfaceEx() == false)
{
ProcessError("Entity View Structs must hold only entity components interfaces.",
entityStructType);
}

PropertyInfo[] properties = fieldInfo.FieldType.GetProperties(
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);

for (int j = properties.Length - 1; j >= 0; --j)
{
if (properties[j].PropertyType.IsGenericType)
{
Type genericTypeDefinition = properties[j].PropertyType.GetGenericTypeDefinition();
if (genericTypeDefinition == DISPATCHONSETTYPE ||
genericTypeDefinition == DISPATCHONCHANGETYPE)
{
continue;
}
}

Type propertyType = properties[j].PropertyType;
if (propertyType != STRINGTYPE)
{
//for EntityViewStructs, component fields that are structs that hold strings
//are allowed
SubCheckFields(propertyType, entityStructType, isStringAllowed: true);
}
}
}
}
}

static void SubCheckFields(Type fieldType, Type entityStructType, bool isStringAllowed = false)
{
if (fieldType.IsPrimitive || fieldType.IsValueType || (isStringAllowed == true && fieldType == STRINGTYPE))
{
if (fieldType.IsValueType && !fieldType.IsEnum && fieldType.IsPrimitive == false)
{
CheckFields(fieldType, false, isStringAllowed);
}

return;
}
ProcessError(MSG, entityStructType, fieldType);
}

static void ProcessError(string message, Type entityViewType, Type fieldType = null)
{
if (fieldType != null)
{
throw new EntityStructException(message, entityViewType, fieldType);
}

throw new EntityStructException(message, entityViewType);
}

static readonly Type DISPATCHONCHANGETYPE = typeof(DispatchOnChange<>);
static readonly Type DISPATCHONSETTYPE = typeof(DispatchOnSet<>);
static readonly Type EGIDType = typeof(EGID);
static readonly Type EXCLUSIVEGROUPSTRUCTTYPE = typeof(ExclusiveGroup.ExclusiveGroupStruct);
static readonly Type SERIALIZABLE_ENTITY_STRUCT = typeof(SerializableEntityStruct);
static readonly Type STRINGTYPE = typeof(string);

internal static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityStructInfoView);
}

public class EntityStructException : Exception
{
public EntityStructException(string message, Type entityViewType, Type type) :
base(message.FastConcat(" entity view: '", entityViewType.ToString(), "', field: '", type.ToString()))
{
}

public EntityStructException(string message, Type entityViewType) :
base(message.FastConcat(" entity view: ", entityViewType.ToString()))
{
}
}
}

+ 0
- 146
EntityBuilder.cs Целия файл

@@ -1,146 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Svelto.DataStructures;
using Svelto.ECS.Hybrid;
using Svelto.ECS.Internal;
using Svelto.Utilities;

namespace Svelto.ECS
{
public class EntityBuilder<T> : IEntityBuilder where T : struct, IEntityStruct
{
static class EntityView
{
internal static readonly FasterList<KeyValuePair<Type, ActionCast<T>>> cachedFields;
internal static readonly Dictionary<Type, Type[]> cachedTypes;
#if DEBUG && !PROFILER
internal static readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType;
#else
internal static readonly Dictionary<Type, object> implementorsByType;
#endif
static EntityView()
{
cachedFields = new FasterList<KeyValuePair<Type, ActionCast<T>>>();

var type = typeof(T);

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

for (var i = fields.Length - 1; i >= 0; --i)
{
var field = fields[i];

var setter = FastInvoke<T>.MakeSetter(field);

cachedFields.Add(new KeyValuePair<Type, ActionCast<T>>(field.FieldType, setter));
}

cachedTypes = new Dictionary<Type, Type[]>();

#if DEBUG && !PROFILER
implementorsByType = new Dictionary<Type, ECSTuple<object, int>>();
#else
implementorsByType = new Dictionary<Type, object>();
#endif
}

internal static void InitCache()
{}

internal static void BuildEntityView(out T entityView)
{
entityView = new T();
}
}

public EntityBuilder()
{
_initializer = DEFAULT_IT;

EntityBuilderUtilities.CheckFields(ENTITY_VIEW_TYPE, NEEDS_REFLECTION);

if (NEEDS_REFLECTION)
EntityView.InitCache();
}

public EntityBuilder(in T initializer) : this()
{
_initializer = initializer;
}

public void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID egid,
IEnumerable<object> implementors)
{
if (dictionary == null)
dictionary = new TypeSafeDictionary<T>();

var castedDic = dictionary as TypeSafeDictionary<T>;

if (NEEDS_REFLECTION)
{
DBC.ECS.Check.Require(implementors != null,
$"Implementors not found while building an EntityView `{typeof(T)}`");
DBC.ECS.Check.Require(castedDic.ContainsKey(egid.entityID) == false,
$"building an entity with already used entity id! id: '{(ulong) egid}', {ENTITY_VIEW_NAME}");

EntityView.BuildEntityView(out var entityView);

this.FillEntityView(ref entityView, entityViewBlazingFastReflection, implementors,
EntityView.implementorsByType, EntityView.cachedTypes);

castedDic.Add(egid.entityID, entityView);
}
else
{
DBC.ECS.Check.Require(!castedDic.ContainsKey(egid.entityID),
$"building an entity with already used entity id! id: '{egid.entityID}'");

castedDic.Add(egid.entityID, _initializer);
}
}

ITypeSafeDictionary IEntityBuilder.Preallocate(ref ITypeSafeDictionary dictionary, uint size)
{
return Preallocate(ref dictionary, size);
}

static ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size)
{
if (dictionary == null)
dictionary = new TypeSafeDictionary<T>(size);
else
dictionary.SetCapacity(size);

return dictionary;
}

public Type GetEntityType()
{
return ENTITY_VIEW_TYPE;
}

static EntityBuilder()
{
ENTITY_VIEW_TYPE = typeof(T);
DEFAULT_IT = default;
NEEDS_REFLECTION = typeof(IEntityViewStruct).IsAssignableFrom(ENTITY_VIEW_TYPE);
HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_VIEW_TYPE);
ENTITY_VIEW_NAME = ENTITY_VIEW_TYPE.ToString();
SetEGIDWithoutBoxing<T>.Warmup();
}

readonly T _initializer;

static FasterList<KeyValuePair<Type, ActionCast<T>>> entityViewBlazingFastReflection =>
EntityView.cachedFields;

internal static readonly Type ENTITY_VIEW_TYPE;
public static readonly bool HAS_EGID;

static readonly T DEFAULT_IT;
static readonly bool NEEDS_REFLECTION;
static readonly string ENTITY_VIEW_NAME;
}
}

+ 0
- 8
EntityBuilder.cs.rej Целия файл

@@ -1,8 +0,0 @@
diff a/Assets/Svelto/Svelto.ECS/EntityBuilder.cs b/Assets/Svelto/Svelto.ECS/EntityBuilder.cs (rejected hunks)
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using DBC.ECS;
using Svelto.DataStructures;
using Svelto.ECS.Hybrid;
using Svelto.ECS.Internal;

+ 154
- 259
EntityCollection.cs Целия файл

@@ -1,326 +1,221 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public struct EntityCollection<T>
public readonly ref struct EntityCollection<T> where T : struct, IEntityComponent
{
public EntityCollection(T[] array, uint count)
static readonly bool IsUnmanaged = TypeSafeDictionary<T>.IsUnmanaged;
public EntityCollection(IBuffer<T> buffer, uint count):this()
{
_array = array;
_count = count;
if (IsUnmanaged)
_nativedBuffer = (NB<T>) buffer;
else
_managedBuffer = (MB<T>) buffer;
_count = count;
}

public EntityIterator GetEnumerator()
{
return new EntityIterator(_array, _count);
}

readonly T[] _array;
readonly uint _count;

public struct EntityIterator : IEnumerator<T>
{
public EntityIterator(T[] array, uint count) : this()
{
_array = array;
_count = count;
_index = -1;
}

public bool MoveNext()
{
return ++_index < _count;
}

public void Reset()
{
_index = -1;
}

public ref T Current => ref _array[_index];
public uint count => _count;

T IEnumerator<T>.Current => throw new NotImplementedException();
object IEnumerator.Current => throw new NotImplementedException();
internal readonly MB<T> _managedBuffer;
internal readonly NB<T> _nativedBuffer;

public void Dispose() {}

readonly T[] _array;
readonly uint _count;
int _index;
}
readonly uint _count;
}
public struct EntityCollection<T1, T2>

public readonly ref struct EntityCollection<T1, T2> where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
public EntityCollection(in (T1[], T2[]) array, uint count)
internal EntityCollection(in EntityCollection<T1> array1, in EntityCollection<T2> array2)
{
_array = array;
_count = count;
_array1 = array1;
_array2 = array2;
}

public EntityIterator GetEnumerator()
public uint count => _array1.count;

internal EntityCollection<T2> buffer2
{
return new EntityIterator(_array, _count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array2;
}

readonly (T1[], T2[]) _array;
readonly uint _count;

public struct EntityIterator : IEnumerator<ValueRef<T1, T2>>
internal EntityCollection<T1> buffer1
{
public EntityIterator((T1[], T2[]) array, uint count) : this()
{
_array = array;
_count = count;
_index = -1;
}

public bool MoveNext()
{
return ++_index < _count;
}

public void Reset()
{
_index = -1;
}

public ValueRef<T1, T2> Current => new ValueRef<T1, T2>(_array, (uint) _index);

ValueRef<T1, T2> IEnumerator<ValueRef<T1, T2>>. Current => throw new NotImplementedException();
object IEnumerator.Current => throw new NotImplementedException();

public void Dispose() {}

readonly (T1[], T2[]) _array;
readonly uint _count;
int _index;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array1;
}

readonly EntityCollection<T1> _array1;
readonly EntityCollection<T2> _array2;
}
public struct EntityCollection<T1, T2, T3>

public readonly ref struct EntityCollection<T1, T2, T3> where T3 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T1 : struct, IEntityComponent
{
public EntityCollection(in (T1[], T2[], T3[]) array, uint count)
internal EntityCollection
(in EntityCollection<T1> array1, in EntityCollection<T2> array2, in EntityCollection<T3> array3)
{
_array = array;
_count = count;
_array1 = array1;
_array2 = array2;
_array3 = array3;
}

public EntityIterator GetEnumerator()
internal EntityCollection<T1> buffer1
{
return new EntityIterator(_array, _count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array1;
}

readonly (T1[], T2[], T3[]) _array;
readonly uint _count;

public struct EntityIterator : IEnumerator<ValueRef<T1, T2, T3>>
internal EntityCollection<T2> buffer2
{
public EntityIterator((T1[], T2[], T3[]) array, uint count) : this()
{
_array = array;
_count = count;
_index = -1;
}

public bool MoveNext()
{
return ++_index < _count;
}

public void Reset()
{
_index = -1;
}

public ValueRef<T1, T2, T3> Current => new ValueRef<T1, T2, T3>(_array, (uint) _index);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array2;
}

ValueRef<T1, T2, T3> IEnumerator<ValueRef<T1, T2, T3>>.Current => throw new NotImplementedException();
object IEnumerator. Current => throw new NotImplementedException();
internal EntityCollection<T3> buffer3
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array3;
}

public void Dispose() {}
internal uint count => buffer1.count;

readonly (T1[], T2[], T3[]) _array;
readonly uint _count;
int _index;
}
readonly EntityCollection<T1> _array1;
readonly EntityCollection<T2> _array2;
readonly EntityCollection<T3> _array3;
}
public struct EntityCollections<T> where T : struct, IEntityStruct

public readonly ref struct EntityCollection<T1, T2, T3, T4>
where T1 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
where T4 : struct, IEntityComponent
{
public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this()
internal EntityCollection
(in EntityCollection<T1> array1, in EntityCollection<T2> array2, in EntityCollection<T3> array3, in EntityCollection<T4> array4)
{
_db = db;
_groups = groups;
_array1 = array1;
_array2 = array2;
_array3 = array3;
_array4 = array4;
}

public EntityGroupsIterator GetEnumerator()
internal EntityCollection<T1> Item1
{
return new EntityGroupsIterator(_db, _groups);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array1;
}

readonly IEntitiesDB _db;
readonly ExclusiveGroup[] _groups;

public struct EntityGroupsIterator : IEnumerator<T>
internal EntityCollection<T2> Item2
{
public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this()
{
_db = db;
_groups = groups;
_indexGroup = -1;
_index = -1;
}

public bool MoveNext()
{
while (_index + 1 >= _count && ++_indexGroup < _groups.Length)
{
_index = -1;
_array = _db.QueryEntities<T>(_groups[_indexGroup], out _count);
}

return ++_index < _count;
}

public void Reset()
{
_index = -1;
_indexGroup = -1;
_count = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array2;
}

public ref T Current => ref _array[_index];
internal EntityCollection<T3> Item3
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array3;
}
internal EntityCollection<T4> Item4
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array4;
}

T IEnumerator<T>.Current => throw new NotImplementedException();
object IEnumerator.Current => throw new NotImplementedException();
internal uint count => _array1.count;

public void Dispose() {}
readonly EntityCollection<T1> _array1;
readonly EntityCollection<T2> _array2;
readonly EntityCollection<T3> _array3;
readonly EntityCollection<T4> _array4;
}

readonly IEntitiesDB _db;
readonly ExclusiveGroup[] _groups;
public readonly struct BT<BufferT1, BufferT2, BufferT3, BufferT4>
{
public readonly BufferT1 buffer1;
public readonly BufferT2 buffer2;
public readonly BufferT3 buffer3;
public readonly BufferT4 buffer4;
public readonly int count;

T[] _array;
uint _count;
int _index;
int _indexGroup;
public BT(BufferT1 bufferT1, BufferT2 bufferT2, BufferT3 bufferT3, BufferT4 bufferT4, uint count) : this()
{
this.buffer1 = bufferT1;
this.buffer2 = bufferT2;
this.buffer3 = bufferT3;
this.buffer4 = bufferT4;
this.count = (int) count;
}
}

public struct EntityCollections<T1, T2> where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct
public readonly struct BT<BufferT1, BufferT2, BufferT3>
{
public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this()
{
_db = db;
_groups = groups;
}
public readonly BufferT1 buffer1;
public readonly BufferT2 buffer2;
public readonly BufferT3 buffer3;
public readonly int count;

public EntityGroupsIterator GetEnumerator()
public BT(BufferT1 bufferT1, BufferT2 bufferT2, BufferT3 bufferT3, uint count) : this()
{
return new EntityGroupsIterator(_db, _groups);
this.buffer1 = bufferT1;
this.buffer2 = bufferT2;
this.buffer3 = bufferT3;
this.count = (int) count;
}

readonly IEntitiesDB _db;
readonly ExclusiveGroup[] _groups;

public struct EntityGroupsIterator : IEnumerator<ValueRef<T1, T2>>
public void Deconstruct(out BufferT1 bufferT1, out BufferT2 bufferT2, out BufferT3 bufferT3, out int count)
{
public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this()
{
_db = db;
_groups = groups;
_indexGroup = -1;
_index = -1;
}

public bool MoveNext()
{
while (_index + 1 >= _count && ++_indexGroup < _groups.Length)
{
_index = -1;
var array1 = _db.QueryEntities<T1>(_groups[_indexGroup], out _count);
var array2 = _db.QueryEntities<T2>(_groups[_indexGroup], out var count1);
_array = (array1, array2);

#if DEBUG && !PROFILER
if (_count != count1)
throw new ECSException("number of entities in group doesn't match");
#endif
}

return ++_index < _count;
}

public void Reset()
{
_index = -1;
_indexGroup = -1;

var array1 = _db.QueryEntities<T1>(_groups[0], out _count);
var array2 = _db.QueryEntities<T2>(_groups[0], out var count1);
_array = (array1, array2);
#if DEBUG && !PROFILER
if (_count != count1)
throw new ECSException("number of entities in group doesn't match");
#endif
}

public ValueRef<T1, T2> Current
{
get
{
var valueRef = new ValueRef<T1, T2>(_array, (uint) _index);
return valueRef;
}
}

ValueRef<T1, T2> IEnumerator<ValueRef<T1, T2>>.Current => throw new NotImplementedException();
object IEnumerator.Current => throw new NotImplementedException();

public void Dispose() {}

readonly IEntitiesDB _db;
readonly ExclusiveGroup[] _groups;
uint _count;
int _index;
int _indexGroup;
(T1[], T2[]) _array;
bufferT1 = buffer1;
bufferT2 = buffer2;
bufferT3 = buffer3;
count = this.count;
}
}
public struct ValueRef<T1, T2>
{
readonly (T1[], T2[]) array;

readonly uint index;
public readonly struct BT<BufferT1>
{
public readonly BufferT1 buffer;
public readonly int count;

public ValueRef(in (T1[], T2[]) entity1, uint i)
public BT(BufferT1 bufferT1, uint count) : this()
{
array = entity1;
index = i;
this.buffer = bufferT1;
this.count = (int) count;
}

public ref T1 entityStructA => ref array.Item1[index];
public ref T2 entityStructB => ref array.Item2[index];
public void Deconstruct(out BufferT1 bufferT1, out int count)
{
bufferT1 = buffer;
count = this.count;
}
public static implicit operator BufferT1(BT<BufferT1> t) => t.buffer;
}
public struct ValueRef<T1, T2, T3>
{
readonly (T1[], T2[], T3[]) array;

readonly uint index;
public readonly struct BT<BufferT1, BufferT2>
{
public readonly BufferT1 buffer1;
public readonly BufferT2 buffer2;
public readonly int count;

public ValueRef(in (T1[], T2[], T3[]) entity1, uint i)
public BT(BufferT1 bufferT1, BufferT2 bufferT2, uint count) : this()
{
array = entity1;
index = i;
this.buffer1 = bufferT1;
this.buffer2 = bufferT2;
this.count = (int) count;
}
public void Deconstruct(out BufferT1 bufferT1, out BufferT2 bufferT2, out int count)
{
bufferT1 = buffer1;
bufferT2 = buffer2;
count = this.count;
}

public ref T1 entityStructA => ref array.Item1[index];
public ref T2 entityStructB => ref array.Item2[index];
public ref T3 entityStructC => ref array.Item3[index];

}
}

+ 63
- 0
EntityComponentInitializer.cs Целия файл

@@ -0,0 +1,63 @@
using System;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public readonly ref struct EntityComponentInitializer
{
public EntityComponentInitializer(EGID id, FasterDictionary<RefWrapperType, ITypeSafeDictionary> group)
{
_group = group;
_ID = id;
}

public EGID EGID => _ID;

public void Init<T>(T initializer) where T : struct, IEntityComponent
{
if (_group.TryGetValue(new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE),
out var typeSafeDictionary) == false) return;

var dictionary = (ITypeSafeDictionary<T>) typeSafeDictionary;

if (ComponentBuilder<T>.HAS_EGID)
SetEGIDWithoutBoxing<T>.SetIDWithoutBoxing(ref initializer, _ID);

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

public ref T GetOrCreate<T>() where T : struct, IEntityComponent
{
ref var entityDictionary = ref _group.GetOrCreate(new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE)
, TypeSafeDictionaryFactory<T>.Create);
var dictionary = (ITypeSafeDictionary<T>) entityDictionary;

return ref dictionary.GetOrCreate(_ID.entityID);
}
public ref T Get<T>() where T : struct, IEntityComponent
{
return ref (_group[new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE)] as ITypeSafeDictionary<T>)[
_ID.entityID];
}
public bool Has<T>() where T : struct, IEntityComponent
{
if (_group.TryGetValue(new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE),
out var typeSafeDictionary))
{
var dictionary = (ITypeSafeDictionary<T>) typeSafeDictionary;

if (dictionary.ContainsKey(_ID.entityID))
return true;
}

return false;
}
readonly EGID _ID;
readonly FasterDictionary<RefWrapperType, ITypeSafeDictionary> _group;
}
}

+ 11
- 0
EntityComponentInitializer.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 005da4cbd47736e1bc2fb1676cb30190
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 12
- 3
EntityDescriptorTemplate.cs Целия файл

@@ -1,17 +1,26 @@
using System;

namespace Svelto.ECS
{
public interface IEntityDescriptor
{
IEntityBuilder[] entitiesToBuild { get; }
IComponentBuilder[] componentsToBuild { get; }
}
public interface IDynamicEntityDescriptor: IEntityDescriptor
{
}

static class EntityDescriptorTemplate<TType> where TType : IEntityDescriptor, new()
{
static EntityDescriptorTemplate()
{
descriptor = new TType();
realDescriptor = new TType();
descriptor = realDescriptor;
}

public static IEntityDescriptor descriptor { get; }
public static TType realDescriptor { get; }
public static Type type => typeof(TType);
public static IEntityDescriptor descriptor { get; }
}
}

+ 33
- 48
EntityFactory.cs Целия файл

@@ -6,92 +6,77 @@ namespace Svelto.ECS.Internal
{
static class EntityFactory
{
public static FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> BuildGroupedEntities(EGID egid,
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd,
IEntityBuilder[] entitiesToBuild,
IEnumerable<object> implementors)
public static FasterDictionary<RefWrapperType, ITypeSafeDictionary> BuildGroupedEntities
(EGID egid, EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd
, IComponentBuilder[] componentsToBuild, IEnumerable<object> implementors, Type implementorType)
{
var group = FetchEntityGroup(egid.groupID, groupEntitiesToAdd);

BuildEntitiesAndAddToGroup(egid, group, entitiesToBuild, implementors);
BuildEntitiesAndAddToGroup(egid, group, componentsToBuild, implementors, implementorType);

return group;
}

static FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> FetchEntityGroup(uint groupID,
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityViewsByType)
static FasterDictionary<RefWrapperType, ITypeSafeDictionary> FetchEntityGroup(ExclusiveGroupStruct groupID,
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityComponentsByType)
{
if (groupEntityViewsByType.current.TryGetValue(groupID, out var group) == false)
if (groupEntityComponentsByType.current.TryGetValue(groupID, out var group) == false)
{
group = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();
group = new FasterDictionary<RefWrapperType, ITypeSafeDictionary>();
groupEntityViewsByType.current.Add(groupID, group);
groupEntityComponentsByType.current.Add(groupID, group);
}

if (groupEntityViewsByType.currentEntitiesCreatedPerGroup.TryGetValue(groupID, out var value) == false)
groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = 0;
if (groupEntityComponentsByType.currentEntitiesCreatedPerGroup.TryGetValue(groupID, out var value) == false)
groupEntityComponentsByType.currentEntitiesCreatedPerGroup[groupID] = 0;
else
groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = value+1;
groupEntityComponentsByType.currentEntitiesCreatedPerGroup[groupID] = value+1;
return group;
}

static void BuildEntitiesAndAddToGroup(EGID entityID,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group,
IEntityBuilder[] entityBuilders, IEnumerable<object> implementors)
static void BuildEntitiesAndAddToGroup
(EGID entityID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> @group
, IComponentBuilder[] componentBuilders, IEnumerable<object> implementors, Type implementorType)
{
#if DEBUG && !PROFILER
var count = componentBuilders.Length;

#if DEBUG && !PROFILE_SVELTO
HashSet<Type> types = new HashSet<Type>();
#endif
InternalBuild(entityID, group, entityBuilders, implementors
#if DEBUG && !PROFILER
, types
#endif
);
}

static void InternalBuild(EGID entityID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group,
IEntityBuilder[] entityBuilders, IEnumerable<object> implementors
#if DEBUG && !PROFILER
, HashSet<Type> types
#endif
)
{
var count = entityBuilders.Length;
#if DEBUG && !PROFILER
for (var index = 0; index < count; ++index)
{
var entityViewType = entityBuilders[index].GetEntityType();
if (types.Contains(entityViewType))
var entityComponentType = componentBuilders[index].GetEntityComponentType();
if (types.Contains(entityComponentType))
{
throw new ECSException("EntityBuilders must be unique inside an EntityDescriptor");
throw new ECSException($"EntityBuilders must be unique inside an EntityDescriptor. Descriptor Type {implementorType} Component Type: {entityComponentType}");
}

types.Add(entityViewType);
types.Add(entityComponentType);
}
#endif
for (var index = 0; index < count; ++index)
{
var entityStructBuilder = entityBuilders[index];
var entityViewType = entityStructBuilder.GetEntityType();
var entityComponentBuilder = componentBuilders[index];
var entityComponentType = entityComponentBuilder.GetEntityComponentType();

BuildEntity(entityID, group, entityViewType, entityStructBuilder, implementors);
BuildEntity(entityID, @group, entityComponentType, entityComponentBuilder, implementors);
}
}

static void BuildEntity(EGID entityID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group,
Type entityViewType, IEntityBuilder entityBuilder, IEnumerable<object> implementors)
static void BuildEntity(EGID entityID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> group,
Type entityComponentType, IComponentBuilder componentBuilder, IEnumerable<object> implementors)
{
var entityViewsPoolWillBeCreated =
group.TryGetValue(new RefWrapper<Type>(entityViewType), out var safeDictionary) == false;
var entityComponentsPoolWillBeCreated =
group.TryGetValue(new RefWrapperType(entityComponentType), out var safeDictionary) == false;

//passing the undefined entityViewsByType inside the entityViewBuilder will allow it to be created with the
//passing the undefined entityComponentsByType inside the entityComponentBuilder will allow it to be created with the
//correct type and casted back to the undefined list. that's how the list will be eventually of the target
//type.
entityBuilder.BuildEntityAndAddToList(ref safeDictionary, entityID, implementors);
componentBuilder.BuildEntityAndAddToList(ref safeDictionary, entityID, implementors);

if (entityViewsPoolWillBeCreated)
group.Add(new RefWrapper<Type>(entityViewType), safeDictionary);
if (entityComponentsPoolWillBeCreated)
group.Add(new RefWrapperType(entityComponentType), safeDictionary);
}
}
}

+ 2
- 2
EntityGroupNotFoundException.cs Целия файл

@@ -4,8 +4,8 @@ namespace Svelto.ECS.Internal
{
class EntityGroupNotFoundException : Exception
{
public EntityGroupNotFoundException(uint groupId, Type type)
: base("entity group not found ".FastConcat(type.ToString()))
public EntityGroupNotFoundException(Type type, string toName)
: base($"entity group {toName} not used for component type ".FastConcat(type.ToString()))
{
}
}

+ 0
- 11
EntityHierarchyStruct.cs Целия файл

@@ -1,11 +0,0 @@
namespace Svelto.ECS
{
public struct EntityHierarchyStruct: IEntityStruct, INeedEGID
{
public readonly ExclusiveGroup.ExclusiveGroupStruct parentGroup;
public EntityHierarchyStruct(ExclusiveGroup group): this() { parentGroup = group; }
public EGID ID { get; set; }
}
}

+ 0
- 11
EntityHierarchyStruct.cs.meta Целия файл

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: dd25991b7ae539ff89f73ae33b98106f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 2
- 2
EntityInfoView.cs Целия файл

@@ -1,7 +1,7 @@
namespace Svelto.ECS
{
struct EntityStructInfoView: IEntityStruct
struct EntityInfoComponent: IEntityComponent
{
public IEntityBuilder[] entitiesToBuild;
public IComponentBuilder[] componentsToBuild;
}
}

+ 1
- 1
EntityNotFoundException.cs Целия файл

@@ -5,7 +5,7 @@ namespace Svelto.ECS
public class EntityNotFoundException : Exception
{
public EntityNotFoundException(EGID entityEGID, Type entityType) : base(
$"entity of type '{entityType}' with ID '{entityEGID.entityID}', group '{(uint) entityEGID.groupID}' not found!")
$"entity of type '{entityType}' with ID '{entityEGID.entityID}', group '{entityEGID.groupID.ToName()}' not found!")
{
}
}

+ 0
- 163
EntityStream.cs Целия файл

@@ -1,163 +0,0 @@
using System;
using Svelto.DataStructures;

namespace Svelto.ECS
{
/// <summary>
/// Do not use this class in place of a normal polling.
/// I eventually realised than in ECS no form of communication other than polling entity components can exist.
/// Using groups, you can have always an optimal set of entity components to poll, so EntityStreams must be used
/// only if:
/// - you want to polling engine to be able to track all the entity changes happening in between polls and not
/// just the current state
/// - you want a thread-safe way to read entity states, which includes all the state changes and not the last
/// one only
/// - you want to communicate between EnginesRoots
/// </summary>
class EntitiesStream : IDisposable
{
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct
{
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false) _streams[TypeRefWrapper<T>.wrapper] = new EntityStream<T>();

return (_streams[TypeRefWrapper<T>.wrapper] as EntityStream<T>).GenerateConsumer(name, capacity);
}

public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity)
where T : unmanaged, IEntityStruct
{
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false) _streams[TypeRefWrapper<T>.wrapper] = new EntityStream<T>();

return (_streams[TypeRefWrapper<T>.wrapper] as EntityStream<T>).GenerateConsumer(group, name, capacity);
}

internal void PublishEntity<T>(ref T entity, EGID egid) where T : unmanaged, IEntityStruct
{
if (_streams.TryGetValue(TypeRefWrapper<T>.wrapper, out var typeSafeStream))
(typeSafeStream as EntityStream<T>).PublishEntity(ref entity, egid);
else
Console.LogDebug("No Consumers are waiting for this entity to change ", typeof(T));
}

readonly ThreadSafeDictionary<RefWrapper<Type>, ITypeSafeStream> _streams =
new ThreadSafeDictionary<RefWrapper<Type>, ITypeSafeStream>();

public void Dispose()
{
_streams.Clear();
}
}

interface ITypeSafeStream
{}

class EntityStream<T> : ITypeSafeStream where T : unmanaged, IEntityStruct
{
public void PublishEntity(ref T entity, EGID egid)
{
for (int i = 0; i < _consumers.Count; i++)
{
if (_consumers[i]._hasGroup)
{
if (egid.groupID == _consumers[i]._group)
{
_consumers[i].Enqueue(entity, egid);
}
}
else
{
_consumers[i].Enqueue(entity, egid);
}
}
}

public Consumer<T> GenerateConsumer(string name, uint capacity)
{
var consumer = new Consumer<T>(name, capacity, this);

_consumers.Add(consumer);

return consumer;
}

public Consumer<T> GenerateConsumer(ExclusiveGroup group, string name, uint capacity)
{
var consumer = new Consumer<T>(group, name, capacity, this);

_consumers.Add(consumer);

return consumer;
}

public void RemoveConsumer(Consumer<T> consumer)
{
_consumers.UnorderedRemove(consumer);
}

readonly FasterListThreadSafe<Consumer<T>> _consumers = new FasterListThreadSafe<Consumer<T>>();
}

public struct Consumer<T> : IDisposable where T : unmanaged, IEntityStruct
{
internal Consumer(string name, uint capacity, EntityStream<T> stream):this()
{
#if DEBUG && !PROFILER
_name = name;
#endif
_ringBuffer = new RingBuffer<ValueTuple<T, EGID>>((int) capacity,
#if DEBUG && !PROFILER
_name
#else
string.Empty
#endif
);

_stream = stream;
}

internal Consumer(ExclusiveGroup group, string name, uint capacity, EntityStream<T> stream) : this(name,
capacity, stream)
{
_group = group;
_hasGroup = true;
}

internal void Enqueue(in T entity, in EGID egid)
{
_ringBuffer.Enqueue((entity, egid));
}

public bool TryDequeue(out T entity)
{
var tryDequeue = _ringBuffer.TryDequeue(out var values);

entity = values.Item1;

return tryDequeue;
}

public bool TryDequeue(out T entity, out EGID id)
{
var tryDequeue = _ringBuffer.TryDequeue(out var values);

entity = values.Item1;
id = values.Item2;

return tryDequeue;
}
public void Flush() { _ringBuffer.Reset(); }
public void Dispose() { _stream.RemoveConsumer(this); }
public uint Count() { return (uint) _ringBuffer.Count; }

readonly RingBuffer<ValueTuple<T, EGID>> _ringBuffer;
readonly EntityStream<T> _stream;

internal readonly ExclusiveGroup _group;
internal readonly bool _hasGroup;

#if DEBUG && !PROFILER
readonly string _name;
#endif
}
}

+ 0
- 75
EntityStructInitializer.cs Целия файл

@@ -1,75 +0,0 @@
using System;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public ref struct EntityStructInitializer
{
public EntityStructInitializer(EGID id, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group)
{
_group = group;
_ID = id;
}

public void Init<T>(T initializer) where T : struct, IEntityStruct
{
if (_group.TryGetValue(new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE),
out var typeSafeDictionary) == false) return;

var dictionary = (TypeSafeDictionary<T>) typeSafeDictionary;

if (EntityBuilder<T>.HAS_EGID)
SetEGIDWithoutBoxing<T>.SetIDWithoutBoxing(ref initializer, _ID);

if (dictionary.TryFindIndex(_ID.entityID, out var findElementIndex))
dictionary.GetDirectValue(findElementIndex) = initializer;
}
public void CopyFrom<T>(T initializer) where T : struct, IEntityStruct
{
var dictionary = (TypeSafeDictionary<T>) _group[new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE)];

if (EntityBuilder<T>.HAS_EGID)
SetEGIDWithoutBoxing<T>.SetIDWithoutBoxing(ref initializer, _ID);

dictionary[_ID.entityID] = initializer;
}

public ref T GetOrCreate<T>() where T : struct, IEntityStruct
{
ref var entityDictionary = ref _group.GetOrCreate(new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE)
, () => new TypeSafeDictionary<T>());
var dictionary = (TypeSafeDictionary<T>) entityDictionary;

return ref dictionary.GetOrCreate(_ID.entityID);
}
public T Get<T>() where T : struct, IEntityStruct
{
return (_group[new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE)] as TypeSafeDictionary<T>)[_ID.entityID];
}

public bool Has<T>() where T : struct, IEntityStruct
{
if (_group.TryGetValue(new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE),
out var typeSafeDictionary))
{
var dictionary = (TypeSafeDictionary<T>) typeSafeDictionary;

if (dictionary.ContainsKey(_ID.entityID))
return true;
}

return false;
}

public static EntityStructInitializer CreateEmptyInitializer()
{
return new EntityStructInitializer(new EGID(), new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>());
}

readonly EGID _ID;
readonly FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> _group;
}
}

+ 0
- 11
EntityStructInitializer.cs.meta Целия файл

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: fb6bf1ad9be534e8a24b96e680030bb8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 14
- 2
EntitySubmissionScheduler.cs Целия файл

@@ -2,8 +2,20 @@ using System;

namespace Svelto.ECS.Schedulers
{
public interface IEntitySubmissionScheduler: IDisposable
public interface IEntitiesSubmissionScheduler: IDisposable
{
EnginesRoot.EntitiesSubmitter onTick { set; }
bool paused { get; set; }
}
public abstract class EntitiesSubmissionScheduler: IEntitiesSubmissionScheduler
{
protected internal abstract EnginesRoot.EntitiesSubmitter onTick { set; }
public abstract void Dispose();
public abstract bool paused { get; set; }
}
public abstract class ISimpleEntitiesSubmissionScheduler: EntitiesSubmissionScheduler
{
public abstract void SubmitEntities();
}
}

+ 20
- 8
EntitySubmitOperation.cs Целия файл

@@ -1,5 +1,4 @@
using System;
using System.Diagnostics;

namespace Svelto.ECS
{
@@ -9,25 +8,37 @@ namespace Svelto.ECS
: IEquatable<EntitySubmitOperation>
{
public readonly EntitySubmitOperationType type;
public readonly IEntityBuilder[] builders;
public readonly IComponentBuilder[] builders;
public readonly EGID fromID;
public readonly EGID toID;
#if DEBUG && !PROFILER
public StackFrame trace;
#if DEBUG && !PROFILE_SVELTO
public System.Diagnostics.StackFrame trace;
#endif

public EntitySubmitOperation(EntitySubmitOperationType operation, EGID from, EGID to,
IEntityBuilder[] builders = null)
IComponentBuilder[] builders = null)
{
type = operation;
this.builders = builders;
fromID = from;
toID = to;
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
trace = default;
#endif
}

public EntitySubmitOperation
(EntitySubmitOperationType operation, ExclusiveGroupStruct @group
, IComponentBuilder[] descriptorComponentsToBuild):this()
{
type = operation;
this.builders = descriptorComponentsToBuild;
fromID = new EGID(0, group);
#if DEBUG && !PROFILE_SVELTO
trace = default;
#endif
}

public static bool operator ==(EntitySubmitOperation obj1, EntitySubmitOperation obj2)
{
return obj1.Equals(obj2);
@@ -48,6 +59,7 @@ namespace Svelto.ECS
{
Swap,
Remove,
RemoveGroup
RemoveGroup,
SwapGroup
}
}

+ 66
- 68
EntityViewUtility.cs Целия файл

@@ -1,77 +1,89 @@
using System;
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using Svelto.Utilities;

namespace Svelto.ECS
{
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
struct ECSTuple<T1, T2>
{
public readonly T1 implementorType;
public readonly T1 instance;
public T2 numberOfImplementations;

public ECSTuple(T1 implementor, T2 v)
{
implementorType = implementor;
instance = implementor;
numberOfImplementations = v;
}
}
#endif

static class EntityViewUtility
static class EntityComponentUtility
{
const string DUPLICATE_IMPLEMENTOR_ERROR =
"<color=teal>Svelto.ECS</color> the same component is implemented with more than one implementor. This is "
+ "considered an error and MUST be fixed. ";

const string NULL_IMPLEMENTOR_ERROR =
"<color=teal>Svelto.ECS</color> Null implementor, please be careful about the implementors passed to avoid "
+ "performance loss ";

public static void FillEntityView<T>(this IEntityBuilder entityBuilder
, ref T entityView
, FasterList<KeyValuePair<Type, ActionCast<T>>>
entityViewBlazingFastReflection
, IEnumerable<object> implementors,
#if DEBUG && !PROFILER
Dictionary<Type, ECSTuple<object, int>> implementorsByType
const string NOT_FOUND_EXCEPTION =
"<color=teal>Svelto.ECS</color> Implementor not found for an EntityComponent. ";

public static void FillEntityComponent<T>
(this IComponentBuilder componentBuilder, ref T entityComponent
, FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> entityComponentBlazingFastReflection
, IEnumerable<object> implementors
#if DEBUG && !PROFILE_SVELTO
,Dictionary<Type, ECSTuple<object, int>> implementorsByType
#else
Dictionary<Type, object> implementorsByType
, Dictionary<Type, object> implementorsByType
#endif
, Dictionary<Type, Type[]> cachedTypes
)
, Dictionary<Type, Type[]> cachedTypeInterfaces)
{
//efficient way to collect the fields of every EntityViewType
var setters =
FasterList<KeyValuePair<Type, ActionCast<T>>>.NoVirt.ToArrayFast(entityViewBlazingFastReflection, out var count);

foreach (var implementor in implementors)
//efficient way to collect the fields of every EntityComponentType
var setters = FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>>.NoVirt.ToArrayFast(
entityComponentBlazingFastReflection, out var count);
//todo this should happen once per T, not once per Build<T>
if (implementors != null)
{
if (implementor != null)
foreach (var implementor in implementors)
{
var type = implementor.GetType();
if (implementor != null)
{
var type = implementor.GetType();

if (cachedTypes.TryGetValue(type, out var interfaces) == false)
interfaces = cachedTypes[type] = type.GetInterfacesEx();
if (cachedTypeInterfaces.TryGetValue(type, out var interfaces) == false)
interfaces = cachedTypeInterfaces[type] = type.GetInterfacesEx();

for (var iindex = 0; iindex < interfaces.Length; iindex++)
{
var componentType = interfaces[iindex];
#if DEBUG && !PROFILER
if (implementorsByType.TryGetValue(componentType, out var implementorData))
for (var iindex = 0; iindex < interfaces.Length; iindex++)
{
implementorData.numberOfImplementations++;
implementorsByType[componentType] = implementorData;
}
else
implementorsByType[componentType] = new ECSTuple<object, int>(implementor, 1);
var componentType = interfaces[iindex];
#if DEBUG && !PROFILE_SVELTO
if (implementorsByType.TryGetValue(componentType, out var implementorData))
{
implementorData.numberOfImplementations++;
implementorsByType[componentType] = implementorData;
}
else
implementorsByType[componentType] = new ECSTuple<object, int>(implementor, 1);
#else
implementorsByType[componentType] = implementor;
#endif
}
}
#if DEBUG && !PROFILE_SVELTO
else
{
Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityComponent "
, componentBuilder
.GetEntityComponentType().ToString()));
}
}
#if DEBUG && !PROFILER
else
{
Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityView ",
entityBuilder.GetEntityType().ToString()));
}
#endif
}
}

for (var i = 0; i < count; i++)
@@ -79,47 +91,33 @@ namespace Svelto.ECS
var fieldSetter = setters[i];
var fieldType = fieldSetter.Key;

#if DEBUG && !PROFILER
ECSTuple<object, int> component;
#if DEBUG && !PROFILE_SVELTO
ECSTuple<object, int> implementor;
#else
object component;
object implementor;
#endif

if (implementorsByType.TryGetValue(fieldType, out component) == false)
if (implementorsByType.TryGetValue(fieldType, out implementor) == false)
{
var e = new ECSException(NOT_FOUND_EXCEPTION + " Component Type: " + fieldType.Name +
" - EntityView: " + entityBuilder.GetEntityType().Name);
var e = new ECSException(NOT_FOUND_EXCEPTION + " Component Type: " + fieldType.Name
+ " - EntityComponent: " + componentBuilder.GetEntityComponentType().Name);

throw e;
}
#if DEBUG && !PROFILER
if (component.numberOfImplementations > 1)
#if DEBUG && !PROFILE_SVELTO
if (implementor.numberOfImplementations > 1)
throw new ECSException(DUPLICATE_IMPLEMENTOR_ERROR.FastConcat(
"Component Type: ", fieldType.Name,
" implementor: ",
component.implementorType
.ToString()) +
" - EntityView: " +
entityBuilder.GetEntityType().Name);
"Component Type: ", fieldType.Name, " implementor: ", implementor.instance.ToString()) +
" - EntityComponent: " + componentBuilder.GetEntityComponentType().Name);
#endif
#if DEBUG && !PROFILER
fieldSetter.Value(ref entityView, component.implementorType);
#if DEBUG && !PROFILE_SVELTO
fieldSetter.Value(ref entityComponent, implementor.instance);
#else
fieldSetter.Value(ref entityView, component);
fieldSetter.Value(ref entityComponent, implementor);
#endif
}

implementorsByType.Clear();
}

const string DUPLICATE_IMPLEMENTOR_ERROR =
"<color=teal>Svelto.ECS</color> the same component is implemented with more than one implementor. This is " +
"considered an error and MUST be fixed. ";

const string NULL_IMPLEMENTOR_ERROR =
"<color=teal>Svelto.ECS</color> Null implementor, please be careful about the implementors passed to avoid " +
"performance loss ";

const string NOT_FOUND_EXCEPTION = "<color=teal>Svelto.ECS</color> Implementor not found for an EntityView. ";
}
}

+ 23
- 120
ExclusiveGroup.cs Целия файл

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

#pragma warning disable 660,661

@@ -17,26 +18,12 @@ namespace Svelto.ECS
/// public static ExclusiveGroup[] GroupOfGroups = { MyExclusiveGroup1, ...}; //for each on this!
/// }
/// </summary>
///

///use this like:
/// public class TriggersGroup : ExclusiveGroup<TriggersGroup> {}
public abstract class NamedExclusiveGroup<T>:ExclusiveGroup
{
public static ExclusiveGroup Group = new ExclusiveGroup();
public static string name = typeof(T).FullName;

public NamedExclusiveGroup() { }

public NamedExclusiveGroup(string recognizeAs) : base(recognizeAs)
{}

public NamedExclusiveGroup(ushort range) : base(range)
{}
}

///To debug it use in your debug window: Svelto.ECS.Debugger.EGID.GetGroupNameFromId(groupID)
public class ExclusiveGroup
{
public const uint MaxNumberOfExclusiveGroups = 2 << 20;
public ExclusiveGroup()
{
_group = ExclusiveGroupStruct.Generate();
@@ -46,7 +33,7 @@ namespace Svelto.ECS
{
_group = ExclusiveGroupStruct.Generate();

_serialisedGroups.Add(recognizeAs, _group);
_knownGroups.Add(recognizeAs, _group);
}

public ExclusiveGroup(ushort range)
@@ -61,7 +48,7 @@ namespace Svelto.ECS
{
return group._group;
}
public static explicit operator uint(ExclusiveGroup group)
{
return group._group;
@@ -71,118 +58,34 @@ namespace Svelto.ECS
{
#if DEBUG
if (a._range == 0)
throw new ECSException("adding values to a not ranged ExclusiveGroup");
throw new ECSException($"Adding values to a not ranged ExclusiveGroup: {(uint)a}");
if (b >= a._range)
throw new ECSException("Using out of range group");
#endif
throw new ECSException($"Using out of range group: {(uint)a} + {b}");
#endif
return a._group + b;
}

readonly ExclusiveGroupStruct _group;

//I use this as parameter because it must not be possible to pass null Exclusive Groups.
public struct ExclusiveGroupStruct : IEquatable<ExclusiveGroupStruct>, IComparable<ExclusiveGroupStruct>,
IEqualityComparer<ExclusiveGroupStruct>
//todo document the use case for this method
public static ExclusiveGroupStruct Search(string holderGroupName)
{
public static bool operator ==(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2)
{
return c1.Equals(c2);
}

public static bool operator !=(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2)
{
return c1.Equals(c2) == false;
}

public bool Equals(ExclusiveGroupStruct other)
{
return other._id == _id;
}

public int CompareTo(ExclusiveGroupStruct other)
{
return other._id.CompareTo(_id);
}

public bool Equals(ExclusiveGroupStruct x, ExclusiveGroupStruct y)
{
return x._id == y._id;
}

public int GetHashCode(ExclusiveGroupStruct obj)
{
return _id.GetHashCode();
}

internal static ExclusiveGroupStruct Generate()
{
ExclusiveGroupStruct groupStruct;

groupStruct._id = _globalId;
DBC.ECS.Check.Require(_globalId + 1 < ushort.MaxValue, "too many exclusive groups created");
_globalId++;

return groupStruct;
}

/// <summary>
/// Use this constructor to reserve N groups
/// </summary>
internal ExclusiveGroupStruct(ushort range)
{
_id = _globalId;
DBC.ECS.Check.Require(_globalId + range < ushort.MaxValue, "too many exclusive groups created");
_globalId += range;
}

internal ExclusiveGroupStruct(uint groupID)
{
_id = groupID;
}

public ExclusiveGroupStruct(byte[] data, uint pos)
{
_id = (uint)(
data[pos++]
| data[pos++] << 8
| data[pos++] << 16
| data[pos++] << 24
);
DBC.ECS.Check.Ensure(_id < _globalId, "Invalid group ID deserialiased");
}

public static implicit operator uint(ExclusiveGroupStruct groupStruct)
{
return groupStruct._id;
}

public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, uint b)
{
var group = new ExclusiveGroupStruct();

group._id = a._id + b;

return group;
}
if (_knownGroups.ContainsKey(holderGroupName) == false)
throw new Exception("Named Group Not Found ".FastConcat(holderGroupName));

uint _id;
static uint _globalId;
return _knownGroups[holderGroupName];
}

public static ExclusiveGroupStruct Search(string holderGroupName)
public override string ToString()
{
if (_serialisedGroups.ContainsKey(holderGroupName) == false)
throw new Exception("Named Group Not Found ".FastConcat(holderGroupName));

return _serialisedGroups[holderGroupName];
return _group.ToString();
}

static readonly Dictionary<string, ExclusiveGroupStruct> _serialisedGroups = new Dictionary<string,
static readonly Dictionary<string, ExclusiveGroupStruct> _knownGroups = new Dictionary<string,
ExclusiveGroupStruct>();

#if DEBUG
readonly ushort _range;
#endif
#endif
readonly ExclusiveGroupStruct _group;
}
}

@@ -260,6 +163,6 @@ namespace Svelto.ECS
}

#if DEBUG
static string[] groupNames = new string[ushort.MaxValue];
static string[] groupNames = new string[ExclusiveGroup.MaxNumberOfExclusiveGroups];
#endif
#endif

+ 139
- 0
ExclusiveGroupStruct.cs Целия файл

@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Svelto.ECS
{
public readonly struct BuildGroup
{
internal BuildGroup(ExclusiveGroupStruct group)
{
this.group = group;
}

public static implicit operator BuildGroup(ExclusiveGroupStruct group)
{
return new BuildGroup(group);
}

public static implicit operator BuildGroup(ExclusiveGroup group)
{
return new BuildGroup(group);
}
public static implicit operator uint(BuildGroup groupStruct)
{
return groupStruct.group;
}

internal ExclusiveGroupStruct @group { get; }
}
[StructLayout(LayoutKind.Explicit, Size = 4)]
public struct ExclusiveGroupStruct : IEquatable<ExclusiveGroupStruct>, IComparable<ExclusiveGroupStruct>,
IEqualityComparer<ExclusiveGroupStruct>
{
public override bool Equals(object obj)
{
return obj is ExclusiveGroupStruct other && Equals(other);
}

public override int GetHashCode()
{
return (int) _id;
}

public static bool operator ==(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2)
{
return c1.Equals(c2);
}

public static bool operator !=(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2)
{
return c1.Equals(c2) == false;
}

public bool Equals(ExclusiveGroupStruct other)
{
return other._id == _id;
}

public int CompareTo(ExclusiveGroupStruct other)
{
return other._id.CompareTo(_id);
}

public bool Equals(ExclusiveGroupStruct x, ExclusiveGroupStruct y)
{
return x._id == y._id;
}

public int GetHashCode(ExclusiveGroupStruct obj)
{
return _id.GetHashCode();
}

public override string ToString()
{
return this.ToName();
}

internal static ExclusiveGroupStruct Generate(byte bitmask = 0)
{
ExclusiveGroupStruct groupStruct;

groupStruct._id = _globalId;
groupStruct._bytemask = bitmask;
DBC.ECS.Check.Require(_globalId + 1 < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created");
_globalId++;

return groupStruct;
}

internal ExclusiveGroupStruct(ExclusiveGroupStruct @group):this() { this = group; }

/// <summary>
/// Use this constructor to reserve N groups
/// </summary>
internal ExclusiveGroupStruct(ushort range):this()
{
_id = _globalId;
DBC.ECS.Check.Require(_globalId + range < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created");
_globalId += range;
}

internal ExclusiveGroupStruct(uint groupID):this()
{
_id = groupID;
}

public ExclusiveGroupStruct(byte[] data, uint pos):this()
{
_id = (uint)(
data[pos]
| data[++pos] << 8
| data[++pos] << 16
);
_bytemask = (byte) (data[++pos] << 24);

DBC.ECS.Check.Ensure(_id < _globalId, "Invalid group ID deserialiased");
}

public static implicit operator uint(ExclusiveGroupStruct groupStruct)
{
return groupStruct._id;
}
public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, uint b)
{
var group = new ExclusiveGroupStruct {_id = a._id + b};

return @group;
}

[FieldOffset(0)] uint _id;
[FieldOffset(3)] byte _bytemask;

static uint _globalId = 1; //it starts from 1 because default EGID is considered not initalized value
}
}

+ 11
- 0
ExclusiveGroupStruct.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 67dde70e73a3374a87d0e9b93385405a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 0
- 61
ExecuteOnEntitiesDB.cs Целия файл

@@ -1,61 +0,0 @@
using System;
using Svelto.DataStructures;

namespace Svelto.ECS.Internal
{
partial class EntitiesDB
{
public void ExecuteOnAllEntities<T>(Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB> action)
where T : struct, IEntityStruct
{
var type = typeof(T);

if (_groupsPerEntity.TryGetValue(new RefWrapper<Type>(type), out var dictionary))
{
foreach (var pair in dictionary)
{
var entities = (pair.Value as TypeSafeDictionary<T>).GetValuesArray(out var innerCount);

if (innerCount > 0)
action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this);
}
}
}

public void ExecuteOnAllEntities
<T, W>(W value, Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB, W> action)
where T : struct, IEntityStruct
{
var type = typeof(T);

if (_groupsPerEntity.TryGetValue(new RefWrapper<Type>(type), out var dic))
{
foreach (var pair in dic)
{
var entities = (pair.Value as TypeSafeDictionary<T>).GetValuesArray(out var innerCount);

if (innerCount > 0)
action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this, value);
}
}
}
public void ExecuteOnAllEntities
<T, W>(ref W value, ExecuteOnAllEntitiesAction<T, W> action)
where T : struct, IEntityStruct
{
var type = typeof(T);

if (_groupsPerEntity.TryGetValue(new RefWrapper<Type>(type), out var dic))
{
foreach (var pair in dic)
{
var entities = (pair.Value as TypeSafeDictionary<T>).GetValuesArray(out var innerCount);

if (innerCount > 0)
action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this, ref value);
}
}
}
}
}

+ 0
- 11
ExecuteOnEntitiesDB.cs.meta Целия файл

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 43acb9dc60f93889a5814ea34494169b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 4
- 4
ExtendibleEntityDescriptor.cs Целия файл

@@ -8,7 +8,7 @@ namespace Svelto.ECS
/// to swap and remove specialized entities from abstract engines
/// </summary>
/// <typeparam name="TType"></typeparam>
public class ExtendibleEntityDescriptor<TType> : IEntityDescriptor where TType : IEntityDescriptor, new()
public class ExtendibleEntityDescriptor<TType> : IDynamicEntityDescriptor where TType : IEntityDescriptor, new()
{
static ExtendibleEntityDescriptor()
{
@@ -17,7 +17,7 @@ namespace Svelto.ECS
$"SerializableEntityDescriptors cannot be used as base entity descriptor: {typeof(TType)}");
}

public ExtendibleEntityDescriptor(IEntityBuilder[] extraEntities)
public ExtendibleEntityDescriptor(IComponentBuilder[] extraEntities)
{
_dynamicDescriptor = new DynamicEntityDescriptor<TType>(extraEntities);
}
@@ -34,14 +34,14 @@ namespace Svelto.ECS
return this;
}

public ExtendibleEntityDescriptor<TType> ExtendWith(IEntityBuilder[] extraEntities)
public ExtendibleEntityDescriptor<TType> ExtendWith(IComponentBuilder[] extraEntities)
{
_dynamicDescriptor.ExtendWith(extraEntities);

return this;
}

public IEntityBuilder[] entitiesToBuild => _dynamicDescriptor.entitiesToBuild;
public IComponentBuilder[] componentsToBuild => _dynamicDescriptor.componentsToBuild;

DynamicEntityDescriptor<TType> _dynamicDescriptor;
}

+ 19
- 0
Extensions/ProcessorCount.cs Целия файл

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

namespace Svelto.ECS
{
internal static class ProcessorCount
{
public static readonly int processorCount = Environment.ProcessorCount;
public static int BatchSize(uint totalIterations)
{
var iterationsPerBatch = totalIterations / processorCount;

if (iterationsPerBatch < 32)
return 32;
return (int) iterationsPerBatch;
}
}
}

+ 11
- 0
Extensions/ProcessorCount.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce4c5807b1b43a598f0b8d7820575611
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 8
- 0
Extensions/Svelto.meta Целия файл

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 30192a9315e83c87a194215a241c9fa1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

+ 70
- 0
Extensions/Svelto/AllGroupsEnumerable.cs Целия файл

@@ -0,0 +1,70 @@
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
/// <summary>
/// ToDo it would be interesting to have a version of this dedicated to unmanaged, IEntityComponent
/// that can be burstifiable
/// </summary>
/// <typeparam name="T1"></typeparam>
public readonly struct AllGroupsEnumerable<T1> where T1 : struct, IEntityComponent
{
public ref struct GroupCollection
{
internal EntityCollection<T1> collection;
internal ExclusiveGroupStruct group;

public void Deconstruct(out EntityCollection<T1> collection, out ExclusiveGroupStruct group)
{
collection = this.collection;
group = this.@group;
}
}
public AllGroupsEnumerable(EntitiesDB db)
{
_db = db;
}
public ref struct GroupsIterator
{
public GroupsIterator(EntitiesDB db) : this()
{
_db = db.FindGroups_INTERNAL(TypeCache<T1>.type).GetEnumerator();
}

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (_db.MoveNext() == true)
{
FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>.KeyValuePairFast group = _db.Current;
ITypeSafeDictionary<T1> typeSafeDictionary = @group.Value as ITypeSafeDictionary<T1>;
if (typeSafeDictionary.count == 0) continue;

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

return true;
}

return false;
}

public GroupCollection Current => _array;

FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>.FasterDictionaryKeyValueEnumerator _db;
GroupCollection _array;
}

public GroupsIterator GetEnumerator()
{
return new GroupsIterator(_db);
}

readonly EntitiesDB _db;
}
}

+ 11
- 0
Extensions/Svelto/AllGroupsEnumerable.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6a6569334364384ea95a60579864448c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 251
- 0
Extensions/Svelto/EntityCollectionExtension.cs Целия файл

@@ -0,0 +1,251 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.DataStructures;
using Svelto.ECS.Hybrid;

namespace Svelto.ECS
{
public static class EntityCollectionExtension
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1>
(in this EntityCollection<T1> ec, out NB<T1> buffer, out int count) where T1 : unmanaged, IEntityComponent
{
buffer = ec._nativedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2>
(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, out NB<T2> buffer2, out int count)
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent
{
buffer1 = ec.buffer1._nativedBuffer;
buffer2 = ec.buffer2._nativedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out NB<T2> buffer2, out NB<T3> buffer3
, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
{
buffer1 = ec.buffer1._nativedBuffer;
buffer2 = ec.buffer2._nativedBuffer;
buffer3 = ec.buffer3._nativedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3, T4>
(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, out NB<T2> buffer2, out NB<T3> buffer3
, out NB<T4> buffer4, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
where T4 : unmanaged, IEntityComponent
{
buffer1 = ec.Item1._nativedBuffer;
buffer2 = ec.Item2._nativedBuffer;
buffer3 = ec.Item3._nativedBuffer;
buffer4 = ec.Item4._nativedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BT<NB<T1>> ToBuffer<T1>(in this EntityCollection<T1> ec) where T1 : unmanaged, IEntityComponent
{
return new BT<NB<T1>>(ec._nativedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BT<NB<T1>, NB<T2>> ToBuffers<T1, T2>
(in this EntityCollection<T1, T2> ec)
where T2 : unmanaged, IEntityComponent where T1 : unmanaged, IEntityComponent
{
return new BT<NB<T1>, NB<T2>>(ec.buffer1._nativedBuffer, ec.buffer2._nativedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BT<NB<T1>, NB<T2>, NB<T3>> ToBuffers<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec)
where T2 : unmanaged, IEntityComponent
where T1 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
{
return new BT<NB<T1>, NB<T2>, NB<T3>>(ec.buffer1._nativedBuffer, ec.buffer2._nativedBuffer
, ec.buffer3._nativedBuffer, ec.count);
}
}

public static class EntityCollectionExtensionB
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1>
(in this EntityCollection<T1> ec, out MB<T1> buffer, out int count) where T1 : struct, IEntityViewComponent
{
buffer = ec._managedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BT<MB<T1>> ToBuffer<T1>(in this EntityCollection<T1> ec) where T1 : struct, IEntityViewComponent
{
return new BT<MB<T1>>(ec._managedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2>
(in this EntityCollection<T1, T2> ec, out MB<T1> buffer1, out MB<T2> buffer2, out int count)
where T1 : struct, IEntityViewComponent where T2 : struct, IEntityViewComponent
{
buffer1 = ec.buffer1._managedBuffer;
buffer2 = ec.buffer2._managedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (MB<T1> buffer1, MB<T2> buffer2, uint count) ToBuffers<T1, T2>
(in this EntityCollection<T1, T2> ec)
where T2 : struct, IEntityViewComponent where T1 : struct, IEntityViewComponent
{
return (ec.buffer1._managedBuffer, ec.buffer2._managedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec, out MB<T1> buffer1, out MB<T2> buffer2, out MB<T3> buffer3
, out int count) where T1 : struct, IEntityViewComponent
where T2 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
{
buffer1 = ec.buffer1._managedBuffer;
buffer2 = ec.buffer2._managedBuffer;
buffer3 = ec.buffer3._managedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (MB<T1> buffer1, MB<T2> buffer2, MB<T3> buffer3, uint count) ToBuffers<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec)
where T2 : struct, IEntityViewComponent
where T1 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
{
return (ec.buffer1._managedBuffer, ec.buffer2._managedBuffer, ec.buffer3._managedBuffer, ec.count);
}
}

public static class EntityCollectionExtensionC
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (NB<T1> buffer1, MB<T2> buffer2, uint count) ToBuffers<T1, T2>
(in this EntityCollection<T1, T2> ec)
where T1 : unmanaged, IEntityComponent where T2 : struct, IEntityViewComponent
{
return (ec.buffer1._nativedBuffer, ec.buffer2._managedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (NB<T1> buffer1, MB<T2> buffer2, MB<T3> buffer3, uint count) ToBuffers<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec)
where T1 : unmanaged, IEntityComponent
where T2 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
{
return (ec.buffer1._nativedBuffer, ec.buffer2._managedBuffer, ec.buffer3._managedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2>
(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, out MB<T2> buffer2, out int count)
where T1 : unmanaged, IEntityComponent where T2 : struct, IEntityViewComponent
{
buffer1 = ec.buffer1._nativedBuffer;
buffer2 = ec.buffer2._managedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out MB<T2> buffer2, out MB<T3> buffer3, out int count)
where T1 : unmanaged, IEntityComponent
where T2 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
{
buffer1 = ec.buffer1._nativedBuffer;
buffer2 = ec.buffer2._managedBuffer;
buffer3 = ec.buffer3._managedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3, T4>
(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, out NB<T2> buffer2, out NB<T3> buffer3
, out MB<T4> buffer4, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
where T4 : struct, IEntityViewComponent
{
buffer1 = ec.Item1._nativedBuffer;
buffer2 = ec.Item2._nativedBuffer;
buffer3 = ec.Item3._nativedBuffer;
buffer4 = ec.Item4._managedBuffer;
count = (int) ec.count;
}
}

public static class EntityCollectionExtensionD
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out NB<T2> buffer2, out MB<T3> buffer3
, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : struct, IEntityViewComponent
{
buffer1 = ec.buffer1._nativedBuffer;
buffer2 = ec.buffer2._nativedBuffer;
buffer3 = ec.buffer3._managedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (NB<T1> buffer1, NB<T2> buffer2, MB<T3> buffer3, uint count) ToBuffers<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec)
where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : struct, IEntityViewComponent
{
return (ec.buffer1._nativedBuffer, ec.buffer2._nativedBuffer, ec.buffer3._managedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>> ToBuffers<T1, T2, T3, T4>
(in this EntityCollection<T1, T2, T3, T4> ec)
where T2 : unmanaged, IEntityComponent
where T1 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
where T4 : unmanaged, IEntityComponent
{
return new BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>>(ec.Item1._nativedBuffer, ec.Item2._nativedBuffer
, ec.Item3._nativedBuffer, ec.Item4._nativedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3, T4>
(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, out NB<T2> buffer2, out MB<T3> buffer3
, out MB<T4> buffer4, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : struct, IEntityViewComponent
where T4 : struct, IEntityViewComponent
{
buffer1 = ec.Item1._nativedBuffer;
buffer2 = ec.Item2._nativedBuffer;
buffer3 = ec.Item3._managedBuffer;
buffer4 = ec.Item4._managedBuffer;
count = (int) ec.count;
}
}
}

+ 11
- 0
Extensions/Svelto/EntityCollectionExtension.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 27ea6afd09113d0ebdfba19bde433004
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 109
- 0
Extensions/Svelto/EntityManagedDBExtensions.cs Целия файл

@@ -0,0 +1,109 @@
using System.Runtime.CompilerServices;
using Svelto.DataStructures;
using Svelto.ECS.Hybrid;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public static class EntityManagedDBExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MB<T> QueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index) where T : struct, IEntityViewComponent
{
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(entityGID, out index, out MB<T> array) == true)
return array;

throw new EntityNotFoundException(entityGID, typeof(T));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryQueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out MB<T> array)
where T : struct, IEntityViewComponent
{
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(entityGID, out index, out array) == true)
return true;

return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryQueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group, out uint index, out MB<T> array)
where T : struct, IEntityViewComponent
{
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(new EGID(id, group), out index, out array) == true)
return true;

return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool QueryEntitiesAndIndexInternal<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out MB<T> buffer) where T : struct, IEntityViewComponent
{
index = 0;
buffer = default;
if (entitiesDb.SafeQueryEntityDictionary<T>(entityGID.groupID, out var safeDictionary) == false)
return false;

if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false)
return false;
buffer = (MB<T>) (safeDictionary as ITypeSafeDictionary<T>).GetValues(out _);

return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T QueryEntity<T>(this EntitiesDB entitiesDb, EGID entityGID) where T : struct, IEntityViewComponent
{
var array = entitiesDb.QueryEntitiesAndIndex<T>(entityGID, out var index);
return ref array[(int) index];
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T QueryEntity<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group) where T : struct, IEntityViewComponent
{
return ref entitiesDb.QueryEntity<T>(new EGID(id, group));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T QueryUniqueEntity<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct group) where T : struct, IEntityViewComponent
{
var (entities, entitiescount) = entitiesDb.QueryEntities<T>(@group);

#if DEBUG && !PROFILE_SVELTO
if (entitiescount == 0)
throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'"));
if (entitiescount != 1)
throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString())
.FastConcat("'"));
#endif
return ref entities[0];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MB<T> GetArrayAndEntityIndex<T>(this EGIDMapper<T> mapper, uint entityID, out uint index) where T : struct, IEntityViewComponent
{
if (mapper._map.TryFindIndex(entityID, out index))
{
return (MB<T>) mapper._map.GetValues(out _);
}

throw new ECSException("Entity not found");
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetArrayAndEntityIndex<T>(this EGIDMapper<T> mapper, uint entityID, out uint index, out MB<T> array) where T : struct, IEntityViewComponent
{
index = default;
if (mapper._map != null && mapper._map.TryFindIndex(entityID, out index))
{
array = (MB<T>) mapper._map.GetValues(out _);
return true;
}

array = default;
return false;
}
}
}

+ 11
- 0
Extensions/Svelto/EntityManagedDBExtensions.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 45c4aaf99be337d0b1599b27d7da352f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 125
- 0
Extensions/Svelto/EntityNativeDBExtensions.cs Целия файл

@@ -0,0 +1,125 @@
using System.Runtime.CompilerServices;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public static class EntityNativeDBExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static AllGroupsEnumerable<T1> QueryEntities<T1>(this EntitiesDB db)
where T1 :struct, IEntityComponent
{
return new AllGroupsEnumerable<T1>(db);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NB<T> QueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index) where T : unmanaged, IEntityComponent
{
if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out NB<T> array) == true)
return array;

throw new EntityNotFoundException(entityGID, typeof(T));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NB<T> QueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group, out uint index) where T : unmanaged, IEntityComponent
{
EGID entityGID = new EGID(id, group);
if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out NB<T> array) == true)
return array;

throw new EntityNotFoundException(entityGID, typeof(T));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryQueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out NB<T> array)
where T : unmanaged, IEntityComponent
{
if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out array) == true)
return true;

return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryQueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group, out uint index, out NB<T> array)
where T : unmanaged, IEntityComponent
{
if (entitiesDb.QueryEntitiesAndIndexInternal(new EGID(id, group), out index, out array) == true)
return true;

return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool QueryEntitiesAndIndexInternal<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out NB<T> buffer) where T : unmanaged, IEntityComponent
{
index = 0;
buffer = default;
if (entitiesDb.SafeQueryEntityDictionary<T>(entityGID.groupID, out var safeDictionary) == false)
return false;

if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false)
return false;
buffer = (NB<T>) (safeDictionary as ITypeSafeDictionary<T>).GetValues(out _);

return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T QueryEntity<T>(this EntitiesDB entitiesDb, EGID entityGID) where T : unmanaged, IEntityComponent
{
var array = entitiesDb.QueryEntitiesAndIndex<T>(entityGID, out var index);
return ref array[(int) index];
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T QueryEntity<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent
{
return ref entitiesDb.QueryEntity<T>(new EGID(id, group));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T QueryUniqueEntity<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent
{
var (entities, entitiescount) = entitiesDb.QueryEntities<T>(@group);

#if DEBUG && !PROFILE_SVELTO
if (entitiescount == 0)
throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'"));
if (entitiescount != 1)
throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString())
.FastConcat("'"));
#endif
return ref entities[0];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NB<T> GetArrayAndEntityIndex<T>(this EGIDMapper<T> mapper, uint entityID, out uint index) where T : unmanaged, IEntityComponent
{
if (mapper._map.TryFindIndex(entityID, out index))
{
return (NB<T>) mapper._map.GetValues(out _);
}

throw new ECSException("Entity not found");
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetArrayAndEntityIndex<T>(this EGIDMapper<T> mapper, uint entityID, out uint index, out NB<T> array) where T : unmanaged, IEntityComponent
{
index = default;
if (mapper._map != null && mapper._map.TryFindIndex(entityID, out index))
{
array = (NB<T>) mapper._map.GetValues(out _);
return true;
}

array = default;
return false;
}
}
}

+ 11
- 0
Extensions/Svelto/EntityNativeDBExtensions.cs.meta Целия файл

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 319a6a0269bf3d1bbca89f52ea1d0aeb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Някои файлове не бяха показани, защото твърде много файлове са промени

Loading…
Отказ
Запис