Browse Source

Svelto.ECS 2.9

changes (random order of importance):
New Serialization framework
more thorough disposing of the EnginesRoot
an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule
IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose.
The IReactOnSwap MovedFrom method has been removed, it is now redundant.
Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines.
EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose.
Groups are not trimmed anymore when they are emptied to avoid allocations.
Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used)
A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors.
Several minor code design improvements/optimisations
tags/2.9.1
sebas77 4 years ago
parent
commit
fb74cb8116
100 changed files with 3777 additions and 2067 deletions
  1. +1
    -1
      Svelto.Common
  2. +0
    -16
      Svelto.ECS.Components/Components/ECSQuaternion.cs
  3. +0
    -19
      Svelto.ECS.Components/Components/ECSRect.cs
  4. +0
    -20
      Svelto.ECS.Components/Components/ECSVector2.cs
  5. +0
    -24
      Svelto.ECS.Components/Components/ECSVector3.cs
  6. +0
    -15
      Svelto.ECS.Components/Components/ECSVector4.cs
  7. +0
    -131
      Svelto.ECS.Components/Components/ExtensionMethods.cs
  8. +0
    -69
      Svelto.ECS.Components/Components/ExtensionMethodsUECS.cs
  9. +0
    -44
      Svelto.ECS.Components/Components/ExtensionMethodsUnity.cs
  10. +0
    -22
      Svelto.ECS.Components/ECSResources/StringECSDB.cs
  11. +0
    -9
      Svelto.ECS.Components/EntityStructs/PositionEntityStruct.cs
  12. +0
    -9
      Svelto.ECS.Components/EntityStructs/RotationEntityStruct.cs
  13. +0
    -9
      Svelto.ECS.Components/EntityStructs/ScalingEntityStruct.cs
  14. +0
    -23
      Svelto.ECS.Components/Svelto.ECS.Components.asmdef
  15. +8
    -0
      Svelto.ECS/AllowMultipleAttribute.cs
  16. +52
    -76
      Svelto.ECS/CheckEntityUtilities.cs
  17. +373
    -373
      Svelto.ECS/DBC.cs
  18. +37
    -0
      Svelto.ECS/DataStructures/SetEGIDWithoutBoxing.cs
  19. +90
    -94
      Svelto.ECS/DataStructures/TypeSafeDictionary.cs
  20. +2
    -5
      Svelto.ECS/Dispatcher/DispatchOnChange.cs
  21. +5
    -8
      Svelto.ECS/Dispatcher/DispatchOnSet.cs
  22. +105
    -16
      Svelto.ECS/DynamicEntityDescriptor.cs
  23. +4
    -4
      Svelto.ECS/ECSException.cs
  24. +8
    -11
      Svelto.ECS/ECSResources/ECSResources.cs
  25. +38
    -0
      Svelto.ECS/ECSResources/ECSString.cs
  26. +12
    -137
      Svelto.ECS/EGID.cs
  27. +30
    -7
      Svelto.ECS/EGIDMapper.cs
  28. +20
    -13
      Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs
  29. +57
    -52
      Svelto.ECS/EnginesRoot.Engines.cs
  30. +172
    -175
      Svelto.ECS/EnginesRoot.Entities.cs
  31. +32
    -22
      Svelto.ECS/EnginesRoot.GenericEntityFactory.cs
  32. +62
    -58
      Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs
  33. +93
    -100
      Svelto.ECS/EnginesRoot.Submission.cs
  34. +114
    -59
      Svelto.ECS/EntitiesDB.cs
  35. +83
    -53
      Svelto.ECS/EntityBuilder.CheckFields.cs
  36. +87
    -28
      Svelto.ECS/EntityBuilder.cs
  37. +150
    -31
      Svelto.ECS/EntityCollection.cs
  38. +6
    -2
      Svelto.ECS/EntityDescriptorTemplate.cs
  39. +45
    -27
      Svelto.ECS/EntityFactory.cs
  40. +29
    -0
      Svelto.ECS/EntityGroup.cs
  41. +1
    -1
      Svelto.ECS/EntityHierarchyStruct.cs
  42. +1
    -6
      Svelto.ECS/EntityInfoView.cs
  43. +4
    -3
      Svelto.ECS/EntityNotFoundException.cs
  44. +74
    -36
      Svelto.ECS/EntityStream.cs
  45. +59
    -20
      Svelto.ECS/EntityStructInitializer.cs
  46. +1
    -3
      Svelto.ECS/EntitySubmissionScheduler.cs
  47. +5
    -8
      Svelto.ECS/EntitySubmitOperation.cs
  48. +0
    -40
      Svelto.ECS/EntityView.cs
  49. +7
    -12
      Svelto.ECS/EntityViewUtility.cs
  50. +135
    -15
      Svelto.ECS/ExclusiveGroup.cs
  51. +22
    -3
      Svelto.ECS/ExecuteOnEntitiesDB.cs
  52. +32
    -3
      Svelto.ECS/ExtendibleEntityDescriptor.cs
  53. +1
    -1
      Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs
  54. +29
    -12
      Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs
  55. +26
    -17
      Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs
  56. +12
    -8
      Svelto.ECS/GenericentityStreamConsumerFactory.cs
  57. +45
    -62
      Svelto.ECS/IEntitiesDB.cs
  58. +3
    -1
      Svelto.ECS/IEntityBuilder.cs
  59. +12
    -23
      Svelto.ECS/IEntityFactory.cs
  60. +0
    -2
      Svelto.ECS/IEntityFunctions.cs
  61. +7
    -3
      Svelto.ECS/IEntityStruct.cs
  62. +2
    -2
      Svelto.ECS/IReactOnAddAndRemove.cs
  63. +4
    -2
      Svelto.ECS/IReactOnSwap.cs
  64. +2
    -2
      Svelto.ECS/Sequencer.cs
  65. +43
    -0
      Svelto.ECS/Serialization/ComposedSerializer.cs
  66. +43
    -0
      Svelto.ECS/Serialization/DefaultSerializer.cs
  67. +46
    -0
      Svelto.ECS/Serialization/DefaultSerializerUtils.cs
  68. +28
    -0
      Svelto.ECS/Serialization/DefaultVersioningFactory.cs
  69. +8
    -0
      Svelto.ECS/Serialization/DoNotSerializeAttribute.cs
  70. +17
    -0
      Svelto.ECS/Serialization/DontSerialize.cs
  71. +198
    -0
      Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs
  72. +76
    -0
      Svelto.ECS/Serialization/EnginesRoot.SerializableEntityHeader.cs
  73. +105
    -0
      Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs
  74. +15
    -0
      Svelto.ECS/Serialization/HashNameAttribute.cs
  75. +9
    -0
      Svelto.ECS/Serialization/IDeserializationFactory.cs
  76. +69
    -0
      Svelto.ECS/Serialization/IEntitySerialization.cs
  77. +15
    -0
      Svelto.ECS/Serialization/ISerializableEntityBuilder.cs
  78. +10
    -0
      Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs
  79. +14
    -0
      Svelto.ECS/Serialization/ISerializationData.cs
  80. +55
    -0
      Svelto.ECS/Serialization/ISerializer.cs
  81. +89
    -0
      Svelto.ECS/Serialization/PartialSerializer.cs
  82. +94
    -0
      Svelto.ECS/Serialization/SerializableEntityBuilder.cs
  83. +122
    -0
      Svelto.ECS/Serialization/SerializableEntityDescriptor.cs
  84. +9
    -0
      Svelto.ECS/Serialization/SerializableEntityStruct.cs
  85. +16
    -0
      Svelto.ECS/Serialization/SerializationEntityDescriptorTemplate.cs
  86. +37
    -0
      Svelto.ECS/Serialization/SimpleSerializationData.cs
  87. +15
    -0
      Svelto.ECS/Serialization/Unsafe.cs
  88. +10
    -6
      Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs
  89. +0
    -13
      Svelto.ECS/StaticEntityDescriptorInfo.cs
  90. +1
    -1
      Svelto.ECS/Svelto.ECS.asmdef
  91. +3
    -0
      Svelto.ECS/Svelto.ECS.csproj
  92. +9
    -0
      Svelto.ECS/TypeCache.cs
  93. +28
    -0
      Svelto.ECS/UnsafeStructRef.cs
  94. +14
    -0
      Svelto.ECS/Utilities.cs
  95. +64
    -0
      Svelto.ECS/WaitForSubmissionEnumerator.cs
  96. +128
    -0
      Svelto.ECS/bin/Debug/netstandard2.0/Svelto.ECS.deps.json
  97. +23
    -0
      Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.AssemblyInfo.cs
  98. +0
    -0
      Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.CopyComplete
  99. +23
    -0
      Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.FileListAbsolute.txt
  100. +142
    -0
      Svelto.ECS/obj/Svelto.ECS.csproj.nuget.dgspec.json

+ 1
- 1
Svelto.Common

@@ -1 +1 @@
Subproject commit 433b39f4a1b9a17301988ecd8a633d049bb4378d
Subproject commit 0d71df6e66f2d0c72d23fa44b740bad4a0ff5e8c

+ 0
- 16
Svelto.ECS.Components/Components/ECSQuaternion.cs View File

@@ -1,16 +0,0 @@
namespace Svelto.ECS.Components
{
public struct ECSQuaternion
{
public static readonly ECSQuaternion identity = new ECSQuaternion(0f, 0f, 0f, 1f);
public float x, y, z, w;

public ECSQuaternion(float X, float Y, float Z, float W)
{
x = X;
y = Y;
z = Z;
w = W;
}
}
}

+ 0
- 19
Svelto.ECS.Components/Components/ECSRect.cs View File

@@ -1,19 +0,0 @@
#if UNITY_5 || UNITY_5_3_OR_NEWER
using UnityEngine;

namespace Svelto.ECS.Components.Unity
{
public struct ECSRect
{
public float x, y, width, height;

public ECSRect(Rect imageUvRect)
{
x = imageUvRect.x;
y = imageUvRect.y;
width = imageUvRect.width;
height = imageUvRect.height;
}
}
}
#endif

+ 0
- 20
Svelto.ECS.Components/Components/ECSVector2.cs View File

@@ -1,20 +0,0 @@
namespace Svelto.ECS.Components
{
public struct ECSVector2
{
public float x, y;

public static readonly ECSVector2 right = new ECSVector2(1f, 0f);
public static readonly ECSVector2 left = new ECSVector2(-1f, 0f);
public static readonly ECSVector2 down = new ECSVector2(0f, -1f);
public static readonly ECSVector2 up = new ECSVector2(0f, 1f);
public static readonly ECSVector2 one = new ECSVector2(1f, 1f);
public static readonly ECSVector2 zero = new ECSVector2(0f, 0f);

public ECSVector2(float X, float Y)
{
x = X;
y = Y;
}
}
}

+ 0
- 24
Svelto.ECS.Components/Components/ECSVector3.cs View File

@@ -1,24 +0,0 @@
namespace Svelto.ECS.Components
{
public struct ECSVector3
{
public float x, y, z;

public static readonly ECSVector3 forward = new ECSVector3(0f, 0f, 1f);
public static readonly ECSVector3 back = new ECSVector3(0f, 0f, -1f);
public static readonly ECSVector3 right = new ECSVector3(1f, 0f, 0f);
public static readonly ECSVector3 left = new ECSVector3(-1f, 0f, 0f);
public static readonly ECSVector3 up = new ECSVector3(0f, 1f, 0f);
public static readonly ECSVector3 down = new ECSVector3(0f, -1f, 0f);

public static readonly ECSVector3 zero = new ECSVector3(0f, 0f, 0f);
public static readonly ECSVector3 one = new ECSVector3(1f, 1f, 1f);

public ECSVector3(float X, float Y, float Z)
{
x = X;
y = Y;
z = Z;
}
}
}

+ 0
- 15
Svelto.ECS.Components/Components/ECSVector4.cs View File

@@ -1,15 +0,0 @@
namespace Svelto.ECS.Components
{
public struct ECSVector4
{
public float x, y, z, w;

public ECSVector4(float X, float Y, float Z, float W)
{
x = X;
y = Y;
z = Z;
w = W;
}
}
}

+ 0
- 131
Svelto.ECS.Components/Components/ExtensionMethods.cs View File

@@ -1,131 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.ECS.Components;

public static partial class ExtensionMethods
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float SqrMagnitude(in this ECSVector2 a) { return a.x * a.x + a.y * a.y; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float SqrMagnitude(in this ECSVector3 a) { return a.x * a.x + a.y * a.y + a.z * a.z; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Magnitude(in this ECSVector2 a) { return (float) Math.Sqrt(a.SqrMagnitude()); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Magnitude(in this ECSVector3 a) { return (float) Math.Sqrt(a.SqrMagnitude()); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref this ECSVector3 vector1, in ECSVector3 vector2)
{
vector1.x += vector2.x;
vector1.y += vector2.y;
vector1.z += vector2.z;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref this ECSVector3 vector1, float x, float y, float z)
{
vector1.x += x;
vector1.y += y;
vector1.z += z;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Set(ref this ECSVector3 vector1, float x, float y, float z)
{
vector1.x = x;
vector1.y = y;
vector1.z = z;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Zero(ref this ECSVector3 vector1)
{
vector1.x = 0;
vector1.y = 0;
vector1.z = 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Set(ref this ECSQuaternion quaternion, float x, float y, float z, float w)
{
quaternion.x = x;
quaternion.y = y;
quaternion.z = z;
quaternion.w = w;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Interpolate(ref this ECSVector3 vector, in ECSVector3 vectorS, in ECSVector3 vectorE, float time)
{
vector.x = vectorS.x * (1 - time) + vectorE.x * time;
vector.y = vectorS.y * (1 - time) + vectorE.y * time;
vector.z = vectorS.z * (1 - time) + vectorE.z * time;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Dot(ref this ECSVector3 vector1, in ECSVector3 vector2)
{
return vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ECSVector3 Cross(ref this ECSVector3 lhs, in ECSVector3 rhs)
{
return new ECSVector3(lhs.y * rhs.z - lhs.z * rhs.y, lhs.z * rhs.x - lhs.x * rhs.z,
lhs.x * rhs.y - lhs.y * rhs.x);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ECSVector3 Mul(in this ECSQuaternion rotation, in ECSVector3 point)
{
float x = rotation.x * 2F;
float y = rotation.y * 2F;
float z = rotation.z * 2F;
float xx = rotation.x * x;
float yy = rotation.y * y;
float zz = rotation.z * z;
float xy = rotation.x * y;
float xz = rotation.x * z;
float yz = rotation.y * z;
float wx = rotation.w * x;
float wy = rotation.w * y;
float wz = rotation.w * z;

return new ECSVector3((1F - (yy + zz)) * point.x + (xy - wz) * point.y + (xz + wy) * point.z,
(xy + wz) * point.x + (1F - (xx + zz)) * point.y + (yz - wx) * point.z,
(xz - wy) * point.x + (yz + wx) * point.y + (1F - (xx + yy)) * point.z);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Swap(ref this ECSVector3 vector, ref ECSVector3 vectorS)
{
float x = vector.x;
float y = vector.y;
float z = vector.z;

vector.x = vectorS.x;
vector.y = vectorS.y;
vector.z = vectorS.z;

vectorS.x = x;
vectorS.y = y;
vectorS.z = z;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sub(ref this ECSVector3 vector1, in ECSVector3 vector2)
{
vector1.x -= vector2.x;
vector1.y -= vector2.y;
vector1.z -= vector2.z;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref ECSVector3 Mul(ref this ECSVector3 vector1, float value)
{
vector1.x *= value;
vector1.y *= value;
vector1.z *= value;

return ref vector1;
}
}

+ 0
- 69
Svelto.ECS.Components/Components/ExtensionMethodsUECS.cs View File

@@ -1,69 +0,0 @@
#if UNITY_MATHEMATICS
using System.Runtime.CompilerServices;
using UnityEngine;
using Svelto.ECS.Components;
using Unity.Mathematics;

public static partial class ExtensionMethods
{
public static float3 ToFloat3(in this ECSVector3 vector)
{
return new float3(vector.x, vector.y, vector.z);
}
public static quaternion ToQuaternion(in this ECSVector4 vector)
{
return new quaternion(vector.x, vector.y, vector.z, vector.w);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Mul(ref this float3 vector1, float value)
{
vector1.x *= value;
vector1.y *= value;
vector1.z *= value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Div(ref this float3 vector1, float value)
{
vector1.x /= value;
vector1.y /= value;
vector1.z /= value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ProjectOnPlane(ref this float3 vector, in float3 planeNormal)
{
var num1 = math.dot(planeNormal,planeNormal);
var num2 = math.dot(vector,planeNormal) / num1;
vector.x -= planeNormal.x * num2;
vector.y -= planeNormal.y * num2;
vector.z -= planeNormal.z * num2;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Normalize(ref this float3 x)
{
x.Mul(math.rsqrt(math.dot(x,x)));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void NormalizeSafe(ref this float3 x)
{
var len = math.dot(x,x);
x = len > math.FLT_MIN_NORMAL ? x * math.rsqrt(len) : float3.zero;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref this float3 a, in float3 b)
{
a.x += b.x;
a.y += b.y;
a.z += b.z;
}

}

#endif

+ 0
- 44
Svelto.ECS.Components/Components/ExtensionMethodsUnity.cs View File

@@ -1,44 +0,0 @@
#if UNITY_2018_3_OR_NEWER
using Svelto.ECS.Components;
using UnityEngine;

public static partial class ExtensionMethods
{
public static Vector2 ToVector2(in this ECSVector2 vector) { return new Vector2(vector.x, vector.y); }
public static ECSVector2 ToECSVector2(in this Vector2 vector) { return new ECSVector2(vector.x, vector.y); }

public static Vector3 ToVector3(in this ECSVector3 vector)
{
return new Vector3(vector.x, vector.y, vector.z);
}
public static ECSVector3 ToECSVector3(in this Vector3 vector)
{
return new ECSVector3(vector.x, vector.y, vector.z);
}

public static Quaternion ToQuaternion(in this ECSQuaternion quaternion)
{
return new Quaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w);
}
public static ECSQuaternion ToECSQuaternion(in this Quaternion quaternion)
{
return new ECSQuaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w);
}

public static void Set(ref this ECSQuaternion ecsQuaternion, in Quaternion quaternion)
{
ecsQuaternion.x = quaternion.x;
ecsQuaternion.y = quaternion.y;
ecsQuaternion.z = quaternion.z;
ecsQuaternion.w = quaternion.w;
}

public static void Set(ref this ECSVector3 ecsVector3, in Vector3 vector3)
{
ecsVector3.x = vector3.x;
ecsVector3.y = vector3.y;
ecsVector3.z = vector3.z;
}
}

#endif

+ 0
- 22
Svelto.ECS.Components/ECSResources/StringECSDB.cs View File

@@ -1,22 +0,0 @@
namespace Svelto.ECS.Experimental
{
public struct ECSString
{
internal uint id;

ECSString(uint toEcs)
{
id = toEcs;
}

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

+ 0
- 9
Svelto.ECS.Components/EntityStructs/PositionEntityStruct.cs View File

@@ -1,9 +0,0 @@
using Svelto.ECS.Components;

namespace Svelto.ECS.EntityStructs
{
public struct PositionEntityStruct : IEntityStruct
{
public ECSVector3 position;
}
}

+ 0
- 9
Svelto.ECS.Components/EntityStructs/RotationEntityStruct.cs View File

@@ -1,9 +0,0 @@
using Svelto.ECS.Components;

namespace Svelto.ECS.EntityStructs
{
public struct RotationEntityStruct : IEntityStruct
{
public ECSQuaternion rotation;
}
}

+ 0
- 9
Svelto.ECS.Components/EntityStructs/ScalingEntityStruct.cs View File

@@ -1,9 +0,0 @@
using Svelto.ECS.Components;

namespace Svelto.ECS.EntityStructs
{
public struct ScalingEntityStruct : IEntityStruct
{
public ECSVector3 scale;
}
}

+ 0
- 23
Svelto.ECS.Components/Svelto.ECS.Components.asmdef View File

@@ -1,23 +0,0 @@
{
"name": "Svelto.ECS.Components.Unity",
"references": [
"Unity.Mathematics",
"Svelto.ECS",
"Svelto.Common"
],
"optionalUnityReferences": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.mathematics",
"expression": "0.0.9",
"define": "UNITY_MATHEMATICS"
}
]
}

+ 8
- 0
Svelto.ECS/AllowMultipleAttribute.cs View File

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

namespace Svelto.ECS
{
public class AllowMultipleAttribute : Attribute
{
}
}

+ 52
- 76
Svelto.ECS/CheckEntityUtilities.cs View File

@@ -1,105 +1,81 @@
#if !DEBUG || PROFILER
#define DISABLE_CHECKS
#if DEBUG && !PROFILER
using System.Collections.Generic;
using Svelto.DataStructures;
#else
using System.Diagnostics;
#endif
using System;
using System.Collections.Generic;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public partial class EnginesRoot
{
#if DISABLE_CHECKS
[Conditional("_CHECKS_DISABLED")]
#endif
void CheckRemoveEntityID(EGID entityID, IEntityDescriptor descriptorEntity)
#if DEBUG && !PROFILER
void CheckRemoveEntityID(EGID egid)
{
var descriptorEntitiesToBuild = descriptorEntity.entitiesToBuild;
if (_groupEntityDB.TryGetValue(entityID.groupID, out var @group))
// Console.LogError("<color=orange>removed</color>".FastConcat(egid.ToString()));
if (_idCheckers.TryGetValue(egid.groupID, out var hash))
{
for (int i = 0; i < descriptorEntitiesToBuild.Length; i++)
{
CheckRemoveEntityID(entityID, descriptorEntitiesToBuild[i].GetEntityType(), group, descriptorEntity.ToString());
}
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));

hash.Remove(egid.entityID);

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

#if DISABLE_CHECKS
[Conditional("_CHECKS_DISABLED")]
#endif
void CheckRemoveEntityID(EGID entityID, Type entityViewType, Dictionary<Type, ITypeSafeDictionary> group, string name)
void CheckAddEntityID(EGID egid)
{
ITypeSafeDictionary entities;
if (group.TryGetValue(entityViewType, out entities))
{
if (entities.Has(entityID.entityID) == false)
{
Console.LogError("Entity ".FastConcat(name, " with not found ID is about to be removed: ")
.FastConcat(entityViewType.ToString())
.FastConcat(" id: ")
.FastConcat(entityID.entityID)
.FastConcat(" groupid: ")
.FastConcat(entityID.groupID));
}
}
// 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
{
Console.LogError("Entity ".FastConcat(name, " with not found ID is about to be removed: ")
.FastConcat(entityViewType.ToString())
.FastConcat(" id: ")
.FastConcat(entityID.entityID)
.FastConcat(" groupid: ")
.FastConcat(entityID.groupID));
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("'"));
}

hash.Add(egid.entityID);
}
void RemoveGroupID(ExclusiveGroup.ExclusiveGroupStruct groupID)
{
_idCheckers.Remove(groupID);
}

#if DISABLE_CHECKS
readonly FasterDictionary<uint, HashSet<uint>> _idCheckers = new FasterDictionary<uint, HashSet<uint>>();
#else
[Conditional("_CHECKS_DISABLED")]
#endif
void CheckAddEntityID<T>(EGID entityID, T descriptorEntity) where T:IEntityDescriptor
void CheckRemoveEntityID(EGID egid)
{
var descriptorEntitiesToBuild = descriptorEntity.entitiesToBuild;
//these are the entities added in this frame
if (_groupEntityDB.TryGetValue(entityID.groupID, out var @group))
{
for (int i = 0; i < descriptorEntitiesToBuild.Length; i++)
{
CheckAddEntityID(entityID, descriptorEntitiesToBuild[i].GetEntityType(), group,
descriptorEntity.ToString());
}
}
}

#if DISABLE_CHECKS
[Conditional("_CHECKS_DISABLED")]
#endif
static void CheckAddEntityID(EGID entityID, Type entityViewType, Dictionary<Type, ITypeSafeDictionary> group,
string name)
void CheckAddEntityID(EGID egid)
{
ITypeSafeDictionary entities;
if (group.TryGetValue(entityViewType, out entities))
{
if (entities.Has(entityID.entityID))
{
Console.LogError("Entity ".FastConcat(name, " with used ID is about to be built: ")
.FastConcat(entityViewType.ToString())
.FastConcat(" id: ")
.FastConcat(entityID.entityID)
.FastConcat(" groupid: ")
.FastConcat(entityID.groupID));
}
}
}
[Conditional("_CHECKS_DISABLED")]
void RemoveGroupID(ExclusiveGroup.ExclusiveGroupStruct groupID)
{
}
#endif
}
}
}

+ 373
- 373
Svelto.ECS/DBC.cs View File

@@ -6,339 +6,339 @@ using System;

namespace DBC.ECS
{
/// <summary>
/// Design By Contract Checks.
///
/// Each method generates an exception or
/// a trace assertion statement if the contract is broken.
/// </summary>
/// <remarks>
/// This example shows how to call the Require method.
/// Assume DBC_CHECK_PRECONDITION is defined.
/// <code>
/// public void Test(int x)
/// {
/// try
/// {
/// Check.Require(x > 1, "x must be > 1");
/// }
/// catch (System.Exception ex)
/// {
/// Console.WriteLine(ex.ToString());
/// }
/// }
/// </code>
/// If you wish to use trace assertion statements, intended for Debug scenarios,
/// rather than exception handling then set
///
/// <code>Check.UseAssertions = true</code>
///
/// You can specify this in your application entry point and maybe make it
/// dependent on conditional compilation flags or configuration file settings, e.g.,
/// <code>
/// #if DBC_USE_ASSERTIONS
/// Check.UseAssertions = true;
/// #endif
/// </code>
/// You can direct output to a Trace listener. For example, you could insert
/// <code>
/// Trace.Listeners.Clear();
/// Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
/// </code>
///
/// or direct output to a file or the Event Log.
///
/// (Note: For ASP.NET clients use the Listeners collection
/// of the Debug, not the Trace, object and, for a Release build, only exception-handling
/// is possible.)
/// </remarks>
///
static class Check
{
#region Interface
/// <summary>
/// Design By Contract Checks.
///
/// Each method generates an exception or
/// a trace assertion statement if the contract is broken.
/// </summary>
/// <remarks>
/// This example shows how to call the Require method.
/// Assume DBC_CHECK_PRECONDITION is defined.
/// <code>
/// public void Test(int x)
/// {
/// try
/// {
/// Check.Require(x > 1, "x must be > 1");
/// }
/// catch (System.Exception ex)
/// {
/// Console.WriteLine(ex.ToString());
/// }
/// }
/// </code>
/// If you wish to use trace assertion statements, intended for Debug scenarios,
/// rather than exception handling then set
///
/// <code>Check.UseAssertions = true</code>
///
/// You can specify this in your application entry point and maybe make it
/// dependent on conditional compilation flags or configuration file settings, e.g.,
/// <code>
/// #if DBC_USE_ASSERTIONS
/// Check.UseAssertions = true;
/// #endif
/// </code>
/// You can direct output to a Trace listener. For example, you could insert
/// <code>
/// Trace.Listeners.Clear();
/// Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
/// </code>
///
/// or direct output to a file or the Event Log.
///
/// (Note: For ASP.NET clients use the Listeners collection
/// of the Debug, not the Trace, object and, for a Release build, only exception-handling
/// is possible.)
/// </remarks>
///
static class Check
{
#region Interface

/// <summary>
/// Precondition check.
/// </summary>
/// <summary>
/// Precondition check.
/// </summary>
#if DISABLE_CHECKS
[Conditional("__NEVER_DEFINED__")]
[Conditional("__NEVER_DEFINED__")]
#endif
public static void Require(bool assertion, string message)
{
if (UseExceptions)
{
if (!assertion)
throw new PreconditionException(message);
}
else
{
Trace.Assert(assertion, "Precondition: " + message);
}
}
public static void Require(bool assertion, string message)
{
if (UseExceptions)
{
if (!assertion)
throw new PreconditionException(message);
}
else
{
Trace.Assert(assertion, "Precondition: " + message);
}
}

/// <summary>
/// Precondition check.
/// </summary>
///
/// <summary>
/// Precondition check.
/// </summary>
///
#if DISABLE_CHECKS
[Conditional("__NEVER_DEFINED__")]
[Conditional("__NEVER_DEFINED__")]
#endif
public static void Require(bool assertion, string message, Exception inner)
{
if (UseExceptions)
{
if (!assertion)
throw new PreconditionException(message, inner);
}
else
{
Trace.Assert(assertion, "Precondition: " + message);
}
}
public static void Require(bool assertion, string message, Exception inner)
{
if (UseExceptions)
{
if (!assertion)
throw new PreconditionException(message, inner);
}
else
{
Trace.Assert(assertion, "Precondition: " + message);
}
}

/// <summary>
/// Precondition check.
/// </summary>
///
/// <summary>
/// Precondition check.
/// </summary>
///
#if DISABLE_CHECKS
[Conditional("__NEVER_DEFINED__")]
[Conditional("__NEVER_DEFINED__")]
#endif
public static void Require(bool assertion)
{
if (UseExceptions)
{
if (!assertion)
throw new PreconditionException("Precondition failed.");
}
else
{
Trace.Assert(assertion, "Precondition failed.");
}
}
/// <summary>
/// Postcondition check.
/// </summary>
///
public static void Require(bool assertion)
{
if (UseExceptions)
{
if (!assertion)
throw new PreconditionException("Precondition failed.");
}
else
{
Trace.Assert(assertion, "Precondition failed.");
}
}
/// <summary>
/// Postcondition check.
/// </summary>
///
#if DISABLE_CHECKS
[Conditional("__NEVER_DEFINED__")]
[Conditional("__NEVER_DEFINED__")]
#endif
public static void Ensure(bool assertion, string message)
{
if (UseExceptions)
{
if (!assertion)
throw new PostconditionException(message);
}
else
{
Trace.Assert(assertion, "Postcondition: " + message);
}
}
public static void Ensure(bool assertion, string message)
{
if (UseExceptions)
{
if (!assertion)
throw new PostconditionException(message);
}
else
{
Trace.Assert(assertion, "Postcondition: " + message);
}
}

/// <summary>
/// Postcondition check.
/// </summary>
///
/// <summary>
/// Postcondition check.
/// </summary>
///
#if DISABLE_CHECKS
[Conditional("__NEVER_DEFINED__")]
[Conditional("__NEVER_DEFINED__")]
#endif
public static void Ensure(bool assertion, string message, Exception inner)
{
if (UseExceptions)
{
if (!assertion)
throw new PostconditionException(message, inner);
}
else
{
Trace.Assert(assertion, "Postcondition: " + message);
}
}
public static void Ensure(bool assertion, string message, Exception inner)
{
if (UseExceptions)
{
if (!assertion)
throw new PostconditionException(message, inner);
}
else
{
Trace.Assert(assertion, "Postcondition: " + message);
}
}

/// <summary>
/// Postcondition check.
/// </summary>
///
/// <summary>
/// Postcondition check.
/// </summary>
///
#if DISABLE_CHECKS
[Conditional("__NEVER_DEFINED__")]
[Conditional("__NEVER_DEFINED__")]
#endif
public static void Ensure(bool assertion)
{
if (UseExceptions)
{
if (!assertion)
throw new PostconditionException("Postcondition failed.");
}
else
{
Trace.Assert(assertion, "Postcondition failed.");
}
}
/// <summary>
/// Invariant check.
/// </summary>
///
public static void Ensure(bool assertion)
{
if (UseExceptions)
{
if (!assertion)
throw new PostconditionException("Postcondition failed.");
}
else
{
Trace.Assert(assertion, "Postcondition failed.");
}
}
/// <summary>
/// Invariant check.
/// </summary>
///
#if DISABLE_CHECKS
[Conditional("__NEVER_DEFINED__")]
[Conditional("__NEVER_DEFINED__")]
#endif
public static void Invariant(bool assertion, string message)
{
if (UseExceptions)
{
if (!assertion)
throw new InvariantException(message);
}
else
{
Trace.Assert(assertion, "Invariant: " + message);
}
}
public static void Invariant(bool assertion, string message)
{
if (UseExceptions)
{
if (!assertion)
throw new InvariantException(message);
}
else
{
Trace.Assert(assertion, "Invariant: " + message);
}
}

/// <summary>
/// Invariant check.
/// </summary>
///
/// <summary>
/// Invariant check.
/// </summary>
///
#if DISABLE_CHECKS
[Conditional("__NEVER_DEFINED__")]
[Conditional("__NEVER_DEFINED__")]
#endif
public static void Invariant(bool assertion, string message, Exception inner)
{
if (UseExceptions)
{
if (!assertion)
throw new InvariantException(message, inner);
}
else
{
Trace.Assert(assertion, "Invariant: " + message);
}
}
public static void Invariant(bool assertion, string message, Exception inner)
{
if (UseExceptions)
{
if (!assertion)
throw new InvariantException(message, inner);
}
else
{
Trace.Assert(assertion, "Invariant: " + message);
}
}

/// <summary>
/// Invariant check.
/// </summary>
///
/// <summary>
/// Invariant check.
/// </summary>
///
#if DISABLE_CHECKS
[Conditional("__NEVER_DEFINED__")]
[Conditional("__NEVER_DEFINED__")]
#endif
public static void Invariant(bool assertion)
{
if (UseExceptions)
{
if (!assertion)
throw new InvariantException("Invariant failed.");
}
else
{
Trace.Assert(assertion, "Invariant failed.");
}
}
public static void Invariant(bool assertion)
{
if (UseExceptions)
{
if (!assertion)
throw new InvariantException("Invariant failed.");
}
else
{
Trace.Assert(assertion, "Invariant failed.");
}
}

/// <summary>
/// Assertion check.
/// </summary>
/// <summary>
/// Assertion check.
/// </summary>
#if DISABLE_CHECKS
[Conditional("__NEVER_DEFINED__")]
[Conditional("__NEVER_DEFINED__")]
#endif
public static void Assert(bool assertion, string message)
{
if (UseExceptions)
{
if (!assertion)
throw new AssertionException(message);
}
else
{
Trace.Assert(assertion, "Assertion: " + message);
}
}
public static void Assert(bool assertion, string message)
{
if (UseExceptions)
{
if (!assertion)
throw new AssertionException(message);
}
else
{
Trace.Assert(assertion, "Assertion: " + message);
}
}

/// <summary>
/// Assertion check.
/// </summary>
///
/// <summary>
/// Assertion check.
/// </summary>
///
#if DISABLE_CHECKS
[Conditional("__NEVER_DEFINED__")]
[Conditional("__NEVER_DEFINED__")]
#endif
public static void Assert(bool assertion, string message, Exception inner)
{
if (UseExceptions)
{
if (!assertion)
throw new AssertionException(message, inner);
}
else
{
Trace.Assert(assertion, "Assertion: " + message);
}
}
public static void Assert(bool assertion, string message, Exception inner)
{
if (UseExceptions)
{
if (!assertion)
throw new AssertionException(message, inner);
}
else
{
Trace.Assert(assertion, "Assertion: " + message);
}
}

/// <summary>
/// Assertion check.
/// </summary>
///
/// <summary>
/// Assertion check.
/// </summary>
///
#if DISABLE_CHECKS
[Conditional("__NEVER_DEFINED__")]
[Conditional("__NEVER_DEFINED__")]
#endif
public static void Assert(bool assertion)
{
if (UseExceptions)
{
if (!assertion)
throw new AssertionException("Assertion failed.");
}
else
{
Trace.Assert(assertion, "Assertion failed.");
}
}
public static void Assert(bool assertion)
{
if (UseExceptions)
{
if (!assertion)
throw new AssertionException("Assertion failed.");
}
else
{
Trace.Assert(assertion, "Assertion failed.");
}
}

/// <summary>
/// Set this if you wish to use Trace Assert statements
/// instead of exception handling.
/// (The Check class uses exception handling by default.)
/// </summary>
public static bool UseAssertions
{

/// <summary>
/// Set this if you wish to use Trace Assert statements
/// instead of exception handling.
/// (The Check class uses exception handling by default.)
/// </summary>
public static bool UseAssertions
{
get
{
return useAssertions;
}
set
{
useAssertions = value;
}
}
#endregion // Interface
get
{
return useAssertions;
}
set
{
useAssertions = value;
}
}

#region Implementation
#endregion // Interface

// No creation
#region Implementation

/// <summary>
/// Is exception handling being used?
/// </summary>
private static bool UseExceptions
{
get
{
return !useAssertions;
}
}
// No creation

/// <summary>
/// Is exception handling being used?
/// </summary>
static bool UseExceptions
{
get
{
return !useAssertions;
}
}

// Are trace assertion statements being used?
// Default is to use exception handling.
private static bool useAssertions = false;
// Are trace assertion statements being used?
// Default is to use exception handling.
static bool useAssertions;

#endregion // Implementation
#endregion // Implementation

} // End Check
} // End Check

class Trace
internal class Trace
{
internal static void Assert(bool assertion, string v)
{
@@ -346,7 +346,7 @@ namespace DBC.ECS
System.Diagnostics.Contracts.Contract.Assert(assertion, v);
#else
System.Diagnostics.Trace.Assert(assertion, v);
#endif
#endif
}
}

@@ -354,93 +354,93 @@ namespace DBC.ECS

/// <summary>
/// Exception raised when a contract is broken.
/// Catch this exception type if you wish to differentiate between
/// Catch this exception type if you wish to differentiate between
/// any DesignByContract exception and other runtime exceptions.
///
///
/// </summary>
public class DesignByContractException : Exception
{
protected DesignByContractException() {}
protected DesignByContractException(string message) : base(message) {}
protected DesignByContractException(string message, Exception inner) : base(message, inner) {}
}
{
protected DesignByContractException() {}
protected DesignByContractException(string message) : base(message) {}
protected DesignByContractException(string message, Exception inner) : base(message, inner) {}
}

/// <summary>
/// Exception raised when a precondition fails.
/// </summary>
public class PreconditionException : DesignByContractException
{
/// <summary>
/// Precondition Exception.
/// </summary>
public PreconditionException() {}
/// <summary>
/// Precondition Exception.
/// </summary>
public PreconditionException(string message) : base(message) {}
/// <summary>
/// Precondition Exception.
/// </summary>
public PreconditionException(string message, Exception inner) : base(message, inner) {}
}
/// <summary>
/// Exception raised when a postcondition fails.
/// </summary>
public class PostconditionException : DesignByContractException
{
/// <summary>
/// Postcondition Exception.
/// </summary>
public PostconditionException() {}
/// <summary>
/// Postcondition Exception.
/// </summary>
public PostconditionException(string message) : base(message) {}
/// <summary>
/// Postcondition Exception.
/// </summary>
public PostconditionException(string message, Exception inner) : base(message, inner) {}
}
/// <summary>
/// Exception raised when a precondition fails.
/// </summary>
public class PreconditionException : DesignByContractException
{
/// <summary>
/// Precondition Exception.
/// </summary>
public PreconditionException() {}
/// <summary>
/// Precondition Exception.
/// </summary>
public PreconditionException(string message) : base(message) {}
/// <summary>
/// Precondition Exception.
/// </summary>
public PreconditionException(string message, Exception inner) : base(message, inner) {}
}

/// <summary>
/// Exception raised when an invariant fails.
/// </summary>
public class InvariantException : DesignByContractException
{
/// <summary>
/// Invariant Exception.
/// </summary>
public InvariantException() {}
/// <summary>
/// Invariant Exception.
/// </summary>
public InvariantException(string message) : base(message) {}
/// <summary>
/// Invariant Exception.
/// </summary>
public InvariantException(string message, Exception inner) : base(message, inner) {}
}
/// <summary>
/// Exception raised when a postcondition fails.
/// </summary>
public class PostconditionException : DesignByContractException
{
/// <summary>
/// Postcondition Exception.
/// </summary>
public PostconditionException() {}
/// <summary>
/// Postcondition Exception.
/// </summary>
public PostconditionException(string message) : base(message) {}
/// <summary>
/// Postcondition Exception.
/// </summary>
public PostconditionException(string message, Exception inner) : base(message, inner) {}
}

/// <summary>
/// Exception raised when an assertion fails.
/// </summary>
public class AssertionException : DesignByContractException
{
/// <summary>
/// Assertion Exception.
/// </summary>
public AssertionException() {}
/// <summary>
/// Assertion Exception.
/// </summary>
public AssertionException(string message) : base(message) {}
/// <summary>
/// Assertion Exception.
/// </summary>
public AssertionException(string message, Exception inner) : base(message, inner) {}
}
/// <summary>
/// Exception raised when an invariant fails.
/// </summary>
public class InvariantException : DesignByContractException
{
/// <summary>
/// Invariant Exception.
/// </summary>
public InvariantException() {}
/// <summary>
/// Invariant Exception.
/// </summary>
public InvariantException(string message) : base(message) {}
/// <summary>
/// Invariant Exception.
/// </summary>
public InvariantException(string message, Exception inner) : base(message, inner) {}
}

/// <summary>
/// Exception raised when an assertion fails.
/// </summary>
public class AssertionException : DesignByContractException
{
/// <summary>
/// Assertion Exception.
/// </summary>
public AssertionException() {}
/// <summary>
/// Assertion Exception.
/// </summary>
public AssertionException(string message) : base(message) {}
/// <summary>
/// Assertion Exception.
/// </summary>
public AssertionException(string message, Exception inner) : base(message, inner) {}
}

#endregion // Exception classes
#endregion // Exception classes

} // End Design By Contract

+ 37
- 0
Svelto.ECS/DataStructures/SetEGIDWithoutBoxing.cs View File

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

namespace Svelto.ECS.Internal
{
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)
{
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;
}

return null;
}

public static void Warmup()
{
}
}
}

+ 90
- 94
Svelto.ECS/DataStructures/TypeSafeDictionary.cs View File

@@ -1,27 +1,28 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.DataStructures.Experimental;

namespace Svelto.ECS.Internal
{
public interface ITypeSafeDictionary
{
int Count { get; }
int Count { get; }
ITypeSafeDictionary Create();

void RemoveEntitiesFromEngines(
Dictionary<Type, FasterList<IEngine>> entityViewEnginesDB, in PlatformProfiler profiler);
void AddEntitiesToEngines(
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDb,
ITypeSafeDictionary realDic, in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group);

void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup,
Dictionary<Type, FasterList<IEngine>> engines, in PlatformProfiler profiler);
void RemoveEntitiesFromEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB,
in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group);

void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId);

void AddEntitiesToEngines(Dictionary<Type, FasterList<IEngine>> entityViewEnginesDb,
ITypeSafeDictionary realDic, in PlatformProfiler profiler);
void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup,
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, in PlatformProfiler profiler);

void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup);
void RemoveEntityFromDictionary(EGID fromEntityGid, in PlatformProfiler profiler);

void SetCapacity(uint size);
void Trim();
@@ -34,111 +35,115 @@ namespace Svelto.ECS.Internal
{
static readonly Type _type = typeof(TValue);
static readonly string _typeName = _type.Name;
static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type);
public TypeSafeDictionary(uint size) : base(size) { }
static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type);
public TypeSafeDictionary(uint size) : base(size) {}
public TypeSafeDictionary() {}
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId)
{
var typeSafeDictionary = entitiesToSubmit as TypeSafeDictionary<TValue>;
foreach (var tuple in typeSafeDictionary)
{
try
{
if (_hasEgid)
{
var needEgid = (INeedEGID)tuple.Value;
needEgid.ID = new EGID(tuple.Key, groupId);
Add(tuple.Key, (TValue) needEgid);
}
else
Add(tuple.Key, ref tuple.Value);
if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref tuple.Value, new EGID(tuple.Key, groupId));

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

public void AddEntitiesToEngines(
Dictionary<Type, FasterList<IEngine>> entityViewEnginesDB,
ITypeSafeDictionary realDic, in PlatformProfiler profiler)
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB,
ITypeSafeDictionary realDic, in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group)
{
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)
{
var typeSafeDictionary = realDic as TypeSafeDictionary<TValue>;
AddEntityViewToEngines(entityViewEnginesDB, ref typeSafeDictionary.GetValueByRef(value.Key), null,
in profiler);
}
in profiler, new EGID(value.Key, group));
}

public bool Has(uint entityIdEntityId) { return ContainsKey(entityIdEntityId); }
public void RemoveEntitiesFromEngines(
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB,
in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group)
{
foreach (var value in this)
RemoveEntityViewFromEngines(entityViewEnginesDB, ref GetValueByRef(value.Key), null, in profiler,
new EGID(value.Key, group));
}

public void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID? toEntityID,
ITypeSafeDictionary toGroup, Dictionary<Type, FasterList<IEngine>> engines,
in PlatformProfiler profiler)
public bool Has(uint entityIdEntityId)
{
var valueIndex = GetValueIndex(fromEntityGid.entityID);
return ContainsKey(entityIdEntityId);
}

public void RemoveEntityFromDictionary(EGID fromEntityGid, in PlatformProfiler profiler)
{
Remove(fromEntityGid.entityID);
}

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

if (toGroup != null)
{
RemoveEntityViewFromEngines(engines, ref _values[valueIndex], fromEntityGid.groupID, in profiler);
var toGroupCasted = toGroup as TypeSafeDictionary<TValue>;
ref var entity = ref _values[valueIndex];
var previousGroup = fromEntityGid.groupID;
///
/// NOTE I WOULD EVENTUALLY NEED TO REUSE THE REAL ID OF THE REMOVING ELEMENT
/// SO THAT I CAN DECREASE THE GLOBAL GROUP COUNT
///
// entity.ID = EGID.UPDATE_REAL_ID_AND_GROUP(entity.ID, toEntityID.groupID, entityCount);
if (_hasEgid)
{
var needEgid = (INeedEGID)entity;
needEgid.ID = toEntityID.Value;
entity = (TValue) needEgid;
}
var index = toGroupCasted.Add(fromEntityGid.entityID, ref entity);

AddEntityViewToEngines(engines, ref toGroupCasted._values[index], previousGroup,
in profiler);
}
else
RemoveEntityViewFromEngines(engines, ref _values[valueIndex], null, in profiler);
ref var entity = ref valuesArray[valueIndex];

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

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

public void RemoveEntitiesFromEngines(
Dictionary<Type, FasterList<IEngine>> entityViewEnginesDB,
in PlatformProfiler profiler)
public void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup,
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, in PlatformProfiler profiler)
{
var values = GetValuesArray(out var count);
var valueIndex = GetIndex(fromEntityGid.entityID);
ref var entity = ref valuesArray[valueIndex];

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

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

for (var i = 0; i < count; i++)
RemoveEntityViewFromEngines(entityViewEnginesDB, ref values[i], null, in profiler);
if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value);

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

AddEntityViewToEngines(engines, ref toGroupCasted.valuesArray[index], previousGroup,
in profiler, toEntityID.Value);
}
else
RemoveEntityViewFromEngines(engines, ref entity, null, in profiler, fromEntityGid);
}

public ITypeSafeDictionary Create() { return new TypeSafeDictionary<TValue>(); }
public ITypeSafeDictionary Create()
{
return new TypeSafeDictionary<TValue>();
}

void AddEntityViewToEngines(Dictionary<Type, FasterList<IEngine>> entityViewEnginesDB,
ref TValue entity,
ExclusiveGroup.ExclusiveGroupStruct? previousGroup,
in PlatformProfiler profiler)
void AddEntityViewToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB,
ref TValue entity, ExclusiveGroup.ExclusiveGroupStruct? previousGroup,
in PlatformProfiler profiler, EGID egid)
{
//get all the engines linked to TValue
if (!entityViewEnginesDB.TryGetValue(_type, out var entityViewsEngines)) return;
if (!entityViewEnginesDB.TryGetValue(new RefWrapper<Type>(_type), out var entityViewsEngines)) return;

if (previousGroup == null)
{
@@ -147,7 +152,7 @@ namespace Svelto.ECS.Internal
{
using (profiler.Sample(entityViewsEngines[i], _typeName))
{
(entityViewsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity);
(entityViewsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity, egid);
}
}
catch (Exception e)
@@ -163,22 +168,23 @@ namespace Svelto.ECS.Internal
{
using (profiler.Sample(entityViewsEngines[i], _typeName))
{
(entityViewsEngines[i] as IReactOnSwap<TValue>).MovedTo(ref entity, previousGroup.Value);
(entityViewsEngines[i] as IReactOnSwap<TValue>).MovedTo(ref entity, previousGroup.Value,
egid);
}
}
catch (Exception e)
{
throw new ECSException(
"Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e);
"Code crashed inside MovedTo callback ".FastConcat(typeof(TValue).ToString()), e);
}
}
}

static void RemoveEntityViewFromEngines(
Dictionary<Type, FasterList<IEngine>> entityViewEnginesDB, ref TValue entity,
ExclusiveGroup.ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler)
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> @group, ref TValue entity,
ExclusiveGroup.ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid)
{
if (!entityViewEnginesDB.TryGetValue(_type, out var entityViewsEngines)) return;
if (!@group.TryGetValue(new RefWrapper<Type>(_type), out var entityViewsEngines)) return;

if (previousGroup == null)
{
@@ -186,7 +192,7 @@ namespace Svelto.ECS.Internal
try
{
using (profiler.Sample(entityViewsEngines[i], _typeName))
(entityViewsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity);
(entityViewsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid);
}
catch (Exception e)
{
@@ -194,13 +200,14 @@ namespace Svelto.ECS.Internal
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e);
}
}
#if SEEMS_UNNECESSARY
else
{
for (var i = 0; i < entityViewsEngines.Count; i++)
try
{
using (profiler.Sample(entityViewsEngines[i], _typeName))
(entityViewsEngines[i] as IReactOnSwap<TValue>).MovedFrom(ref entity);
(entityViewsEngines[i] as IReactOnSwap<TValue>).MovedFrom(ref entity, egid);
}
catch (Exception e)
{
@@ -208,18 +215,7 @@ namespace Svelto.ECS.Internal
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e);
}
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref TValue FindElement(uint entityGidEntityId)
{
#if DEBUG && !PROFILER
if (TryFindIndex(entityGidEntityId, out var findIndex) == false)
throw new Exception("Entity not found in this group ".FastConcat(typeof(TValue).ToString()));
#else
TryFindIndex(entityGidEntityId, out var findIndex);
#endif
return ref _values[findIndex];
}
}
}

+ 2
- 5
Svelto.ECS/Dispatcher/DispatchOnChange.cs View File

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

namespace Svelto.ECS
{
public class DispatchOnChange<T> : DispatchOnSet<T> where T:struct, IEquatable<T>
public class DispatchOnChange<T> : DispatchOnSet<T> where T:IEquatable<T>
{
public DispatchOnChange(EGID senderID) : base(senderID)
{ }
@@ -15,10 +15,7 @@ namespace Svelto.ECS
base.value = value;
}

get
{
return _value;
}
get => _value;
}
}
}

+ 5
- 8
Svelto.ECS/Dispatcher/DispatchOnSet.cs View File

@@ -1,14 +1,11 @@
using System;
using Svelto.WeakEvents;

namespace Svelto.ECS
{
public class DispatchOnSet<T> where T:struct
public class DispatchOnSet<T>
{
public DispatchOnSet(EGID senderID)
{
_subscribers = new WeakEvent<EGID, T>();
_senderID = senderID;
}
@@ -18,8 +15,8 @@ namespace Svelto.ECS
{
_value = value;

if(_paused == false)
_subscribers.Invoke(_senderID, value);
if (_paused == false)
_subscribers(_senderID, value);
}
}
@@ -39,7 +36,7 @@ namespace Svelto.ECS
protected T _value;
readonly EGID _senderID;

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

+ 105
- 16
Svelto.ECS/DynamicEntityDescriptor.cs View File

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

namespace Svelto.ECS
{
@@ -8,32 +9,120 @@ namespace Svelto.ECS
/// 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> : IEntityDescriptor where TType : IEntityDescriptor, new()
{
public DynamicEntityDescriptor(IEntityBuilder[] extraEntities)
internal DynamicEntityDescriptor(bool isExtendible) : this()
{
DBC.ECS.Check.Require(extraEntities.Length > 0,
"don't use a DynamicEntityDescriptorInfo if you don't need to use extra EntityViews");

var defaultEntities = EntityDescriptorTemplate<TType>.descriptor.entitiesToBuild;
var length = defaultEntities.Length;

entitiesToBuild = new IEntityBuilder[length + extraEntities.Length + 1];
_entitiesToBuild = new IEntityBuilder[length + 1];

Array.Copy(defaultEntities, 0, entitiesToBuild, 0, length);
Array.Copy(extraEntities, 0, entitiesToBuild, length, extraEntities.Length);
Array.Copy(defaultEntities, 0, _entitiesToBuild, 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>
(
new EntityStructInfoView
{
entitiesToBuild = _entitiesToBuild
}
);
}

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

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

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

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

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

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

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

if (extraEntitiesLength == 0)
{
localEntitiesToBuild = startingEntities;
return localEntitiesToBuild;
}

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

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

Array.Copy(extraEntities, 0, localEntitiesToBuild, 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>
(
new EntityStructInfoView
{
entitiesToBuild = localEntitiesToBuild
}
);

return localEntitiesToBuild;
}

static int SetupSpecialEntityStruct(IEntityBuilder[] defaultEntities, out IEntityBuilder[] entitiesToBuild,
int extraLenght)
{
int length = defaultEntities.Length;
int index = -1;

var builder = new EntityBuilder<EntityStructInfoView>
for (var i = 0; i < length; i++)
{
_initializer = new EntityStructInfoView
{
entitiesToBuild = entitiesToBuild,
type = typeof(TType)
//the special entity already exists
if (defaultEntities[i].GetEntityType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW)
{
index = i;
break;
}
};
entitiesToBuild[entitiesToBuild.Length - 1] = builder;
}

if (index == -1)
{
index = length + extraLenght;
entitiesToBuild = new IEntityBuilder[index + 1];
}
else
entitiesToBuild = new IEntityBuilder[length + extraLenght];

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

return index;
}

public IEntityBuilder[] entitiesToBuild { get; }

public IEntityBuilder[] entitiesToBuild => _entitiesToBuild;

IEntityBuilder[] _entitiesToBuild;
}
}

+ 4
- 4
Svelto.ECS/ECSException.cs View File

@@ -1,13 +1,13 @@
using System;

namespace Svelto.ECS.Internal
namespace Svelto.ECS
{
class ECSException : Exception
public class ECSException : Exception
{
public ECSException(string message):base(message)
public ECSException(string message):base("<color=red>".FastConcat(message, "</color>"))
{}
public ECSException(string message, Exception innerE):base(message, innerE)
public ECSException(string message, Exception innerE):base("<color=red>".FastConcat(message, "</color>"), innerE)
{}
}
}

Svelto.ECS.Components/ECSResources/ECSResources.cs → Svelto.ECS/ECSResources/ECSResources.cs View File

@@ -11,7 +11,12 @@ namespace Svelto.ECS.Experimental
static class ResourcesECSDB<T>
{
internal static readonly FasterList<T> _resources = new FasterList<T>();
static readonly FasterList<T> _resources = new FasterList<T>();

internal static ref T resources(uint id)
{
return ref _resources[(int) id - 1];
}

internal static uint ToECS(T resource)
{
@@ -25,7 +30,7 @@ namespace Svelto.ECS.Experimental
if (id - 1 < _resources.Count)
return _resources[(int) id - 1];
return default(T);
return default;
}
}

@@ -34,17 +39,9 @@ namespace Svelto.ECS.Experimental
public static void Set<T>(ref this ECSResources<T> resource, T newText)
{
if (resource.id != 0)
ResourcesECSDB<T>._resources[(int) resource.id] = newText;
ResourcesECSDB<T>.resources(resource.id) = newText;
else
resource.id = ResourcesECSDB<T>.ToECS(newText);
}
public static void Set(ref this ECSString resource, string newText)
{
if (resource.id != 0)
ResourcesECSDB<string>._resources[(int) resource.id] = newText;
else
resource.id = ResourcesECSDB<string>.ToECS(newText);
}
}
}

+ 38
- 0
Svelto.ECS/ECSResources/ECSString.cs View File

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

namespace Svelto.ECS.Experimental
{
[Serialization.DoNotSerialize]
public struct ECSString:IEquatable<ECSString>
{
uint id;

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

public static implicit operator string(ECSString ecsString)
{
return ResourcesECSDB<string>.FromECS(ecsString.id);
}
public void Set(string newText)
{
if (id != 0)
ResourcesECSDB<string>.resources(id) = newText;
else
id = ResourcesECSDB<string>.ToECS(newText);
}

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

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

+ 12
- 137
Svelto.ECS/EGID.cs View File

@@ -1,14 +1,15 @@
#if !REAL_ID
using System;
using System.Collections.Generic;

#pragma warning disable 660,661

namespace Svelto.ECS
{
//todo: add debug map
[Serialization.DoNotSerialize]
[Serializable]
public struct EGID:IEquatable<EGID>,IEqualityComparer<EGID>,IComparable<EGID>
{
readonly ulong _GID;

public uint entityID => (uint) (_GID & 0xFFFFFFFF);

public ExclusiveGroup.ExclusiveGroupStruct groupID => new ExclusiveGroup.ExclusiveGroupStruct((uint) (_GID >> 32));
@@ -17,17 +18,17 @@ namespace Svelto.ECS
{
return obj1._GID == obj2._GID;
}
public static bool operator !=(EGID obj1, EGID obj2)
{
return obj1._GID != obj2._GID;
}
public EGID(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) : this()
{
_GID = MAKE_GLOBAL_ID(entityID, groupID);
}
static ulong MAKE_GLOBAL_ID(uint entityId, uint groupId)
{
return (ulong)groupId << 32 | ((ulong)entityId & 0xFFFFFFFF);
@@ -37,10 +38,10 @@ namespace Svelto.ECS
{
return id.entityID;
}
//in the way it's used, ulong must be always the same for each id/group
public static explicit operator ulong(EGID id) { return id._GID; }
public bool Equals(EGID other)
{
return _GID == other._GID;
@@ -65,138 +66,12 @@ namespace Svelto.ECS
{
_GID = MAKE_GLOBAL_ID(entityID, groupID);
}
}
}

#else

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

#pragma warning disable 660,661

namespace Svelto.ECS
{
public struct EGID:IEquatable<EGID>,IEqualityComparer<EGID>,IComparable<EGID>
{
readonly ulong _GID;
const int idbits = 22; //one bit is reserved
const int groupbits = 20;
const int realidbits = 21;

public EGID(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) : this()
{
DBC.ECS.Check.Require(entityID < bit21, "the entityID value is outside the range, max value: (2^22)-1");
DBC.ECS.Check.Require(groupID < bit20, "the groupID value is outside the range");
_GID = MAKE_GLOBAL_ID(entityID, groupID, 0, 1);
}

const uint bit21 = 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001_1111_1111_1111_1111_1111;
const uint bit22 = 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0011_1111_1111_1111_1111_1111;
const uint bit20 = 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_1111_1111_1111_1111_1111;

public uint entityID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return (uint) (_GID & bit22); }
}
public ExclusiveGroup.ExclusiveGroupStruct groupID =>
new ExclusiveGroup.ExclusiveGroupStruct((uint) ((_GID >> idbits) & bit20));

// 1 21 20 1 21
// | | realid | groupid |R| entityID |

static ulong MAKE_GLOBAL_ID(uint entityId, uint groupId, uint realId, byte hasID)
public override string ToString()
{
var makeGlobalId = (((ulong)realId & bit21) << (idbits+groupbits)) | (((ulong)groupId & bit20) << idbits) | ((ulong)entityId & bit22);

return makeGlobalId | (ulong) (hasID << idbits + groupbits + realidbits);
return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID);
}

public static explicit operator uint(EGID id)
{
return id.entityID;
}
public static bool operator ==(EGID obj1, EGID obj2)
{
throw new NotSupportedException();
}
public static bool operator !=(EGID obj1, EGID obj2)
{
throw new NotSupportedException();
}

public bool Equals(EGID other)
{
throw new NotSupportedException();
}

public bool Equals(EGID x, EGID y)
{
throw new NotSupportedException();
}
public int CompareTo(EGID other)
{
throw new NotSupportedException();
}

//in the way it's used, ulong must be always the same for each id/group
public static explicit operator ulong(EGID id)
{
throw new NotSupportedException();
}

public int GetHashCode(EGID egid)
{
throw new NotSupportedException();
}
internal EGID(ulong GID) : this()
{
_GID = GID;
}

internal EGID(uint entityID, uint groupID) : this()
{
_GID = MAKE_GLOBAL_ID(entityID, groupID, 0, 1);
}
internal static EGID UPDATE_REAL_ID_AND_GROUP(EGID egid, uint toGroupID, uint realID)
{
if (egid.hasID == 0)
return new EGID(MAKE_GLOBAL_ID(SAFE_ID(realID), toGroupID, realID, 0));
return new EGID(MAKE_GLOBAL_ID(egid.entityID, toGroupID, realID, 1));
}
internal static EGID UPDATE_REAL_ID(EGID egid, uint realID)
{
if (egid.hasID == 0)
return new EGID(MAKE_GLOBAL_ID(SAFE_ID(realID), egid.groupID, realID, 0));
return new EGID(MAKE_GLOBAL_ID(egid.entityID, egid.groupID, realID, 1));
}
internal static EGID CREATE_WITHOUT_ID(uint toGroupID, uint realID)
{
var _GID = MAKE_GLOBAL_ID(SAFE_ID(realID), toGroupID, realID, 0);
return new EGID(_GID);
}

public byte hasID { get { return (byte) (_GID >> idbits + groupbits + realidbits); } }

internal uint realID
{
get { return ((uint)(_GID >> idbits + groupbits)) & bit21; }
}

static uint SAFE_ID(uint u) { return u | (bit21 + 1); }
readonly ulong _GID;
}
}
#endif

+ 30
- 7
Svelto.ECS/EGIDMapper.cs View File

@@ -1,28 +1,51 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.ECS.Internal;
using System.Runtime.InteropServices;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public struct EGIDMapper<T> where T : struct, IEntityStruct
{
internal TypeSafeDictionary<T> map;
internal FasterDictionary<uint, T> map;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Entity(uint entityID)
{
return ref map.FindElement(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()));
#else
map.TryFindIndex(entityID, out var findIndex);
#endif
return ref map.valuesArray[findIndex];
}
public bool TryQueryEntity(uint entityID, out T @value)
public bool TryGetEntity(uint entityID, out T value)
{
if (map.TryFindIndex(entityID, out var index))
{
@value = map.GetDirectValue(index);
value = map.GetDirectValue(index);
return true;
}

@value = default;
value = default;
return false;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeStructRef<T> EntityUnsafeRef(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()));
#else
map.TryFindIndex(entityID, out var findIndex);
#endif
var alloc = GCHandle.Alloc(map.valuesArray, GCHandleType.Pinned);
return new UnsafeStructRef<T>(ref map.valuesArray[findIndex], alloc);
}
}
}
}


+ 20
- 13
Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs View File

@@ -1,7 +1,7 @@
using Svelto.DataStructures.Experimental;
using EntitiesDB =
Svelto.DataStructures.Experimental.FasterDictionary<uint, System.Collections.Generic.Dictionary<System.Type,
Svelto.ECS.Internal.ITypeSafeDictionary>>;
using System;
using System.Collections;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

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

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

public void ClearOther()
@@ -28,22 +30,27 @@ namespace Svelto.ECS
//do not remove the dictionaries of entities per type created so far, they will be reused
foreach (var entitiesPerType in groups.Value)
{
//clear the dictionary of entities create do far (it won't allocate though)
//clear the dictionary of entities create do far (it won't allocate though)
entitiesPerType.Value.Clear();
}
}

otherEntitiesCreatedPerGroup.Clear();
}
internal FasterDictionary<uint, uint> currentEntitiesCreatedPerGroup;
internal FasterDictionary<uint, uint> otherEntitiesCreatedPerGroup;
internal EntitiesDB current;
internal EntitiesDB other;

readonly EntitiesDB _entityViewsToAddBufferA = new EntitiesDB();
readonly EntitiesDB _entityViewsToAddBufferB = new EntitiesDB();
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>();
@@ -52,7 +59,7 @@ namespace Svelto.ECS
{
currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA;
otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB;
current = _entityViewsToAddBufferA;
other = _entityViewsToAddBufferB;
}


+ 57
- 52
Svelto.ECS/EnginesRoot.Engines.cs View File

@@ -1,15 +1,28 @@
using System;
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.DataStructures.Experimental;
using Svelto.ECS.Internal;
using Svelto.ECS.Schedulers;
using Svelto.WeakEvents;

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

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

readonly DataStructures.WeakReference<EnginesRoot> _weakReference;
}
/// <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
@@ -21,37 +34,47 @@ namespace Svelto.ECS
public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler)
{
_entitiesOperations = new FasterDictionary<ulong, EntitySubmitOperation>();
_reactiveEnginesAddRemove = new Dictionary<Type, FasterList<IEngine>>();
_reactiveEnginesSwap = new Dictionary<Type, FasterList<IEngine>>();
_enginesSet = new HashSet<IEngine>();
_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>();
_transientEntitiesOperations = new FasterList<EntitySubmitOperation>();

_groupEntityDB = new FasterDictionary<uint, Dictionary<Type, ITypeSafeDictionary>>();
_groupsPerEntity = new Dictionary<Type, FasterDictionary<uint, ITypeSafeDictionary>>();
_groupEntityViewsDB = new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>();
_groupsPerEntity = new FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>>();
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd();

_entitiesStream = new EntitiesStream();
_entitiesDB = new EntitiesDB(_groupEntityDB, _groupsPerEntity, _entitiesStream);
_entitiesDB = new EntitiesDB(_groupEntityViewsDB, _groupsPerEntity, _entitiesStream);
_scheduler = entityViewScheduler;
_scheduler.onTick = new WeakAction(SubmitEntityViews);
_scheduler.onTick = new EntitiesSubmitter(this);
}
public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler, bool isDeserializationOnly):this(entityViewScheduler)
{
_isDeserializationOnly = isDeserializationOnly;
}

public void AddEngine(IEngine engine)
{
DBC.ECS.Check.Require(_enginesSet.Contains(engine) == false,
"The same engine has been added more than once "
.FastConcat(engine.ToString()));

var type = engine.GetType();
var refWrapper = new RefWrapper<Type>(type);
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()));
try
{
if (engine is IReactOnAddAndRemove viewEngine)
CheckEntityViewsEngine<IReactOnAddAndRemove>(viewEngine, _reactiveEnginesAddRemove);
CheckEntityViewsEngine(viewEngine, _reactiveEnginesAddRemove);
if (engine is IReactOnSwap viewEngineSwap)
CheckEntityViewsEngine<IReactOnSwap>(viewEngineSwap, _reactiveEnginesSwap);
CheckEntityViewsEngine(viewEngineSwap, _reactiveEnginesSwap);

_enginesTypeSet.Add(refWrapper);
_enginesSet.Add(engine);

if (engine is IDisposable)
@@ -65,15 +88,12 @@ namespace Svelto.ECS
}
catch (Exception e)
{
#if !DEBUG
throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString()), e);
#else
Console.LogException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString()), e);
#endif
throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " "), e);
}
}
void CheckEntityViewsEngine<T>(IEngine engine, Dictionary<Type, FasterList<IEngine>> engines)

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

@@ -89,9 +109,10 @@ namespace Svelto.ECS
}

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

@@ -99,40 +120,24 @@ namespace Svelto.ECS
}
}

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

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

list.Add(engine);
}

readonly Dictionary<Type, FasterList<IEngine>> _reactiveEnginesAddRemove;
readonly Dictionary<Type, FasterList<IEngine>> _reactiveEnginesSwap;
readonly HashSet<IEngine> _enginesSet;
readonly FasterList<IDisposable> _disposableEngines;
//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
//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, Dictionary<Type, ITypeSafeDictionary>> _groupEntityDB;
//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 Dictionary<Type, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity;
readonly EntitiesStream _entitiesStream;
readonly EntitiesDB _entitiesDB;
readonly FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> _reactiveEnginesAddRemove;
readonly FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> _reactiveEnginesSwap;
readonly FasterList<IDisposable> _disposableEngines;
static readonly Type OBJECT_TYPE = typeof(object);
static readonly Type ENTITY_INFO_VIEW_TYPE = typeof(EntityStructInfoView);
readonly FasterList<IEngine> _enginesSet;
readonly HashSet<Type> _enginesTypeSet;
}
}

+ 172
- 175
Svelto.ECS/EnginesRoot.Entities.cs View File

@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures.Experimental;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
@@ -18,28 +18,35 @@ namespace Svelto.ECS
{
using (var profiler = new PlatformProfiler("Final Dispose"))
{
foreach (var groups in _groupEntityDB)
foreach (var groups in _groupEntityViewsDB)
{
foreach (var entityList in groups.Value)
{
try
{
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler);
}
catch (Exception e)
{
Console.LogException(e);
}
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove,
profiler, new ExclusiveGroup.ExclusiveGroupStruct(groups.Key));
}
}

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

foreach (var engine in _disposableEngines)
try
{
engine.Dispose();
}
catch (Exception e)
{
Console.LogException(e);
}

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

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

_entitiesStream.Dispose();
}

GC.SuppressFinalize(this);
@@ -56,252 +63,242 @@ namespace Svelto.ECS
///
public IEntityStreamConsumerFactory GenerateConsumerFactory()
{
return new GenericentityStreamConsumerFactory(new DataStructures.WeakReference<EnginesRoot>(this));
return new GenericEntityStreamConsumerFactory(this);
}

public IEntityFactory GenerateEntityFactory()
{
return new GenericEntityFactory(new DataStructures.WeakReference<EnginesRoot>(this));
return new GenericEntityFactory(this);
}

public IEntityFunctions GenerateEntityFunctions()
{
return new GenericEntityFunctions(new DataStructures.WeakReference<EnginesRoot>(this));
return new GenericEntityFunctions(this);
}

///--------------------------------------------
[MethodImpl(MethodImplOptions.AggressiveInlining)]
EntityStructInitializer BuildEntity<T>(EGID entityID, object[] implementors) where T : IEntityDescriptor, new()
EntityStructInitializer BuildEntity(EGID entityID, IEntityBuilder[] entitiesToBuild,
IEnumerable<object> implementors = null)
{
return BuildEntity(entityID, EntityDescriptorTemplate<T>.descriptor, implementors);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
EntityStructInitializer BuildEntity<T>(EGID entityID, T entityDescriptor, object[] implementors)
where T : IEntityDescriptor
{
var descriptorEntitiesToBuild = entityDescriptor.entitiesToBuild;

CheckAddEntityID(entityID, entityDescriptor);
CheckAddEntityID(entityID);

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

return new EntityStructInitializer(entityID, dic);
}

#if REAL_ID
[MethodImpl(MethodImplOptions.AggressiveInlining)]
EntityStructInitializer BuildEntity<T>(ExclusiveGroup.ExclusiveGroupStruct groupID, object[] implementors) where T : IEntityDescriptor, new()
{
//temporary egid, will change during the real submission, needed for the initialization look up
var egid = EGID.CREATE_WITHOUT_ID(groupID, _groupedEntityToAdd.entitiesBuiltThisSubmission++);
var dic = EntityFactory.BuildGroupedEntities(egid, _groupedEntityToAdd.current,
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, implementors);
return new EntityStructInitializer(egid, dic.groups);
}
#endif

///--------------------------------------------
void Preallocate<T>(uint groupID, uint size) where T : IEntityDescriptor, new()
{
var entityViewsToBuild = EntityDescriptorTemplate<T>.descriptor.entitiesToBuild;
var numberOfEntityViews = entityViewsToBuild.Length;
var numberOfEntityViews = entityViewsToBuild.Length;

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

for (var index = 0; index < numberOfEntityViews; index++)
{
var entityViewBuilder = entityViewsToBuild[index];
var entityViewType = entityViewBuilder.GetEntityType();

if (group.TryGetValue(entityViewType, out var dbList) == false)
group[entityViewType] = entityViewBuilder.Preallocate(ref dbList, size);
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);

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

groupedGroup[groupID] = dbList;
}
}

///--------------------------------------------
///
void MoveEntity(IEntityBuilder[] entityBuilders, EGID fromEntityGID, Type originalDescriptorType, EGID? toEntityGID)
///
void MoveEntityFromAndToEngines(IEntityBuilder[] entityBuilders, EGID fromEntityGID, EGID? toEntityGID)
{
using (var sampler = new PlatformProfiler("Move Entity"))
using (var sampler = new PlatformProfiler("Move Entity From Engines"))
{
//for each entity view generated by the entity descriptor
if (_groupEntityDB.TryGetValue(fromEntityGID.groupID, out var fromGroup) == false)
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));
.FastConcat(" group: ").FastConcat(fromEntityGID.groupID));

//Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor!
bool correctEntityDescriptorFound = true;

EntityStructInfoView entityInfoView = default;
if (fromGroup.TryGetValue(ENTITY_INFO_VIEW_TYPE, out var entityInfoViewDic) &&
if (fromGroup.TryGetValue(new RefWrapper<Type>(EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW),
out var entityInfoViewDic) &&
(entityInfoViewDic as TypeSafeDictionary<EntityStructInfoView>).TryGetValue(
fromEntityGID.entityID, out entityInfoView) && (correctEntityDescriptorFound =
entityInfoView.type == originalDescriptorType))
{
var entitiesToMove = entityInfoView.entitiesToBuild;

Dictionary<Type, ITypeSafeDictionary> toGroup = null;

if (toEntityGID != null)
{
var toGroupID = toEntityGID.Value.groupID;
if (_groupEntityDB.TryGetValue(toGroupID, out toGroup) == false)
toGroup = _groupEntityDB[toGroupID] = new Dictionary<Type, ITypeSafeDictionary>();
}

for (int i = 0; i < entitiesToMove.Length; i++)
MoveEntityView(fromEntityGID, toEntityGID, toGroup, ref fromGroup,
entitiesToMove[i].GetEntityType(), sampler);
}
fromEntityGID.entityID, out var entityInfoView))
MoveEntities(fromEntityGID, toEntityGID, entityInfoView.entitiesToBuild, fromGroup, sampler);
//otherwise it's a normal static entity descriptor
else
{
#if DEBUG && !PROFILER
if (correctEntityDescriptorFound == false)
throw new ECSException(INVALID_DYNAMIC_DESCRIPTOR_ERROR.FastConcat(" ID ").FastConcat(fromEntityGID.entityID)
.FastConcat(" group ID ").FastConcat(fromEntityGID.groupID).FastConcat(
" descriptor found: ", entityInfoView.type.Name, " descriptor Excepted ",
originalDescriptorType.Name));
#endif

Dictionary<Type, ITypeSafeDictionary> toGroup = null;
if (toEntityGID != null)
{
var toGroupID = toEntityGID.Value.groupID;
if (_groupEntityDB.TryGetValue(toGroupID, out toGroup) == false)
toGroup = _groupEntityDB[toGroupID] = new Dictionary<Type, ITypeSafeDictionary>();
}

for (var i = 0; i < entityBuilders.Length; i++)
MoveEntityView(fromEntityGID, toEntityGID, toGroup, ref fromGroup,
entityBuilders[i].GetEntityType(), sampler);
}
MoveEntities(fromEntityGID, toEntityGID, entityBuilders, fromGroup, sampler);
}
}

void MoveEntityView(EGID entityGID, EGID? toEntityGID, Dictionary<Type, ITypeSafeDictionary> toGroup,
ref Dictionary<Type, ITypeSafeDictionary> fromGroup, Type entityViewType, in PlatformProfiler profiler)
void MoveEntities(EGID fromEntityGID, EGID? toEntityGID, IEntityBuilder[] entitiesToMove,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, PlatformProfiler sampler)
{
if (fromGroup.TryGetValue(entityViewType, out var fromTypeSafeDictionary) == false)
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = null;

if (toEntityGID != null)
{
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID)
.FastConcat(" group: ").FastConcat(entityGID.groupID));
var toGroupID = toEntityGID.Value.groupID;

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

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

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

//in case we want to move to a new group, otherwise is just a remove
if (toEntityGID != null)
{
if (toGroup.TryGetValue(entityViewType, out toEntitiesDictionary) == false)
{
toEntitiesDictionary = fromTypeSafeDictionary.Create();
toGroup.Add(entityViewType, toEntitiesDictionary);
}
//then remove all the entities from the dictionary
for (var i = 0; i < entitiesToMove.Length; i++)
RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityType(), sampler);
}

if (_groupsPerEntity.TryGetValue(entityViewType, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[entityViewType] = new FasterDictionary<uint, ITypeSafeDictionary>();
void CopyEntityToDictionary(EGID entityGID, EGID toEntityGID,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityViewType)
{
var wrapper = new RefWrapper<Type>(entityViewType);

groupedGroup[toEntityGID.Value.groupID] = toEntitiesDictionary;
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));
}

#if DEBUG && !PROFILER
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
{
throw new EntityNotFoundException(entityGID, entityViewType);
}
#endif
if (toGroup.TryGetValue(wrapper, out var toEntitiesDictionary) == false)
{
toEntitiesDictionary = fromTypeSafeDictionary.Create();
toGroup.Add(wrapper, toEntitiesDictionary);
}

fromTypeSafeDictionary.MoveEntityFromDictionaryAndEngines(entityGID, toEntityGID,
toEntitiesDictionary,
toEntityGID == null ? _reactiveEnginesAddRemove :
_reactiveEnginesSwap,
in profiler);
//todo: this must be unit tested properly
if (_groupsPerEntity.TryGetValue(wrapper, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[wrapper] =
new FasterDictionary<uint, ITypeSafeDictionary>();

if (fromTypeSafeDictionary.Count == 0) //clean up
{
_groupsPerEntity[entityViewType].Remove(entityGID.groupID);
groupedGroup[toEntityGID.groupID] = toEntitiesDictionary;

//I don't remove the group if empty on purpose, in case it needs to be reused however I trim it to save
//memory
fromTypeSafeDictionary.Trim();
}
fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary);
}

void RemoveGroupAndEntitiesFromDB(uint groupID, Type entityDescriptor)
void MoveEntityViewFromAndToEngines(EGID entityGID, EGID? toEntityGID,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityViewType,
in PlatformProfiler profiler)
{
throw new NotImplementedException();
/* var profiler = new PlatformProfiler();
using (profiler.StartNewSession("Remove Group Of Entities"))
{
FasterDictionary<int, ITypeSafeDictionary> @group;
if (_groupsPerEntity.TryGetValue(entityDescriptor, out group))
{
if (group.TryGetValue())
foreach (var entity in group)
{
MoveEntity(entity.);
}
}
}*/
//add all the entities
var refWrapper = new RefWrapper<Type>(entityViewType);
if (fromGroup.TryGetValue(refWrapper, out var fromTypeSafeDictionary) == false)
{
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID)
.FastConcat(" group: ").FastConcat(entityGID.groupID));
}

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);
#endif
fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID,
toEntitiesDictionary, toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap,
in profiler);
}

void RemoveGroupAndEntitiesFromDB(uint groupID)
void RemoveEntityFromDictionary(EGID entityGID,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, Type entityViewType,
in PlatformProfiler profiler)
{
using (var profiler = new PlatformProfiler("Remove Group"))
var refWrapper = new RefWrapper<Type>(entityViewType);
if (fromGroup.TryGetValue(refWrapper, out var fromTypeSafeDictionary) == false)
{
var dictionariesOfEntities = _groupEntityDB[groupID];
foreach (var dictionaryOfEntities in dictionariesOfEntities)
{
var platformProfiler = profiler;
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, platformProfiler);
var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key];
groupedGroupOfEntities.Remove(groupID);
}
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID)
.FastConcat(" group: ").FastConcat(entityGID.groupID));
}

//careful, in this case I assume you really don't want to use this group anymore
//so I remove it from the database
_groupEntityDB.Remove(groupID);
fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID, profiler);

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

///--------------------------------------------
void SwapEntityGroup(IEntityBuilder[] builders, Type originalEntityDescriptor, EGID fromEntityID,
EGID toEntityID)
/// <summary>
/// Todo: I should keep the group, but I need to mark the group as deleted for the Exist function to work
/// </summary>
/// <param name="groupID"></param>
/// <param name="profiler"></param>
void RemoveGroupAndEntitiesFromDB(uint groupID, in PlatformProfiler profiler)
{
DBC.ECS.Check.Require(fromEntityID.groupID != toEntityID.groupID,
"entity group is the same of the destination group");
var dictionariesOfEntities = _groupEntityViewsDB[groupID];
foreach (var dictionaryOfEntities in dictionariesOfEntities)
{
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler,
new ExclusiveGroup.ExclusiveGroupStruct(groupID));
var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key];
groupedGroupOfEntities.Remove(groupID);
}

MoveEntity(builders, fromEntityID, originalEntityDescriptor, toEntityID);
//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);
}

internal Consumer<T> GenerateConsumer<T>(string name, int capacity) where T : unmanaged, IEntityStruct
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct
{
return _entitiesStream.GenerateConsumer<T>(name, capacity);
}

public Consumer<T> GenerateConsumer<T>(ExclusiveGroup @group, string name, int capacity) where T : unmanaged,
public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) where T : unmanaged,
IEntityStruct
{
return _entitiesStream.GenerateConsumer<T>(@group, name, capacity);
return _entitiesStream.GenerateConsumer<T>(group, name, capacity);
}
const string INVALID_DYNAMIC_DESCRIPTOR_ERROR =
"Found an entity requesting an invalid dynamic descriptor, this " +
"can happen only if you are building different entities with the " +
"same ID in the same group! The operation will continue using" + "the base descriptor only ";

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

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

readonly EntitiesDB _entitiesDB;
readonly EntitiesStream _entitiesStream;
}
}

+ 32
- 22
Svelto.ECS/EnginesRoot.GenericEntityFactory.cs View File

@@ -1,47 +1,57 @@
namespace Svelto.ECS
using System.Collections.Generic;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public partial class EnginesRoot
{
class GenericEntityFactory : IEntityFactory
{
public GenericEntityFactory(DataStructures.WeakReference<EnginesRoot> weakReference)
public GenericEntityFactory(EnginesRoot weakReference)
{
_weakEngine = weakReference;
_enginesRoot = new WeakReference<EnginesRoot>(weakReference);
}

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

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

#if REAL_ID
public EntityStructInitializer BuildEntity<T>(ExclusiveGroup.ExclusiveGroupStruct groupID, object[] implementors = null) where T : IEntityDescriptor, new()
public EntityStructInitializer BuildEntity<T>(EGID egid, T entityDescriptor,
IEnumerable<object> implementors)
where T : IEntityDescriptor
{
return _weakEngine.Target.BuildEntity<T>(groupID, implementors);
return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.entitiesToBuild, implementors);
}
#endif

public EntityStructInitializer BuildEntity<T>(EGID egid, T entityDescriptor, object[] implementors) where T:IEntityDescriptor
public EntityStructInitializer BuildEntity<T>(uint entityID,
ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, IEnumerable<object> implementors)
where T : IEntityDescriptor
{
return _weakEngine.Target.BuildEntity(egid, entityDescriptor, implementors);
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId),
descriptorEntity.entitiesToBuild,
implementors);
}

public EntityStructInitializer BuildEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, object[] implementors) where T:IEntityDescriptor
{
return _weakEngine.Target.BuildEntity(new EGID(entityID, groupStructId), descriptorEntity, implementors);
}
public void PreallocateEntitySpace<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) where T : IEntityDescriptor, new()
public void PreallocateEntitySpace<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size)
where T : IEntityDescriptor, new()
{
_weakEngine.Target.Preallocate<T>(groupStructId, size);
_enginesRoot.Target.Preallocate<T>(groupStructId, size);
}
readonly DataStructures.WeakReference<EnginesRoot> _weakEngine;

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

+ 62
- 58
Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs View File

@@ -1,22 +1,24 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Svelto.ECS.Internal;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public partial class EnginesRoot
{
/// <summary>
/// todo: EnginesRoot was a weakreference to give the change to inject
/// entityfunctions from other engines root. It probably should be reverted
/// </summary>
sealed class GenericEntityFunctions : IEntityFunctions
{
readonly DataStructures.WeakReference<EnginesRoot> _weakReference;

internal GenericEntityFunctions(DataStructures.WeakReference<EnginesRoot> weakReference)
internal GenericEntityFunctions(EnginesRoot weakReference)
{
_weakReference = weakReference;
_enginesRoot = new WeakReference<EnginesRoot>(weakReference);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T :
public void RemoveEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T :
IEntityDescriptor, new()
{
RemoveEntity<T>(new EGID(entityID, groupID));
@@ -25,46 +27,40 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntity<T>(EGID entityEGID) where T : IEntityDescriptor, new()
{
_weakReference.Target.CheckRemoveEntityID(entityEGID, EntityDescriptorTemplate<T>.descriptor);
_enginesRoot.Target.CheckRemoveEntityID(entityEGID);

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

public void RemoveEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupID)
where T : IEntityDescriptor, new()
{
throw new NotImplementedException();
_enginesRoot.Target.QueueEntitySubmitOperation<T>(
new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID,
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID)
{
_weakReference.Target.QueueEntitySubmitOperation(
new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(), new EGID(0, groupID)));
_enginesRoot.Target.RemoveGroupID(groupID);

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID,
ExclusiveGroup.ExclusiveGroupStruct toGroupID)
public void SwapEntityGroup<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID,
ExclusiveGroup.ExclusiveGroupStruct 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, ExclusiveGroup.ExclusiveGroupStruct toGroupID)
where T : IEntityDescriptor, new()
{
SwapEntityGroup<T>(fromID, new EGID(fromID.entityID, (uint)toGroupID));
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, ExclusiveGroup.ExclusiveGroupStruct toGroupID
, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup)
where T : IEntityDescriptor, new()
{
if (fromID.groupID != mustBeFromGroup)
@@ -72,55 +68,63 @@ namespace Svelto.ECS

SwapEntityGroup<T>(fromID, toGroupID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(EGID fromID, EGID toID)
where T : IEntityDescriptor, new()
{
_weakReference.Target.QueueEntitySubmitOperation<T>(
new EntitySubmitOperation(EntitySubmitOperationType.Swap,
fromID, toID, EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, typeof(T)));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(EGID fromID, EGID toID
, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup)
, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup)
where T : IEntityDescriptor, new()
{
if (fromID.groupID != mustBeFromGroup)
throw new ECSException("Entity is not coming from the expected group");

SwapEntityGroup<T>(fromID, toID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(EGID fromID, EGID toID)
where T : IEntityDescriptor, new()
{
_enginesRoot.Target.CheckRemoveEntityID(fromID);
_enginesRoot.Target.CheckAddEntityID(toID);

_enginesRoot.Target.QueueEntitySubmitOperation<T>(
new EntitySubmitOperation(EntitySubmitOperationType.Swap,
fromID, toID, EntityDescriptorTemplate<T>.descriptor.entitiesToBuild));
}
//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside
//engines of other enginesRoot
readonly WeakReference<EnginesRoot> _enginesRoot;
}
void QueueEntitySubmitOperation(EntitySubmitOperation entitySubmitOperation)
{
#if DEBUG && !PROFILER
entitySubmitOperation.trace = Environment.StackTrace;
#endif
_entitiesOperations.Add((ulong)entitySubmitOperation.fromID, ref entitySubmitOperation);
#if DEBUG && !PROFILER
entitySubmitOperation.trace = new StackFrame(1, true);
#endif
_entitiesOperations.Add((ulong) entitySubmitOperation.fromID, entitySubmitOperation);
}

void QueueEntitySubmitOperation<T>(EntitySubmitOperation entitySubmitOperation) where T:IEntityDescriptor
void QueueEntitySubmitOperation<T>(EntitySubmitOperation entitySubmitOperation) where T : IEntityDescriptor
{
#if DEBUG && !PROFILER
entitySubmitOperation.trace = Environment.StackTrace;
if (_entitiesOperations.TryGetValue((ulong) entitySubmitOperation.fromID, out var entitySubmitedOperation) == true)
#if DEBUG && !PROFILER
entitySubmitOperation.trace = new 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(),
" from ID: ", entitySubmitOperation.fromID.entityID.ToString())
.FastConcat( " previous operation type: ",
_entitiesOperations[(ulong) entitySubmitOperation.fromID].type
.ToString()));
throw new ECSException("Only one entity operation per submission is allowed"
.FastConcat(" entityViewType: ")
.FastConcat(typeof(T).Name)
.FastConcat(" submission type ", entitySubmitOperation.type.ToString(),
" from ID: ", entitySubmitOperation.fromID.entityID.ToString())
.FastConcat(" previous operation type: ",
_entitiesOperations[(ulong) entitySubmitOperation.fromID].type
.ToString()));
}
else
#endif
_entitiesOperations.Set((ulong)entitySubmitOperation.fromID, ref entitySubmitOperation);
#endif
_entitiesOperations.Set((ulong) entitySubmitOperation.fromID, entitySubmitOperation);
}
}
}

+ 93
- 100
Svelto.ECS/EnginesRoot.Submission.cs View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.DataStructures.Experimental;
using Svelto.ECS.Internal;
using Svelto.ECS.Schedulers;

@@ -11,125 +9,120 @@ namespace Svelto.ECS
public partial class EnginesRoot
{
readonly FasterList<EntitySubmitOperation> _transientEntitiesOperations;
void SubmitEntityViews()
{
using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission"))
{
if (_entitiesOperations.Count > 0)
int iterations = 0;
do
{
using (profiler.Sample("Remove and Swap operations"))
{
_transientEntitiesOperations.FastClear();
var entitySubmitOperations = _entitiesOperations.GetValuesArray(out var count);
_transientEntitiesOperations.AddRange(entitySubmitOperations, count);
_entitiesOperations.FastClear();
SingleSubmission(profiler);
} while ((_groupedEntityToAdd.currentEntitiesCreatedPerGroup.Count > 0 ||
_entitiesOperations.Count > 0) && ++iterations < 5);

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

void SingleSubmission(in PlatformProfiler profiler)
{
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.FastClear();

var entitiesOperations = _transientEntitiesOperations.ToArrayFast();
for (var i = 0; i < _transientEntitiesOperations.Count; i++)
var entitiesOperations = _transientEntitiesOperations.ToArrayFast();
for (var i = 0; i < _transientEntitiesOperations.Count; i++)
{
try
{
try
switch (entitiesOperations[i].type)
{
switch (entitiesOperations[i].type)
{
case EntitySubmitOperationType.Swap:
SwapEntityGroup(entitiesOperations[i].builders,
entitiesOperations[i].entityDescriptor,
entitiesOperations[i].fromID,
entitiesOperations[i].toID);
break;
case EntitySubmitOperationType.Remove:
MoveEntity(entitiesOperations[i].builders,
entitiesOperations[i].fromID,
entitiesOperations[i].entityDescriptor, null);
break;
case EntitySubmitOperationType.RemoveGroup:
if (entitiesOperations[i].entityDescriptor == null)
RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromID.groupID);
else
RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromID.groupID,
entitiesOperations[i].entityDescriptor);

break;
}
case EntitySubmitOperationType.Swap:
MoveEntityFromAndToEngines(entitiesOperations[i].builders,
entitiesOperations[i].fromID,
entitiesOperations[i].toID);
break;
case EntitySubmitOperationType.Remove:
MoveEntityFromAndToEngines(entitiesOperations[i].builders,
entitiesOperations[i].fromID, null);
break;
case EntitySubmitOperationType.RemoveGroup:
RemoveGroupAndEntitiesFromDB(
entitiesOperations[i].fromID.groupID, profiler);
break;
}
catch (Exception e)
{
var str = "Crash while executing Entity Operation "
.FastConcat(entitiesOperations[i].type.ToString());
#if RELAXED_ECS && !PROFILER
Console.LogException(str.FastConcat(" "
#if DEBUG && !PROFILER
, entitiesOperations[i].trace
#endif
), e);
#else
throw new ECSException(str.FastConcat(" ")
#if DEBUG && !PROFILER
.FastConcat(entitiesOperations[i].trace)
#endif
, e);
}
catch (Exception e)
{
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())
#endif
}
, e);
}
}
}
}

if (_groupedEntityToAdd.currentEntitiesCreatedPerGroup.Count > 0)
_groupedEntityToAdd.Swap();

if (_groupedEntityToAdd.otherEntitiesCreatedPerGroup.Count > 0)
{
using (profiler.Sample("Add operations"))
{
using (profiler.Sample("Add operations"))
try
{
//use other as source from now on current will be use to write new entityViews
_groupedEntityToAdd.Swap();

try
{
//Note: if N entity of the same type are added on the same frame the Add callback is called
//N times on the same frame. if the Add callback builds a new entity, that entity will not
//be available in the database until the N callbacks are done. Solving this could be
//complicated as callback and database update must be interleaved.
AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd, profiler);
}
finally
{
//other can be cleared now, but let's avoid deleting the dictionary every time
_groupedEntityToAdd.ClearOther();
}
AddEntityViewsToTheDBAndSuitableEngines(profiler);
}
finally
{
//other can be cleared now, but let's avoid deleting the dictionary every time
_groupedEntityToAdd.ClearOther();
}
}
}
}

void AddEntityViewsToTheDBAndSuitableEngines(DoubleBufferedEntitiesToAdd dbgroupsOfEntitiesToSubmit,
in PlatformProfiler profiler)
void AddEntityViewsToTheDBAndSuitableEngines(in PlatformProfiler profiler)
{
//each group is indexed by entity view type. for each type there is a dictionary indexed by entityID
var groupsOfEntitiesToSubmit = dbgroupsOfEntitiesToSubmit.other;
foreach (var groupOfEntitiesToSubmit in groupsOfEntitiesToSubmit)
{
foreach (var groupOfEntitiesToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup)
{
var groupID = groupOfEntitiesToSubmit.Key;
if (dbgroupsOfEntitiesToSubmit.otherEntitiesCreatedPerGroup.ContainsKey(groupID) == false) continue;

//if the group doesn't exist in the current DB let's create it first
if (_groupEntityDB.TryGetValue(groupID, out var groupDB) == false)
groupDB = _groupEntityDB[groupID] = new Dictionary<Type, ITypeSafeDictionary>();
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 groupOfEntitiesToSubmit.Value)
foreach (var entityViewsToSubmit in _groupedEntityToAdd.other[groupID])
{
var type = entityViewsToSubmit.Key;
var type = entityViewsToSubmit.Key;
var typeSafeDictionary = entityViewsToSubmit.Value;
if (groupDB.TryGetValue(type, out var dbDic) == false)
dbDic = groupDB[type] = typeSafeDictionary.Create();

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

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

if (_groupsPerEntity.TryGetValue(type, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[type] = new FasterDictionary<uint, ITypeSafeDictionary>();
if (_groupsPerEntity.TryGetValue(wrapper, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[wrapper] = new FasterDictionary<uint, ITypeSafeDictionary>();
groupedGroup[groupID] = dbDic;
}
}
@@ -138,25 +131,25 @@ namespace Svelto.ECS
//created by the entity built
using (profiler.Sample("Add entities to engines"))
{
foreach (var groupToSubmit in groupsOfEntitiesToSubmit)
foreach (var groupToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup)
{
var groupID = groupToSubmit.Key;
var groupDB = _groupEntityDB[groupID];
foreach (var entityViewsPerType in groupToSubmit.Value)

var groupDB = _groupEntityViewsDB[groupID];

foreach (var entityViewsToSubmit in _groupedEntityToAdd.other[groupID])
{
var realDic = groupDB[entityViewsPerType.Key];
entityViewsPerType.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, realDic, in profiler);
var realDic = groupDB[new RefWrapper<Type>(entityViewsToSubmit.Key)];

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

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

+ 114
- 59
Svelto.ECS/EntitiesDB.cs View File

@@ -3,17 +3,17 @@
#endif

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Svelto.DataStructures;
using Svelto.DataStructures.Experimental;

namespace Svelto.ECS.Internal
{
partial class EntitiesDB : IEntitiesDB
{
internal EntitiesDB(FasterDictionary<uint, Dictionary<Type, ITypeSafeDictionary>> groupEntityViewsDB,
Dictionary<Type, FasterDictionary<uint, ITypeSafeDictionary>> groupsPerEntity, EntitiesStream entityStream)
internal EntitiesDB(
FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> groupEntityViewsDB,
FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> groupsPerEntity,
EntitiesStream entityStream)
{
_groupEntityViewsDB = groupEntityViewsDB;
_groupsPerEntity = groupsPerEntity;
@@ -21,35 +21,40 @@ namespace Svelto.ECS.Internal
}

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

if (count != 1) throw new ECSException("Unique entities must be unique! ".FastConcat(typeof(T).ToString()));
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
{
T[] array;
T[] array;
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out var index)) != null)
return ref array[index];

throw new EntityNotFoundException(entityGID, typeof(T));
}

public ref T QueryEntity<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct @group) where T : struct, IEntityStruct
public ref T QueryEntity<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct
{
return ref QueryEntity<T>(new EGID(id, @group));
return ref QueryEntity<T>(new EGID(id, group));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) where T : struct, IEntityStruct
public T[] QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
where T : struct, IEntityStruct
{
uint @group = groupStruct;
uint group = groupStruct;
count = 0;
if (QueryEntitySafeDictionary(@group, out TypeSafeDictionary<T> typeSafeDictionary) == false)
if (SafeQueryEntityDictionary(group, out TypeSafeDictionary<T> typeSafeDictionary) == false)
return RetrieveEmptyEntityViewArray<T>();

return typeSafeDictionary.GetValuesArray(out count);
@@ -61,6 +66,18 @@ namespace Svelto.ECS.Internal
return new EntityCollection<T>(QueryEntities<T>(groupStruct, out var count), count);
}

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

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

public EntityCollections<T> QueryEntities<T>(ExclusiveGroup[] groups) where T : struct, IEntityStruct
{
return new EntityCollections<T>(this, groups);
@@ -74,17 +91,21 @@ namespace Svelto.ECS.Internal

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public (T1[], T2[]) QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
where T1 : struct, IEntityStruct
where T1 : struct, IEntityStruct
where T2 : struct, IEntityStruct
{
var T1entities = QueryEntities<T1>(@groupStruct, out var countCheck);
var T2entities = QueryEntities<T2>(@groupStruct, out count);
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: ".
FastConcat(typeof(T1).ToString()).FastConcat(
"Entity 2: ".FastConcat(typeof(T2).ToString())));

{
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);
}
@@ -94,34 +115,48 @@ namespace Svelto.ECS.Internal
<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct
{
var T1entities = QueryEntities<T1>(@groupStruct, out var countCheck1);
var T2entities = QueryEntities<T2>(@groupStruct, out var countCheck2);
var T3entities = QueryEntities<T3>(@groupStruct, out count);
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(" Entity 2: ".FastConcat(typeof(T2).ToString()).
FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString()))));
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);
}

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

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

typeSafeDictionary.GetValuesArray(out _);

return mapper;
}

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

mapper.map = typeSafeDictionary;

return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] QueryEntitiesAndIndex<T>(EGID entityGID, out uint index) where T : struct, IEntityStruct
{
@@ -133,7 +168,8 @@ namespace Svelto.ECS.Internal
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array) where T : struct, IEntityStruct
public bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array)
where T : struct, IEntityStruct
{
if ((array = QueryEntitiesAndIndexInternal<T>(entityGid, out index)) != null)
return true;
@@ -142,13 +178,15 @@ namespace Svelto.ECS.Internal
}

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryEntitiesAndIndex<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) where T : struct, IEntityStruct
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);
}
@@ -156,11 +194,19 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists<T>(EGID entityGID) where T : struct, IEntityStruct
{
if (QueryEntitySafeDictionary(entityGID.groupID, out TypeSafeDictionary<T> casted) == false) return false;
if (SafeQueryEntityDictionary(entityGID.groupID, out TypeSafeDictionary<T> casted) == false) return false;

return casted != null && casted.ContainsKey(entityGID.entityID);
}

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

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid)
{
@@ -184,14 +230,14 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityStruct
{
_entityStream.PublishEntity(ref QueryEntity<T>(egid));
_entityStream.PublishEntity(ref QueryEntity<T>(egid), egid);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
T[] QueryEntitiesAndIndexInternal<T>(EGID entityGID, out uint index) where T : struct, IEntityStruct
{
index = 0;
if (QueryEntitySafeDictionary(entityGID.groupID, out TypeSafeDictionary<T> safeDictionary) == false)
if (SafeQueryEntityDictionary(entityGID.groupID, out TypeSafeDictionary<T> safeDictionary) == false)
return null;

if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false)
@@ -201,46 +247,55 @@ namespace Svelto.ECS.Internal
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
bool QueryEntitySafeDictionary<T>(uint group, out TypeSafeDictionary<T> typeSafeDictionary) where T : struct, IEntityStruct
bool SafeQueryEntityDictionary<T>(uint group, out TypeSafeDictionary<T> typeSafeDictionary)
where T : struct, IEntityStruct
{
typeSafeDictionary = null;

//search for the group
if (_groupEntityViewsDB.TryGetValue(group, out var entitiesInGroupPerType) == false)
return false;

//search for the indexed entities in the group
if (entitiesInGroupPerType.TryGetValue(typeof(T), out var safeDictionary) == false)
if (UnsafeQueryEntityDictionary(group, TypeCache<T>.type, out var safeDictionary) == false)
{
typeSafeDictionary = default;
return false;
}

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

return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static ReadOnlyCollectionStruct<T> RetrieveEmptyEntityViewList<T>()
internal bool UnsafeQueryEntityDictionary(uint group, Type type, out ITypeSafeDictionary typeSafeDictionary)
{
var arrayFast = FasterList<T>.DefaultList.ToArrayFast();
//search for the group
if (_groupEntityViewsDB.TryGetValue(group, out var entitiesInGroupPerType) == false)
{
typeSafeDictionary = null;
return false;
}

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static T[] RetrieveEmptyEntityViewArray<T>()
{
return FasterList<T>.DefaultList.ToArrayFast();
return EmptyList<T>.emptyArray;
}

//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, Dictionary<Type, ITypeSafeDictionary>> _groupEntityViewsDB;
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> _groupEntityViewsDB;

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

static class EmptyList<T>
{
internal static readonly T[] emptyArray = new T[0];
}
}
}
}

+ 83
- 53
Svelto.ECS/EntityBuilder.CheckFields.cs View File

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

namespace Svelto.ECS
{
public static class EntityBuilderUtilities
internal static class EntityBuilderUtilities
{
#if DISABLE_CHECKS
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 type, bool needsReflection)
public static void CheckFields(Type entityStructType, bool needsReflection)
{
if (type == ENTITY_STRUCT_INFO_VIEW || type == EGIDType || type == ECLUSIVEGROUPSTRUCTTYPE)
if (entityStructType == ENTITY_STRUCT_INFO_VIEW ||
entityStructType == EGIDType ||
entityStructType == EXCLUSIVEGROUPSTRUCTTYPE ||
entityStructType == SERIALIZABLE_ENTITY_STRUCT)
{
return;
}

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

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

for (int i = fields.Length - 1; i >= 0; --i)
for (var i = fields.Length - 1; i >= 0; --i)
{
var field = fields[i];
var fieldFieldType = field.FieldType;
SubCheckFields(fieldFieldType);
FieldInfo fieldInfo = fields[i];
Type fieldType = fieldInfo.FieldType;
SubCheckFields(fieldType, entityStructType);
}
}
else
{
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
FieldInfo[] fields = entityStructType.GetFields(BindingFlags.Public | BindingFlags.Instance);

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

for (int i = fields.Length - 1; i >= 0; --i)
{
var field = fields[i];
if (field.FieldType.IsInterfaceEx() == false)
ProcessError("Entity View Structs must hold only entity components interfaces.", type);
var properties = field.FieldType.GetProperties(BindingFlags.Public |
BindingFlags.Instance | BindingFlags.DeclaredOnly);
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 == true)
if (properties[j].PropertyType.IsGenericType)
{
var genericTypeDefinition = properties[j].PropertyType.GetGenericTypeDefinition();
Type genericTypeDefinition = properties[j].PropertyType.GetGenericTypeDefinition();
if (genericTypeDefinition == DISPATCHONSETTYPE ||
genericTypeDefinition == DISPATCHONCHANGETYPE) continue;
genericTypeDefinition == DISPATCHONCHANGETYPE)
{
continue;
}
}

var propertyType = properties[j].PropertyType;
Type propertyType = properties[j].PropertyType;
if (propertyType != STRINGTYPE)
SubCheckFields(propertyType);
{
SubCheckFields(propertyType, entityStructType);
}
}
}
}
}

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

return;
}
ProcessError("Entity Structs field and Entity View Struct components must hold value types.",
fieldFieldType);

ProcessError(MSG, entityStructType, fieldType);
}
static void ProcessError(string message, Type type)
static void ProcessError(string message, Type entityViewType, Type fieldType = null)
{
#if !RELAXED_ECS
Type ENTITY_VIEW_TYPE = typeof(Type);
throw new EntityStructException(message, ENTITY_VIEW_TYPE, type);
#endif
if (fieldType != null)
{
throw new EntityStructException(message, entityViewType, fieldType);
}

throw new EntityStructException(message, entityViewType);
}
static readonly Type EGIDType = typeof(EGID);
static readonly Type ECLUSIVEGROUPSTRUCTTYPE = typeof(ExclusiveGroup.ExclusiveGroupStruct);
static readonly Type DISPATCHONSETTYPE = typeof(DispatchOnSet<>);
static readonly Type DISPATCHONCHANGETYPE = typeof(DispatchOnChange<>);
static readonly Type STRINGTYPE = typeof(String);
static readonly Type ENTITY_VIEW_TYPE = typeof(Type);
static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityStructInfoView);

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, 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()))
{
}
}
}

+ 87
- 28
Svelto.ECS/EntityBuilder.cs View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Svelto.DataStructures;
using Svelto.ECS.Hybrid;
using Svelto.ECS.Internal;
@@ -9,6 +10,51 @@ 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;
@@ -16,10 +62,16 @@ namespace Svelto.ECS
EntityBuilderUtilities.CheckFields(ENTITY_VIEW_TYPE, NEEDS_REFLECTION);

if (NEEDS_REFLECTION)
EntityView<T>.InitCache();
EntityView.InitCache();
}

public void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID entityID, object[] implementors)
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>();
@@ -28,21 +80,24 @@ namespace Svelto.ECS

if (NEEDS_REFLECTION)
{
DBC.ECS.Check.Require(implementors != null, "Implementors not found while building an EntityView");
DBC.ECS.Check.Require(castedDic.ContainsKey(entityID.entityID) == false,
"building an entity with already used entity id! id: ".FastConcat((ulong) entityID)
.FastConcat(" ", ENTITY_VIEW_NAME));
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);

EntityView<T>.BuildEntityView(out var entityView);
this.FillEntityView(ref entityView, entityViewBlazingFastReflection, implementors,
EntityView.implementorsByType, EntityView.cachedTypes);

this.FillEntityView(ref entityView, entityViewBlazingFastReflection, implementors, implementorsByType,
cachedTypes);
castedDic.Add(entityID.entityID, ref entityView);
castedDic.Add(egid.entityID, entityView);
}
else
{
castedDic.Add(entityID.entityID, _initializer);
DBC.ECS.Check.Require(!castedDic.ContainsKey(egid.entityID),
$"building an entity with already used entity id! id: '{egid.entityID}'");

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

@@ -61,27 +116,31 @@ namespace Svelto.ECS
return dictionary;
}

public Type GetEntityType() { return ENTITY_VIEW_TYPE; }
public Type GetEntityType()
{
return ENTITY_VIEW_TYPE;
}

#if DEBUG && !PROFILER
readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType =
new Dictionary<Type, ECSTuple<object, int>>();
#else
readonly Dictionary<Type, object> implementorsByType = new Dictionary<Type, object>();
#endif
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();
}

//this is used to avoid newing a dictionary every time, but it's used locally only and it's cleared for each use
readonly Dictionary<Type, Type[]> cachedTypes = new Dictionary<Type, Type[]>();
readonly T _initializer;

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

internal static readonly Type ENTITY_VIEW_TYPE = typeof(T);
static readonly T DEFAULT_IT = default;
static readonly bool NEEDS_REFLECTION = typeof(IEntityViewStruct).IsAssignableFrom(typeof(T));
static readonly string ENTITY_VIEW_NAME = ENTITY_VIEW_TYPE.ToString();
internal static readonly bool HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_VIEW_TYPE);
internal static readonly Type ENTITY_VIEW_TYPE;
public static readonly bool HAS_EGID;

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

+ 150
- 31
Svelto.ECS/EntityCollection.cs View File

@@ -1,7 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
@@ -52,7 +51,103 @@ namespace Svelto.ECS
int _index;
}
}
public struct EntityCollection<T1, T2>
{
public EntityCollection(in (T1[], T2[]) array, uint count)
{
_array = array;
_count = count;
}

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

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

public struct EntityIterator : IEnumerator<ValueRef<T1, T2>>
{
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;
}
}
public struct EntityCollection<T1, T2, T3>
{
public EntityCollection(in (T1[], T2[], T3[]) array, uint count)
{
_array = array;
_count = count;
}

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

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

public struct EntityIterator : IEnumerator<ValueRef<T1, T2, T3>>
{
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);

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

public void Dispose() {}

readonly (T1[], T2[], T3[]) _array;
readonly uint _count;
int _index;
}
}
public struct EntityCollections<T> where T : struct, IEntityStruct
{
public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this()
@@ -94,7 +189,7 @@ namespace Svelto.ECS
{
_index = -1;
_indexGroup = -1;
_array = _db.QueryEntities<T>(_groups[0], out _count);
_count = 0;
}

public ref T Current => ref _array[_index];
@@ -130,7 +225,7 @@ namespace Svelto.ECS
readonly IEntitiesDB _db;
readonly ExclusiveGroup[] _groups;

public struct EntityGroupsIterator : IEnumerator<EntityGroupsIterator.ValueRef>
public struct EntityGroupsIterator : IEnumerator<ValueRef<T1, T2>>
{
public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this()
{
@@ -145,8 +240,9 @@ namespace Svelto.ECS
while (_index + 1 >= _count && ++_indexGroup < _groups.Length)
{
_index = -1;
_array1 = _db.QueryEntities<T1>(_groups[_indexGroup], out _count);
_array2 = _db.QueryEntities<T2>(_groups[_indexGroup], out var count1);
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)
@@ -161,47 +257,70 @@ namespace Svelto.ECS
{
_index = -1;
_indexGroup = -1;
_array1 = _db.QueryEntities<T1>(_groups[0], out _count);
_array2 = _db.QueryEntities<T2>(_groups[0], out var count1);

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 Current => new ValueRef(_array1, _array2, (uint) _index);
public ValueRef<T1, T2> Current
{
get
{
var valueRef = new ValueRef<T1, T2>(_array, (uint) _index);
return valueRef;
}
}

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

public void Dispose() {}

readonly IEntitiesDB _db;
readonly IEntitiesDB _db;
readonly ExclusiveGroup[] _groups;
uint _count;
int _index;
int _indexGroup;
T2[] _array2;
T1[] _array1;
uint _count;
int _index;
int _indexGroup;
(T1[], T2[]) _array;
}
}
public struct ValueRef<T1, T2>
{
readonly (T1[], T2[]) array;

public struct ValueRef
{
readonly T1[] array1;
readonly T2[] array2;
readonly uint index;

readonly uint index;
public ValueRef(in (T1[], T2[]) entity1, uint i)
{
array = entity1;
index = i;
}

public ValueRef(T1[] entity1, T2[] entity2, uint i) : this()
{
array1 = entity1;
array2 = entity2;
index = i;
}
public ref T1 entityStructA => ref array.Item1[index];
public ref T2 entityStructB => ref array.Item2[index];
}
public struct ValueRef<T1, T2, T3>
{
readonly (T1[], T2[], T3[]) array;

public ref T1 Item1 => ref array1[index];
public ref T2 Item2 => ref array2[index];
}
readonly uint index;

public ValueRef(in (T1[], T2[], T3[]) entity1, uint i)
{
array = entity1;
index = i;
}

public ref T1 entityStructA => ref array.Item1[index];
public ref T2 entityStructB => ref array.Item2[index];
public ref T3 entityStructC => ref array.Item3[index];

}
}
}

Svelto.ECS/EntityDescriptorInfo.cs → Svelto.ECS/EntityDescriptorTemplate.cs View File

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

static class EntityDescriptorTemplate<TType> where TType : IEntityDescriptor, new()
{
internal static readonly StaticEntityDescriptorInfo<TType> descriptor
= new StaticEntityDescriptorInfo<TType>(new TType());
static EntityDescriptorTemplate()
{
descriptor = new TType();
}

public static IEntityDescriptor descriptor { get; }
}
}

+ 45
- 27
Svelto.ECS/EntityFactory.cs View File

@@ -1,71 +1,89 @@
using System;
using System.Collections.Generic;
using Svelto.DataStructures;

namespace Svelto.ECS.Internal
{
static class EntityFactory
{
internal static Dictionary<Type, ITypeSafeDictionary> BuildGroupedEntities(EGID egid,
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd, IEntityBuilder[] entitiesToBuild,
object[] implementors)
public static FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> BuildGroupedEntities(EGID egid,
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd,
IEntityBuilder[] entitiesToBuild,
IEnumerable<object> implementors)
{
var @group = FetchEntityGroup(egid.groupID, groupEntitiesToAdd);
var group = FetchEntityGroup(egid.groupID, groupEntitiesToAdd);

BuildEntitiesAndAddToGroup(egid, group, entitiesToBuild, implementors);

return group;
}

static Dictionary<Type, ITypeSafeDictionary> FetchEntityGroup(uint groupID,
static FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> FetchEntityGroup(uint groupID,
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityViewsByType)
{
if (groupEntityViewsByType.current.TryGetValue(groupID, out Dictionary<Type, ITypeSafeDictionary> @group) ==
false)
if (groupEntityViewsByType.current.TryGetValue(groupID, out var group) == false)
{
@group = new Dictionary<Type, ITypeSafeDictionary>();
group = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();
groupEntityViewsByType.current.Add(groupID, @group);
groupEntityViewsByType.current.Add(groupID, group);
}

groupEntityViewsByType.currentEntitiesCreatedPerGroup.TryGetValue(groupID, out var value);
groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = value+1;
if (groupEntityViewsByType.currentEntitiesCreatedPerGroup.TryGetValue(groupID, out var value) == false)
groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = 0;
else
groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = value+1;
return @group;
return group;
}

static void BuildEntitiesAndAddToGroup(EGID entityID,
Dictionary<Type, ITypeSafeDictionary> @group,
IEntityBuilder[] entitiesToBuild,
object[] implementors)
static void BuildEntitiesAndAddToGroup(EGID entityID,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group,
IEntityBuilder[] entityBuilders, IEnumerable<object> implementors)
{
var count = entitiesToBuild.Length;
#if DEBUG && !PROFILER
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 = entitiesToBuild[index].GetEntityType();
var entityViewType = entityBuilders[index].GetEntityType();
if (types.Contains(entityViewType))
{
throw new ECSException("EntityBuilders must be unique inside an EntityDescriptor");
}
types.Add(entityViewType);
}
#endif
for (var index = 0; index < count; ++index)
{
var entityViewBuilder = entitiesToBuild[index];
var entityViewType = entityViewBuilder.GetEntityType();
var entityStructBuilder = entityBuilders[index];
var entityViewType = entityStructBuilder.GetEntityType();

BuildEntity(entityID, @group, entityViewType, entityViewBuilder, implementors);
BuildEntity(entityID, group, entityViewType, entityStructBuilder, implementors);
}
}

static void BuildEntity(EGID entityID, Dictionary<Type, ITypeSafeDictionary> @group, Type entityViewType,
IEntityBuilder entityBuilder, object[] implementors)
static void BuildEntity(EGID entityID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group,
Type entityViewType, IEntityBuilder entityBuilder, IEnumerable<object> implementors)
{
var entityViewsPoolWillBeCreated = @group.TryGetValue(entityViewType, out var safeDictionary) == false;
var entityViewsPoolWillBeCreated =
group.TryGetValue(new RefWrapper<Type>(entityViewType), out var safeDictionary) == false;

//passing the undefined entityViewsByType inside the entityViewBuilder 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
@@ -73,7 +91,7 @@ namespace Svelto.ECS.Internal
entityBuilder.BuildEntityAndAddToList(ref safeDictionary, entityID, implementors);

if (entityViewsPoolWillBeCreated)
@group.Add(entityViewType, safeDictionary);
group.Add(new RefWrapper<Type>(entityViewType), safeDictionary);
}
}
}

+ 29
- 0
Svelto.ECS/EntityGroup.cs View File

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

namespace Svelto.ECS
{
public struct EntityGroup
{
internal EntityGroup(FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> entitiesInGroupPerType, uint _groupID)
{
_group = entitiesInGroupPerType;
groupID = _groupID;
}

public ref T QueryEntity<T>(uint entityGidEntityId) where T : struct, IEntityStruct
{
return ref (_group[new RefWrapper<Type>(typeof(T))] as TypeSafeDictionary<T>).GetValueByRef(
entityGidEntityId);
}

public bool Exists<T>(uint entityGidEntityId) where T : struct, IEntityStruct
{
return (_group[new RefWrapper<Type>(typeof(T))] as TypeSafeDictionary<T>).ContainsKey(entityGidEntityId);
}

readonly FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> _group;
public uint groupID;
}
}

+ 1
- 1
Svelto.ECS/EntityHierarchyStruct.cs View File

@@ -4,7 +4,7 @@ namespace Svelto.ECS
{
public readonly ExclusiveGroup.ExclusiveGroupStruct parentGroup;
public EntityHierarchyStruct(ExclusiveGroup @group): this() { parentGroup = group; }
public EntityHierarchyStruct(ExclusiveGroup group): this() { parentGroup = group; }
public EGID ID { get; set; }
}

+ 1
- 6
Svelto.ECS/EntityInfoView.cs View File

@@ -1,12 +1,7 @@
using System;

namespace Svelto.ECS
{
public struct EntityStructInfoView: IEntityStruct, INeedEGID
struct EntityStructInfoView: IEntityStruct
{
public EGID ID { get; set; }
public Type type { get; set; }

public IEntityBuilder[] entitiesToBuild;
}
}

+ 4
- 3
Svelto.ECS/EntityNotFoundException.cs View File

@@ -4,8 +4,9 @@ namespace Svelto.ECS
{
public class EntityNotFoundException : Exception
{
public EntityNotFoundException(EGID entityGidEntityId, Type type)
: base("entity not found ".FastConcat(type.ToString()))
{}
public EntityNotFoundException(EGID entityEGID, Type entityType) : base(
$"entity of type '{entityType}' with ID '{entityEGID.entityID}', group '{(uint) entityEGID.groupID}' not found!")
{
}
}
}

+ 74
- 36
Svelto.ECS/EntityStream.cs View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
@@ -17,70 +15,78 @@ namespace Svelto.ECS
/// one only
/// - you want to communicate between EnginesRoots
/// </summary>
class EntitiesStream
class EntitiesStream : IDisposable
{
internal Consumer<T> GenerateConsumer<T>(string name, int capacity) where T : unmanaged, IEntityStruct
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct
{
if (_streams.ContainsKey(typeof(T)) == false) _streams[typeof(T)] = new EntityStream<T>();

return (_streams[typeof(T)] as EntityStream<T>).GenerateConsumer(name, capacity);
}
public Consumer<T> GenerateConsumer<T>(ExclusiveGroup @group, string name, int capacity) where T : unmanaged, IEntityStruct

public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity)
where T : unmanaged, IEntityStruct
{
if (_streams.ContainsKey(typeof(T)) == false) _streams[typeof(T)] = new EntityStream<T>();

return (_streams[typeof(T)] as EntityStream<T>).GenerateConsumer( @group, name, capacity);
return (_streams[typeof(T)] as EntityStream<T>).GenerateConsumer(group, name, capacity);
}

internal void PublishEntity<T>(ref T entity) where T : unmanaged, IEntityStruct
internal void PublishEntity<T>(ref T entity, EGID egid) where T : unmanaged, IEntityStruct
{
if (_streams.TryGetValue(typeof(T), out var typeSafeStream))
(typeSafeStream as EntityStream<T>).PublishEntity(ref entity);
(typeSafeStream as EntityStream<T>).PublishEntity(ref entity, egid);
else
Console.LogWarningDebug("No Consumers are waiting for this entity to change ", typeof(T));
Console.LogDebug("No Consumers are waiting for this entity to change ", typeof(T));
}

readonly ConcurrentDictionary<Type, ITypeSafeStream> _streams =
new ConcurrentDictionary<Type, ITypeSafeStream>();

public void Dispose()
{
_streams.Clear();
}
}

interface ITypeSafeStream
{}

class EntityStream<T>:ITypeSafeStream where T:unmanaged, IEntityStruct
class EntityStream<T> : ITypeSafeStream where T : unmanaged, IEntityStruct
{
public void PublishEntity(ref T entity)
public void PublishEntity(ref T entity, EGID egid)
{
for (int i = 0; i < _consumers.Count; i++)
{
if (_consumers[i]._hasGroup)
{
if (EntityBuilder<T>.HAS_EGID && (entity as INeedEGID).ID.groupID == _consumers[i]._group)
if (egid.groupID == _consumers[i]._group)
{
_consumers[i].Enqueue(ref entity);
_consumers[i].Enqueue(entity, egid);
}
}
else
_consumers[i].Enqueue(ref entity);
{
_consumers[i].Enqueue(entity, egid);
}
}
}

public Consumer<T> GenerateConsumer(string name, int capacity)
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, int capacity)
public Consumer<T> GenerateConsumer(ExclusiveGroup group, string name, uint capacity)
{
var consumer = new Consumer<T>(group, name, capacity, this);
_consumers.Add(consumer);
return consumer;
}

@@ -92,34 +98,66 @@ namespace Svelto.ECS
readonly FasterListThreadSafe<Consumer<T>> _consumers = new FasterListThreadSafe<Consumer<T>>();
}

public struct Consumer<T>: IDisposable where T:unmanaged, IEntityStruct
public struct Consumer<T> : IDisposable where T : unmanaged, IEntityStruct
{
internal Consumer(string name, int capacity, EntityStream<T> stream): this()
internal Consumer(string name, uint capacity, EntityStream<T> stream):this()
{
_ringBuffer = new RingBuffer<T>(capacity);
#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, int capacity, EntityStream<T> stream):this(name, capacity, stream)

internal Consumer(ExclusiveGroup group, string name, uint capacity, EntityStream<T> stream) : this(name,
capacity, stream)
{
_group = group;
_hasGroup = true;
}

internal void Enqueue(ref T entity)
internal void Enqueue(in T entity, in EGID egid)
{
_ringBuffer.Enqueue(ref entity, _name);
_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 bool TryDequeue(out T entity) { return _ringBuffer.TryDequeue(out entity, _name); }
public void Flush() { _ringBuffer.Reset(); }
public void Dispose() { _stream.RemoveConsumer(this); }
public uint Count() { return (uint) _ringBuffer.Count; }

readonly RingBuffer<T> _ringBuffer;
readonly EntityStream<T> _stream;
readonly string _name;
readonly RingBuffer<ValueTuple<T, EGID>> _ringBuffer;
readonly EntityStream<T> _stream;
internal readonly ExclusiveGroup _group;
internal readonly bool _hasGroup;
internal readonly bool _hasGroup;
#if DEBUG && !PROFILER
readonly string _name;
#endif
}
}
}

+ 59
- 20
Svelto.ECS/EntityStructInitializer.cs View File

@@ -1,36 +1,75 @@
using System;
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public struct EntityStructInitializer
public ref struct EntityStructInitializer
{
public EntityStructInitializer(EGID id, Dictionary<Type, ITypeSafeDictionary> @group)
public EntityStructInitializer(EGID id, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group)
{
_group = @group;
ID = id;
_group = group;
_ID = id;
}

public void Init<T>(T initializer) where T: struct, IEntityStruct
public void Init<T>(T initializer) where T : struct, IEntityStruct
{
if (_group.TryGetValue(EntityBuilder<T>.ENTITY_VIEW_TYPE, out var typeSafeDictionary) == true)
{
var dictionary = typeSafeDictionary as TypeSafeDictionary<T>;
if (_group.TryGetValue(new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE),
out var typeSafeDictionary) == false) return;

if (EntityBuilder<T>.HAS_EGID)
{
var needEgid = ((INeedEGID) initializer);
needEgid.ID = ID;
initializer = (T) needEgid;
}
var dictionary = (TypeSafeDictionary<T>) typeSafeDictionary;

if (dictionary.TryFindIndex(ID.entityID, out var findElementIndex))
dictionary.GetDirectValue(findElementIndex) = initializer;
}
if (EntityBuilder<T>.HAS_EGID)
SetEGIDWithoutBoxing<T>.SetIDWithoutBoxing(ref initializer, _ID);

if (dictionary.TryFindIndex(_ID.entityID, out var findElementIndex))
dictionary.GetDirectValue(findElementIndex) = initializer;
}
readonly EGID ID;
readonly Dictionary<Type, ITypeSafeDictionary> _group;
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;
}
}

+ 1
- 3
Svelto.ECS/EntitySubmissionScheduler.cs View File

@@ -1,9 +1,7 @@
using Svelto.WeakEvents;

namespace Svelto.ECS.Schedulers
{
public interface IEntitySubmissionScheduler
{
WeakAction onTick { set; }
EnginesRoot.EntitiesSubmitter onTick { set; }
}
}

+ 5
- 8
Svelto.ECS/EntitySubmitOperation.cs View File

@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;

namespace Svelto.ECS
{
@@ -11,23 +12,19 @@ namespace Svelto.ECS
public readonly IEntityBuilder[] builders;
public readonly EGID fromID;
public readonly EGID toID;
public readonly Type entityDescriptor;
#if DEBUG && !PROFILER
public string trace;
public StackFrame trace;
#endif

public EntitySubmitOperation(EntitySubmitOperationType operation, EGID from, EGID to,
IEntityBuilder[] builders = null,
Type entityDescriptor = null)
IEntityBuilder[] builders = null)
{
type = operation;
this.builders = builders;
fromID = from;
fromID = from;
toID = to;

this.entityDescriptor = entityDescriptor;
#if DEBUG && !PROFILER
trace = string.Empty;
trace = default;
#endif
}


+ 0
- 40
Svelto.ECS/EntityView.cs View File

@@ -1,40 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Svelto.DataStructures;
using Svelto.Utilities;

namespace Svelto.ECS
{
static class EntityView<T> where T: struct, IEntityStruct
{
internal static readonly FasterList<KeyValuePair<Type, ActionCast<T>>> cachedFields;

static EntityView()
{
cachedFields = new FasterList<KeyValuePair<Type, ActionCast<T>>>();
var type = typeof(T);

var fields = type.GetFields(BindingFlags.Public |
BindingFlags.Instance);
for (int i = fields.Length - 1; i >= 0; --i)
{
var field = fields[i];

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

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

+ 7
- 12
Svelto.ECS/EntityViewUtility.cs View File

@@ -6,7 +6,6 @@ using Svelto.Utilities;

namespace Svelto.ECS
{

#if DEBUG && !PROFILER
struct ECSTuple<T1, T2>
{
@@ -21,7 +20,6 @@ namespace Svelto.ECS
}
#endif


static class EntityViewUtility
{

@@ -29,7 +27,7 @@ namespace Svelto.ECS
, ref T entityView
, FasterList<KeyValuePair<Type, ActionCast<T>>>
entityViewBlazingFastReflection
, object[] implementors,
, IEnumerable<object> implementors,
#if DEBUG && !PROFILER
Dictionary<Type, ECSTuple<object, int>> implementorsByType
#else
@@ -40,13 +38,10 @@ namespace Svelto.ECS
{
//efficient way to collect the fields of every EntityViewType
var setters =
FasterList<KeyValuePair<Type, ActionCast<T>>>
.NoVirt.ToArrayFast(entityViewBlazingFastReflection, out var count);
FasterList<KeyValuePair<Type, ActionCast<T>>>.NoVirt.ToArrayFast(entityViewBlazingFastReflection, out var count);

for (var index = 0; index < implementors.Length; index++)
foreach (var implementor in implementors)
{
var implementor = implementors[index];

if (implementor != null)
{
var type = implementor.GetType();
@@ -73,7 +68,7 @@ namespace Svelto.ECS
#if DEBUG && !PROFILER
else
{
Svelto.Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityView ",
Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityView ",
entityBuilder.GetEntityType().ToString()));
}
#endif
@@ -118,13 +113,13 @@ namespace Svelto.ECS
}

const string DUPLICATE_IMPLEMENTOR_ERROR =
"<color=orange>Svelto.ECS</color> the same component is implemented with more than one implementor. This is " +
"<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=orange>Svelto.ECS</color> Null implementor, please be careful about the implementors passed to avoid " +
"<color=teal>Svelto.ECS</color> Null implementor, please be careful about the implementors passed to avoid " +
"performance loss ";

const string NOT_FOUND_EXCEPTION = "<color=orange>Svelto.ECS</color> Implementor not found for an EntityView. ";
const string NOT_FOUND_EXCEPTION = "<color=teal>Svelto.ECS</color> Implementor not found for an EntityView. ";
}
}

+ 135
- 15
Svelto.ECS/ExclusiveGroup.cs View File

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

#pragma warning disable 660,661

namespace Svelto.ECS
@@ -17,42 +18,68 @@ namespace Svelto.ECS
/// }
/// </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)
{}
}

public class ExclusiveGroup
{
public ExclusiveGroup()
{
_group = ExclusiveGroupStruct.Generate();
}
public ExclusiveGroup(string recognizeAs)
{
_group = ExclusiveGroupStruct.Generate();
_serialisedGroups.Add(recognizeAs, _group);
}
public ExclusiveGroup(ushort range)
{
_group = new ExclusiveGroupStruct(range);
#if DEBUG
_range = range;
#endif
}
public static implicit operator ExclusiveGroupStruct(ExclusiveGroup group)
{
return group._group;
}
public static explicit operator uint(ExclusiveGroup group)
public static explicit operator uint(ExclusiveGroup group)
{
return group._group;
}

public static ExclusiveGroupStruct operator+(ExclusiveGroup a, uint b)
{
#if DEBUG
if (a._range == 0)
throw new ECSException("adding values to a not ranged ExclusiveGroup");
if (b >= a._range)
throw new ECSException("Using out of range group");
#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>
@@ -107,17 +134,29 @@ namespace Svelto.ECS
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();
@@ -127,19 +166,100 @@ namespace Svelto.ECS
return group;
}

uint _id;
uint _id;
static uint _globalId;
}

public static ExclusiveGroupStruct Search(string holderGroupName)
{
if (_serialisedGroups.ContainsKey(holderGroupName) == false)
throw new Exception("Serialized Group Not Found ".FastConcat(holderGroupName));
throw new Exception("Named Group Not Found ".FastConcat(holderGroupName));
return _serialisedGroups[holderGroupName];
}

static readonly Dictionary<string, ExclusiveGroupStruct> _serialisedGroups = new Dictionary<string,
ExclusiveGroupStruct>();
static readonly Dictionary<string, ExclusiveGroupStruct> _serialisedGroups = new Dictionary<string,
ExclusiveGroupStruct>();
#if DEBUG
readonly ushort _range;
#endif
}
}
}

#if future
public static void ConstructStaticGroups()
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

// Assemblies or types aren't guaranteed to be returned in the same order,
// and I couldn't find proof that `GetTypes()` returns them in fixed order either,
// even for builds made with the exact same source code.
// So will sort reflection results by name before constructing groups.
var groupFields = new List<KeyValuePair<string, FieldInfo>>();

foreach (Assembly assembly in assemblies)
{
Type[] types = GetTypesSafe(assembly);

foreach (Type type in types)
{
if (type == null || !type.IsClass)
{
continue;
}

// Groups defined as static members in static classes
if (type.IsSealed && type.IsAbstract)
{
FieldInfo[] fields = type.GetFields();
foreach(var field in fields)
{
if (field.IsStatic && typeof(ExclusiveGroup).IsAssignableFrom(field.FieldType))
{
groupFields.Add(new KeyValuePair<string, FieldInfo>($"{type.FullName}.{field.Name}", field));
}
}
}
// Groups defined as classes
else if (type.BaseType != null
&& type.BaseType.IsGenericType
&& type.BaseType.GetGenericTypeDefinition() == typeof(ExclusiveGroup<>))
{
FieldInfo field = type.GetField("Group",
BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);

groupFields.Add(new KeyValuePair<string, FieldInfo>(type.FullName, field));
}
}
}

groupFields.Sort((a, b) => string.CompareOrdinal(a.Key, b.Key));

for (int i = 0; i < groupFields.Count; ++i)
{
groupFields[i].Value.GetValue(null);
#if DEBUG
var group = (ExclusiveGroup) groupFields[i].Value.GetValue(null);
groupNames[(uint) group] = groupFields[i].Key;
#endif
}
}

static Type[] GetTypesSafe(Assembly assembly)
{
try
{
Type[] types = assembly.GetTypes();

return types;
}
catch (ReflectionTypeLoadException e)
{
return e.Types;
}
}

#if DEBUG
static string[] groupNames = new string[ushort.MaxValue];
#endif
#endif

+ 22
- 3
Svelto.ECS/ExecuteOnEntitiesDB.cs View File

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

namespace Svelto.ECS.Internal
{
@@ -9,9 +10,9 @@ namespace Svelto.ECS.Internal
{
var type = typeof(T);

if (_groupsPerEntity.TryGetValue(type, out var dic))
if (_groupsPerEntity.TryGetValue(new RefWrapper<Type>(type), out var dictionary))
{
foreach (var pair in dic)
foreach (var pair in dictionary)
{
var entities = (pair.Value as TypeSafeDictionary<T>).GetValuesArray(out var innerCount);

@@ -27,7 +28,7 @@ namespace Svelto.ECS.Internal
{
var type = typeof(T);

if (_groupsPerEntity.TryGetValue(type, out var dic))
if (_groupsPerEntity.TryGetValue(new RefWrapper<Type>(type), out var dic))
{
foreach (var pair in dic)
{
@@ -38,5 +39,23 @@ namespace Svelto.ECS.Internal
}
}
}
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);
}
}
}
}
}

+ 32
- 3
Svelto.ECS/ExtendibleEntityDescriptor.cs View File

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

namespace Svelto.ECS
{
/// <summary>
@@ -5,15 +8,41 @@ namespace Svelto.ECS
/// to swap and remove specialized entities from abstract engines
/// </summary>
/// <typeparam name="TType"></typeparam>
public abstract class ExtendibleEntityDescriptor<TType>:IEntityDescriptor where TType : IEntityDescriptor, new()
public class ExtendibleEntityDescriptor<TType> : IEntityDescriptor where TType : IEntityDescriptor, new()
{
protected ExtendibleEntityDescriptor(IEntityBuilder[] extraEntities)
static ExtendibleEntityDescriptor()
{
if (typeof(ISerializableEntityDescriptor).IsAssignableFrom(typeof(TType)))
throw new Exception(
$"SerializableEntityDescriptors cannot be used as base entity descriptor: {typeof(TType)}");
}

public ExtendibleEntityDescriptor(IEntityBuilder[] extraEntities)
{
_dynamicDescriptor = new DynamicEntityDescriptor<TType>(extraEntities);
}

public ExtendibleEntityDescriptor()
{
_dynamicDescriptor = new DynamicEntityDescriptor<TType>(true);
}

public ExtendibleEntityDescriptor<TType> ExtendWith<T>() where T : IEntityDescriptor, new()
{
_dynamicDescriptor.ExtendWith<T>();

return this;
}

public ExtendibleEntityDescriptor<TType> ExtendWith(IEntityBuilder[] extraEntities)
{
_dynamicDescriptor.ExtendWith(extraEntities);

return this;
}

public IEntityBuilder[] entitiesToBuild => _dynamicDescriptor.entitiesToBuild;

readonly DynamicEntityDescriptor<TType> _dynamicDescriptor;
DynamicEntityDescriptor<TType> _dynamicDescriptor;
}
}

+ 1
- 1
Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs View File

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

#pragma warning disable 649
[SerializeField] string _groupName;
[SerializeField] ushort _id = 0;
[SerializeField] ushort _id;
#pragma warning restore 649
}
}

+ 29
- 12
Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs View File

@@ -5,7 +5,8 @@ namespace Svelto.ECS.Unity
{
public static class SveltoGUIHelper
{
public static T CreateFromPrefab<T>(ref uint startIndex, Transform contextHolder, IEntityFactory factory, ExclusiveGroup group) where T : MonoBehaviour, IEntityDescriptorHolder
public static T CreateFromPrefab<T>(ref uint startIndex, Transform contextHolder, IEntityFactory factory,
ExclusiveGroup group, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder
{
var holder = Create<T>(new EGID(startIndex++, group), contextHolder, factory);
var childs = contextHolder.GetComponentsInChildren<IEntityDescriptorHolder>(true);
@@ -14,18 +15,28 @@ namespace Svelto.ECS.Unity
{
if (child.GetType() != typeof(T))
{
var childImplementors = (child as MonoBehaviour).GetComponents<IImplementor>();
startIndex = InternalBuildAll(startIndex, child, factory, group, childImplementors);
var monoBehaviour = child as MonoBehaviour;
var childImplementors = monoBehaviour.GetComponents<IImplementor>();
startIndex = InternalBuildAll(
startIndex,
child,
factory,
group,
childImplementors,
groupNamePostfix);
}
}

return holder;
}

public static T Create<T>(EGID ID, Transform contextHolder,
IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder
public static T Create<T>(EGID ID, Transform contextHolder, IEntityFactory factory)
where T : MonoBehaviour, IEntityDescriptorHolder
{
var holder = contextHolder.GetComponentInChildren<T>(true);
DBC.ECS.Check.Assert(holder != null, $"`{nameof(holder)}` is null! No component of type " +
$"`{typeof(T)}` was found between its children.");

var implementors = holder.GetComponents<IImplementor>();

factory.BuildEntity(ID, holder.GetDescriptor(), implementors);
@@ -34,7 +45,8 @@ namespace Svelto.ECS.Unity
}

public static EntityStructInitializer CreateWithEntity<T>(EGID ID, Transform contextHolder,
IEntityFactory factory, out T holder) where T : MonoBehaviour, IEntityDescriptorHolder
IEntityFactory factory, out T holder)
where T : MonoBehaviour, IEntityDescriptorHolder
{
holder = contextHolder.GetComponentInChildren<T>(true);
var implementors = holder.GetComponents<IImplementor>();
@@ -42,8 +54,8 @@ namespace Svelto.ECS.Unity
return factory.BuildEntity(ID, holder.GetDescriptor(), implementors);
}

public static uint CreateAll<T>(uint startIndex, ExclusiveGroup group, Transform contextHolder,
IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder
public static uint CreateAll<T>(uint startIndex, ExclusiveGroup group,
Transform contextHolder, IEntityFactory factory, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder
{
var holders = contextHolder.GetComponentsInChildren<T>(true);

@@ -51,18 +63,23 @@ namespace Svelto.ECS.Unity
{
var implementors = holder.GetComponents<IImplementor>();

startIndex = InternalBuildAll(startIndex, holder, factory, group, implementors);
startIndex = InternalBuildAll(startIndex, holder, factory, group, implementors, groupNamePostfix);
}

return startIndex;
}

static uint InternalBuildAll(uint startIndex, IEntityDescriptorHolder descriptorHolder, IEntityFactory factory, ExclusiveGroup group, IImplementor[] implementors)
static uint InternalBuildAll(uint startIndex, IEntityDescriptorHolder descriptorHolder,
IEntityFactory factory, ExclusiveGroup group, IImplementor[] implementors, string groupNamePostfix)
{
ExclusiveGroup.ExclusiveGroupStruct realGroup = group;

if (string.IsNullOrEmpty(descriptorHolder.groupName) == false)
realGroup = ExclusiveGroup.Search(descriptorHolder.groupName);
{
realGroup = ExclusiveGroup.Search(!string.IsNullOrEmpty(groupNamePostfix)
? $"{descriptorHolder.groupName}{groupNamePostfix}"
: descriptorHolder.groupName);
}

EGID egid;
var holderId = descriptorHolder.id;
@@ -79,4 +96,4 @@ namespace Svelto.ECS.Unity
}
}
}
#endif
#endif

+ 26
- 17
Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs View File

@@ -1,53 +1,62 @@
#if UNITY_5 || UNITY_5_3_OR_NEWER
using Object = UnityEngine.Object;
using System;
using System.Collections;
using Svelto.WeakEvents;
using UnityEngine;

namespace Svelto.ECS.Schedulers.Unity
{
//The EntitySubmissionScheduler has been introduced to make the entity views submission logic platform independent
//You can customize the scheduler if you wish
public class UnityEntitySubmissionScheduler : IEntitySubmissionScheduler
public class UnityEntitySubmissionScheduler : IEntitySubmissionScheduler, IDisposable
{
class Scheduler : MonoBehaviour
{
IEnumerator Start()
public Scheduler()
{
_coroutine = Coroutine();
}

void Update()
{
_coroutine.MoveNext();
}
IEnumerator Coroutine()
{
while (true)
{
yield return _wait;

if (onTick.IsValid)
onTick.Invoke();
else
yield break;

onTick.Invoke();
}
}

readonly WaitForEndOfFrame _wait = new WaitForEndOfFrame();
readonly IEnumerator _coroutine;
public WeakAction onTick;
public EnginesRoot.EntitiesSubmitter onTick;
}
public UnityEntitySubmissionScheduler(string name = "ECSScheduler") { _name = name; }

public void Dispose()
{
Object.Destroy(_scheduler.gameObject);
}
public WeakAction onTick
public EnginesRoot.EntitiesSubmitter onTick
{
set
{
if (_scheduler == null)
{
GameObject go = new GameObject(_name);

_scheduler = go.AddComponent<Scheduler>();
}
if (_scheduler == null) _scheduler = new GameObject(_name).AddComponent<Scheduler>();
_scheduler.onTick = value;
}
}

Scheduler _scheduler;
string _name;
readonly string _name;
}
}
#endif

+ 12
- 8
Svelto.ECS/GenericentityStreamConsumerFactory.cs View File

@@ -1,29 +1,33 @@
using Svelto.DataStructures;

namespace Svelto.ECS
{
class GenericentityStreamConsumerFactory : IEntityStreamConsumerFactory
class GenericEntityStreamConsumerFactory : IEntityStreamConsumerFactory
{
public GenericentityStreamConsumerFactory(DataStructures.WeakReference<EnginesRoot> weakReference)
public GenericEntityStreamConsumerFactory(EnginesRoot weakReference)
{
_enginesRoot = weakReference;
_enginesRoot = new WeakReference<EnginesRoot>(weakReference);
}

public Consumer<T> GenerateConsumer<T>(string name, int capacity) where T : unmanaged, IEntityStruct
public Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct
{
return _enginesRoot.Target.GenerateConsumer<T>(name, capacity);
}

public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, int capacity) where T : unmanaged, IEntityStruct
public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) where T : unmanaged, IEntityStruct
{
return _enginesRoot.Target.GenerateConsumer<T>(group, name, capacity);
}

readonly DataStructures.WeakReference<EnginesRoot> _enginesRoot;
//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside
//engines of other enginesRoot
readonly WeakReference<EnginesRoot> _enginesRoot;
}
public interface IEntityStreamConsumerFactory
{
Consumer<T> GenerateConsumer<T>(string name, int capacity) where T : unmanaged, IEntityStruct;
Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, int capacity)
Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct;
Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity)
where T : unmanaged, IEntityStruct;
}
}

+ 45
- 62
Svelto.ECS/IEntitiesDB.cs View File

@@ -2,17 +2,21 @@ using System;

namespace Svelto.ECS
{
public delegate void ExecuteOnAllEntitiesAction<T, W>(T[] entities, ExclusiveGroup.ExclusiveGroupStruct group,
uint count, IEntitiesDB db, ref W value);

public interface IEntitiesDB
{
///////////////////////////////////////////////////
/// Query entities
/// ECS systems are meant to work on a set of Entities. These methods allow to iterate over entity
/// structs inside a given group or an array of groups
// Query entities
// ECS systems are meant to work on a set of Entities. These methods allow to iterate over entity
// structs inside a given group or an array of groups
///////////////////////////////////////////////////
/// <summary>
/// Fast and raw return of entities buffer.
/// Fast and raw return of entities buffer.
/// </summary>
/// <param name="groupStruct"></param>
/// <param name="count"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
@@ -22,7 +26,7 @@ namespace Svelto.ECS
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct;
(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;
/// <summary>
/// return entities that can be iterated through the EntityCollection iterator
/// </summary>
@@ -31,29 +35,33 @@ namespace Svelto.ECS
/// <returns></returns>
EntityCollection<T> QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
where T : struct, IEntityStruct;
EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct;
EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityStruct
where T2 : struct, IEntityStruct
where T3 : struct, IEntityStruct;

/// <summary>
/// return entities found in multiple groups, that can be iterated through the EntityCollection iterator
/// This method is useful to write abstracted engines
/// </summary>
/// <param name="groupStruct"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
EntityCollections<T> QueryEntities<T>(ExclusiveGroup[] groups) where T : struct, IEntityStruct;
EntityCollections<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup[] groups)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct;
///////////////////////////////////////////////////
/// Query entities regardless the group
/// these methods are necessary to create abstracted engines. Engines that can iterate over entities regardless
/// the group
// Query entities regardless the group
// these methods are necessary to create abstracted engines. Engines that can iterate over entities regardless
// the group
///////////////////////////////////////////////////

/// <summary>
/// Execute an action on ALL the entities regardless the group. This function doesn't guarantee cache
/// friendliness even if just EntityStructs are used. Safety checks are in place,
/// </summary>
/// <param name="damageableGroups"></param>
/// <param name="action"></param>
/// <typeparam name="T"></typeparam>
void ExecuteOnAllEntities<T>(Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB> action)
@@ -66,18 +74,20 @@ namespace Svelto.ECS
/// <param name="action"></param>
/// <typeparam name="T"></typeparam>
/// <typeparam name="W"></typeparam>
void ExecuteOnAllEntities<T, W>(W value,
Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB, W> action)
void ExecuteOnAllEntities<T, W>(ref W value, ExecuteOnAllEntitiesAction<T, W> action)
where T : struct, IEntityStruct;

void ExecuteOnAllEntities<T, W>(W value, Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB, W> action)
where T : struct, IEntityStruct;

///////////////////////////////////////////////////
/// Query single entities
/// ECS systems are meant to work on a set of Entities. Working on a single entity is sometime necessary, hence
/// the following methods
/// However Because of the double hashing required to identify a specific entity, these function are slower than
/// other query methods when used multiple times!
// Query single entities
// ECS systems are meant to work on a set of Entities. Working on a single entity is sometime necessary, hence
// the following methods
// However Because of the double hashing required to identify a specific entity, these function are slower than
// other query methods when used multiple times!
///////////////////////////////////////////////////
/// <summary>
/// QueryUniqueEntity is a contract method that explicitly declare the intention to have just on entity in a
/// specific group, usually used for GUI elements
@@ -86,7 +96,7 @@ namespace Svelto.ECS
/// <typeparam name="T"></typeparam>
/// <returns></returns>
ref T QueryUniqueEntity<T>(ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct;
/// <summary>
/// return a specific entity by reference.
/// </summary>
@@ -99,8 +109,8 @@ namespace Svelto.ECS
/// <summary>
///
///QueryEntitiesAndIndex is useful to optimize cases when multiple entity structs from the same entity must
/// be queried. This is the use case:
///
/// be queried. This is the use case:
///
///ref var ghostPosition = ref entitiesDB.QueryEntitiesAndIndex<PositionEntityStruct>
/// (MockupRenderingGroups.GhostCubeID, out var index)[index];
///ref var ghostScaling = ref entitiesDB.QueryEntities<ScalingEntityStruct>
@@ -118,9 +128,9 @@ namespace Svelto.ECS
T[] QueryEntitiesAndIndex<T>(EGID entityGid, out uint index) where T : struct, IEntityStruct;
T[] QueryEntitiesAndIndex<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index)
where T : struct, IEntityStruct;
/// <summary>
/// Like QueryEntitiesAndIndex and only way to get an index only if exists
/// Like QueryEntitiesAndIndex and only way to get an index only if exists
/// </summary>
/// <param name="entityGid"></param>
/// <param name="index"></param>
@@ -130,24 +140,24 @@ namespace Svelto.ECS
bool TryQueryEntitiesAndIndex
<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array)
where T : struct, IEntityStruct;
/// <summary>
/// this method returns a mapped version of the entity array so that is possible to work on multiple entities
/// inside the group through their EGID. This version skip a level of indirection so it's a bit faster than
/// using QueryEntity multiple times (with different EGIDs).
/// However mapping can be slow so it must be used for not performance critical paths
/// </summary>
/// <param name="groupID"></param>
/// <param name="mapper"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId)
where T : struct, IEntityStruct;
bool TryQueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper)
where T : struct, IEntityStruct;

///////////////////////////////////////////////////
/// Utility methods
// Utility methods
///////////////////////////////////////////////////
/// <summary>
/// check if a specific entity exists
/// </summary>
@@ -155,12 +165,13 @@ namespace Svelto.ECS
/// <typeparam name="T"></typeparam>
/// <returns></returns>
bool Exists<T>(EGID egid) where T : struct, IEntityStruct;
bool Exists<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct;
bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid);

/// <summary>
/// know if there is any entity struct in a specific group
/// </summary>
/// <param name="group"></param>
/// <param name="groupStruct"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
bool HasAny<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct;
@@ -172,39 +183,11 @@ namespace Svelto.ECS
/// <typeparam name="T"></typeparam>
/// <returns></returns>
uint Count<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct;
///////////////////////////////////////////////////
/// Publish entities in the entity stream
/// The Entity Stream is a communication mean based on the Publisher/Consumer model. The pattern is
/// 1) changing an entity
/// 2) publish the entity modified
///////////////////////////////////////////////////

/// <summary>
/// Publish an Entity Change, through the Entity Stream linked to this database
/// Use case:
/// public void Add(ref ButtonEntityViewStruct buttonEntityViewStruct)
/// {
/// buttonEntityViewStruct.buttonClick.buttonEvent = new DispatchOnSet<ButtonEvents>(buttonEntityViewStruct.ID);
///
/// buttonEntityViewStruct.buttonClick.buttonEvent.NotifyOnValueSet(_enqueueButtonChange);
/// }
///
/// public void Remove(ref ButtonEntityViewStruct buttonEntityViewStruct)
/// {
/// buttonEntityViewStruct.buttonClick.buttonEvent.StopNotify(_enqueueButtonChange);
/// }
///
/// void EnqueueButtonChange(EGID egid, ButtonEvents value)
/// {
/// entitiesDB.QueryEntity<ButtonEntityStruct>(egid) = new ButtonEntityStruct(egid, value);
///
/// entitiesDB.PublishEntityChange<ButtonEntityStruct>(egid);
/// }
/// </summary>
/// <param name="egid"></param>
/// <typeparam name="T"></typeparam>
void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityStruct;

}
}
}

+ 3
- 1
Svelto.ECS/IEntityBuilder.cs View File

@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public interface IEntityBuilder
{
void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID entityID, object[] implementors);
void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID egid,
IEnumerable<object> implementors);
ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size);

Type GetEntityType();


+ 12
- 23
Svelto.ECS/IEntityFactory.cs View File

@@ -1,3 +1,5 @@
using System.Collections.Generic;

namespace Svelto.ECS
{
/// <summary>
@@ -12,7 +14,7 @@ namespace Svelto.ECS
///{
/// public static readonly ExclusiveGroups PlayerEntitiesGroup = new ExclusiveGroups();
///}
///
///
/// </summary>
public interface IEntityFactory
{
@@ -25,7 +27,7 @@ namespace Svelto.ECS
/// <param name="size"></param>
void PreallocateEntitySpace<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size)
where T : IEntityDescriptor, new();
/// <summary>
/// The EntityDescriptor doesn't need to be ever instantiated. It just describes the Entity
/// itself in terms of EntityViews to build. The Implementors are passed to fill the
@@ -41,24 +43,12 @@ namespace Svelto.ECS
/// <param name="ed"></param>
/// <param name="implementors"></param>
EntityStructInitializer BuildEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId,
object[] implementors = null)
IEnumerable<object> implementors = null)
where T : IEntityDescriptor, new();
EntityStructInitializer BuildEntity<T>(EGID egid, object[] implementors = null)

EntityStructInitializer BuildEntity<T>(EGID egid, IEnumerable<object> implementors = null)
where T:IEntityDescriptor, new();

#if REAL_ID
/// <summary>
/// BuildEntity version without specifying the entity ID. The complete EGID will be found inside
/// the EntityStructInitializer and/or the single entity components
/// </summary>
/// <param name="groupID"></param>
/// <param name="implementors"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
EntityStructInitializer BuildEntity<T>(ExclusiveGroup.ExclusiveGroupStruct groupID, object[] implementors = null)
where T : IEntityDescriptor, new();
#endif
/// <summary>
/// When the type of the entity is not known (this is a special case!) an EntityDescriptorInfo
/// can be built in place of the generic parameter T.
@@ -66,12 +56,11 @@ namespace Svelto.ECS
/// <param name="entityID"></param>
/// <param name="entityDescriptor"></param>
/// <param name="implementors"></param>
///
EntityStructInitializer BuildEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId,
T descriptorEntity,
object[] implementors = null)
where T : IEntityDescriptor;
EntityStructInitializer BuildEntity<T>(EGID egid, T entityDescriptor, object[] implementors = null)
///
EntityStructInitializer BuildEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId,
T descriptorEntity, IEnumerable<object> implementors = null) where T : IEntityDescriptor;

EntityStructInitializer BuildEntity<T>(EGID egid, T entityDescriptor, IEnumerable<object> implementors = null)
where T : IEntityDescriptor;
}
}

+ 0
- 2
Svelto.ECS/IEntityFunctions.cs View File

@@ -8,8 +8,6 @@ namespace Svelto.ECS
void RemoveEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new();
void RemoveEntity<T>(EGID entityegid) where T : IEntityDescriptor, new();
void RemoveEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new();

void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID);
void SwapEntityGroup<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new();


+ 7
- 3
Svelto.ECS/IEntityStruct.cs View File

@@ -1,9 +1,13 @@
namespace Svelto.ECS
{
///<summary>EntityStruct MUST implement IEntiyStruct</summary>
///<summary>EntityStruct MUST implement IEntityStruct</summary>
public interface IEntityStruct
{}
{
}

/// <summary>
/// use INeedEGID on an IEntityStruct only if you need the EGID
/// </summary>
public interface INeedEGID
{
EGID ID { get; set; }


+ 2
- 2
Svelto.ECS/IReactOnAddAndRemove.cs View File

@@ -4,7 +4,7 @@ namespace Svelto.ECS
{
public interface IReactOnAddAndRemove<T> : IReactOnAddAndRemove where T : IEntityStruct
{
void Add(ref T entityView);
void Remove(ref T entityView);
void Add(ref T entityView, EGID egid);
void Remove(ref T entityView, EGID egid);
}
}

+ 4
- 2
Svelto.ECS/IReactOnSwap.cs View File

@@ -4,7 +4,9 @@ namespace Svelto.ECS
{
public interface IReactOnSwap<T> : IReactOnSwap where T : IEntityStruct
{
void MovedTo(ref T entityView, ExclusiveGroup.ExclusiveGroupStruct previousGroup);
void MovedFrom(ref T entityView);
void MovedTo(ref T entityView, ExclusiveGroup.ExclusiveGroupStruct previousGroup, EGID egid);
#if SEEMS_UNNECESSARY
void MovedFrom(ref T entityView, EGID egid);
#endif
}
}

+ 2
- 2
Svelto.ECS/Sequencer.cs View File

@@ -12,7 +12,7 @@ namespace Svelto.ECS
{
_steps = new Dictionary<IEngine, To>();

for (int i = 0; i < values.Length; i++)
for (var i = 0; i < values.Length; i++)
_steps.Add(values[i].from, values[i].to);
}
}
@@ -106,7 +106,7 @@ namespace Svelto.ECS

public void Next<C>(IEngine engine, C condition, EGID id) where C:struct, IConvertible
{
C branch = condition;
var branch = condition;
var to = (_steps._steps[engine] as To<C>);
var steps = to._tos[branch];


+ 43
- 0
Svelto.ECS/Serialization/ComposedSerializer.cs View File

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

namespace Svelto.ECS.Serialization
{
public class ComposedSerializer<T, X, Y> : ISerializer<T>
where T : unmanaged, IEntityStruct
where X : class, ISerializer<T>, new()
where Y : class, ISerializer<T>, new()
{
public ComposedSerializer()
{
_serializers = new ISerializer<T>[2];
_serializers[0] = new X();
_serializers[1] = new Y();
}

public bool Serialize(in T value, ISerializationData serializationData)
{
foreach (ISerializer<T> s in _serializers)
{
serializationData.data.ExpandBy(s.size);
if (s.SerializeSafe(value, serializationData))
return true;
}

throw new Exception($"ComposedSerializer for {typeof(T)} did not serialize any data!");
}

public bool Deserialize(ref T value, ISerializationData serializationData)
{
foreach (ISerializer<T> s in _serializers)
{
if (s.DeserializeSafe(ref value, serializationData))
return true;
}

throw new Exception($"ComposedSerializer for {typeof(T)} did not deserialize any data!");
}

public uint size => 0;
ISerializer<T>[] _serializers;
}
}

+ 43
- 0
Svelto.ECS/Serialization/DefaultSerializer.cs View File

@@ -0,0 +1,43 @@
using Svelto.Common;

namespace Svelto.ECS.Serialization
{
public class DefaultSerializer<T> : ISerializer<T> where T : unmanaged, IEntityStruct
{
public static readonly uint SIZEOFT = SerializableEntityBuilder<T>.SIZE;

public uint size => SIZEOFT;

static DefaultSerializer()
{
var _type = typeof(T);

foreach (var field in _type.GetFields())
{
if (field.FieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) && field.IsPrivate == false)
throw new ECSException("field cannot be serialised ".FastConcat(_type.FullName));
}

if (_type.GetProperties().Length > (EntityBuilder<T>.HAS_EGID ? 1 : 0))
throw new ECSException("serializable entity struct must be property less ".FastConcat(_type.FullName));
}

public bool Serialize(in T value, ISerializationData serializationData)
{
DefaultSerializerUtils.CopyToByteArray(value, serializationData.data.ToArrayFast(), serializationData.dataPos);

serializationData.dataPos += SIZEOFT;

return true;
}

public bool Deserialize(ref T value, ISerializationData serializationData)
{
value = DefaultSerializerUtils.CopyFromByteArray<T>(serializationData.data.ToArrayFast(), serializationData.dataPos);

serializationData.dataPos += SIZEOFT;

return true;
}
}
}

+ 46
- 0
Svelto.ECS/Serialization/DefaultSerializerUtils.cs View File

@@ -0,0 +1,46 @@
using System;
using Svelto.ECS;

public static class DefaultSerializerUtils
{
public static unsafe void CopyToByteArray<T>(in T src, byte[] data, uint offsetDst) where T : unmanaged, IEntityStruct
{
fixed (void* dstPtr = data)
{
void* dstOffsetPtr;
if (IntPtr.Size == sizeof(int))
{
dstOffsetPtr = (void*) (((IntPtr) dstPtr).ToInt32() + ((IntPtr) offsetDst).ToInt32());
}
else
{
dstOffsetPtr = (void*) (((IntPtr) dstPtr).ToInt64() + ((IntPtr) offsetDst).ToInt64());
}

*(T*) dstOffsetPtr = src;
}
}

public static unsafe T CopyFromByteArray<T>(byte[] data, uint offsetSrc) where T : unmanaged, IEntityStruct
{
T dst = new T();

void* dstPtr = &dst;
fixed (void* srcPtr = data)
{
void* srcOffsetPtr;
if (IntPtr.Size == sizeof(int))
{
srcOffsetPtr = (void*) (((IntPtr) srcPtr).ToInt32() + ((IntPtr) offsetSrc).ToInt32());
}
else
{
srcOffsetPtr = (void*) (((IntPtr) srcPtr).ToInt64() + ((IntPtr) offsetSrc).ToInt64());
}

*(T*) dstPtr = *(T*) srcOffsetPtr;
}

return dst;
}
}

+ 28
- 0
Svelto.ECS/Serialization/DefaultVersioningFactory.cs View File

@@ -0,0 +1,28 @@
using System.Collections.Generic;

namespace Svelto.ECS.Serialization
{
public class DefaultVersioningFactory<T> : IDeserializationFactory where T : IEntityDescriptor, new()
{
public EntityStructInitializer BuildDeserializedEntity(EGID egid,
ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor,
SerializationType serializationType,
IEntitySerialization entitySerialization)
{
var initializer = _factory.BuildEntity<T>(egid, _implementors);
entitySerialization.DeserializeEntityStructs(serializationData, entityDescriptor, initializer, SerializationType.Storage);

return initializer;
}
public DefaultVersioningFactory(IEntityFactory factory) { _factory = factory; }
public DefaultVersioningFactory(IEntityFactory factory, IEnumerable<object> implementors) { _factory = factory;
_implementors = implementors;
}
readonly IEntityFactory _factory;
readonly IEnumerable<object> _implementors;
}
}

+ 8
- 0
Svelto.ECS/Serialization/DoNotSerializeAttribute.cs View File

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

namespace Svelto.ECS.Serialization
{
public class DoNotSerializeAttribute : Attribute
{
}
}

+ 17
- 0
Svelto.ECS/Serialization/DontSerialize.cs View File

@@ -0,0 +1,17 @@
namespace Svelto.ECS.Serialization
{
public class DontSerialize<T> : ISerializer<T> where T : unmanaged, IEntityStruct
{
public uint size => 0;

public bool Serialize(in T value, ISerializationData serializationData)
{
return false;
}

public bool Deserialize(ref T value, ISerializationData serializationData)
{
return false;
}
}
}

+ 198
- 0
Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs View File

@@ -0,0 +1,198 @@
using System;
using Svelto.Common;
using Svelto.ECS.Internal;
using Svelto.ECS.Serialization;

namespace Svelto.ECS
{
//todo: this should not be at framework level
public enum SerializationType
{
Network,
Storage,

Length
}

public partial class EnginesRoot
{
readonly bool _isDeserializationOnly;

sealed class EntitySerialization : IEntitySerialization
{
public void SerializeEntity(EGID egid, ISerializationData serializationData,
SerializationType serializationType)
{
var entitiesDb = _enginesRoot._entitiesDB;

//needs to retrieve the meta data associated with the entity
ref var serializableEntityStruct = ref entitiesDb.QueryEntity<SerializableEntityStruct>(egid);
uint descriptorHash = serializableEntityStruct.descriptorHash;

SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap;
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);
var entityStructsToSerialise = entityDescriptor.entitiesToSerialize;

var header =
new SerializableEntityHeader(descriptorHash, egid, (byte) entityStructsToSerialise.Length);
header.Copy(serializationData);

for (int index = 0; index < entityStructsToSerialise.Length; index++)
{
var entityBuilder = entityStructsToSerialise[index];

serializationData.BeginNextEntityStruct();
SerializeEntityStruct(egid, entityBuilder, serializationData, serializationType);
}
}

public EntityStructInitializer DeserializeNewEntity(EGID egid, ISerializationData serializationData,
SerializationType serializationType)
{
//todo: SerializableEntityHeader may be needed to be customizable
var serializableEntityHeader = new SerializableEntityHeader(serializationData);

uint descriptorHash = serializableEntityHeader.descriptorHash;
SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap;
var factory = serializationDescriptorMap.GetSerializationFactory(descriptorHash);
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);

//default factory
if (factory == null)
{
var initializer = _enginesRoot.BuildEntity(egid,
_enginesRoot._isDeserializationOnly
? entityDescriptor.entitiesToSerialize
: entityDescriptor.entitiesToBuild);

DeserializeEntityStructs(serializationData, entityDescriptor, initializer, serializationType);

return initializer;
}

//custom factory
return factory.BuildDeserializedEntity(egid, serializationData, entityDescriptor, serializationType,
this);
}

public void DeserializeEntity(ISerializationData serializationData, SerializationType serializationType)
{
var serializableEntityHeader = new SerializableEntityHeader(serializationData);

EGID egid = serializableEntityHeader.egid;

DeserializeEntityInternal(serializationData, egid, serializableEntityHeader, serializationType);
}

public void DeserializeEntity(EGID egid, ISerializationData serializationData,
SerializationType serializationType)
{
var serializableEntityHeader = new SerializableEntityHeader(serializationData);

DeserializeEntityInternal(serializationData, egid, serializableEntityHeader, serializationType);
}

public void DeserializeEntityStructs(ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor,
in EntityStructInitializer initializer, SerializationType serializationType)
{
foreach (var serializableEntityBuilder in entityDescriptor.entitiesToSerialize)
{
serializationData.BeginNextEntityStruct();
serializableEntityBuilder.Deserialize(serializationData, initializer, serializationType);
}
}

public void DeserializeEntityToSwap(EGID localEgid, EGID toEgid)
{
EntitiesDB entitiesDb = _enginesRoot._entitiesDB;
ref var serializableEntityStruct = ref entitiesDb.QueryEntity<SerializableEntityStruct>(localEgid);

SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap;
uint descriptorHash = serializableEntityStruct.descriptorHash;
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);

var entitySubmitOperation = new EntitySubmitOperation(
EntitySubmitOperationType.Swap,
localEgid,
toEgid,
entityDescriptor.entitiesToBuild);

_enginesRoot.CheckRemoveEntityID(localEgid);
_enginesRoot.CheckAddEntityID(toEgid);

_enginesRoot.QueueEntitySubmitOperation(entitySubmitOperation);
}

public void DeserializeEntityToDelete(EGID egid)
{
EntitiesDB entitiesDB = _enginesRoot._entitiesDB;
ref var serializableEntityStruct = ref entitiesDB.QueryEntity<SerializableEntityStruct>(egid);
uint descriptorHash = serializableEntityStruct.descriptorHash;

SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap;
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);

_enginesRoot.CheckRemoveEntityID(egid);

var entitySubmitOperation = new EntitySubmitOperation(
EntitySubmitOperationType.Remove,
egid,
egid,
entityDescriptor.entitiesToBuild);

_enginesRoot.QueueEntitySubmitOperation(entitySubmitOperation);
}

public void RegisterSerializationFactory<T>(IDeserializationFactory deserializationFactory)
where T : ISerializableEntityDescriptor, new()
{
SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap;
serializationDescriptorMap.RegisterSerializationFactory<T>(deserializationFactory);
}

internal EntitySerialization(EnginesRoot enginesRoot)
{
_enginesRoot = enginesRoot;
}

void SerializeEntityStruct(EGID entityGID, ISerializableEntityBuilder entityBuilder,
ISerializationData serializationData, SerializationType serializationType)
{
uint groupId = entityGID.groupID;
Type entityType = entityBuilder.GetEntityType();
if (!_enginesRoot._entitiesDB.UnsafeQueryEntityDictionary(groupId, entityType,
out var safeDictionary))
{
throw new Exception("Entity Serialization failed");
}

entityBuilder.Serialize(entityGID.entityID, safeDictionary, serializationData, serializationType);
}

void DeserializeEntityInternal(ISerializationData serializationData, EGID egid,
SerializableEntityHeader serializableEntityHeader, SerializationType serializationType)
{
SerializationDescriptorMap descriptorMap = _enginesRoot.serializationDescriptorMap;
var entityDescriptor = descriptorMap.GetDescriptorFromHash(serializableEntityHeader.descriptorHash);

foreach (var serializableEntityBuilder in entityDescriptor.entitiesToSerialize)
{
_enginesRoot._entitiesDB.UnsafeQueryEntityDictionary(egid.groupID,
serializableEntityBuilder.GetEntityType(), out var safeDictionary);

serializationData.BeginNextEntityStruct();
serializableEntityBuilder.Deserialize(egid.entityID, safeDictionary, serializationData,
serializationType);
}
}

readonly EnginesRoot _enginesRoot;
}

public IEntitySerialization GenerateEntitySerializer()
{
return new EntitySerialization(this);
}
}
}

+ 76
- 0
Svelto.ECS/Serialization/EnginesRoot.SerializableEntityHeader.cs View File

@@ -0,0 +1,76 @@
using Svelto.DataStructures;

namespace Svelto.ECS
{
public partial class EnginesRoot
{
struct SerializableEntityHeader
{
public readonly uint descriptorHash;
public readonly byte entityStructsCount;

const uint SIZE = 4 + 4 + 4 + 1;

internal SerializableEntityHeader(uint descriptorHash_, EGID egid_, byte entityStructsCount_)
{
entityStructsCount = entityStructsCount_;
descriptorHash = descriptorHash_;
egid = egid_;
}

internal SerializableEntityHeader(ISerializationData serializationData)
{
descriptorHash = (uint)
(serializationData.data[serializationData.dataPos++]
| serializationData.data[serializationData.dataPos++] << 8
| serializationData.data[serializationData.dataPos++] << 16
| serializationData.data[serializationData.dataPos++] << 24);

uint entityID = (uint)
(serializationData.data[serializationData.dataPos++]
| serializationData.data[serializationData.dataPos++] << 8
| serializationData.data[serializationData.dataPos++] << 16
| serializationData.data[serializationData.dataPos++] << 24);

uint groupID = (uint)
(serializationData.data[serializationData.dataPos++]
| serializationData.data[serializationData.dataPos++] << 8
| serializationData.data[serializationData.dataPos++] << 16
| serializationData.data[serializationData.dataPos++] << 24);

entityStructsCount = serializationData.data[serializationData.dataPos++];

egid = new EGID(entityID, new ExclusiveGroup.ExclusiveGroupStruct(groupID));
}

internal void Copy(ISerializationData serializationData)
{
serializationData.data.ExpandBy(SIZE);

// Splitting the descriptorHash_ (uint, 32 bit) into four bytes.
serializationData.data[serializationData.dataPos++] = (byte) (descriptorHash & 0xff);
serializationData.data[serializationData.dataPos++] = (byte) ((descriptorHash >> 8) & 0xff);
serializationData.data[serializationData.dataPos++] = (byte) ((descriptorHash >> 16) & 0xff);
serializationData.data[serializationData.dataPos++] = (byte) ((descriptorHash >> 24) & 0xff);

// Splitting the entityID (uint, 32 bit) into four bytes.
uint entityID = egid.entityID;
serializationData.data[serializationData.dataPos++] = (byte) (entityID & 0xff);
serializationData.data[serializationData.dataPos++] = (byte) ((entityID >> 8) & 0xff);
serializationData.data[serializationData.dataPos++] = (byte) ((entityID >> 16) & 0xff);
serializationData.data[serializationData.dataPos++] = (byte) ((entityID >> 24) & 0xff);

// Splitting the groupID (uint, 32 bit) into four bytes.
uint groupID = egid.groupID;
serializationData.data[serializationData.dataPos++] = (byte) (groupID & 0xff);
serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 8) & 0xff);
serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 16) & 0xff);
serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 24) & 0xff);

serializationData.data[serializationData.dataPos++] = entityStructsCount;
}

internal readonly EGID egid; //this can't be used safely!
}
}
}

+ 105
- 0
Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Svelto.ECS.Serialization;

namespace Svelto.ECS
{
partial class EnginesRoot
{
sealed class SerializationDescriptorMap
{
/// <summary>
/// Here we want to register all the EntityDescriptors that need to be serialized for network play.
///
/// Remember! This needs to in sync across different clients and server as the values are serialized across
/// the network also want this to not change so we can save to a DB
/// </summary>
internal SerializationDescriptorMap()
{
_descriptors = new Dictionary<uint, ISerializableEntityDescriptor>();
_factories = new Dictionary<uint, IDeserializationFactory>();

Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
foreach (Type type in GetTypesSafe(assembly))
{
if (type != null && type.IsClass && type.GetConstructor(Type.EmptyTypes) != null &&
typeof(ISerializableEntityDescriptor).IsAssignableFrom(type))
{
var descriptor = Activator.CreateInstance(type) as ISerializableEntityDescriptor;

RegisterEntityDescriptor(descriptor);
}
}
}
}

static IEnumerable<Type> GetTypesSafe(Assembly assembly)
{
try
{
Type[] types = assembly.GetTypes();

return types;
}
catch (ReflectionTypeLoadException e)
{
return e.Types;
}
}

void RegisterEntityDescriptor(ISerializableEntityDescriptor descriptor)
{
if (descriptor == null)
{
return;
}

uint descriptorHash = descriptor.hash;

#if DEBUG && !PROFILER
if (_descriptors.ContainsKey(descriptorHash))
{
throw new Exception($"Hash Collision of '{descriptor.GetType()}' against " +
$"'{_descriptors[descriptorHash]} ::: {descriptorHash}'");
}
#endif

_descriptors[descriptorHash] = descriptor;
}

public ISerializableEntityDescriptor GetDescriptorFromHash(uint descriptorID)
{
#if DEBUG && !PROFILER
DBC.ECS.Check.Require(_descriptors.ContainsKey(descriptorID),
$"Could not find descriptor with ID '{descriptorID}'!");
#endif

return _descriptors[descriptorID];
}

public IDeserializationFactory GetSerializationFactory(uint descriptorID)
{
return _factories.TryGetValue(descriptorID, out var factory) ? factory : null;
}

public void RegisterSerializationFactory<Descriptor>(IDeserializationFactory deserializationFactory)
where Descriptor : ISerializableEntityDescriptor, new()
{
_factories.Add(SerializationEntityDescriptorTemplate<Descriptor>.hash, deserializationFactory);
}


readonly Dictionary<uint, ISerializableEntityDescriptor> _descriptors;
readonly Dictionary<uint, IDeserializationFactory> _factories;
}

/// <summary>
/// The map of serializable entity hashes to the serializable entity builders (to know the entity structs
/// to serialize)
/// </summary>
SerializationDescriptorMap serializationDescriptorMap { get; } = new SerializationDescriptorMap();
}
}

+ 15
- 0
Svelto.ECS/Serialization/HashNameAttribute.cs View File

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

namespace Svelto.ECS.Serialization
{
[AttributeUsage(AttributeTargets.Class)]
public class HashNameAttribute:Attribute
{
public HashNameAttribute(string name)
{
_name = name;
}
internal string _name;
}
}

+ 9
- 0
Svelto.ECS/Serialization/IDeserializationFactory.cs View File

@@ -0,0 +1,9 @@
namespace Svelto.ECS.Serialization
{
public interface IDeserializationFactory
{
EntityStructInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor, SerializationType serializationType,
IEntitySerialization entitySerialization);
}
}

+ 69
- 0
Svelto.ECS/Serialization/IEntitySerialization.cs View File

@@ -0,0 +1,69 @@
namespace Svelto.ECS.Serialization
{
public interface IEntitySerialization
{
/// <summary>
/// Fill the serializationData of the entitiesToSerialize of this descriptor
/// </summary>
/// <param name="egid"></param>
/// <param name="serializedData"></param>
/// <param name="serializationType"></param>
/// <returns>Size in bytes of the newly instantiated entity</returns>
void SerializeEntity(EGID egid, ISerializationData serializationData, SerializationType serializationType);

/// <summary>
/// Deserialize a serializationData and copy directly onto the appropriate entities
/// </summary>
/// <param name="data"></param>
/// <param name="dataPos"></param>
/// <param name="serializationType"></param>
void DeserializeEntity(ISerializationData serializationData, SerializationType serializationType);

/// <summary>
/// Deserialize a serializationData and copy directly onto the appropriate entities with explicit EGID
/// </summary>
/// <param name="egid"></param>
/// <param name="data"></param>
/// <param name="dataPos"></param>
/// <param name="serializationType"></param>
void DeserializeEntity(EGID egid, ISerializationData serializationData, SerializationType serializationType);
/// <summary>
/// Deserialize a serializationData and copy directly to an previously created EntityStructInitializer
/// </summary>
/// <param name="serializationData"></param>
/// <param name="entityDescriptor"></param>
/// <param name="initializer"></param>
/// <param name="serializationType"></param>
void DeserializeEntityStructs(ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor,
in EntityStructInitializer initializer, SerializationType serializationType);

/// <summary>
/// Contrary to the other Deserialize methods that assume that the entity exists, this method is used to deserialise
/// a new Entity
/// </summary>
/// <param name="egid"></param>
/// <param name="serializationData"></param>
/// <param name="serializationType"></param>
/// <returns></returns>
EntityStructInitializer DeserializeNewEntity(EGID egid, ISerializationData serializationData,
SerializationType serializationType);

/// <summary>
/// Special Entity Swap method that works without knowing the EntityDescriptor to swap
/// </summary>
/// <param name="localEgid"></param>
/// <param name="toEgid"></param>
void DeserializeEntityToSwap(EGID localEgid, EGID toEgid);

/// <summary>
/// Special Entity delete method that works without knowing the EntityDescriptor to delete
/// </summary>
/// <param name="egid"></param>
void DeserializeEntityToDelete(EGID egid);

void RegisterSerializationFactory<T>(IDeserializationFactory deserializationFactory)
where T : ISerializableEntityDescriptor, new();
}
}

+ 15
- 0
Svelto.ECS/Serialization/ISerializableEntityBuilder.cs View File

@@ -0,0 +1,15 @@
using Svelto.ECS.Internal;

namespace Svelto.ECS.Serialization
{
public interface ISerializableEntityBuilder : IEntityBuilder
{
void Serialize(uint id, ITypeSafeDictionary dictionary, ISerializationData serializationData, SerializationType serializationType);

void Deserialize(uint id, ITypeSafeDictionary dictionary, ISerializationData serializationData, SerializationType serializationType);

void Deserialize(ISerializationData serializationData, in EntityStructInitializer initializer, SerializationType serializationType);
void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer, in EntityStructInitializer destinationInitializer);
}
}

+ 10
- 0
Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs View File

@@ -0,0 +1,10 @@
namespace Svelto.ECS.Serialization
{
public interface ISerializableEntityDescriptor : IEntityDescriptor
{
uint hash { get; }
ISerializableEntityBuilder[] entitiesToSerialize { get; }
void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer, in EntityStructInitializer destinationInitializer);
}
}

+ 14
- 0
Svelto.ECS/Serialization/ISerializationData.cs View File

@@ -0,0 +1,14 @@
using Svelto.DataStructures;

namespace Svelto.ECS
{
public interface ISerializationData
{
uint dataPos { get; set; }
FasterList<byte> data { get; }

void ReuseAsNew();
void Reset();
void BeginNextEntityStruct();
}
}

+ 55
- 0
Svelto.ECS/Serialization/ISerializer.cs View File

@@ -0,0 +1,55 @@
#if DEBUG && !PROFILER
using System;
#endif

namespace Svelto.ECS.Serialization
{
public interface ISerializer<T>
where T : unmanaged, IEntityStruct
{
bool Serialize(in T value, ISerializationData serializationData);
bool Deserialize(ref T value, ISerializationData serializationData);

uint size { get; }
}

public static class SerializerExt
{
public static bool SerializeSafe<T>(this ISerializer<T> serializer, in T value, ISerializationData serializationData)
where T : unmanaged, IEntityStruct
{
#if DEBUG && !PROFILER
uint posBefore = serializationData.dataPos;
#endif
bool res = serializer.Serialize(value, serializationData);
#if DEBUG && !PROFILER
// size == 0 is a special case when we don't know the size in advance
if (serializer.size != 0 && serializationData.dataPos != posBefore + serializer.size)
{
throw new IndexOutOfRangeException(
$"Size mismatch when serializing {typeof(T).FullName} using {serializer.GetType().FullName}, " +
$"expected offset {posBefore + serializer.size}, got {serializationData.dataPos}");
}
#endif
return res;
}

public static bool DeserializeSafe<T>(this ISerializer<T> serializer, ref T value, ISerializationData serializationData)
where T : unmanaged, IEntityStruct
{
#if DEBUG && !PROFILER
uint posBefore = serializationData.dataPos;
#endif
bool res = serializer.Deserialize(ref value, serializationData);
#if DEBUG && !PROFILER
if (serializer.size != 0 && serializationData.dataPos != posBefore + serializer.size)
{
throw new IndexOutOfRangeException(
$"Size mismatch when deserializing {typeof(T).FullName} using {serializer.GetType().FullName}, " +
$"expected offset {posBefore + serializer.size}, got {serializationData.dataPos}");
}
#endif
return res;
}
}
}

+ 89
- 0
Svelto.ECS/Serialization/PartialSerializer.cs View File

@@ -0,0 +1,89 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using Svelto.DataStructures;
using Attribute = System.Attribute;

namespace Svelto.ECS.Serialization
{
[AttributeUsage(AttributeTargets.Field)]
public class PartialSerializerFieldAttribute : Attribute
{}

public class PartialSerializer<T> : ISerializer<T>
where T : unmanaged, IEntityStruct
{
static PartialSerializer()
{
Type myType = typeof(T);
FieldInfo[] myMembers = myType.GetFields();

for (int i = 0; i < myMembers.Length; i++)
{
Object[] myAttributes = myMembers[i].GetCustomAttributes(true);
for (int j = 0; j < myAttributes.Length; j++)
{
if (myAttributes[j] is PartialSerializerFieldAttribute)
{
if (myMembers[i].FieldType == typeof(EGID))
throw new ECSException("EGID fields cannot be serialised ".FastConcat(myType.FullName));

var offset = Marshal.OffsetOf<T>(myMembers[i].Name);
var sizeOf = (uint)Marshal.SizeOf(myMembers[i].FieldType);
offsets.Add(((uint) offset.ToInt32(), sizeOf));
totalSize += sizeOf;
}
}
}

if (myType.GetProperties().Length > (EntityBuilder<T>.HAS_EGID ? 1 : 0))
throw new ECSException("serializable entity struct must be property less ".FastConcat(myType.FullName));
}

public bool Serialize(in T value, ISerializationData serializationData)
{
unsafe
{
fixed (byte* dataptr = serializationData.data.ToArrayFast())
{
var entityStruct = value;
foreach ((uint offset, uint size) offset in offsets)
{
byte* srcPtr = (byte*) &entityStruct + offset.offset;
//todo move to Unsafe Copy when available as it is faster
Buffer.MemoryCopy(srcPtr, dataptr + serializationData.dataPos,
serializationData.data.Count - serializationData.dataPos, offset.size);
serializationData.dataPos += offset.size;
}
}
}

return true;
}

public bool Deserialize(ref T value, ISerializationData serializationData)
{
unsafe
{
T tempValue = value; //todo: temporary solution I want to get rid of this copy
fixed (byte* dataptr = serializationData.data.ToArrayFast())
foreach ((uint offset, uint size) offset in offsets)
{
byte* dstPtr = (byte*) &tempValue + offset.offset;
//todo move to Unsafe Copy when available as it is faster
Buffer.MemoryCopy(dataptr + serializationData.dataPos, dstPtr, offset.size, offset.size);
serializationData.dataPos += offset.size;
}

value = tempValue; //todo: temporary solution I want to get rid of this copy
}

return true;
}

public uint size => totalSize;

static readonly FasterList<(uint, uint)> offsets = new FasterList<(uint, uint)>();
static readonly uint totalSize;
}
}

+ 94
- 0
Svelto.ECS/Serialization/SerializableEntityBuilder.cs View File

@@ -0,0 +1,94 @@
using System;
using Svelto.Common;
using Svelto.ECS.Internal;

namespace Svelto.ECS.Serialization
{
public class SerializableEntityBuilder<T> : EntityBuilder<T>, ISerializableEntityBuilder
where T : unmanaged, IEntityStruct
{
public static readonly uint SIZE = UnsafeUtils.SizeOf<T>();

static SerializableEntityBuilder()
{}

public SerializableEntityBuilder()
{
_serializers = new ISerializer<T>[(int) SerializationType.Length];
for (int i = 0; i < (int) SerializationType.Length; i++)
{
_serializers[i] = new DefaultSerializer<T>();
}
}

public SerializableEntityBuilder(params ValueTuple<SerializationType, ISerializer<T>>[] serializers)
{
_serializers = new ISerializer<T>[(int) SerializationType.Length];
for (int i = 0; i < serializers.Length; i++)
{
ref (SerializationType, ISerializer<T>) s = ref serializers[i];
_serializers[(int) s.Item1] = s.Item2;
}

// Just in case the above are the same type
for (int i = 0; i < (int) SerializationType.Length; i++)
{
if (_serializers[i] == null) _serializers[i] = new DontSerialize<T>();
}
}

public void Serialize(uint entityID, ITypeSafeDictionary dictionary,
ISerializationData serializationData, SerializationType serializationType)
{
ISerializer<T> serializer = _serializers[(int)serializationType];

var safeDictionary = (TypeSafeDictionary<T>) dictionary;
if (safeDictionary.TryFindIndex(entityID, out uint index) == false)
{
throw new ECSException("Entity Serialization failed");
}

T[] values = safeDictionary.GetValuesArray(out _);
ref T val = ref values[index];

serializationData.dataPos = (uint) serializationData.data.Count;

serializationData.data.ExpandBy(serializer.size);
serializer.SerializeSafe(val, serializationData);
}

public void Deserialize(uint entityID, ITypeSafeDictionary dictionary,
ISerializationData serializationData, SerializationType serializationType)
{
ISerializer<T> serializer = _serializers[(int) serializationType];

// Handle the case when an entity struct is gone
var safeDictionary = (TypeSafeDictionary<T>) dictionary;
if (safeDictionary.TryFindIndex(entityID, out uint index) == false)
{
throw new ECSException("Entity Deserialization failed");
}

T[] values = safeDictionary.GetValuesArray(out _);
ref T val = ref values[index];

serializer.DeserializeSafe(ref val, serializationData);
}

public void Deserialize(ISerializationData serializationData
, in EntityStructInitializer initializer, SerializationType serializationType)
{
ISerializer<T> serializer = _serializers[(int) serializationType];

serializer.DeserializeSafe(ref initializer.GetOrCreate<T>(), serializationData);
}

public void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer,
in EntityStructInitializer destinationInitializer)
{
destinationInitializer.CopyFrom(sourceInitializer.Get<T>());
}

readonly ISerializer<T>[] _serializers;
}
}

+ 122
- 0
Svelto.ECS/Serialization/SerializableEntityDescriptor.cs View File

@@ -0,0 +1,122 @@
using System;
using System.Reflection;
using System.Text;
using Svelto.DataStructures;
using Svelto.Utilities;

namespace Svelto.ECS.Serialization
{
public static class DesignatedHash
{
public static readonly Func<byte[], uint> Hash = Murmur3.MurmurHash3_x86_32;
}

public abstract class SerializableEntityDescriptor<TType> : ISerializableEntityDescriptor
where TType : IEntityDescriptor, new()
{
static SerializableEntityDescriptor()
{
IEntityBuilder[] defaultEntities = EntityDescriptorTemplate<TType>.descriptor.entitiesToBuild;

var hashNameAttribute = _type.GetCustomAttribute<HashNameAttribute>();
if (hashNameAttribute == null)
{
throw new Exception("HashName attribute not found on the serializable type ".FastConcat(_type.FullName));
}

_hash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(hashNameAttribute._name));

var (index, dynamicIndex) = SetupSpecialEntityStruct(defaultEntities, out _entitiesToBuild);
if (index == -1)
{
index = _entitiesToBuild.Length - 1;
}

// Stores the hash of this EntityDescriptor
_entitiesToBuild[index] = new EntityBuilder<SerializableEntityStruct>
(
new SerializableEntityStruct
{
descriptorHash = _hash
}
);

// If the current serializable is an ExtendibleDescriptor, I have to update it.
if (dynamicIndex != -1)
{
_entitiesToBuild[dynamicIndex] = new EntityBuilder<EntityStructInfoView>
(
new EntityStructInfoView
{
entitiesToBuild = _entitiesToBuild
}
);
}

/////
var entitiesToSerialize = new FasterList<ISerializableEntityBuilder>();
_entitiesToSerializeMap = new FasterDictionary<RefWrapper<Type>, ISerializableEntityBuilder>();
foreach (IEntityBuilder e in defaultEntities)
{
if (e is ISerializableEntityBuilder serializableEntityBuilder)
{
var entityType = serializableEntityBuilder.GetEntityType();
_entitiesToSerializeMap[new RefWrapper<Type>(entityType)] = serializableEntityBuilder;
entitiesToSerialize.Add(serializableEntityBuilder);
}
}
_entitiesToSerialize = entitiesToSerialize.ToArray();
}

static (int indexSerial, int indexDynamic) SetupSpecialEntityStruct(IEntityBuilder[] defaultEntities,
out IEntityBuilder[] entitiesToBuild)
{
int length = defaultEntities.Length;
int newLenght = length + 1;

int indexSerial = -1;
int indexDynamic = -1;

for (var i = 0; i < length; ++i)
{
if (defaultEntities[i].GetEntityType() == _serializableStructType)
{
indexSerial = i;
--newLenght;
}

if (defaultEntities[i].GetEntityType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW)
{
indexDynamic = i;
}
}

entitiesToBuild = new IEntityBuilder[newLenght];

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

return (indexSerial, indexDynamic);
}
public void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer, in EntityStructInitializer destinationInitializer)
{
foreach (ISerializableEntityBuilder e in entitiesToSerialize)
{
e.CopySerializedEntityStructs(sourceInitializer, destinationInitializer);
}
}

public IEntityBuilder[] entitiesToBuild => _entitiesToBuild;
public uint hash => _hash;
public ISerializableEntityBuilder[] entitiesToSerialize => _entitiesToSerialize;

static readonly IEntityBuilder[] _entitiesToBuild;
static readonly FasterDictionary<RefWrapper<Type>, ISerializableEntityBuilder> _entitiesToSerializeMap;
static readonly ISerializableEntityBuilder[] _entitiesToSerialize;

static readonly uint _hash;
static readonly Type _serializableStructType = typeof(SerializableEntityStruct);
static readonly Type _type = typeof(TType);
}
}

+ 9
- 0
Svelto.ECS/Serialization/SerializableEntityStruct.cs View File

@@ -0,0 +1,9 @@
namespace Svelto.ECS
{
internal struct SerializableEntityStruct : IEntityStruct, INeedEGID
{
public uint descriptorHash;

public EGID ID { get; set; }
}
}

+ 16
- 0
Svelto.ECS/Serialization/SerializationEntityDescriptorTemplate.cs View File

@@ -0,0 +1,16 @@
namespace Svelto.ECS.Serialization
{
public static class SerializationEntityDescriptorTemplate<TType> where TType : ISerializableEntityDescriptor, new()
{
static SerializationEntityDescriptorTemplate()
{
var serializableEntityDescriptor = new TType();
hash = serializableEntityDescriptor.hash;

entityDescriptor = (ISerializableEntityDescriptor) EntityDescriptorTemplate<TType>.descriptor;
}

public static uint hash { get; }
public static ISerializableEntityDescriptor entityDescriptor { get; }
}
}

+ 37
- 0
Svelto.ECS/Serialization/SimpleSerializationData.cs View File

@@ -0,0 +1,37 @@
using Svelto.DataStructures;

namespace Svelto.ECS
{
public class SimpleSerializationData : ISerializationData
{
public uint dataPos { get; set; }
public FasterList<byte> data { get; set; }

public SimpleSerializationData(FasterList<byte> d)
{
data = d;
}

public void ResetWithNewData(FasterList<byte> newData)
{
dataPos = 0;

data = newData;
}

public void ReuseAsNew()
{
dataPos = 0;

data.ResetToReuse();
}
public void Reset()
{
dataPos = 0;
}

public void BeginNextEntityStruct()
{}
}
}

+ 15
- 0
Svelto.ECS/Serialization/Unsafe.cs View File

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

namespace Svelto.Common
{
public class UnsafeUtils
{
public static uint SizeOf<T>() where T : unmanaged, IEntityStruct
{
unsafe
{
return (uint) sizeof(T);
}
}
}
}

+ 10
- 6
Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs View File

@@ -1,17 +1,21 @@
using Svelto.ECS.Schedulers;
using Svelto.WeakEvents;
using System;
using Svelto.ECS.Schedulers;

namespace Svelto.ECS
{
//This scheduler shouldn't be used in production and it's meant to be
//used for Unit Tests only
//This scheduler shouldn't be used in production and it's meant to be used for Unit Tests only
public class SimpleSubmissionEntityViewScheduler : IEntitySubmissionScheduler
{
public void SubmitEntities()
{
onTick.Invoke();
_onTick.Invoke();
}
public WeakAction onTick { set; private get; }
EnginesRoot.EntitiesSubmitter IEntitySubmissionScheduler.onTick
{
set => _onTick = value;
}
EnginesRoot.EntitiesSubmitter _onTick;
}
}

+ 0
- 13
Svelto.ECS/StaticEntityDescriptorInfo.cs View File

@@ -1,13 +0,0 @@
namespace Svelto.ECS
{
class StaticEntityDescriptorInfo<TType>: IEntityDescriptor where TType : IEntityDescriptor
{
internal StaticEntityDescriptorInfo(TType descriptor)
{
entitiesToBuild = descriptor.entitiesToBuild;
}

public IEntityBuilder[] entitiesToBuild { get; }
}
}


+ 1
- 1
Svelto.ECS/Svelto.ECS.asmdef View File

@@ -6,7 +6,7 @@
"optionalUnityReferences": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,


+ 3
- 0
Svelto.ECS/Svelto.ECS.csproj View File

@@ -7,15 +7,18 @@
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<UseSharedCompilation>false</UseSharedCompilation>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<UseSharedCompilation>false</UseSharedCompilation>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Svelto.Common\Svelto.Common.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.2" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.6.0-preview8.19405.3" />
</ItemGroup>
</Project>

+ 9
- 0
Svelto.ECS/TypeCache.cs View File

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

namespace Svelto.ECS.Internal
{
public class TypeCache<T>
{
public static Type type = typeof(T);
}
}

+ 28
- 0
Svelto.ECS/UnsafeStructRef.cs View File

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

namespace Svelto.ECS
{
public struct UnsafeStructRef<T>: IDisposable
{
readonly unsafe void* pointer;
GCHandle _handle;

public UnsafeStructRef(ref T entityStruct, GCHandle handle)
{
unsafe
{
_handle = handle;
pointer = Unsafe.AsPointer(ref entityStruct);
}
}

public unsafe ref T refvalue => ref Unsafe.AsRef<T>(pointer);

public void Dispose()
{
_handle.Free();
}
}
}

+ 14
- 0
Svelto.ECS/Utilities.cs View File

@@ -0,0 +1,14 @@
using System.Runtime.InteropServices;

namespace Svelto.ECS
{
public static class Utilities
{
public static UnsafeStructRef<T> ToUnsafeRef<T>(this T[] entityStructs, uint index) where T : struct, IEntityStruct
{
var alloc = GCHandle.Alloc(entityStructs, GCHandleType.Pinned);
return new UnsafeStructRef<T>(ref entityStructs[index], alloc);
}
}
}

+ 64
- 0
Svelto.ECS/WaitForSubmissionEnumerator.cs View File

@@ -0,0 +1,64 @@
using System;
using System.Collections;

namespace Svelto.ECS
{
public class WaitForSubmissionEnumerator : IEnumerator
{
class SubmissionEntityDescriptor : GenericEntityDescriptor<SubmissionSignalStruct>
{
internal static readonly ExclusiveGroup SubmissionGroup = new ExclusiveGroup();
}

readonly IEntityFactory _entityFactory;
readonly IEntitiesDB _entitiesDB;
readonly IEntityFunctions _entityFunctions;

int _state;

public WaitForSubmissionEnumerator(IEntityFunctions entityFunctions, IEntityFactory entityFactory,
IEntitiesDB entitiesDb)
{
_entityFactory = entityFactory;
_entityFunctions = entityFunctions;
_entitiesDB = entitiesDb;
}

public bool MoveNext()
{
switch (_state)
{
case 0:
_counter = _COUNTER++;
_entityFactory.BuildEntity<SubmissionEntityDescriptor>(new EGID((uint) _counter,
SubmissionEntityDescriptor.SubmissionGroup));
_state = 1;
return true;
case 1:
if (_entitiesDB.Exists<SubmissionSignalStruct>(new EGID((uint) _counter,
SubmissionEntityDescriptor.SubmissionGroup)) == false)
return true;

_entityFunctions.RemoveEntity<SubmissionEntityDescriptor>(new EGID((uint) _counter,
SubmissionEntityDescriptor.SubmissionGroup));
_state = 0;
return false;
}

throw new Exception("something is wrong");
}

void IEnumerator.Reset()
{
throw new NotImplementedException();
}

public object Current { get; }

struct SubmissionSignalStruct : IEntityStruct
{}

int _counter;
static int _COUNTER;
}
}

+ 128
- 0
Svelto.ECS/bin/Debug/netstandard2.0/Svelto.ECS.deps.json View File

@@ -0,0 +1,128 @@
{
"runtimeTarget": {
"name": ".NETStandard,Version=v2.0/",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETStandard,Version=v2.0": {},
".NETStandard,Version=v2.0/": {
"Svelto.ECS/1.0.0": {
"dependencies": {
"NETStandard.Library": "2.0.3",
"Svelto.Common": "1.0.0",
"System.Memory": "4.5.2",
"System.Runtime.CompilerServices.Unsafe": "4.6.0-preview8.19405.3"
},
"runtime": {
"Svelto.ECS.dll": {}
}
},
"Microsoft.NETCore.Platforms/1.1.0": {},
"NETStandard.Library/2.0.3": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0"
}
},
"System.Buffers/4.4.0": {
"runtime": {
"lib/netstandard2.0/System.Buffers.dll": {
"assemblyVersion": "4.0.2.0",
"fileVersion": "4.6.25519.3"
}
}
},
"System.Memory/4.5.2": {
"dependencies": {
"System.Buffers": "4.4.0",
"System.Numerics.Vectors": "4.4.0",
"System.Runtime.CompilerServices.Unsafe": "4.6.0-preview8.19405.3"
},
"runtime": {
"lib/netstandard2.0/System.Memory.dll": {
"assemblyVersion": "4.0.1.0",
"fileVersion": "4.6.27129.4"
}
}
},
"System.Numerics.Vectors/4.4.0": {
"runtime": {
"lib/netstandard2.0/System.Numerics.Vectors.dll": {
"assemblyVersion": "4.1.3.0",
"fileVersion": "4.6.25519.3"
}
}
},
"System.Runtime.CompilerServices.Unsafe/4.6.0-preview8.19405.3": {
"runtime": {
"lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": {
"assemblyVersion": "4.0.5.0",
"fileVersion": "4.700.19.40503"
}
}
},
"Svelto.Common/1.0.0": {
"dependencies": {
"System.Memory": "4.5.2"
},
"runtime": {
"Svelto.Common.dll": {}
}
}
}
},
"libraries": {
"Svelto.ECS/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Microsoft.NETCore.Platforms/1.1.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==",
"path": "microsoft.netcore.platforms/1.1.0",
"hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512"
},
"NETStandard.Library/2.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-548M6mnBSJWxsIlkQHfbzoYxpiYFXZZSL00p4GHYv8PkiqFBnnT68mW5mGEsA/ch9fDO9GkPgkFQpWiXZN7mAQ==",
"path": "netstandard.library/2.0.3",
"hashPath": "netstandard.library.2.0.3.nupkg.sha512"
},
"System.Buffers/4.4.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Ii2bedd4HVzddupdU35n3ygohUPlNn7MDimBOYcwWNce2NizQ1fCSaQJY1Tzv80aMqOGpVcU4wZr/Xe50xcTwg==",
"path": "system.buffers/4.4.0",
"hashPath": "system.buffers.4.4.0.nupkg.sha512"
},
"System.Memory/4.5.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Ohf9h6em5CjPaAzupGtW/TWMzX6aVfIGPh7bq5KQmfvYJr+DqvOa0cRS9ggFKGIuvKTModCQEacGC/CwE8j7cg==",
"path": "system.memory/4.5.2",
"hashPath": "system.memory.4.5.2.nupkg.sha512"
},
"System.Numerics.Vectors/4.4.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-gdRrUJs1RrjW3JB5p82hYjA67xoeFLvh0SdSIWjTiN8qExlbFt/RtXwVYNc5BukJ/f9OKzQQS6gakzbJeHTqHg==",
"path": "system.numerics.vectors/4.4.0",
"hashPath": "system.numerics.vectors.4.4.0.nupkg.sha512"
},
"System.Runtime.CompilerServices.Unsafe/4.6.0-preview8.19405.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-1EHM+Tp0/RlDu4cQpYl/+OsikV6DqVK9WXLZenKA0jNVgAvmKG4eD7czEIDR1w3IQjqywhIdIrqm1C4wJutVnA==",
"path": "system.runtime.compilerservices.unsafe/4.6.0-preview8.19405.3",
"hashPath": "system.runtime.compilerservices.unsafe.4.6.0-preview8.19405.3.nupkg.sha512"
},
"Svelto.Common/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

+ 23
- 0
Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.AssemblyInfo.cs View File

@@ -0,0 +1,23 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Reflection;

[assembly: System.Reflection.AssemblyCompanyAttribute("Svelto.ECS")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyProductAttribute("Svelto.ECS")]
[assembly: System.Reflection.AssemblyTitleAttribute("Svelto.ECS")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

// Generated by the MSBuild WriteCodeFragment class.


+ 0
- 0
Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.CopyComplete View File


+ 23
- 0
Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.FileListAbsolute.txt View File

@@ -0,0 +1,23 @@
E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.ECS.deps.json
E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.ECS.dll
E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.ECS.pdb
E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.Common.dll
E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.Common.pdb
E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.csprojAssemblyReference.cache
E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.csproj.CoreCompileInputs.cache
E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.AssemblyInfoInputs.cache
E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.AssemblyInfo.cs
E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.csproj.CopyComplete
E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.dll
E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.pdb
E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.ECS.deps.json
E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.ECS.dll
E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.ECS.pdb
E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.Common.dll
E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.Common.pdb
E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.csprojAssemblyReference.cache
E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.AssemblyInfoInputs.cache
E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.AssemblyInfo.cs
E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.csproj.CopyComplete
E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.dll
E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.pdb

+ 142
- 0
Svelto.ECS/obj/Svelto.ECS.csproj.nuget.dgspec.json View File

@@ -0,0 +1,142 @@
{
"format": 1,
"restore": {
"E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\Svelto.ECS.csproj": {}
},
"projects": {
"E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj",
"projectName": "Svelto.Common",
"projectPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj",
"packagesPath": "C:\\Users\\Seb-e-Sam\\.nuget\\packages\\",
"outputPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder"
],
"configFilePaths": [
"C:\\Users\\Seb-e-Sam\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"netstandard2.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Users\\Seb-e-Sam\\AppData\\Local\\Xenko\\NugetDev": {},
"https://api.nuget.org/v3/index.json": {},
"https://packages.xenko.com/nuget": {}
},
"frameworks": {
"netstandard2.0": {
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
}
},
"frameworks": {
"netstandard2.0": {
"dependencies": {
"NETStandard.Library": {
"suppressParent": "All",
"target": "Package",
"version": "[2.0.3, )",
"autoReferenced": true
},
"System.Memory": {
"target": "Package",
"version": "[4.5.2, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48"
],
"assetTargetFallback": true,
"warn": true
}
}
},
"E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\Svelto.ECS.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\Svelto.ECS.csproj",
"projectName": "Svelto.ECS",
"projectPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\Svelto.ECS.csproj",
"packagesPath": "C:\\Users\\Seb-e-Sam\\.nuget\\packages\\",
"outputPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder"
],
"configFilePaths": [
"C:\\Users\\Seb-e-Sam\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"netstandard2.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Users\\Seb-e-Sam\\AppData\\Local\\Xenko\\NugetDev": {},
"https://api.nuget.org/v3/index.json": {},
"https://packages.xenko.com/nuget": {}
},
"frameworks": {
"netstandard2.0": {
"projectReferences": {
"E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj": {
"projectPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj"
}
}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
}
},
"frameworks": {
"netstandard2.0": {
"dependencies": {
"NETStandard.Library": {
"suppressParent": "All",
"target": "Package",
"version": "[2.0.3, )",
"autoReferenced": true
},
"System.Memory": {
"target": "Package",
"version": "[4.5.2, )"
},
"System.Runtime.CompilerServices.Unsafe": {
"target": "Package",
"version": "[4.6.0-preview8.19405.3, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48"
],
"assetTargetFallback": true,
"warn": true
}
}
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save