diff --git a/Svelto.Common b/Svelto.Common index 433b39f..0d71df6 160000 --- a/Svelto.Common +++ b/Svelto.Common @@ -1 +1 @@ -Subproject commit 433b39f4a1b9a17301988ecd8a633d049bb4378d +Subproject commit 0d71df6e66f2d0c72d23fa44b740bad4a0ff5e8c diff --git a/Svelto.ECS.Components/Components/ECSQuaternion.cs b/Svelto.ECS.Components/Components/ECSQuaternion.cs deleted file mode 100644 index c88ccb2..0000000 --- a/Svelto.ECS.Components/Components/ECSQuaternion.cs +++ /dev/null @@ -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; - } - } -} diff --git a/Svelto.ECS.Components/Components/ECSRect.cs b/Svelto.ECS.Components/Components/ECSRect.cs deleted file mode 100644 index bf2fb7c..0000000 --- a/Svelto.ECS.Components/Components/ECSRect.cs +++ /dev/null @@ -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 \ No newline at end of file diff --git a/Svelto.ECS.Components/Components/ECSVector2.cs b/Svelto.ECS.Components/Components/ECSVector2.cs deleted file mode 100644 index a48a5d4..0000000 --- a/Svelto.ECS.Components/Components/ECSVector2.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/Svelto.ECS.Components/Components/ECSVector3.cs b/Svelto.ECS.Components/Components/ECSVector3.cs deleted file mode 100644 index fea272f..0000000 --- a/Svelto.ECS.Components/Components/ECSVector3.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/Svelto.ECS.Components/Components/ECSVector4.cs b/Svelto.ECS.Components/Components/ECSVector4.cs deleted file mode 100644 index 17396fb..0000000 --- a/Svelto.ECS.Components/Components/ECSVector4.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/Svelto.ECS.Components/Components/ExtensionMethods.cs b/Svelto.ECS.Components/Components/ExtensionMethods.cs deleted file mode 100644 index ea70e97..0000000 --- a/Svelto.ECS.Components/Components/ExtensionMethods.cs +++ /dev/null @@ -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; - } -} diff --git a/Svelto.ECS.Components/Components/ExtensionMethodsUECS.cs b/Svelto.ECS.Components/Components/ExtensionMethodsUECS.cs deleted file mode 100644 index 2cb943a..0000000 --- a/Svelto.ECS.Components/Components/ExtensionMethodsUECS.cs +++ /dev/null @@ -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 \ No newline at end of file diff --git a/Svelto.ECS.Components/Components/ExtensionMethodsUnity.cs b/Svelto.ECS.Components/Components/ExtensionMethodsUnity.cs deleted file mode 100644 index d8d4136..0000000 --- a/Svelto.ECS.Components/Components/ExtensionMethodsUnity.cs +++ /dev/null @@ -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 \ No newline at end of file diff --git a/Svelto.ECS.Components/ECSResources/StringECSDB.cs b/Svelto.ECS.Components/ECSResources/StringECSDB.cs deleted file mode 100644 index d977085..0000000 --- a/Svelto.ECS.Components/ECSResources/StringECSDB.cs +++ /dev/null @@ -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.FromECS(ecsString.id); - } - - public static implicit operator ECSString(string text) - { - return new ECSString(ResourcesECSDB.ToECS(text)); - } - } -} \ No newline at end of file diff --git a/Svelto.ECS.Components/EntityStructs/PositionEntityStruct.cs b/Svelto.ECS.Components/EntityStructs/PositionEntityStruct.cs deleted file mode 100644 index b86a6ac..0000000 --- a/Svelto.ECS.Components/EntityStructs/PositionEntityStruct.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Svelto.ECS.Components; - -namespace Svelto.ECS.EntityStructs -{ - public struct PositionEntityStruct : IEntityStruct - { - public ECSVector3 position; - } -} \ No newline at end of file diff --git a/Svelto.ECS.Components/EntityStructs/RotationEntityStruct.cs b/Svelto.ECS.Components/EntityStructs/RotationEntityStruct.cs deleted file mode 100644 index 2b30560..0000000 --- a/Svelto.ECS.Components/EntityStructs/RotationEntityStruct.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Svelto.ECS.Components; - -namespace Svelto.ECS.EntityStructs -{ - public struct RotationEntityStruct : IEntityStruct - { - public ECSQuaternion rotation; - } -} \ No newline at end of file diff --git a/Svelto.ECS.Components/EntityStructs/ScalingEntityStruct.cs b/Svelto.ECS.Components/EntityStructs/ScalingEntityStruct.cs deleted file mode 100644 index 89e535a..0000000 --- a/Svelto.ECS.Components/EntityStructs/ScalingEntityStruct.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Svelto.ECS.Components; - -namespace Svelto.ECS.EntityStructs -{ - public struct ScalingEntityStruct : IEntityStruct - { - public ECSVector3 scale; - } -} diff --git a/Svelto.ECS.Components/Svelto.ECS.Components.asmdef b/Svelto.ECS.Components/Svelto.ECS.Components.asmdef deleted file mode 100644 index cdf669d..0000000 --- a/Svelto.ECS.Components/Svelto.ECS.Components.asmdef +++ /dev/null @@ -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" - } - ] -} \ No newline at end of file diff --git a/Svelto.ECS/AllowMultipleAttribute.cs b/Svelto.ECS/AllowMultipleAttribute.cs new file mode 100644 index 0000000..37c26ec --- /dev/null +++ b/Svelto.ECS/AllowMultipleAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace Svelto.ECS +{ + public class AllowMultipleAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/Svelto.ECS/CheckEntityUtilities.cs b/Svelto.ECS/CheckEntityUtilities.cs index 5c26698..0b061f6 100644 --- a/Svelto.ECS/CheckEntityUtilities.cs +++ b/Svelto.ECS/CheckEntityUtilities.cs @@ -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("removed".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 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("added ".FastConcat(egid.ToString())); + + if (_idCheckers.TryGetValue(egid.groupID, out var hash) == false) + hash = _idCheckers[egid.groupID] = new HashSet(); 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> _idCheckers = new FasterDictionary>(); +#else [Conditional("_CHECKS_DISABLED")] -#endif - void CheckAddEntityID(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 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 } -} \ No newline at end of file +} diff --git a/Svelto.ECS/DBC.cs b/Svelto.ECS/DBC.cs index 300cef0..144c690 100644 --- a/Svelto.ECS/DBC.cs +++ b/Svelto.ECS/DBC.cs @@ -6,339 +6,339 @@ using System; namespace DBC.ECS { - /// - /// Design By Contract Checks. - /// - /// Each method generates an exception or - /// a trace assertion statement if the contract is broken. - /// - /// - /// This example shows how to call the Require method. - /// Assume DBC_CHECK_PRECONDITION is defined. - /// - /// public void Test(int x) - /// { - /// try - /// { - /// Check.Require(x > 1, "x must be > 1"); - /// } - /// catch (System.Exception ex) - /// { - /// Console.WriteLine(ex.ToString()); - /// } - /// } - /// - /// If you wish to use trace assertion statements, intended for Debug scenarios, - /// rather than exception handling then set - /// - /// Check.UseAssertions = true - /// - /// You can specify this in your application entry point and maybe make it - /// dependent on conditional compilation flags or configuration file settings, e.g., - /// - /// #if DBC_USE_ASSERTIONS - /// Check.UseAssertions = true; - /// #endif - /// - /// You can direct output to a Trace listener. For example, you could insert - /// - /// Trace.Listeners.Clear(); - /// Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); - /// - /// - /// 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.) - /// - /// - static class Check - { - #region Interface + /// + /// Design By Contract Checks. + /// + /// Each method generates an exception or + /// a trace assertion statement if the contract is broken. + /// + /// + /// This example shows how to call the Require method. + /// Assume DBC_CHECK_PRECONDITION is defined. + /// + /// public void Test(int x) + /// { + /// try + /// { + /// Check.Require(x > 1, "x must be > 1"); + /// } + /// catch (System.Exception ex) + /// { + /// Console.WriteLine(ex.ToString()); + /// } + /// } + /// + /// If you wish to use trace assertion statements, intended for Debug scenarios, + /// rather than exception handling then set + /// + /// Check.UseAssertions = true + /// + /// You can specify this in your application entry point and maybe make it + /// dependent on conditional compilation flags or configuration file settings, e.g., + /// + /// #if DBC_USE_ASSERTIONS + /// Check.UseAssertions = true; + /// #endif + /// + /// You can direct output to a Trace listener. For example, you could insert + /// + /// Trace.Listeners.Clear(); + /// Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); + /// + /// + /// 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.) + /// + /// + static class Check + { + #region Interface - /// - /// Precondition check. - /// + /// + /// Precondition check. + /// #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); + } + } - /// - /// Precondition check. - /// - /// + /// + /// Precondition check. + /// + /// #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); + } + } - /// - /// Precondition check. - /// - /// + /// + /// Precondition check. + /// + /// #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."); - } - } - - /// - /// Postcondition check. - /// - /// + public static void Require(bool assertion) + { + if (UseExceptions) + { + if (!assertion) + throw new PreconditionException("Precondition failed."); + } + else + { + Trace.Assert(assertion, "Precondition failed."); + } + } + + /// + /// Postcondition check. + /// + /// #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); + } + } - /// - /// Postcondition check. - /// - /// + /// + /// Postcondition check. + /// + /// #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); + } + } - /// - /// Postcondition check. - /// - /// + /// + /// Postcondition check. + /// + /// #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."); - } - } - - /// - /// Invariant check. - /// - /// + public static void Ensure(bool assertion) + { + if (UseExceptions) + { + if (!assertion) + throw new PostconditionException("Postcondition failed."); + } + else + { + Trace.Assert(assertion, "Postcondition failed."); + } + } + + /// + /// Invariant check. + /// + /// #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); + } + } - /// - /// Invariant check. - /// - /// + /// + /// Invariant check. + /// + /// #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); + } + } - /// - /// Invariant check. - /// - /// + /// + /// Invariant check. + /// + /// #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."); + } + } - /// - /// Assertion check. - /// + /// + /// Assertion check. + /// #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); + } + } - /// - /// Assertion check. - /// - /// + /// + /// Assertion check. + /// + /// #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); + } + } - /// - /// Assertion check. - /// - /// + /// + /// Assertion check. + /// + /// #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."); + } + } + + /// + /// Set this if you wish to use Trace Assert statements + /// instead of exception handling. + /// (The Check class uses exception handling by default.) + /// + public static bool UseAssertions + { - /// - /// Set this if you wish to use Trace Assert statements - /// instead of exception handling. - /// (The Check class uses exception handling by default.) - /// - 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 - /// - /// Is exception handling being used? - /// - private static bool UseExceptions - { - get - { - return !useAssertions; - } - } + // No creation + + /// + /// Is exception handling being used? + /// + 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 /// /// 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. - /// + /// /// 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) {} + } - /// - /// Exception raised when a precondition fails. - /// - public class PreconditionException : DesignByContractException - { - /// - /// Precondition Exception. - /// - public PreconditionException() {} - /// - /// Precondition Exception. - /// - public PreconditionException(string message) : base(message) {} - /// - /// Precondition Exception. - /// - public PreconditionException(string message, Exception inner) : base(message, inner) {} - } - - /// - /// Exception raised when a postcondition fails. - /// - public class PostconditionException : DesignByContractException - { - /// - /// Postcondition Exception. - /// - public PostconditionException() {} - /// - /// Postcondition Exception. - /// - public PostconditionException(string message) : base(message) {} - /// - /// Postcondition Exception. - /// - public PostconditionException(string message, Exception inner) : base(message, inner) {} - } + /// + /// Exception raised when a precondition fails. + /// + public class PreconditionException : DesignByContractException + { + /// + /// Precondition Exception. + /// + public PreconditionException() {} + /// + /// Precondition Exception. + /// + public PreconditionException(string message) : base(message) {} + /// + /// Precondition Exception. + /// + public PreconditionException(string message, Exception inner) : base(message, inner) {} + } - /// - /// Exception raised when an invariant fails. - /// - public class InvariantException : DesignByContractException - { - /// - /// Invariant Exception. - /// - public InvariantException() {} - /// - /// Invariant Exception. - /// - public InvariantException(string message) : base(message) {} - /// - /// Invariant Exception. - /// - public InvariantException(string message, Exception inner) : base(message, inner) {} - } + /// + /// Exception raised when a postcondition fails. + /// + public class PostconditionException : DesignByContractException + { + /// + /// Postcondition Exception. + /// + public PostconditionException() {} + /// + /// Postcondition Exception. + /// + public PostconditionException(string message) : base(message) {} + /// + /// Postcondition Exception. + /// + public PostconditionException(string message, Exception inner) : base(message, inner) {} + } - /// - /// Exception raised when an assertion fails. - /// - public class AssertionException : DesignByContractException - { - /// - /// Assertion Exception. - /// - public AssertionException() {} - /// - /// Assertion Exception. - /// - public AssertionException(string message) : base(message) {} - /// - /// Assertion Exception. - /// - public AssertionException(string message, Exception inner) : base(message, inner) {} - } + /// + /// Exception raised when an invariant fails. + /// + public class InvariantException : DesignByContractException + { + /// + /// Invariant Exception. + /// + public InvariantException() {} + /// + /// Invariant Exception. + /// + public InvariantException(string message) : base(message) {} + /// + /// Invariant Exception. + /// + public InvariantException(string message, Exception inner) : base(message, inner) {} + } + + /// + /// Exception raised when an assertion fails. + /// + public class AssertionException : DesignByContractException + { + /// + /// Assertion Exception. + /// + public AssertionException() {} + /// + /// Assertion Exception. + /// + public AssertionException(string message) : base(message) {} + /// + /// Assertion Exception. + /// + public AssertionException(string message, Exception inner) : base(message, inner) {} + } - #endregion // Exception classes + #endregion // Exception classes } // End Design By Contract diff --git a/Svelto.ECS/DataStructures/SetEGIDWithoutBoxing.cs b/Svelto.ECS/DataStructures/SetEGIDWithoutBoxing.cs new file mode 100644 index 0000000..730fd10 --- /dev/null +++ b/Svelto.ECS/DataStructures/SetEGIDWithoutBoxing.cs @@ -0,0 +1,37 @@ +using System; +using System.Linq.Expressions; +using System.Reflection; + +namespace Svelto.ECS.Internal +{ + internal static class SetEGIDWithoutBoxing where T : struct, IEntityStruct + { + internal delegate void ActionCast(ref T target, EGID egid); + + public static readonly ActionCast SetIDWithoutBoxing = MakeSetter(); + + static ActionCast MakeSetter() + { + if (EntityBuilder.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(assignExp, targetExp, valueExp).Compile(); + + return setter; + } + + return null; + } + + public static void Warmup() + { + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs index 7a53f93..53a54be 100644 --- a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs +++ b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs @@ -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> entityViewEnginesDB, in PlatformProfiler profiler); + void AddEntitiesToEngines( + FasterDictionary, FasterList> entityViewEnginesDb, + ITypeSafeDictionary realDic, in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group); - void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, - Dictionary> engines, in PlatformProfiler profiler); + void RemoveEntitiesFromEngines(FasterDictionary, FasterList> entityViewEnginesDB, + in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group); void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId); - void AddEntitiesToEngines(Dictionary> entityViewEnginesDb, - ITypeSafeDictionary realDic, in PlatformProfiler profiler); + void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, + FasterDictionary, FasterList> 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; - + 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.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> entityViewEnginesDB, - ITypeSafeDictionary realDic, in PlatformProfiler profiler) + FasterDictionary, FasterList> entityViewEnginesDB, + ITypeSafeDictionary realDic, in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group) { + var typeSafeDictionary = realDic as TypeSafeDictionary; + + //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; - 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, FasterList> 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> 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; - 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.SetIDWithoutBoxing(ref entity, toEntityID); - Remove(fromEntityGid.entityID); + toGroupCasted.Add(fromEntityGid.entityID, entity); + } } - public void RemoveEntitiesFromEngines( - Dictionary> entityViewEnginesDB, - in PlatformProfiler profiler) + public void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, + FasterDictionary, FasterList> 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; + var previousGroup = fromEntityGid.groupID; - for (var i = 0; i < count; i++) - RemoveEntityViewFromEngines(entityViewEnginesDB, ref values[i], null, in profiler); + if (_hasEgid) SetEGIDWithoutBoxing.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(); } + public ITypeSafeDictionary Create() + { + return new TypeSafeDictionary(); + } - void AddEntityViewToEngines(Dictionary> entityViewEnginesDB, - ref TValue entity, - ExclusiveGroup.ExclusiveGroupStruct? previousGroup, - in PlatformProfiler profiler) + void AddEntityViewToEngines(FasterDictionary, FasterList> 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), 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).Add(ref entity); + (entityViewsEngines[i] as IReactOnAddAndRemove).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).MovedTo(ref entity, previousGroup.Value); + (entityViewsEngines[i] as IReactOnSwap).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> entityViewEnginesDB, ref TValue entity, - ExclusiveGroup.ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler) + FasterDictionary, FasterList> @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), 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).Remove(ref entity); + (entityViewsEngines[i] as IReactOnAddAndRemove).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).MovedFrom(ref entity); + (entityViewsEngines[i] as IReactOnSwap).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]; } } } \ No newline at end of file diff --git a/Svelto.ECS/Dispatcher/DispatchOnChange.cs b/Svelto.ECS/Dispatcher/DispatchOnChange.cs index cb399ff..d1ce756 100644 --- a/Svelto.ECS/Dispatcher/DispatchOnChange.cs +++ b/Svelto.ECS/Dispatcher/DispatchOnChange.cs @@ -2,7 +2,7 @@ using System; namespace Svelto.ECS { - public class DispatchOnChange : DispatchOnSet where T:struct, IEquatable + public class DispatchOnChange : DispatchOnSet where T:IEquatable { public DispatchOnChange(EGID senderID) : base(senderID) { } @@ -15,10 +15,7 @@ namespace Svelto.ECS base.value = value; } - get - { - return _value; - } + get => _value; } } } diff --git a/Svelto.ECS/Dispatcher/DispatchOnSet.cs b/Svelto.ECS/Dispatcher/DispatchOnSet.cs index 120ce32..d407ae8 100644 --- a/Svelto.ECS/Dispatcher/DispatchOnSet.cs +++ b/Svelto.ECS/Dispatcher/DispatchOnSet.cs @@ -1,14 +1,11 @@ using System; -using Svelto.WeakEvents; namespace Svelto.ECS { - public class DispatchOnSet where T:struct + public class DispatchOnSet { public DispatchOnSet(EGID senderID) { - _subscribers = new WeakEvent(); - _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 _subscribers; - bool _paused; + Action _subscribers; + bool _paused; } } diff --git a/Svelto.ECS/DynamicEntityDescriptor.cs b/Svelto.ECS/DynamicEntityDescriptor.cs index 4fcf164..f40d3e7 100644 --- a/Svelto.ECS/DynamicEntityDescriptor.cs +++ b/Svelto.ECS/DynamicEntityDescriptor.cs @@ -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 /// /// - public struct DynamicEntityDescriptor:IEntityDescriptor where TType : IEntityDescriptor, new() + public struct DynamicEntityDescriptor : 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.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 + ( + new EntityStructInfoView + { + entitiesToBuild = _entitiesToBuild + } + ); + } + + public DynamicEntityDescriptor(IEntityBuilder[] extraEntityBuilders) : this() + { + var extraEntitiesLength = extraEntityBuilders.Length; + + _entitiesToBuild = Construct(extraEntitiesLength, extraEntityBuilders, + EntityDescriptorTemplate.descriptor.entitiesToBuild); + } + + public DynamicEntityDescriptor(FasterList extraEntityBuilders) : this() + { + var extraEntities = extraEntityBuilders.ToArrayFast(); + var extraEntitiesLength = extraEntityBuilders.Count; + + _entitiesToBuild = Construct(extraEntitiesLength, extraEntities, + EntityDescriptorTemplate.descriptor.entitiesToBuild); + } + + public void ExtendWith() where T : IEntityDescriptor, new() + { + var newEntitiesToBuild = EntityDescriptorTemplate.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 + ( + 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 + 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; } } \ No newline at end of file diff --git a/Svelto.ECS/ECSException.cs b/Svelto.ECS/ECSException.cs index 712edd6..b815f84 100644 --- a/Svelto.ECS/ECSException.cs +++ b/Svelto.ECS/ECSException.cs @@ -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("".FastConcat(message, "")) {} - public ECSException(string message, Exception innerE):base(message, innerE) + public ECSException(string message, Exception innerE):base("".FastConcat(message, ""), innerE) {} } } \ No newline at end of file diff --git a/Svelto.ECS.Components/ECSResources/ECSResources.cs b/Svelto.ECS/ECSResources/ECSResources.cs similarity index 66% rename from Svelto.ECS.Components/ECSResources/ECSResources.cs rename to Svelto.ECS/ECSResources/ECSResources.cs index 2ebba29..70ba614 100644 --- a/Svelto.ECS.Components/ECSResources/ECSResources.cs +++ b/Svelto.ECS/ECSResources/ECSResources.cs @@ -11,7 +11,12 @@ namespace Svelto.ECS.Experimental static class ResourcesECSDB { - internal static readonly FasterList _resources = new FasterList(); + static readonly FasterList _resources = new FasterList(); + + 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(ref this ECSResources resource, T newText) { if (resource.id != 0) - ResourcesECSDB._resources[(int) resource.id] = newText; + ResourcesECSDB.resources(resource.id) = newText; else resource.id = ResourcesECSDB.ToECS(newText); } - - public static void Set(ref this ECSString resource, string newText) - { - if (resource.id != 0) - ResourcesECSDB._resources[(int) resource.id] = newText; - else - resource.id = ResourcesECSDB.ToECS(newText); - } } } \ No newline at end of file diff --git a/Svelto.ECS/ECSResources/ECSString.cs b/Svelto.ECS/ECSResources/ECSString.cs new file mode 100644 index 0000000..4eac06c --- /dev/null +++ b/Svelto.ECS/ECSResources/ECSString.cs @@ -0,0 +1,38 @@ +using System; + +namespace Svelto.ECS.Experimental +{ + [Serialization.DoNotSerialize] + public struct ECSString:IEquatable + { + uint id; + + public ECSString(string newText) + { + id = ResourcesECSDB.ToECS(newText); + } + + public static implicit operator string(ECSString ecsString) + { + return ResourcesECSDB.FromECS(ecsString.id); + } + + public void Set(string newText) + { + if (id != 0) + ResourcesECSDB.resources(id) = newText; + else + id = ResourcesECSDB.ToECS(newText); + } + + public bool Equals(ECSString other) + { + return other.id == id; + } + + public override string ToString() + { + return ResourcesECSDB.FromECS(id); + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/EGID.cs b/Svelto.ECS/EGID.cs index c466147..4e7d768 100644 --- a/Svelto.ECS/EGID.cs +++ b/Svelto.ECS/EGID.cs @@ -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,IEqualityComparer,IComparable { - 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,IEqualityComparer,IComparable - { - 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 \ No newline at end of file diff --git a/Svelto.ECS/EGIDMapper.cs b/Svelto.ECS/EGIDMapper.cs index 428ba6f..ba2ceb5 100644 --- a/Svelto.ECS/EGIDMapper.cs +++ b/Svelto.ECS/EGIDMapper.cs @@ -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 where T : struct, IEntityStruct { - internal TypeSafeDictionary map; + internal FasterDictionary 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 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(ref map.valuesArray[findIndex], alloc); + } } -} \ No newline at end of file +} + diff --git a/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs b/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs index 79972b4..8625abe 100644 --- a/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs +++ b/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs @@ -1,7 +1,7 @@ -using Svelto.DataStructures.Experimental; -using EntitiesDB = - Svelto.DataStructures.Experimental.FasterDictionary>; +using System; +using System.Collections; +using Svelto.DataStructures; +using Svelto.ECS.Internal; namespace Svelto.ECS { @@ -17,7 +17,9 @@ namespace Svelto.ECS void Swap(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 currentEntitiesCreatedPerGroup; internal FasterDictionary otherEntitiesCreatedPerGroup; - - internal EntitiesDB current; - internal EntitiesDB other; - readonly EntitiesDB _entityViewsToAddBufferA = new EntitiesDB(); - readonly EntitiesDB _entityViewsToAddBufferB = new EntitiesDB(); + internal FasterDictionary, ITypeSafeDictionary>> current; + internal FasterDictionary, ITypeSafeDictionary>> other; + + readonly FasterDictionary, ITypeSafeDictionary>> + _entityViewsToAddBufferA = + new FasterDictionary, ITypeSafeDictionary>>(); + + readonly FasterDictionary, ITypeSafeDictionary>> + _entityViewsToAddBufferB = + new FasterDictionary, ITypeSafeDictionary>>(); readonly FasterDictionary _entitiesCreatedPerGroupA = new FasterDictionary(); readonly FasterDictionary _entitiesCreatedPerGroupB = new FasterDictionary(); @@ -52,7 +59,7 @@ namespace Svelto.ECS { currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA; otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB; - + current = _entityViewsToAddBufferA; other = _entityViewsToAddBufferB; } diff --git a/Svelto.ECS/EnginesRoot.Engines.cs b/Svelto.ECS/EnginesRoot.Engines.cs index 3d0f0ae..8438e5b 100644 --- a/Svelto.ECS/EnginesRoot.Engines.cs +++ b/Svelto.ECS/EnginesRoot.Engines.cs @@ -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); + } + + public void Invoke() + { + if (_weakReference.IsValid) + _weakReference.Target.SubmitEntityViews(); + } + + readonly DataStructures.WeakReference _weakReference; + } /// /// 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(); - _reactiveEnginesAddRemove = new Dictionary>(); - _reactiveEnginesSwap = new Dictionary>(); - _enginesSet = new HashSet(); + _reactiveEnginesAddRemove = new FasterDictionary, FasterList>(); + _reactiveEnginesSwap = new FasterDictionary, FasterList>(); + _enginesSet = new FasterList(); + _enginesTypeSet = new HashSet(); _disposableEngines = new FasterList(); _transientEntitiesOperations = new FasterList(); - _groupEntityDB = new FasterDictionary>(); - _groupsPerEntity = new Dictionary>(); + _groupEntityViewsDB = new FasterDictionary, ITypeSafeDictionary>>(); + _groupsPerEntity = new FasterDictionary, FasterDictionary>(); _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); + 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(viewEngine, _reactiveEnginesAddRemove); - + CheckEntityViewsEngine(viewEngine, _reactiveEnginesAddRemove); + if (engine is IReactOnSwap viewEngineSwap) - CheckEntityViewsEngine(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(IEngine engine, Dictionary> engines) + + void CheckEntityViewsEngine(T engine, FasterDictionary, FasterList> engines) + where T : class, IEngine { var interfaces = engine.GetType().GetInterfaces(); @@ -89,9 +109,10 @@ namespace Svelto.ECS } static void AddEngine(T engine, Type[] entityViewTypes, - Dictionary> engines) where T:IEngine + FasterDictionary, FasterList> 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 engine, Dictionary> engines, Type type) where T : IEngine + static void AddEngine(T engine, FasterDictionary, FasterList> engines, Type type) + where T : class, IEngine { - if (engines.TryGetValue(type, out var list) == false) + if (engines.TryGetValue(new RefWrapper(type), out var list) == false) { - list = new FasterList(); + list = new FasterList(); - engines.Add(type, list); + engines.Add(new RefWrapper(type), list); } list.Add(engine); } - readonly Dictionary> _reactiveEnginesAddRemove; - readonly Dictionary> _reactiveEnginesSwap; - readonly HashSet _enginesSet; - readonly FasterList _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> _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> _groupsPerEntity; - - readonly EntitiesStream _entitiesStream; - readonly EntitiesDB _entitiesDB; + readonly FasterDictionary, FasterList> _reactiveEnginesAddRemove; + readonly FasterDictionary, FasterList> _reactiveEnginesSwap; + readonly FasterList _disposableEngines; - static readonly Type OBJECT_TYPE = typeof(object); - static readonly Type ENTITY_INFO_VIEW_TYPE = typeof(EntityStructInfoView); + readonly FasterList _enginesSet; + readonly HashSet _enginesTypeSet; } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.Entities.cs b/Svelto.ECS/EnginesRoot.Entities.cs index e34914a..518ac13 100644 --- a/Svelto.ECS/EnginesRoot.Entities.cs +++ b/Svelto.ECS/EnginesRoot.Entities.cs @@ -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(this)); + return new GenericEntityStreamConsumerFactory(this); } public IEntityFactory GenerateEntityFactory() { - return new GenericEntityFactory(new DataStructures.WeakReference(this)); + return new GenericEntityFactory(this); } public IEntityFunctions GenerateEntityFunctions() { - return new GenericEntityFunctions(new DataStructures.WeakReference(this)); + return new GenericEntityFunctions(this); } ///-------------------------------------------- [MethodImpl(MethodImplOptions.AggressiveInlining)] - EntityStructInitializer BuildEntity(EGID entityID, object[] implementors) where T : IEntityDescriptor, new() + EntityStructInitializer BuildEntity(EGID entityID, IEntityBuilder[] entitiesToBuild, + IEnumerable implementors = null) { - return BuildEntity(entityID, EntityDescriptorTemplate.descriptor, implementors); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - EntityStructInitializer BuildEntity(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(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.descriptor.entitiesToBuild, implementors); - - return new EntityStructInitializer(egid, dic.groups); - } -#endif - ///-------------------------------------------- void Preallocate(uint groupID, uint size) where T : IEntityDescriptor, new() { var entityViewsToBuild = EntityDescriptorTemplate.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(); + if (_groupEntityViewsDB.TryGetValue(groupID, out var group) == false) + group = _groupEntityViewsDB[groupID] = new FasterDictionary, 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(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(); + if (_groupsPerEntity.TryGetValue(refWrapper, out var groupedGroup) == false) + groupedGroup = _groupsPerEntity[refWrapper] = + new FasterDictionary(); 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(EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW), + out var entityInfoViewDic) && (entityInfoViewDic as TypeSafeDictionary).TryGetValue( - fromEntityGID.entityID, out entityInfoView) && (correctEntityDescriptorFound = - entityInfoView.type == originalDescriptorType)) - { - var entitiesToMove = entityInfoView.entitiesToBuild; - - Dictionary toGroup = null; - - if (toEntityGID != null) - { - var toGroupID = toEntityGID.Value.groupID; - - if (_groupEntityDB.TryGetValue(toGroupID, out toGroup) == false) - toGroup = _groupEntityDB[toGroupID] = new Dictionary(); - } - - 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 toGroup = null; - if (toEntityGID != null) - { - var toGroupID = toEntityGID.Value.groupID; - - if (_groupEntityDB.TryGetValue(toGroupID, out toGroup) == false) - toGroup = _groupEntityDB[toGroupID] = new Dictionary(); - } - - 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 toGroup, - ref Dictionary fromGroup, Type entityViewType, in PlatformProfiler profiler) + void MoveEntities(EGID fromEntityGID, EGID? toEntityGID, IEntityBuilder[] entitiesToMove, + FasterDictionary, ITypeSafeDictionary> fromGroup, PlatformProfiler sampler) { - if (fromGroup.TryGetValue(entityViewType, out var fromTypeSafeDictionary) == false) + FasterDictionary, 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, 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(); + void CopyEntityToDictionary(EGID entityGID, EGID toEntityGID, + FasterDictionary, ITypeSafeDictionary> fromGroup, + FasterDictionary, ITypeSafeDictionary> toGroup, Type entityViewType) + { + var wrapper = new RefWrapper(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(); - 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, ITypeSafeDictionary> fromGroup, + FasterDictionary, ITypeSafeDictionary> toGroup, Type entityViewType, + in PlatformProfiler profiler) { - throw new NotImplementedException(); - /* var profiler = new PlatformProfiler(); - using (profiler.StartNewSession("Remove Group Of Entities")) - { - FasterDictionary @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(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, ITypeSafeDictionary> fromGroup, Type entityViewType, + in PlatformProfiler profiler) { - using (var profiler = new PlatformProfiler("Remove Group")) + var refWrapper = new RefWrapper(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) + /// + /// Todo: I should keep the group, but I need to mark the group as deleted for the Exist function to work + /// + /// + /// + 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 GenerateConsumer(string name, int capacity) where T : unmanaged, IEntityStruct + internal Consumer GenerateConsumer(string name, uint capacity) where T : unmanaged, IEntityStruct { return _entitiesStream.GenerateConsumer(name, capacity); } - - public Consumer GenerateConsumer(ExclusiveGroup @group, string name, int capacity) where T : unmanaged, + + public Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) where T : unmanaged, IEntityStruct { - return _entitiesStream.GenerateConsumer(@group, name, capacity); + return _entitiesStream.GenerateConsumer(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, 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, FasterDictionary> _groupsPerEntity; + + readonly EntitiesDB _entitiesDB; + readonly EntitiesStream _entitiesStream; } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs b/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs index e3164e5..8308e86 100644 --- a/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs +++ b/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs @@ -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 weakReference) + public GenericEntityFactory(EnginesRoot weakReference) { - _weakEngine = weakReference; + _enginesRoot = new WeakReference(weakReference); } - public EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, object[] implementors) where T : IEntityDescriptor, new() + public EntityStructInitializer BuildEntity(uint entityID, + ExclusiveGroup.ExclusiveGroupStruct groupStructId, IEnumerable implementors = null) + where T : IEntityDescriptor, new() { - return _weakEngine.Target.BuildEntity(new EGID(entityID, groupStructId), implementors); + return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId), + EntityDescriptorTemplate.descriptor.entitiesToBuild, implementors); } - public EntityStructInitializer BuildEntity(EGID egid, object[] implementors) where T : IEntityDescriptor, new() + public EntityStructInitializer BuildEntity(EGID egid, IEnumerable implementors = null) + where T : IEntityDescriptor, new() { - return _weakEngine.Target.BuildEntity(egid, implementors); + return _enginesRoot.Target.BuildEntity(egid, + EntityDescriptorTemplate.descriptor.entitiesToBuild, implementors); } -#if REAL_ID - public EntityStructInitializer BuildEntity(ExclusiveGroup.ExclusiveGroupStruct groupID, object[] implementors = null) where T : IEntityDescriptor, new() + public EntityStructInitializer BuildEntity(EGID egid, T entityDescriptor, + IEnumerable implementors) + where T : IEntityDescriptor { - return _weakEngine.Target.BuildEntity(groupID, implementors); + return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.entitiesToBuild, implementors); } -#endif - public EntityStructInitializer BuildEntity(EGID egid, T entityDescriptor, object[] implementors) where T:IEntityDescriptor + public EntityStructInitializer BuildEntity(uint entityID, + ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, IEnumerable 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(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(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) where T : IEntityDescriptor, new() + public void PreallocateEntitySpace(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) + where T : IEntityDescriptor, new() { - _weakEngine.Target.Preallocate(groupStructId, size); + _enginesRoot.Target.Preallocate(groupStructId, size); } - - readonly DataStructures.WeakReference _weakEngine; + + //enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside +//engines of other enginesRoot + readonly WeakReference _enginesRoot; } } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs b/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs index 111dccc..d5a2cbd 100644 --- a/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs +++ b/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs @@ -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 { + /// + /// todo: EnginesRoot was a weakreference to give the change to inject + /// entityfunctions from other engines root. It probably should be reverted + /// sealed class GenericEntityFunctions : IEntityFunctions { - readonly DataStructures.WeakReference _weakReference; - - internal GenericEntityFunctions(DataStructures.WeakReference weakReference) + internal GenericEntityFunctions(EnginesRoot weakReference) { - _weakReference = weakReference; + _enginesRoot = new WeakReference(weakReference); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RemoveEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : + public void RemoveEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new() { RemoveEntity(new EGID(entityID, groupID)); @@ -25,46 +27,40 @@ namespace Svelto.ECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveEntity(EGID entityEGID) where T : IEntityDescriptor, new() { - _weakReference.Target.CheckRemoveEntityID(entityEGID, EntityDescriptorTemplate.descriptor); + _enginesRoot.Target.CheckRemoveEntityID(entityEGID); - _weakReference.Target.QueueEntitySubmitOperation( - new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID, - EntityDescriptorTemplate.descriptor.entitiesToBuild, typeof(T))); - } - - - public void RemoveEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) - where T : IEntityDescriptor, new() - { - throw new NotImplementedException(); + _enginesRoot.Target.QueueEntitySubmitOperation( + new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID, + EntityDescriptorTemplate.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(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, - ExclusiveGroup.ExclusiveGroupStruct toGroupID) + public void SwapEntityGroup(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, + ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new() { SwapEntityGroup(new EGID(entityID, fromGroupID), toGroupID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SwapEntityGroup(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) + public void SwapEntityGroup(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new() { - SwapEntityGroup(fromID, new EGID(fromID.entityID, (uint)toGroupID)); + SwapEntityGroup(fromID, new EGID(fromID.entityID, (uint) toGroupID)); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SwapEntityGroup(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID - , ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) + public void SwapEntityGroup(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID + , ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new() { if (fromID.groupID != mustBeFromGroup) @@ -72,55 +68,63 @@ namespace Svelto.ECS SwapEntityGroup(fromID, toGroupID); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SwapEntityGroup(EGID fromID, EGID toID) - where T : IEntityDescriptor, new() - { - _weakReference.Target.QueueEntitySubmitOperation( - new EntitySubmitOperation(EntitySubmitOperationType.Swap, - fromID, toID, EntityDescriptorTemplate.descriptor.entitiesToBuild, typeof(T))); - } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SwapEntityGroup(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(fromID, toID); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SwapEntityGroup(EGID fromID, EGID toID) + where T : IEntityDescriptor, new() + { + _enginesRoot.Target.CheckRemoveEntityID(fromID); + _enginesRoot.Target.CheckAddEntityID(toID); + + _enginesRoot.Target.QueueEntitySubmitOperation( + new EntitySubmitOperation(EntitySubmitOperationType.Swap, + fromID, toID, EntityDescriptorTemplate.descriptor.entitiesToBuild)); + } + + //enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside +//engines of other enginesRoot + readonly WeakReference _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(EntitySubmitOperation entitySubmitOperation) where T:IEntityDescriptor + void QueueEntitySubmitOperation(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); } } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.Submission.cs b/Svelto.ECS/EnginesRoot.Submission.cs index fa1daeb..8ecaa15 100644 --- a/Svelto.ECS/EnginesRoot.Submission.cs +++ b/Svelto.ECS/EnginesRoot.Submission.cs @@ -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 _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(); - + if (_groupEntityViewsDB.TryGetValue(groupID, out var groupDB) == false) + groupDB = _groupEntityViewsDB[groupID] = + new FasterDictionary, 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); + 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(); - + if (_groupsPerEntity.TryGetValue(wrapper, out var groupedGroup) == false) + groupedGroup = _groupsPerEntity[wrapper] = new FasterDictionary(); + 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(entityViewsToSubmit.Key)]; + + entityViewsToSubmit.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, realDic, in profiler, + new ExclusiveGroup.ExclusiveGroupStruct(groupToSubmit.Key)); } } } } - readonly DoubleBufferedEntitiesToAdd _groupedEntityToAdd; + DoubleBufferedEntitiesToAdd _groupedEntityToAdd; readonly IEntitySubmissionScheduler _scheduler; readonly FasterDictionary _entitiesOperations; - - //temp } } \ No newline at end of file diff --git a/Svelto.ECS/EntitiesDB.cs b/Svelto.ECS/EntitiesDB.cs index 490d25c..fb49c7a 100644 --- a/Svelto.ECS/EntitiesDB.cs +++ b/Svelto.ECS/EntitiesDB.cs @@ -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> groupEntityViewsDB, - Dictionary> groupsPerEntity, EntitiesStream entityStream) + internal EntitiesDB( + FasterDictionary, ITypeSafeDictionary>> groupEntityViewsDB, + FasterDictionary, FasterDictionary> groupsPerEntity, + EntitiesStream entityStream) { _groupEntityViewsDB = groupEntityViewsDB; _groupsPerEntity = groupsPerEntity; @@ -21,35 +21,40 @@ namespace Svelto.ECS.Internal } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T QueryUniqueEntity(ExclusiveGroup.ExclusiveGroupStruct @group) where T : struct, IEntityStruct + public ref T QueryUniqueEntity(ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct { - var entities = QueryEntities(@group, out var count); + var entities = QueryEntities(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(EGID entityGID) where T : struct, IEntityStruct { - T[] array; + T[] array; if ((array = QueryEntitiesAndIndexInternal(entityGID, out var index)) != null) return ref array[index]; throw new EntityNotFoundException(entityGID, typeof(T)); } - public ref T QueryEntity(uint id, ExclusiveGroup.ExclusiveGroupStruct @group) where T : struct, IEntityStruct + public ref T QueryEntity(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct { - return ref QueryEntity(new EGID(id, @group)); + return ref QueryEntity(new EGID(id, group)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) where T : struct, IEntityStruct + public T[] QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) + where T : struct, IEntityStruct { - uint @group = groupStruct; + uint group = groupStruct; count = 0; - if (QueryEntitySafeDictionary(@group, out TypeSafeDictionary typeSafeDictionary) == false) + if (SafeQueryEntityDictionary(group, out TypeSafeDictionary typeSafeDictionary) == false) return RetrieveEmptyEntityViewArray(); return typeSafeDictionary.GetValuesArray(out count); @@ -61,6 +66,18 @@ namespace Svelto.ECS.Internal return new EntityCollection(QueryEntities(groupStruct, out var count), count); } + public EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct + { + return new EntityCollection(QueryEntities(groupStruct, out var count), count); + } + + public EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct + { + return new EntityCollection(QueryEntities(groupStruct, out var count), count); + } + public EntityCollections QueryEntities(ExclusiveGroup[] groups) where T : struct, IEntityStruct { return new EntityCollections(this, groups); @@ -74,17 +91,21 @@ namespace Svelto.ECS.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public (T1[], T2[]) QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) - where T1 : struct, IEntityStruct + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct { - var T1entities = QueryEntities(@groupStruct, out var countCheck); - var T2entities = QueryEntities(@groupStruct, out count); + var T1entities = QueryEntities(groupStruct, out var countCheck); + var T2entities = QueryEntities(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 (ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct { - var T1entities = QueryEntities(@groupStruct, out var countCheck1); - var T2entities = QueryEntities(@groupStruct, out var countCheck2); - var T3entities = QueryEntities(@groupStruct, out count); + var T1entities = QueryEntities(groupStruct, out var countCheck1); + var T2entities = QueryEntities(groupStruct, out var countCheck2); + var T3entities = QueryEntities(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 QueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId) where T : struct, IEntityStruct + public EGIDMapper QueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId) + where T : struct, IEntityStruct { - uint groupId = groupStructId; - if (QueryEntitySafeDictionary(groupId, out TypeSafeDictionary typeSafeDictionary) == false) - throw new EntityGroupNotFoundException(groupId, typeof(T)); + if (SafeQueryEntityDictionary(groupStructId, out TypeSafeDictionary typeSafeDictionary) == false) + throw new EntityGroupNotFoundException(groupStructId, typeof(T)); EGIDMapper mapper; mapper.map = typeSafeDictionary; - typeSafeDictionary.GetValuesArray(out _); - return mapper; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryQueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId, + out EGIDMapper mapper) + where T : struct, IEntityStruct + { + mapper = default; + if (SafeQueryEntityDictionary(groupStructId, out TypeSafeDictionary typeSafeDictionary) == false) + return false; + + mapper.map = typeSafeDictionary; + + return true; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T[] QueryEntitiesAndIndex(EGID entityGID, out uint index) where T : struct, IEntityStruct { @@ -133,7 +168,8 @@ namespace Svelto.ECS.Internal } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) where T : struct, IEntityStruct + public bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) + where T : struct, IEntityStruct { if ((array = QueryEntitiesAndIndexInternal(entityGid, out index)) != null) return true; @@ -142,13 +178,15 @@ namespace Svelto.ECS.Internal } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] QueryEntitiesAndIndex(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) where T : struct, IEntityStruct + public T[] QueryEntitiesAndIndex(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) + where T : struct, IEntityStruct { return QueryEntitiesAndIndex(new EGID(id, group), out index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryQueryEntitiesAndIndex(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) where T : struct, IEntityStruct + public bool TryQueryEntitiesAndIndex(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(EGID entityGID) where T : struct, IEntityStruct { - if (QueryEntitySafeDictionary(entityGID.groupID, out TypeSafeDictionary casted) == false) return false; + if (SafeQueryEntityDictionary(entityGID.groupID, out TypeSafeDictionary casted) == false) return false; return casted != null && casted.ContainsKey(entityGID.entityID); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Exists(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct + { + if (SafeQueryEntityDictionary(group, out TypeSafeDictionary 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(EGID egid) where T : unmanaged, IEntityStruct { - _entityStream.PublishEntity(ref QueryEntity(egid)); + _entityStream.PublishEntity(ref QueryEntity(egid), egid); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] T[] QueryEntitiesAndIndexInternal(EGID entityGID, out uint index) where T : struct, IEntityStruct { index = 0; - if (QueryEntitySafeDictionary(entityGID.groupID, out TypeSafeDictionary safeDictionary) == false) + if (SafeQueryEntityDictionary(entityGID.groupID, out TypeSafeDictionary 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(uint group, out TypeSafeDictionary typeSafeDictionary) where T : struct, IEntityStruct + bool SafeQueryEntityDictionary(uint group, out TypeSafeDictionary 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.type, out var safeDictionary) == false) + { + typeSafeDictionary = default; return false; + } //return the indexes entities if they exist - typeSafeDictionary = (safeDictionary as TypeSafeDictionary); + typeSafeDictionary = safeDictionary as TypeSafeDictionary; return true; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - static ReadOnlyCollectionStruct RetrieveEmptyEntityViewList() + internal bool UnsafeQueryEntityDictionary(uint group, Type type, out ITypeSafeDictionary typeSafeDictionary) { - var arrayFast = FasterList.DefaultList.ToArrayFast(); + //search for the group + if (_groupEntityViewsDB.TryGetValue(group, out var entitiesInGroupPerType) == false) + { + typeSafeDictionary = null; + return false; + } - return new ReadOnlyCollectionStruct(arrayFast, 0); + //search for the indexed entities in the group + return entitiesInGroupPerType.TryGetValue(new RefWrapper(type), out typeSafeDictionary); } [MethodImpl(MethodImplOptions.AggressiveInlining)] static T[] RetrieveEmptyEntityViewArray() { - return FasterList.DefaultList.ToArrayFast(); + return EmptyList.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> _groupEntityViewsDB; + readonly FasterDictionary, 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> _groupsPerEntity; - readonly EntitiesStream _entityStream; + readonly FasterDictionary, FasterDictionary> _groupsPerEntity; + readonly EntitiesStream _entityStream; + + static class EmptyList + { + internal static readonly T[] emptyArray = new T[0]; + } } -} +} \ No newline at end of file diff --git a/Svelto.ECS/EntityBuilder.CheckFields.cs b/Svelto.ECS/EntityBuilder.CheckFields.cs index c733561..99b7d0e 100644 --- a/Svelto.ECS/EntityBuilder.CheckFields.cs +++ b/Svelto.ECS/EntityBuilder.CheckFields.cs @@ -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())) + { + } } } \ No newline at end of file diff --git a/Svelto.ECS/EntityBuilder.cs b/Svelto.ECS/EntityBuilder.cs index b0de643..78aaffa 100644 --- a/Svelto.ECS/EntityBuilder.cs +++ b/Svelto.ECS/EntityBuilder.cs @@ -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 : IEntityBuilder where T : struct, IEntityStruct { + static class EntityView + { + internal static readonly FasterList>> cachedFields; + internal static readonly Dictionary cachedTypes; +#if DEBUG && !PROFILER + internal static readonly Dictionary> implementorsByType; +#else + internal static readonly Dictionary implementorsByType; +#endif + static EntityView() + { + cachedFields = new FasterList>>(); + + 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.MakeSetter(field); + + cachedFields.Add(new KeyValuePair>(field.FieldType, setter)); + } + + cachedTypes = new Dictionary(); + +#if DEBUG && !PROFILER + implementorsByType = new Dictionary>(); +#else + implementorsByType = new Dictionary(); +#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.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 implementors) { if (dictionary == null) dictionary = new TypeSafeDictionary(); @@ -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.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> implementorsByType = - new Dictionary>(); -#else - readonly Dictionary implementorsByType = new Dictionary(); -#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.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 cachedTypes = new Dictionary(); + readonly T _initializer; static FasterList>> entityViewBlazingFastReflection => - EntityView.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; } } \ No newline at end of file diff --git a/Svelto.ECS/EntityCollection.cs b/Svelto.ECS/EntityCollection.cs index 2521610..355e0e8 100644 --- a/Svelto.ECS/EntityCollection.cs +++ b/Svelto.ECS/EntityCollection.cs @@ -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 + { + 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> + { + 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 Current => new ValueRef(_array, (uint) _index); + + ValueRef IEnumerator>. 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 + { + 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> + { + 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 Current => new ValueRef(_array, (uint) _index); + + ValueRef IEnumerator>.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 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(_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 + public struct EntityGroupsIterator : IEnumerator> { 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(_groups[_indexGroup], out _count); - _array2 = _db.QueryEntities(_groups[_indexGroup], out var count1); + var array1 = _db.QueryEntities(_groups[_indexGroup], out _count); + var array2 = _db.QueryEntities(_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(_groups[0], out _count); - _array2 = _db.QueryEntities(_groups[0], out var count1); + + var array1 = _db.QueryEntities(_groups[0], out _count); + var array2 = _db.QueryEntities(_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 Current + { + get + { + var valueRef = new ValueRef(_array, (uint) _index); + return valueRef; + } + } - ValueRef IEnumerator.Current => throw new NotImplementedException(); + ValueRef IEnumerator>.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 + { + 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 + { + 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]; + } -} \ No newline at end of file +} diff --git a/Svelto.ECS/EntityDescriptorInfo.cs b/Svelto.ECS/EntityDescriptorTemplate.cs similarity index 58% rename from Svelto.ECS/EntityDescriptorInfo.cs rename to Svelto.ECS/EntityDescriptorTemplate.cs index 72000aa..b96e3bd 100644 --- a/Svelto.ECS/EntityDescriptorInfo.cs +++ b/Svelto.ECS/EntityDescriptorTemplate.cs @@ -7,7 +7,11 @@ namespace Svelto.ECS static class EntityDescriptorTemplate where TType : IEntityDescriptor, new() { - internal static readonly StaticEntityDescriptorInfo descriptor - = new StaticEntityDescriptorInfo(new TType()); + static EntityDescriptorTemplate() + { + descriptor = new TType(); + } + + public static IEntityDescriptor descriptor { get; } } } diff --git a/Svelto.ECS/EntityFactory.cs b/Svelto.ECS/EntityFactory.cs index 42674db..3556ae8 100644 --- a/Svelto.ECS/EntityFactory.cs +++ b/Svelto.ECS/EntityFactory.cs @@ -1,71 +1,89 @@ using System; using System.Collections.Generic; +using Svelto.DataStructures; namespace Svelto.ECS.Internal { static class EntityFactory { - internal static Dictionary BuildGroupedEntities(EGID egid, - EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd, IEntityBuilder[] entitiesToBuild, - object[] implementors) + public static FasterDictionary, ITypeSafeDictionary> BuildGroupedEntities(EGID egid, + EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd, + IEntityBuilder[] entitiesToBuild, + IEnumerable implementors) { - var @group = FetchEntityGroup(egid.groupID, groupEntitiesToAdd); + var group = FetchEntityGroup(egid.groupID, groupEntitiesToAdd); BuildEntitiesAndAddToGroup(egid, group, entitiesToBuild, implementors); return group; } - static Dictionary FetchEntityGroup(uint groupID, + static FasterDictionary, ITypeSafeDictionary> FetchEntityGroup(uint groupID, EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityViewsByType) { - if (groupEntityViewsByType.current.TryGetValue(groupID, out Dictionary @group) == - false) + if (groupEntityViewsByType.current.TryGetValue(groupID, out var group) == false) { - @group = new Dictionary(); + group = new FasterDictionary, 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 @group, - IEntityBuilder[] entitiesToBuild, - object[] implementors) + static void BuildEntitiesAndAddToGroup(EGID entityID, + FasterDictionary, ITypeSafeDictionary> group, + IEntityBuilder[] entityBuilders, IEnumerable implementors) { - var count = entitiesToBuild.Length; #if DEBUG && !PROFILER HashSet types = new HashSet(); - +#endif + InternalBuild(entityID, group, entityBuilders, implementors +#if DEBUG && !PROFILER + , types +#endif + ); + } + + static void InternalBuild(EGID entityID, FasterDictionary, ITypeSafeDictionary> group, + IEntityBuilder[] entityBuilders, IEnumerable implementors +#if DEBUG && !PROFILER + , HashSet 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 @group, Type entityViewType, - IEntityBuilder entityBuilder, object[] implementors) + static void BuildEntity(EGID entityID, FasterDictionary, ITypeSafeDictionary> group, + Type entityViewType, IEntityBuilder entityBuilder, IEnumerable implementors) { - var entityViewsPoolWillBeCreated = @group.TryGetValue(entityViewType, out var safeDictionary) == false; + var entityViewsPoolWillBeCreated = + group.TryGetValue(new RefWrapper(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(entityViewType), safeDictionary); } } } \ No newline at end of file diff --git a/Svelto.ECS/EntityGroup.cs b/Svelto.ECS/EntityGroup.cs new file mode 100644 index 0000000..b12c1a9 --- /dev/null +++ b/Svelto.ECS/EntityGroup.cs @@ -0,0 +1,29 @@ +using System; +using Svelto.DataStructures; +using Svelto.ECS.Internal; + +namespace Svelto.ECS +{ + public struct EntityGroup + { + internal EntityGroup(FasterDictionary, ITypeSafeDictionary> entitiesInGroupPerType, uint _groupID) + { + _group = entitiesInGroupPerType; + groupID = _groupID; + } + + public ref T QueryEntity(uint entityGidEntityId) where T : struct, IEntityStruct + { + return ref (_group[new RefWrapper(typeof(T))] as TypeSafeDictionary).GetValueByRef( + entityGidEntityId); + } + + public bool Exists(uint entityGidEntityId) where T : struct, IEntityStruct + { + return (_group[new RefWrapper(typeof(T))] as TypeSafeDictionary).ContainsKey(entityGidEntityId); + } + + readonly FasterDictionary, ITypeSafeDictionary> _group; + public uint groupID; + } +} \ No newline at end of file diff --git a/Svelto.ECS/EntityHierarchyStruct.cs b/Svelto.ECS/EntityHierarchyStruct.cs index 6bf4f48..c0fe044 100644 --- a/Svelto.ECS/EntityHierarchyStruct.cs +++ b/Svelto.ECS/EntityHierarchyStruct.cs @@ -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; } } diff --git a/Svelto.ECS/EntityInfoView.cs b/Svelto.ECS/EntityInfoView.cs index 16d2442..98711f8 100644 --- a/Svelto.ECS/EntityInfoView.cs +++ b/Svelto.ECS/EntityInfoView.cs @@ -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; } } \ No newline at end of file diff --git a/Svelto.ECS/EntityNotFoundException.cs b/Svelto.ECS/EntityNotFoundException.cs index b8de5e4..e2e4fda 100644 --- a/Svelto.ECS/EntityNotFoundException.cs +++ b/Svelto.ECS/EntityNotFoundException.cs @@ -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!") + { + } } } \ No newline at end of file diff --git a/Svelto.ECS/EntityStream.cs b/Svelto.ECS/EntityStream.cs index 14484ec..948d4d7 100644 --- a/Svelto.ECS/EntityStream.cs +++ b/Svelto.ECS/EntityStream.cs @@ -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 /// - class EntitiesStream + class EntitiesStream : IDisposable { - internal Consumer GenerateConsumer(string name, int capacity) where T : unmanaged, IEntityStruct + internal Consumer GenerateConsumer(string name, uint capacity) where T : unmanaged, IEntityStruct { if (_streams.ContainsKey(typeof(T)) == false) _streams[typeof(T)] = new EntityStream(); return (_streams[typeof(T)] as EntityStream).GenerateConsumer(name, capacity); } - - public Consumer GenerateConsumer(ExclusiveGroup @group, string name, int capacity) where T : unmanaged, IEntityStruct + + public Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) + where T : unmanaged, IEntityStruct { if (_streams.ContainsKey(typeof(T)) == false) _streams[typeof(T)] = new EntityStream(); - return (_streams[typeof(T)] as EntityStream).GenerateConsumer( @group, name, capacity); + return (_streams[typeof(T)] as EntityStream).GenerateConsumer(group, name, capacity); } - internal void PublishEntity(ref T entity) where T : unmanaged, IEntityStruct + internal void PublishEntity(ref T entity, EGID egid) where T : unmanaged, IEntityStruct { if (_streams.TryGetValue(typeof(T), out var typeSafeStream)) - (typeSafeStream as EntityStream).PublishEntity(ref entity); + (typeSafeStream as EntityStream).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 _streams = new ConcurrentDictionary(); + + public void Dispose() + { + _streams.Clear(); + } } interface ITypeSafeStream {} - class EntityStream:ITypeSafeStream where T:unmanaged, IEntityStruct + class EntityStream : 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.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 GenerateConsumer(string name, int capacity) + public Consumer GenerateConsumer(string name, uint capacity) { var consumer = new Consumer(name, capacity, this); - + _consumers.Add(consumer); - + return consumer; } - - public Consumer GenerateConsumer(ExclusiveGroup @group, string name, int capacity) + + public Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) { var consumer = new Consumer(group, name, capacity, this); - + _consumers.Add(consumer); - + return consumer; } @@ -92,34 +98,66 @@ namespace Svelto.ECS readonly FasterListThreadSafe> _consumers = new FasterListThreadSafe>(); } - public struct Consumer: IDisposable where T:unmanaged, IEntityStruct + public struct Consumer : IDisposable where T : unmanaged, IEntityStruct { - internal Consumer(string name, int capacity, EntityStream stream): this() + internal Consumer(string name, uint capacity, EntityStream stream):this() { - _ringBuffer = new RingBuffer(capacity); +#if DEBUG && !PROFILER _name = name; +#endif + _ringBuffer = new RingBuffer>((int) capacity, +#if DEBUG && !PROFILER + _name +#else + string.Empty +#endif + ); + _stream = stream; } - - internal Consumer(ExclusiveGroup @group, string name, int capacity, EntityStream stream):this(name, capacity, stream) + + internal Consumer(ExclusiveGroup group, string name, uint capacity, EntityStream 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 _ringBuffer; - readonly EntityStream _stream; - readonly string _name; + readonly RingBuffer> _ringBuffer; + readonly EntityStream _stream; + internal readonly ExclusiveGroup _group; - internal readonly bool _hasGroup; + internal readonly bool _hasGroup; + +#if DEBUG && !PROFILER + readonly string _name; +#endif } -} \ No newline at end of file +} diff --git a/Svelto.ECS/EntityStructInitializer.cs b/Svelto.ECS/EntityStructInitializer.cs index 0b9e88b..ed85d53 100644 --- a/Svelto.ECS/EntityStructInitializer.cs +++ b/Svelto.ECS/EntityStructInitializer.cs @@ -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 @group) + public EntityStructInitializer(EGID id, FasterDictionary, ITypeSafeDictionary> group) { - _group = @group; - ID = id; + _group = group; + _ID = id; } - public void Init(T initializer) where T: struct, IEntityStruct + public void Init(T initializer) where T : struct, IEntityStruct { - if (_group.TryGetValue(EntityBuilder.ENTITY_VIEW_TYPE, out var typeSafeDictionary) == true) - { - var dictionary = typeSafeDictionary as TypeSafeDictionary; + if (_group.TryGetValue(new RefWrapper(EntityBuilder.ENTITY_VIEW_TYPE), + out var typeSafeDictionary) == false) return; - if (EntityBuilder.HAS_EGID) - { - var needEgid = ((INeedEGID) initializer); - needEgid.ID = ID; - initializer = (T) needEgid; - } + var dictionary = (TypeSafeDictionary) typeSafeDictionary; - if (dictionary.TryFindIndex(ID.entityID, out var findElementIndex)) - dictionary.GetDirectValue(findElementIndex) = initializer; - } + if (EntityBuilder.HAS_EGID) + SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref initializer, _ID); + + if (dictionary.TryFindIndex(_ID.entityID, out var findElementIndex)) + dictionary.GetDirectValue(findElementIndex) = initializer; } - readonly EGID ID; - readonly Dictionary _group; + public void CopyFrom(T initializer) where T : struct, IEntityStruct + { + var dictionary = (TypeSafeDictionary) _group[new RefWrapper(EntityBuilder.ENTITY_VIEW_TYPE)]; + + if (EntityBuilder.HAS_EGID) + SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref initializer, _ID); + + dictionary[_ID.entityID] = initializer; + } + + public ref T GetOrCreate() where T : struct, IEntityStruct + { + ref var entityDictionary = ref _group.GetOrCreate(new RefWrapper(EntityBuilder.ENTITY_VIEW_TYPE) + , () => new TypeSafeDictionary()); + var dictionary = (TypeSafeDictionary) entityDictionary; + + return ref dictionary.GetOrCreate(_ID.entityID); + } + + public T Get() where T : struct, IEntityStruct + { + return (_group[new RefWrapper(EntityBuilder.ENTITY_VIEW_TYPE)] as TypeSafeDictionary)[_ID.entityID]; + } + + public bool Has() where T : struct, IEntityStruct + { + if (_group.TryGetValue(new RefWrapper(EntityBuilder.ENTITY_VIEW_TYPE), + out var typeSafeDictionary)) + { + var dictionary = (TypeSafeDictionary) typeSafeDictionary; + + if (dictionary.ContainsKey(_ID.entityID)) + return true; + } + + return false; + } + + public static EntityStructInitializer CreateEmptyInitializer() + { + return new EntityStructInitializer(new EGID(), new FasterDictionary, ITypeSafeDictionary>()); + } + + readonly EGID _ID; + readonly FasterDictionary, ITypeSafeDictionary> _group; } } \ No newline at end of file diff --git a/Svelto.ECS/EntitySubmissionScheduler.cs b/Svelto.ECS/EntitySubmissionScheduler.cs index 93e4b4c..5ddc91a 100644 --- a/Svelto.ECS/EntitySubmissionScheduler.cs +++ b/Svelto.ECS/EntitySubmissionScheduler.cs @@ -1,9 +1,7 @@ -using Svelto.WeakEvents; - namespace Svelto.ECS.Schedulers { public interface IEntitySubmissionScheduler { - WeakAction onTick { set; } + EnginesRoot.EntitiesSubmitter onTick { set; } } } \ No newline at end of file diff --git a/Svelto.ECS/EntitySubmitOperation.cs b/Svelto.ECS/EntitySubmitOperation.cs index 7570474..57299be 100644 --- a/Svelto.ECS/EntitySubmitOperation.cs +++ b/Svelto.ECS/EntitySubmitOperation.cs @@ -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 } diff --git a/Svelto.ECS/EntityView.cs b/Svelto.ECS/EntityView.cs deleted file mode 100644 index f52afa6..0000000 --- a/Svelto.ECS/EntityView.cs +++ /dev/null @@ -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 where T: struct, IEntityStruct - { - internal static readonly FasterList>> cachedFields; - - static EntityView() - { - cachedFields = new FasterList>>(); - - 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 setter = FastInvoke.MakeSetter(field); - - cachedFields.Add(new KeyValuePair>(field.FieldType, setter)); - } - } - - internal static void InitCache() - {} - - internal static void BuildEntityView(out T entityView) - { - entityView = new T {}; - } - } -} \ No newline at end of file diff --git a/Svelto.ECS/EntityViewUtility.cs b/Svelto.ECS/EntityViewUtility.cs index 76190ef..8e4be9b 100644 --- a/Svelto.ECS/EntityViewUtility.cs +++ b/Svelto.ECS/EntityViewUtility.cs @@ -6,7 +6,6 @@ using Svelto.Utilities; namespace Svelto.ECS { - #if DEBUG && !PROFILER struct ECSTuple { @@ -21,7 +20,6 @@ namespace Svelto.ECS } #endif - static class EntityViewUtility { @@ -29,7 +27,7 @@ namespace Svelto.ECS , ref T entityView , FasterList>> entityViewBlazingFastReflection - , object[] implementors, + , IEnumerable implementors, #if DEBUG && !PROFILER Dictionary> implementorsByType #else @@ -40,13 +38,10 @@ namespace Svelto.ECS { //efficient way to collect the fields of every EntityViewType var setters = - FasterList>> - .NoVirt.ToArrayFast(entityViewBlazingFastReflection, out var count); + FasterList>>.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 = - "Svelto.ECS the same component is implemented with more than one implementor. This is " + + "Svelto.ECS the same component is implemented with more than one implementor. This is " + "considered an error and MUST be fixed. "; const string NULL_IMPLEMENTOR_ERROR = - "Svelto.ECS Null implementor, please be careful about the implementors passed to avoid " + + "Svelto.ECS Null implementor, please be careful about the implementors passed to avoid " + "performance loss "; - const string NOT_FOUND_EXCEPTION = "Svelto.ECS Implementor not found for an EntityView. "; + const string NOT_FOUND_EXCEPTION = "Svelto.ECS Implementor not found for an EntityView. "; } } \ No newline at end of file diff --git a/Svelto.ECS/ExclusiveGroup.cs b/Svelto.ECS/ExclusiveGroup.cs index 2b4ef91..959f7cc 100644 --- a/Svelto.ECS/ExclusiveGroup.cs +++ b/Svelto.ECS/ExclusiveGroup.cs @@ -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 /// } /// /// + + ///use this like: + /// public class TriggersGroup : ExclusiveGroup {} + public abstract class NamedExclusiveGroup: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, IComparable, IEqualityComparer @@ -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 _serialisedGroups = new Dictionary(); + static readonly Dictionary _serialisedGroups = new Dictionary(); +#if DEBUG + readonly ushort _range; +#endif } -} \ No newline at end of file +} + +#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>(); + + 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($"{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(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 \ No newline at end of file diff --git a/Svelto.ECS/ExecuteOnEntitiesDB.cs b/Svelto.ECS/ExecuteOnEntitiesDB.cs index de3a6b0..375dfb4 100644 --- a/Svelto.ECS/ExecuteOnEntitiesDB.cs +++ b/Svelto.ECS/ExecuteOnEntitiesDB.cs @@ -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), out var dictionary)) { - foreach (var pair in dic) + foreach (var pair in dictionary) { var entities = (pair.Value as TypeSafeDictionary).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), out var dic)) { foreach (var pair in dic) { @@ -38,5 +39,23 @@ namespace Svelto.ECS.Internal } } } + + public void ExecuteOnAllEntities + (ref W value, ExecuteOnAllEntitiesAction action) + where T : struct, IEntityStruct + { + var type = typeof(T); + + if (_groupsPerEntity.TryGetValue(new RefWrapper(type), out var dic)) + { + foreach (var pair in dic) + { + var entities = (pair.Value as TypeSafeDictionary).GetValuesArray(out var innerCount); + + if (innerCount > 0) + action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this, ref value); + } + } + } } } \ No newline at end of file diff --git a/Svelto.ECS/ExtendibleEntityDescriptor.cs b/Svelto.ECS/ExtendibleEntityDescriptor.cs index 170f8a8..3b420fd 100644 --- a/Svelto.ECS/ExtendibleEntityDescriptor.cs +++ b/Svelto.ECS/ExtendibleEntityDescriptor.cs @@ -1,3 +1,6 @@ +using System; +using Svelto.ECS.Serialization; + namespace Svelto.ECS { /// @@ -5,15 +8,41 @@ namespace Svelto.ECS /// to swap and remove specialized entities from abstract engines /// /// - public abstract class ExtendibleEntityDescriptor:IEntityDescriptor where TType : IEntityDescriptor, new() + public class ExtendibleEntityDescriptor : 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(extraEntities); } + public ExtendibleEntityDescriptor() + { + _dynamicDescriptor = new DynamicEntityDescriptor(true); + } + + public ExtendibleEntityDescriptor ExtendWith() where T : IEntityDescriptor, new() + { + _dynamicDescriptor.ExtendWith(); + + return this; + } + + public ExtendibleEntityDescriptor ExtendWith(IEntityBuilder[] extraEntities) + { + _dynamicDescriptor.ExtendWith(extraEntities); + + return this; + } + public IEntityBuilder[] entitiesToBuild => _dynamicDescriptor.entitiesToBuild; - readonly DynamicEntityDescriptor _dynamicDescriptor; + DynamicEntityDescriptor _dynamicDescriptor; } } \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs b/Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs index 85654a4..28b6898 100644 --- a/Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs +++ b/Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs @@ -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 } } diff --git a/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs b/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs index 61167f1..160615d 100644 --- a/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs +++ b/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs @@ -5,7 +5,8 @@ namespace Svelto.ECS.Unity { public static class SveltoGUIHelper { - public static T CreateFromPrefab(ref uint startIndex, Transform contextHolder, IEntityFactory factory, ExclusiveGroup group) where T : MonoBehaviour, IEntityDescriptorHolder + public static T CreateFromPrefab(ref uint startIndex, Transform contextHolder, IEntityFactory factory, + ExclusiveGroup group, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder { var holder = Create(new EGID(startIndex++, group), contextHolder, factory); var childs = contextHolder.GetComponentsInChildren(true); @@ -14,18 +15,28 @@ namespace Svelto.ECS.Unity { if (child.GetType() != typeof(T)) { - var childImplementors = (child as MonoBehaviour).GetComponents(); - startIndex = InternalBuildAll(startIndex, child, factory, group, childImplementors); + var monoBehaviour = child as MonoBehaviour; + var childImplementors = monoBehaviour.GetComponents(); + startIndex = InternalBuildAll( + startIndex, + child, + factory, + group, + childImplementors, + groupNamePostfix); } } return holder; } - public static T Create(EGID ID, Transform contextHolder, - IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder + public static T Create(EGID ID, Transform contextHolder, IEntityFactory factory) + where T : MonoBehaviour, IEntityDescriptorHolder { var holder = contextHolder.GetComponentInChildren(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(); factory.BuildEntity(ID, holder.GetDescriptor(), implementors); @@ -34,7 +45,8 @@ namespace Svelto.ECS.Unity } public static EntityStructInitializer CreateWithEntity(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(true); var implementors = holder.GetComponents(); @@ -42,8 +54,8 @@ namespace Svelto.ECS.Unity return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); } - public static uint CreateAll(uint startIndex, ExclusiveGroup group, Transform contextHolder, - IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder + public static uint CreateAll(uint startIndex, ExclusiveGroup group, + Transform contextHolder, IEntityFactory factory, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder { var holders = contextHolder.GetComponentsInChildren(true); @@ -51,18 +63,23 @@ namespace Svelto.ECS.Unity { var implementors = holder.GetComponents(); - 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 \ No newline at end of file +#endif diff --git a/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs b/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs index 2a2ba0a..be60f78 100644 --- a/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs +++ b/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs @@ -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(); - } + if (_scheduler == null) _scheduler = new GameObject(_name).AddComponent(); + _scheduler.onTick = value; } } Scheduler _scheduler; - string _name; + readonly string _name; } } #endif \ No newline at end of file diff --git a/Svelto.ECS/GenericentityStreamConsumerFactory.cs b/Svelto.ECS/GenericentityStreamConsumerFactory.cs index 45369b2..4fc0269 100644 --- a/Svelto.ECS/GenericentityStreamConsumerFactory.cs +++ b/Svelto.ECS/GenericentityStreamConsumerFactory.cs @@ -1,29 +1,33 @@ +using Svelto.DataStructures; + namespace Svelto.ECS { - class GenericentityStreamConsumerFactory : IEntityStreamConsumerFactory + class GenericEntityStreamConsumerFactory : IEntityStreamConsumerFactory { - public GenericentityStreamConsumerFactory(DataStructures.WeakReference weakReference) + public GenericEntityStreamConsumerFactory(EnginesRoot weakReference) { - _enginesRoot = weakReference; + _enginesRoot = new WeakReference(weakReference); } - public Consumer GenerateConsumer(string name, int capacity) where T : unmanaged, IEntityStruct + public Consumer GenerateConsumer(string name, uint capacity) where T : unmanaged, IEntityStruct { return _enginesRoot.Target.GenerateConsumer(name, capacity); } - public Consumer GenerateConsumer(ExclusiveGroup group, string name, int capacity) where T : unmanaged, IEntityStruct + public Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) where T : unmanaged, IEntityStruct { return _enginesRoot.Target.GenerateConsumer(group, name, capacity); } - readonly DataStructures.WeakReference _enginesRoot; +//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside +//engines of other enginesRoot + readonly WeakReference _enginesRoot; } public interface IEntityStreamConsumerFactory { - Consumer GenerateConsumer(string name, int capacity) where T : unmanaged, IEntityStruct; - Consumer GenerateConsumer(ExclusiveGroup group, string name, int capacity) + Consumer GenerateConsumer(string name, uint capacity) where T : unmanaged, IEntityStruct; + Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) where T : unmanaged, IEntityStruct; } } \ No newline at end of file diff --git a/Svelto.ECS/IEntitiesDB.cs b/Svelto.ECS/IEntitiesDB.cs index ca5b7c1..b2734a2 100644 --- a/Svelto.ECS/IEntitiesDB.cs +++ b/Svelto.ECS/IEntitiesDB.cs @@ -2,17 +2,21 @@ using System; namespace Svelto.ECS { + public delegate void ExecuteOnAllEntitiesAction(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 /////////////////////////////////////////////////// - + /// - /// Fast and raw return of entities buffer. + /// Fast and raw return of entities buffer. /// + /// /// /// /// @@ -22,7 +26,7 @@ namespace Svelto.ECS where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; (T1[], T2[], T3[]) QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct; - + /// /// return entities that can be iterated through the EntityCollection iterator /// @@ -31,29 +35,33 @@ namespace Svelto.ECS /// EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; - + EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; + EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) + where T1 : struct, IEntityStruct + where T2 : struct, IEntityStruct + where T3 : struct, IEntityStruct; + /// /// return entities found in multiple groups, that can be iterated through the EntityCollection iterator /// This method is useful to write abstracted engines /// - /// /// /// EntityCollections QueryEntities(ExclusiveGroup[] groups) where T : struct, IEntityStruct; EntityCollections QueryEntities(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 /////////////////////////////////////////////////// /// /// 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, /// - /// /// /// void ExecuteOnAllEntities(Action action) @@ -66,18 +74,20 @@ namespace Svelto.ECS /// /// /// - void ExecuteOnAllEntities(W value, - Action action) + void ExecuteOnAllEntities(ref W value, ExecuteOnAllEntitiesAction action) + where T : struct, IEntityStruct; + + void ExecuteOnAllEntities(W value, Action 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! /////////////////////////////////////////////////// - + /// /// 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 /// /// ref T QueryUniqueEntity(ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct; - + /// /// return a specific entity by reference. /// @@ -99,8 +109,8 @@ namespace Svelto.ECS /// /// ///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 /// (MockupRenderingGroups.GhostCubeID, out var index)[index]; ///ref var ghostScaling = ref entitiesDB.QueryEntities @@ -118,9 +128,9 @@ namespace Svelto.ECS T[] QueryEntitiesAndIndex(EGID entityGid, out uint index) where T : struct, IEntityStruct; T[] QueryEntitiesAndIndex(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) where T : struct, IEntityStruct; - + /// - /// Like QueryEntitiesAndIndex and only way to get an index only if exists + /// Like QueryEntitiesAndIndex and only way to get an index only if exists /// /// /// @@ -130,24 +140,24 @@ namespace Svelto.ECS bool TryQueryEntitiesAndIndex (uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) where T : struct, IEntityStruct; - + /// /// 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 /// - /// - /// /// /// EGIDMapper QueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId) where T : struct, IEntityStruct; - + bool TryQueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId, out EGIDMapper mapper) + where T : struct, IEntityStruct; + /////////////////////////////////////////////////// - /// Utility methods + // Utility methods /////////////////////////////////////////////////// - + /// /// check if a specific entity exists /// @@ -155,12 +165,13 @@ namespace Svelto.ECS /// /// bool Exists(EGID egid) where T : struct, IEntityStruct; + bool Exists(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct; bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid); /// /// know if there is any entity struct in a specific group /// - /// + /// /// /// bool HasAny(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; @@ -172,39 +183,11 @@ namespace Svelto.ECS /// /// uint Count(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 - /////////////////////////////////////////////////// /// - /// 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(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(egid) = new ButtonEntityStruct(egid, value); -/// -/// entitiesDB.PublishEntityChange(egid); -/// } /// /// /// void PublishEntityChange(EGID egid) where T : unmanaged, IEntityStruct; - } -} \ No newline at end of file +} diff --git a/Svelto.ECS/IEntityBuilder.cs b/Svelto.ECS/IEntityBuilder.cs index b349b0b..da7b527 100644 --- a/Svelto.ECS/IEntityBuilder.cs +++ b/Svelto.ECS/IEntityBuilder.cs @@ -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 implementors); ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size); Type GetEntityType(); diff --git a/Svelto.ECS/IEntityFactory.cs b/Svelto.ECS/IEntityFactory.cs index 4d7e8a1..14b7e89 100644 --- a/Svelto.ECS/IEntityFactory.cs +++ b/Svelto.ECS/IEntityFactory.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace Svelto.ECS { /// @@ -12,7 +14,7 @@ namespace Svelto.ECS ///{ /// public static readonly ExclusiveGroups PlayerEntitiesGroup = new ExclusiveGroups(); ///} - /// + /// /// public interface IEntityFactory { @@ -25,7 +27,7 @@ namespace Svelto.ECS /// void PreallocateEntitySpace(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) where T : IEntityDescriptor, new(); - + /// /// 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 /// /// EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, - object[] implementors = null) + IEnumerable implementors = null) where T : IEntityDescriptor, new(); - EntityStructInitializer BuildEntity(EGID egid, object[] implementors = null) + + EntityStructInitializer BuildEntity(EGID egid, IEnumerable implementors = null) where T:IEntityDescriptor, new(); -#if REAL_ID - /// - /// BuildEntity version without specifying the entity ID. The complete EGID will be found inside - /// the EntityStructInitializer and/or the single entity components - /// - /// - /// - /// - /// - EntityStructInitializer BuildEntity(ExclusiveGroup.ExclusiveGroupStruct groupID, object[] implementors = null) - where T : IEntityDescriptor, new(); -#endif - /// /// 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 /// /// /// - /// - EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, - T descriptorEntity, - object[] implementors = null) - where T : IEntityDescriptor; - EntityStructInitializer BuildEntity(EGID egid, T entityDescriptor, object[] implementors = null) + /// + EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, + T descriptorEntity, IEnumerable implementors = null) where T : IEntityDescriptor; + + EntityStructInitializer BuildEntity(EGID egid, T entityDescriptor, IEnumerable implementors = null) where T : IEntityDescriptor; } } diff --git a/Svelto.ECS/IEntityFunctions.cs b/Svelto.ECS/IEntityFunctions.cs index 49c28ac..9d04e0d 100644 --- a/Svelto.ECS/IEntityFunctions.cs +++ b/Svelto.ECS/IEntityFunctions.cs @@ -8,8 +8,6 @@ namespace Svelto.ECS void RemoveEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); void RemoveEntity(EGID entityegid) where T : IEntityDescriptor, new(); - void RemoveEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); - void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID); void SwapEntityGroup(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new(); diff --git a/Svelto.ECS/IEntityStruct.cs b/Svelto.ECS/IEntityStruct.cs index 02308a8..379b4b4 100644 --- a/Svelto.ECS/IEntityStruct.cs +++ b/Svelto.ECS/IEntityStruct.cs @@ -1,9 +1,13 @@ namespace Svelto.ECS { - ///EntityStruct MUST implement IEntiyStruct + ///EntityStruct MUST implement IEntityStruct public interface IEntityStruct - {} - + { + } + + /// + /// use INeedEGID on an IEntityStruct only if you need the EGID + /// public interface INeedEGID { EGID ID { get; set; } diff --git a/Svelto.ECS/IReactOnAddAndRemove.cs b/Svelto.ECS/IReactOnAddAndRemove.cs index 7323984..0e35eb7 100644 --- a/Svelto.ECS/IReactOnAddAndRemove.cs +++ b/Svelto.ECS/IReactOnAddAndRemove.cs @@ -4,7 +4,7 @@ namespace Svelto.ECS { public interface IReactOnAddAndRemove : 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); } } \ No newline at end of file diff --git a/Svelto.ECS/IReactOnSwap.cs b/Svelto.ECS/IReactOnSwap.cs index 0d5722c..e9d0f0d 100644 --- a/Svelto.ECS/IReactOnSwap.cs +++ b/Svelto.ECS/IReactOnSwap.cs @@ -4,7 +4,9 @@ namespace Svelto.ECS { public interface IReactOnSwap : 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 } } \ No newline at end of file diff --git a/Svelto.ECS/Sequencer.cs b/Svelto.ECS/Sequencer.cs index 0b0a25d..da67c6f 100644 --- a/Svelto.ECS/Sequencer.cs +++ b/Svelto.ECS/Sequencer.cs @@ -12,7 +12,7 @@ namespace Svelto.ECS { _steps = new Dictionary(); - 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(IEngine engine, C condition, EGID id) where C:struct, IConvertible { - C branch = condition; + var branch = condition; var to = (_steps._steps[engine] as To); var steps = to._tos[branch]; diff --git a/Svelto.ECS/Serialization/ComposedSerializer.cs b/Svelto.ECS/Serialization/ComposedSerializer.cs new file mode 100644 index 0000000..c78bdf3 --- /dev/null +++ b/Svelto.ECS/Serialization/ComposedSerializer.cs @@ -0,0 +1,43 @@ +using System; + +namespace Svelto.ECS.Serialization +{ + public class ComposedSerializer : ISerializer + where T : unmanaged, IEntityStruct + where X : class, ISerializer, new() + where Y : class, ISerializer, new() + { + public ComposedSerializer() + { + _serializers = new ISerializer[2]; + _serializers[0] = new X(); + _serializers[1] = new Y(); + } + + public bool Serialize(in T value, ISerializationData serializationData) + { + foreach (ISerializer 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 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[] _serializers; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/DefaultSerializer.cs b/Svelto.ECS/Serialization/DefaultSerializer.cs new file mode 100644 index 0000000..38e905f --- /dev/null +++ b/Svelto.ECS/Serialization/DefaultSerializer.cs @@ -0,0 +1,43 @@ +using Svelto.Common; + +namespace Svelto.ECS.Serialization +{ + public class DefaultSerializer : ISerializer where T : unmanaged, IEntityStruct + { + public static readonly uint SIZEOFT = SerializableEntityBuilder.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.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(serializationData.data.ToArrayFast(), serializationData.dataPos); + + serializationData.dataPos += SIZEOFT; + + return true; + } + } +} diff --git a/Svelto.ECS/Serialization/DefaultSerializerUtils.cs b/Svelto.ECS/Serialization/DefaultSerializerUtils.cs new file mode 100644 index 0000000..fcc600d --- /dev/null +++ b/Svelto.ECS/Serialization/DefaultSerializerUtils.cs @@ -0,0 +1,46 @@ +using System; +using Svelto.ECS; + +public static class DefaultSerializerUtils +{ + public static unsafe void CopyToByteArray(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(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; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/DefaultVersioningFactory.cs b/Svelto.ECS/Serialization/DefaultVersioningFactory.cs new file mode 100644 index 0000000..8af213d --- /dev/null +++ b/Svelto.ECS/Serialization/DefaultVersioningFactory.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace Svelto.ECS.Serialization +{ + public class DefaultVersioningFactory : IDeserializationFactory where T : IEntityDescriptor, new() + { + public EntityStructInitializer BuildDeserializedEntity(EGID egid, + ISerializationData serializationData, + ISerializableEntityDescriptor entityDescriptor, + SerializationType serializationType, + IEntitySerialization entitySerialization) + { + var initializer = _factory.BuildEntity(egid, _implementors); + + entitySerialization.DeserializeEntityStructs(serializationData, entityDescriptor, initializer, SerializationType.Storage); + + return initializer; + } + + public DefaultVersioningFactory(IEntityFactory factory) { _factory = factory; } + public DefaultVersioningFactory(IEntityFactory factory, IEnumerable implementors) { _factory = factory; + _implementors = implementors; + } + + readonly IEntityFactory _factory; + readonly IEnumerable _implementors; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/DoNotSerializeAttribute.cs b/Svelto.ECS/Serialization/DoNotSerializeAttribute.cs new file mode 100644 index 0000000..b74485a --- /dev/null +++ b/Svelto.ECS/Serialization/DoNotSerializeAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace Svelto.ECS.Serialization +{ + public class DoNotSerializeAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/DontSerialize.cs b/Svelto.ECS/Serialization/DontSerialize.cs new file mode 100644 index 0000000..f5cba8a --- /dev/null +++ b/Svelto.ECS/Serialization/DontSerialize.cs @@ -0,0 +1,17 @@ +namespace Svelto.ECS.Serialization +{ + public class DontSerialize : ISerializer 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; + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs b/Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs new file mode 100644 index 0000000..011d5a9 --- /dev/null +++ b/Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs @@ -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(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(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(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(IDeserializationFactory deserializationFactory) + where T : ISerializableEntityDescriptor, new() + { + SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; + serializationDescriptorMap.RegisterSerializationFactory(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); + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/EnginesRoot.SerializableEntityHeader.cs b/Svelto.ECS/Serialization/EnginesRoot.SerializableEntityHeader.cs new file mode 100644 index 0000000..32ccc67 --- /dev/null +++ b/Svelto.ECS/Serialization/EnginesRoot.SerializableEntityHeader.cs @@ -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! + } + } +} diff --git a/Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs b/Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs new file mode 100644 index 0000000..6e23c57 --- /dev/null +++ b/Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs @@ -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 + { + /// + /// 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 + /// + internal SerializationDescriptorMap() + { + _descriptors = new Dictionary(); + _factories = new Dictionary(); + + 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 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(IDeserializationFactory deserializationFactory) + where Descriptor : ISerializableEntityDescriptor, new() + { + _factories.Add(SerializationEntityDescriptorTemplate.hash, deserializationFactory); + } + + + readonly Dictionary _descriptors; + readonly Dictionary _factories; + } + + /// + /// The map of serializable entity hashes to the serializable entity builders (to know the entity structs + /// to serialize) + /// + SerializationDescriptorMap serializationDescriptorMap { get; } = new SerializationDescriptorMap(); + } +} diff --git a/Svelto.ECS/Serialization/HashNameAttribute.cs b/Svelto.ECS/Serialization/HashNameAttribute.cs new file mode 100644 index 0000000..f2c6ed2 --- /dev/null +++ b/Svelto.ECS/Serialization/HashNameAttribute.cs @@ -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; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/IDeserializationFactory.cs b/Svelto.ECS/Serialization/IDeserializationFactory.cs new file mode 100644 index 0000000..1045f71 --- /dev/null +++ b/Svelto.ECS/Serialization/IDeserializationFactory.cs @@ -0,0 +1,9 @@ +namespace Svelto.ECS.Serialization +{ + public interface IDeserializationFactory + { + EntityStructInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, + ISerializableEntityDescriptor entityDescriptor, SerializationType serializationType, + IEntitySerialization entitySerialization); + } +} diff --git a/Svelto.ECS/Serialization/IEntitySerialization.cs b/Svelto.ECS/Serialization/IEntitySerialization.cs new file mode 100644 index 0000000..d143280 --- /dev/null +++ b/Svelto.ECS/Serialization/IEntitySerialization.cs @@ -0,0 +1,69 @@ +namespace Svelto.ECS.Serialization +{ + public interface IEntitySerialization + { + /// + /// Fill the serializationData of the entitiesToSerialize of this descriptor + /// + /// + /// + /// + /// Size in bytes of the newly instantiated entity + void SerializeEntity(EGID egid, ISerializationData serializationData, SerializationType serializationType); + + /// + /// Deserialize a serializationData and copy directly onto the appropriate entities + /// + /// + /// + /// + void DeserializeEntity(ISerializationData serializationData, SerializationType serializationType); + + /// + /// Deserialize a serializationData and copy directly onto the appropriate entities with explicit EGID + /// + /// + /// + /// + /// + void DeserializeEntity(EGID egid, ISerializationData serializationData, SerializationType serializationType); + + /// + /// Deserialize a serializationData and copy directly to an previously created EntityStructInitializer + /// + /// + /// + /// + /// + void DeserializeEntityStructs(ISerializationData serializationData, + ISerializableEntityDescriptor entityDescriptor, + in EntityStructInitializer initializer, SerializationType serializationType); + + /// + /// Contrary to the other Deserialize methods that assume that the entity exists, this method is used to deserialise + /// a new Entity + /// + /// + /// + /// + /// + EntityStructInitializer DeserializeNewEntity(EGID egid, ISerializationData serializationData, + SerializationType serializationType); + + /// + /// Special Entity Swap method that works without knowing the EntityDescriptor to swap + /// + /// + /// + void DeserializeEntityToSwap(EGID localEgid, EGID toEgid); + + /// + /// Special Entity delete method that works without knowing the EntityDescriptor to delete + /// + /// + void DeserializeEntityToDelete(EGID egid); + + void RegisterSerializationFactory(IDeserializationFactory deserializationFactory) + where T : ISerializableEntityDescriptor, new(); + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/ISerializableEntityBuilder.cs b/Svelto.ECS/Serialization/ISerializableEntityBuilder.cs new file mode 100644 index 0000000..bc7197e --- /dev/null +++ b/Svelto.ECS/Serialization/ISerializableEntityBuilder.cs @@ -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); + } +} diff --git a/Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs b/Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs new file mode 100644 index 0000000..6d23dd1 --- /dev/null +++ b/Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs @@ -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); + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/ISerializationData.cs b/Svelto.ECS/Serialization/ISerializationData.cs new file mode 100644 index 0000000..852d43a --- /dev/null +++ b/Svelto.ECS/Serialization/ISerializationData.cs @@ -0,0 +1,14 @@ +using Svelto.DataStructures; + +namespace Svelto.ECS +{ + public interface ISerializationData + { + uint dataPos { get; set; } + FasterList data { get; } + + void ReuseAsNew(); + void Reset(); + void BeginNextEntityStruct(); + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/ISerializer.cs b/Svelto.ECS/Serialization/ISerializer.cs new file mode 100644 index 0000000..39fa588 --- /dev/null +++ b/Svelto.ECS/Serialization/ISerializer.cs @@ -0,0 +1,55 @@ +#if DEBUG && !PROFILER +using System; +#endif + +namespace Svelto.ECS.Serialization +{ + public interface ISerializer + 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(this ISerializer 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(this ISerializer 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; + } + } +} diff --git a/Svelto.ECS/Serialization/PartialSerializer.cs b/Svelto.ECS/Serialization/PartialSerializer.cs new file mode 100644 index 0000000..a21348f --- /dev/null +++ b/Svelto.ECS/Serialization/PartialSerializer.cs @@ -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 : ISerializer + 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(myMembers[i].Name); + var sizeOf = (uint)Marshal.SizeOf(myMembers[i].FieldType); + offsets.Add(((uint) offset.ToInt32(), sizeOf)); + totalSize += sizeOf; + } + } + } + + if (myType.GetProperties().Length > (EntityBuilder.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; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/SerializableEntityBuilder.cs b/Svelto.ECS/Serialization/SerializableEntityBuilder.cs new file mode 100644 index 0000000..b5cdaea --- /dev/null +++ b/Svelto.ECS/Serialization/SerializableEntityBuilder.cs @@ -0,0 +1,94 @@ +using System; +using Svelto.Common; +using Svelto.ECS.Internal; + +namespace Svelto.ECS.Serialization +{ + public class SerializableEntityBuilder : EntityBuilder, ISerializableEntityBuilder + where T : unmanaged, IEntityStruct + { + public static readonly uint SIZE = UnsafeUtils.SizeOf(); + + static SerializableEntityBuilder() + {} + + public SerializableEntityBuilder() + { + _serializers = new ISerializer[(int) SerializationType.Length]; + for (int i = 0; i < (int) SerializationType.Length; i++) + { + _serializers[i] = new DefaultSerializer(); + } + } + + public SerializableEntityBuilder(params ValueTuple>[] serializers) + { + _serializers = new ISerializer[(int) SerializationType.Length]; + for (int i = 0; i < serializers.Length; i++) + { + ref (SerializationType, ISerializer) 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(); + } + } + + public void Serialize(uint entityID, ITypeSafeDictionary dictionary, + ISerializationData serializationData, SerializationType serializationType) + { + ISerializer serializer = _serializers[(int)serializationType]; + + var safeDictionary = (TypeSafeDictionary) 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 serializer = _serializers[(int) serializationType]; + + // Handle the case when an entity struct is gone + var safeDictionary = (TypeSafeDictionary) 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 serializer = _serializers[(int) serializationType]; + + serializer.DeserializeSafe(ref initializer.GetOrCreate(), serializationData); + } + + public void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer, + in EntityStructInitializer destinationInitializer) + { + destinationInitializer.CopyFrom(sourceInitializer.Get()); + } + + readonly ISerializer[] _serializers; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/SerializableEntityDescriptor.cs b/Svelto.ECS/Serialization/SerializableEntityDescriptor.cs new file mode 100644 index 0000000..9efb21b --- /dev/null +++ b/Svelto.ECS/Serialization/SerializableEntityDescriptor.cs @@ -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 Hash = Murmur3.MurmurHash3_x86_32; + } + + public abstract class SerializableEntityDescriptor : ISerializableEntityDescriptor + where TType : IEntityDescriptor, new() + { + static SerializableEntityDescriptor() + { + IEntityBuilder[] defaultEntities = EntityDescriptorTemplate.descriptor.entitiesToBuild; + + var hashNameAttribute = _type.GetCustomAttribute(); + 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 + ( + new SerializableEntityStruct + { + descriptorHash = _hash + } + ); + + // If the current serializable is an ExtendibleDescriptor, I have to update it. + if (dynamicIndex != -1) + { + _entitiesToBuild[dynamicIndex] = new EntityBuilder + ( + new EntityStructInfoView + { + entitiesToBuild = _entitiesToBuild + } + ); + } + + ///// + var entitiesToSerialize = new FasterList(); + _entitiesToSerializeMap = new FasterDictionary, ISerializableEntityBuilder>(); + foreach (IEntityBuilder e in defaultEntities) + { + if (e is ISerializableEntityBuilder serializableEntityBuilder) + { + var entityType = serializableEntityBuilder.GetEntityType(); + _entitiesToSerializeMap[new RefWrapper(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, ISerializableEntityBuilder> _entitiesToSerializeMap; + static readonly ISerializableEntityBuilder[] _entitiesToSerialize; + + static readonly uint _hash; + static readonly Type _serializableStructType = typeof(SerializableEntityStruct); + static readonly Type _type = typeof(TType); + } +} diff --git a/Svelto.ECS/Serialization/SerializableEntityStruct.cs b/Svelto.ECS/Serialization/SerializableEntityStruct.cs new file mode 100644 index 0000000..1e10932 --- /dev/null +++ b/Svelto.ECS/Serialization/SerializableEntityStruct.cs @@ -0,0 +1,9 @@ +namespace Svelto.ECS +{ + internal struct SerializableEntityStruct : IEntityStruct, INeedEGID + { + public uint descriptorHash; + + public EGID ID { get; set; } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/SerializationEntityDescriptorTemplate.cs b/Svelto.ECS/Serialization/SerializationEntityDescriptorTemplate.cs new file mode 100644 index 0000000..1b1de3a --- /dev/null +++ b/Svelto.ECS/Serialization/SerializationEntityDescriptorTemplate.cs @@ -0,0 +1,16 @@ +namespace Svelto.ECS.Serialization +{ + public static class SerializationEntityDescriptorTemplate where TType : ISerializableEntityDescriptor, new() + { + static SerializationEntityDescriptorTemplate() + { + var serializableEntityDescriptor = new TType(); + hash = serializableEntityDescriptor.hash; + + entityDescriptor = (ISerializableEntityDescriptor) EntityDescriptorTemplate.descriptor; + } + + public static uint hash { get; } + public static ISerializableEntityDescriptor entityDescriptor { get; } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/SimpleSerializationData.cs b/Svelto.ECS/Serialization/SimpleSerializationData.cs new file mode 100644 index 0000000..71c7d54 --- /dev/null +++ b/Svelto.ECS/Serialization/SimpleSerializationData.cs @@ -0,0 +1,37 @@ +using Svelto.DataStructures; + +namespace Svelto.ECS +{ + public class SimpleSerializationData : ISerializationData + { + public uint dataPos { get; set; } + public FasterList data { get; set; } + + public SimpleSerializationData(FasterList d) + { + data = d; + } + + public void ResetWithNewData(FasterList newData) + { + dataPos = 0; + + data = newData; + } + + public void ReuseAsNew() + { + dataPos = 0; + + data.ResetToReuse(); + } + + public void Reset() + { + dataPos = 0; + } + + public void BeginNextEntityStruct() + {} + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/Unsafe.cs b/Svelto.ECS/Serialization/Unsafe.cs new file mode 100644 index 0000000..dd6ea52 --- /dev/null +++ b/Svelto.ECS/Serialization/Unsafe.cs @@ -0,0 +1,15 @@ +using Svelto.ECS; + +namespace Svelto.Common +{ + public class UnsafeUtils + { + public static uint SizeOf() where T : unmanaged, IEntityStruct + { + unsafe + { + return (uint) sizeof(T); + } + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs b/Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs index d316c5c..7735e1c 100644 --- a/Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs +++ b/Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs @@ -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; } } \ No newline at end of file diff --git a/Svelto.ECS/StaticEntityDescriptorInfo.cs b/Svelto.ECS/StaticEntityDescriptorInfo.cs deleted file mode 100644 index 3acc6df..0000000 --- a/Svelto.ECS/StaticEntityDescriptorInfo.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Svelto.ECS -{ - class StaticEntityDescriptorInfo: IEntityDescriptor where TType : IEntityDescriptor - { - internal StaticEntityDescriptorInfo(TType descriptor) - { - entitiesToBuild = descriptor.entitiesToBuild; - } - - public IEntityBuilder[] entitiesToBuild { get; } - } -} - diff --git a/Svelto.ECS/Svelto.ECS.asmdef b/Svelto.ECS/Svelto.ECS.asmdef index 49133e0..d561d80 100644 --- a/Svelto.ECS/Svelto.ECS.asmdef +++ b/Svelto.ECS/Svelto.ECS.asmdef @@ -6,7 +6,7 @@ "optionalUnityReferences": [], "includePlatforms": [], "excludePlatforms": [], - "allowUnsafeCode": false, + "allowUnsafeCode": true, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, diff --git a/Svelto.ECS/Svelto.ECS.csproj b/Svelto.ECS/Svelto.ECS.csproj index d0f78fe..9fdf31b 100644 --- a/Svelto.ECS/Svelto.ECS.csproj +++ b/Svelto.ECS/Svelto.ECS.csproj @@ -7,15 +7,18 @@ AnyCPU false + true AnyCPU false + true + \ No newline at end of file diff --git a/Svelto.ECS/TypeCache.cs b/Svelto.ECS/TypeCache.cs new file mode 100644 index 0000000..bd10908 --- /dev/null +++ b/Svelto.ECS/TypeCache.cs @@ -0,0 +1,9 @@ +using System; + +namespace Svelto.ECS.Internal +{ + public class TypeCache + { + public static Type type = typeof(T); + } +} \ No newline at end of file diff --git a/Svelto.ECS/UnsafeStructRef.cs b/Svelto.ECS/UnsafeStructRef.cs new file mode 100644 index 0000000..7ace92e --- /dev/null +++ b/Svelto.ECS/UnsafeStructRef.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Svelto.ECS +{ + public struct UnsafeStructRef: 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(pointer); + + public void Dispose() + { + _handle.Free(); + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Utilities.cs b/Svelto.ECS/Utilities.cs new file mode 100644 index 0000000..3229dd9 --- /dev/null +++ b/Svelto.ECS/Utilities.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Svelto.ECS +{ + public static class Utilities + { + public static UnsafeStructRef ToUnsafeRef(this T[] entityStructs, uint index) where T : struct, IEntityStruct + { + var alloc = GCHandle.Alloc(entityStructs, GCHandleType.Pinned); + + return new UnsafeStructRef(ref entityStructs[index], alloc); + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/WaitForSubmissionEnumerator.cs b/Svelto.ECS/WaitForSubmissionEnumerator.cs new file mode 100644 index 0000000..d5a1732 --- /dev/null +++ b/Svelto.ECS/WaitForSubmissionEnumerator.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections; + +namespace Svelto.ECS +{ + public class WaitForSubmissionEnumerator : IEnumerator + { + class SubmissionEntityDescriptor : GenericEntityDescriptor + { + 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(new EGID((uint) _counter, + SubmissionEntityDescriptor.SubmissionGroup)); + _state = 1; + return true; + case 1: + if (_entitiesDB.Exists(new EGID((uint) _counter, + SubmissionEntityDescriptor.SubmissionGroup)) == false) + return true; + + _entityFunctions.RemoveEntity(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; + } +} \ No newline at end of file diff --git a/Svelto.ECS/bin/Debug/netstandard2.0/Svelto.ECS.deps.json b/Svelto.ECS/bin/Debug/netstandard2.0/Svelto.ECS.deps.json new file mode 100644 index 0000000..ff5c143 --- /dev/null +++ b/Svelto.ECS/bin/Debug/netstandard2.0/Svelto.ECS.deps.json @@ -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": "" + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.AssemblyInfo.cs b/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.AssemblyInfo.cs new file mode 100644 index 0000000..45755d3 --- /dev/null +++ b/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.AssemblyInfo.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +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. + diff --git a/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.CopyComplete b/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.CopyComplete new file mode 100644 index 0000000..e69de29 diff --git a/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.FileListAbsolute.txt b/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..aad0fa2 --- /dev/null +++ b/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.FileListAbsolute.txt @@ -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 diff --git a/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.dgspec.json b/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.dgspec.json new file mode 100644 index 0000000..a446841 --- /dev/null +++ b/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.dgspec.json @@ -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 + } + } + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.props b/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.props new file mode 100644 index 0000000..7539763 --- /dev/null +++ b/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.props @@ -0,0 +1,15 @@ + + + + True + NuGet + $(MSBuildThisFileDirectory)project.assets.json + $(UserProfile)\.nuget\packages\ + C:\Users\Seb-e-Sam\.nuget\packages\;C:\Program Files\dotnet\sdk\NuGetFallbackFolder + PackageReference + 5.3.0 + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + \ No newline at end of file diff --git a/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.targets b/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.targets new file mode 100644 index 0000000..8f2d2d6 --- /dev/null +++ b/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.targets @@ -0,0 +1,9 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + \ No newline at end of file diff --git a/Svelto.ECS/obj/project.assets.json b/Svelto.ECS/obj/project.assets.json new file mode 100644 index 0000000..1d27c5b --- /dev/null +++ b/Svelto.ECS/obj/project.assets.json @@ -0,0 +1,432 @@ +{ + "version": 3, + "targets": { + ".NETStandard,Version=v2.0": { + "Microsoft.NETCore.Platforms/1.1.0": { + "type": "package", + "compile": { + "lib/netstandard1.0/_._": {} + }, + "runtime": { + "lib/netstandard1.0/_._": {} + } + }, + "NETStandard.Library/2.0.3": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + }, + "compile": { + "lib/netstandard1.0/_._": {} + }, + "runtime": { + "lib/netstandard1.0/_._": {} + }, + "build": { + "build/netstandard2.0/NETStandard.Library.targets": {} + } + }, + "System.Buffers/4.4.0": { + "type": "package", + "compile": { + "ref/netstandard2.0/System.Buffers.dll": {} + }, + "runtime": { + "lib/netstandard2.0/System.Buffers.dll": {} + } + }, + "System.Memory/4.5.2": { + "type": "package", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + }, + "compile": { + "lib/netstandard2.0/System.Memory.dll": {} + }, + "runtime": { + "lib/netstandard2.0/System.Memory.dll": {} + } + }, + "System.Numerics.Vectors/4.4.0": { + "type": "package", + "compile": { + "ref/netstandard2.0/System.Numerics.Vectors.dll": {} + }, + "runtime": { + "lib/netstandard2.0/System.Numerics.Vectors.dll": {} + } + }, + "System.Runtime.CompilerServices.Unsafe/4.6.0-preview8.19405.3": { + "type": "package", + "compile": { + "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": {} + }, + "runtime": { + "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": {} + } + }, + "Svelto.Common/1.0.0": { + "type": "project", + "framework": ".NETStandard,Version=v2.0", + "dependencies": { + "System.Memory": "4.5.2" + }, + "compile": { + "bin/placeholder/Svelto.Common.dll": {} + }, + "runtime": { + "bin/placeholder/Svelto.Common.dll": {} + } + } + } + }, + "libraries": { + "Microsoft.NETCore.Platforms/1.1.0": { + "sha512": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==", + "type": "package", + "path": "microsoft.netcore.platforms/1.1.0", + "files": [ + ".nupkg.metadata", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/netstandard1.0/_._", + "microsoft.netcore.platforms.1.1.0.nupkg.sha512", + "microsoft.netcore.platforms.nuspec", + "runtime.json" + ] + }, + "NETStandard.Library/2.0.3": { + "sha512": "548M6mnBSJWxsIlkQHfbzoYxpiYFXZZSL00p4GHYv8PkiqFBnnT68mW5mGEsA/ch9fDO9GkPgkFQpWiXZN7mAQ==", + "type": "package", + "path": "netstandard.library/2.0.3", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "build/netstandard2.0/NETStandard.Library.targets", + "build/netstandard2.0/ref/Microsoft.Win32.Primitives.dll", + "build/netstandard2.0/ref/System.AppContext.dll", + "build/netstandard2.0/ref/System.Collections.Concurrent.dll", + "build/netstandard2.0/ref/System.Collections.NonGeneric.dll", + "build/netstandard2.0/ref/System.Collections.Specialized.dll", + "build/netstandard2.0/ref/System.Collections.dll", + "build/netstandard2.0/ref/System.ComponentModel.Composition.dll", + "build/netstandard2.0/ref/System.ComponentModel.EventBasedAsync.dll", + "build/netstandard2.0/ref/System.ComponentModel.Primitives.dll", + "build/netstandard2.0/ref/System.ComponentModel.TypeConverter.dll", + "build/netstandard2.0/ref/System.ComponentModel.dll", + "build/netstandard2.0/ref/System.Console.dll", + "build/netstandard2.0/ref/System.Core.dll", + "build/netstandard2.0/ref/System.Data.Common.dll", + "build/netstandard2.0/ref/System.Data.dll", + "build/netstandard2.0/ref/System.Diagnostics.Contracts.dll", + "build/netstandard2.0/ref/System.Diagnostics.Debug.dll", + "build/netstandard2.0/ref/System.Diagnostics.FileVersionInfo.dll", + "build/netstandard2.0/ref/System.Diagnostics.Process.dll", + "build/netstandard2.0/ref/System.Diagnostics.StackTrace.dll", + "build/netstandard2.0/ref/System.Diagnostics.TextWriterTraceListener.dll", + "build/netstandard2.0/ref/System.Diagnostics.Tools.dll", + "build/netstandard2.0/ref/System.Diagnostics.TraceSource.dll", + "build/netstandard2.0/ref/System.Diagnostics.Tracing.dll", + "build/netstandard2.0/ref/System.Drawing.Primitives.dll", + "build/netstandard2.0/ref/System.Drawing.dll", + "build/netstandard2.0/ref/System.Dynamic.Runtime.dll", + "build/netstandard2.0/ref/System.Globalization.Calendars.dll", + "build/netstandard2.0/ref/System.Globalization.Extensions.dll", + "build/netstandard2.0/ref/System.Globalization.dll", + "build/netstandard2.0/ref/System.IO.Compression.FileSystem.dll", + "build/netstandard2.0/ref/System.IO.Compression.ZipFile.dll", + "build/netstandard2.0/ref/System.IO.Compression.dll", + "build/netstandard2.0/ref/System.IO.FileSystem.DriveInfo.dll", + "build/netstandard2.0/ref/System.IO.FileSystem.Primitives.dll", + "build/netstandard2.0/ref/System.IO.FileSystem.Watcher.dll", + "build/netstandard2.0/ref/System.IO.FileSystem.dll", + "build/netstandard2.0/ref/System.IO.IsolatedStorage.dll", + "build/netstandard2.0/ref/System.IO.MemoryMappedFiles.dll", + "build/netstandard2.0/ref/System.IO.Pipes.dll", + "build/netstandard2.0/ref/System.IO.UnmanagedMemoryStream.dll", + "build/netstandard2.0/ref/System.IO.dll", + "build/netstandard2.0/ref/System.Linq.Expressions.dll", + "build/netstandard2.0/ref/System.Linq.Parallel.dll", + "build/netstandard2.0/ref/System.Linq.Queryable.dll", + "build/netstandard2.0/ref/System.Linq.dll", + "build/netstandard2.0/ref/System.Net.Http.dll", + "build/netstandard2.0/ref/System.Net.NameResolution.dll", + "build/netstandard2.0/ref/System.Net.NetworkInformation.dll", + "build/netstandard2.0/ref/System.Net.Ping.dll", + "build/netstandard2.0/ref/System.Net.Primitives.dll", + "build/netstandard2.0/ref/System.Net.Requests.dll", + "build/netstandard2.0/ref/System.Net.Security.dll", + "build/netstandard2.0/ref/System.Net.Sockets.dll", + "build/netstandard2.0/ref/System.Net.WebHeaderCollection.dll", + "build/netstandard2.0/ref/System.Net.WebSockets.Client.dll", + "build/netstandard2.0/ref/System.Net.WebSockets.dll", + "build/netstandard2.0/ref/System.Net.dll", + "build/netstandard2.0/ref/System.Numerics.dll", + "build/netstandard2.0/ref/System.ObjectModel.dll", + "build/netstandard2.0/ref/System.Reflection.Extensions.dll", + "build/netstandard2.0/ref/System.Reflection.Primitives.dll", + "build/netstandard2.0/ref/System.Reflection.dll", + "build/netstandard2.0/ref/System.Resources.Reader.dll", + "build/netstandard2.0/ref/System.Resources.ResourceManager.dll", + "build/netstandard2.0/ref/System.Resources.Writer.dll", + "build/netstandard2.0/ref/System.Runtime.CompilerServices.VisualC.dll", + "build/netstandard2.0/ref/System.Runtime.Extensions.dll", + "build/netstandard2.0/ref/System.Runtime.Handles.dll", + "build/netstandard2.0/ref/System.Runtime.InteropServices.RuntimeInformation.dll", + "build/netstandard2.0/ref/System.Runtime.InteropServices.dll", + "build/netstandard2.0/ref/System.Runtime.Numerics.dll", + "build/netstandard2.0/ref/System.Runtime.Serialization.Formatters.dll", + "build/netstandard2.0/ref/System.Runtime.Serialization.Json.dll", + "build/netstandard2.0/ref/System.Runtime.Serialization.Primitives.dll", + "build/netstandard2.0/ref/System.Runtime.Serialization.Xml.dll", + "build/netstandard2.0/ref/System.Runtime.Serialization.dll", + "build/netstandard2.0/ref/System.Runtime.dll", + "build/netstandard2.0/ref/System.Security.Claims.dll", + "build/netstandard2.0/ref/System.Security.Cryptography.Algorithms.dll", + "build/netstandard2.0/ref/System.Security.Cryptography.Csp.dll", + "build/netstandard2.0/ref/System.Security.Cryptography.Encoding.dll", + "build/netstandard2.0/ref/System.Security.Cryptography.Primitives.dll", + "build/netstandard2.0/ref/System.Security.Cryptography.X509Certificates.dll", + "build/netstandard2.0/ref/System.Security.Principal.dll", + "build/netstandard2.0/ref/System.Security.SecureString.dll", + "build/netstandard2.0/ref/System.ServiceModel.Web.dll", + "build/netstandard2.0/ref/System.Text.Encoding.Extensions.dll", + "build/netstandard2.0/ref/System.Text.Encoding.dll", + "build/netstandard2.0/ref/System.Text.RegularExpressions.dll", + "build/netstandard2.0/ref/System.Threading.Overlapped.dll", + "build/netstandard2.0/ref/System.Threading.Tasks.Parallel.dll", + "build/netstandard2.0/ref/System.Threading.Tasks.dll", + "build/netstandard2.0/ref/System.Threading.Thread.dll", + "build/netstandard2.0/ref/System.Threading.ThreadPool.dll", + "build/netstandard2.0/ref/System.Threading.Timer.dll", + "build/netstandard2.0/ref/System.Threading.dll", + "build/netstandard2.0/ref/System.Transactions.dll", + "build/netstandard2.0/ref/System.ValueTuple.dll", + "build/netstandard2.0/ref/System.Web.dll", + "build/netstandard2.0/ref/System.Windows.dll", + "build/netstandard2.0/ref/System.Xml.Linq.dll", + "build/netstandard2.0/ref/System.Xml.ReaderWriter.dll", + "build/netstandard2.0/ref/System.Xml.Serialization.dll", + "build/netstandard2.0/ref/System.Xml.XDocument.dll", + "build/netstandard2.0/ref/System.Xml.XPath.XDocument.dll", + "build/netstandard2.0/ref/System.Xml.XPath.dll", + "build/netstandard2.0/ref/System.Xml.XmlDocument.dll", + "build/netstandard2.0/ref/System.Xml.XmlSerializer.dll", + "build/netstandard2.0/ref/System.Xml.dll", + "build/netstandard2.0/ref/System.dll", + "build/netstandard2.0/ref/mscorlib.dll", + "build/netstandard2.0/ref/netstandard.dll", + "build/netstandard2.0/ref/netstandard.xml", + "lib/netstandard1.0/_._", + "netstandard.library.2.0.3.nupkg.sha512", + "netstandard.library.nuspec" + ] + }, + "System.Buffers/4.4.0": { + "sha512": "Ii2bedd4HVzddupdU35n3ygohUPlNn7MDimBOYcwWNce2NizQ1fCSaQJY1Tzv80aMqOGpVcU4wZr/Xe50xcTwg==", + "type": "package", + "path": "system.buffers/4.4.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/netcoreapp2.0/_._", + "lib/netstandard1.1/System.Buffers.dll", + "lib/netstandard1.1/System.Buffers.xml", + "lib/netstandard2.0/System.Buffers.dll", + "lib/netstandard2.0/System.Buffers.xml", + "ref/netcoreapp2.0/_._", + "ref/netstandard1.1/System.Buffers.dll", + "ref/netstandard1.1/System.Buffers.xml", + "ref/netstandard2.0/System.Buffers.dll", + "ref/netstandard2.0/System.Buffers.xml", + "system.buffers.4.4.0.nupkg.sha512", + "system.buffers.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "System.Memory/4.5.2": { + "sha512": "Ohf9h6em5CjPaAzupGtW/TWMzX6aVfIGPh7bq5KQmfvYJr+DqvOa0cRS9ggFKGIuvKTModCQEacGC/CwE8j7cg==", + "type": "package", + "path": "system.memory/4.5.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/netcoreapp2.1/_._", + "lib/netstandard1.1/System.Memory.dll", + "lib/netstandard1.1/System.Memory.xml", + "lib/netstandard2.0/System.Memory.dll", + "lib/netstandard2.0/System.Memory.xml", + "ref/netcoreapp2.1/_._", + "system.memory.4.5.2.nupkg.sha512", + "system.memory.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "System.Numerics.Vectors/4.4.0": { + "sha512": "gdRrUJs1RrjW3JB5p82hYjA67xoeFLvh0SdSIWjTiN8qExlbFt/RtXwVYNc5BukJ/f9OKzQQS6gakzbJeHTqHg==", + "type": "package", + "path": "system.numerics.vectors/4.4.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.Numerics.Vectors.dll", + "lib/net46/System.Numerics.Vectors.xml", + "lib/netcoreapp2.0/_._", + "lib/netstandard1.0/System.Numerics.Vectors.dll", + "lib/netstandard1.0/System.Numerics.Vectors.xml", + "lib/netstandard2.0/System.Numerics.Vectors.dll", + "lib/netstandard2.0/System.Numerics.Vectors.xml", + "lib/portable-net45+win8+wp8+wpa81/System.Numerics.Vectors.dll", + "lib/portable-net45+win8+wp8+wpa81/System.Numerics.Vectors.xml", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.Numerics.Vectors.dll", + "ref/net46/System.Numerics.Vectors.xml", + "ref/netcoreapp2.0/_._", + "ref/netstandard1.0/System.Numerics.Vectors.dll", + "ref/netstandard1.0/System.Numerics.Vectors.xml", + "ref/netstandard2.0/System.Numerics.Vectors.dll", + "ref/netstandard2.0/System.Numerics.Vectors.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.numerics.vectors.4.4.0.nupkg.sha512", + "system.numerics.vectors.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "System.Runtime.CompilerServices.Unsafe/4.6.0-preview8.19405.3": { + "sha512": "1EHM+Tp0/RlDu4cQpYl/+OsikV6DqVK9WXLZenKA0jNVgAvmKG4eD7czEIDR1w3IQjqywhIdIrqm1C4wJutVnA==", + "type": "package", + "path": "system.runtime.compilerservices.unsafe/4.6.0-preview8.19405.3", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll", + "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.xml", + "lib/netstandard1.0/System.Runtime.CompilerServices.Unsafe.dll", + "lib/netstandard1.0/System.Runtime.CompilerServices.Unsafe.xml", + "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll", + "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml", + "ref/netstandard1.0/System.Runtime.CompilerServices.Unsafe.dll", + "ref/netstandard1.0/System.Runtime.CompilerServices.Unsafe.xml", + "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll", + "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml", + "system.runtime.compilerservices.unsafe.4.6.0-preview8.19405.3.nupkg.sha512", + "system.runtime.compilerservices.unsafe.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "Svelto.Common/1.0.0": { + "type": "project", + "path": "../Svelto.Common/Svelto.Common.csproj", + "msbuildProject": "../Svelto.Common/Svelto.Common.csproj" + } + }, + "projectFileDependencyGroups": { + ".NETStandard,Version=v2.0": [ + "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" + ] + }, + "packageFolders": { + "C:\\Users\\Seb-e-Sam\\.nuget\\packages\\": {}, + "C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder": {} + }, + "project": { + "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 + } + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/obj/project.packagespec.json b/Svelto.ECS/obj/project.packagespec.json new file mode 100644 index 0000000..d048127 --- /dev/null +++ b/Svelto.ECS/obj/project.packagespec.json @@ -0,0 +1,66 @@ +{ + "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", + "outputPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder" + ], + "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 + } + } +} \ No newline at end of file diff --git a/Svelto.Services/Experimental/IServiceEventContainer.cs b/Svelto.Services/Experimental/IServiceEventContainer.cs new file mode 100644 index 0000000..cca960d --- /dev/null +++ b/Svelto.Services/Experimental/IServiceEventContainer.cs @@ -0,0 +1,11 @@ +using System; + +namespace Svelto.ServiceLayer.Experimental +{ + public interface IServiceEventContainer : IDisposable + { + //Delegate constraints to store delegates without needing a signature + void ListenTo(TDelegate callBack) + where TListener : class, IServiceEventListener where TDelegate : Delegate; + } +} diff --git a/Svelto.Services/Experimental/IServiceEventContainerFactory.cs b/Svelto.Services/Experimental/IServiceEventContainerFactory.cs new file mode 100644 index 0000000..801263e --- /dev/null +++ b/Svelto.Services/Experimental/IServiceEventContainerFactory.cs @@ -0,0 +1,8 @@ + +namespace Svelto.ServiceLayer.Experimental +{ + interface IServiceEventContainerFactory + { + IServiceEventContainer Create(); + } +} diff --git a/Svelto.Services/Experimental/IServiceEventListener.cs b/Svelto.Services/Experimental/IServiceEventListener.cs new file mode 100644 index 0000000..694da39 --- /dev/null +++ b/Svelto.Services/Experimental/IServiceEventListener.cs @@ -0,0 +1,14 @@ +using System; + +namespace Svelto.ServiceLayer.Experimental +{ + public interface IServiceEventListener : IServiceEventListenerBase where TDelegate : Delegate + { + void SetCallback(TDelegate callback); + } + + // This interface exists so we can use one type which can represent any of the interfaces above + public interface IServiceEventListenerBase : IDisposable + { + } +} diff --git a/Svelto.Services/Experimental/ServiceEventContainer.cs b/Svelto.Services/Experimental/ServiceEventContainer.cs new file mode 100644 index 0000000..29e3423 --- /dev/null +++ b/Svelto.Services/Experimental/ServiceEventContainer.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; + +namespace Svelto.ServiceLayer.Experimental +{ + public abstract class ServiceEventContainer : IServiceEventContainer + { + public void Dispose() + { + foreach (var listener in _listeners) + { + listener.Dispose(); + } + + _listeners.Clear(); + } + + protected ServiceEventContainer() + { + //call all the AddRelation in the implementation if you wish + } + + public void ListenTo(TDelegate callBack) + where TListener : class, IServiceEventListener where TDelegate : Delegate + { + var concreteType = _registeredTypes[typeof(TListener)]; + var listener = (TListener)Activator.CreateInstance(concreteType); + listener.SetCallback(callBack); + _listeners.Add(listener); + } + + protected void AddRelation() where TInterface : IServiceEventListenerBase + where TConcrete : TInterface + { + _registeredTypes.Add(typeof(TInterface), typeof(TConcrete)); + } + + readonly List _listeners = new List(); + readonly Dictionary _registeredTypes = new Dictionary(); + } +} \ No newline at end of file diff --git a/Svelto.Services/IServiceRequest.cs b/Svelto.Services/IServiceRequest.cs new file mode 100644 index 0000000..23b7bd9 --- /dev/null +++ b/Svelto.Services/IServiceRequest.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using Svelto.Tasks; + +namespace Svelto.ServiceLayer +{ + public interface IServiceRequest + { + IEnumerator Execute(); + } + + public interface IServiceRequest: IServiceRequest + { + IServiceRequest Inject(TDependency registerData); + } +} diff --git a/Svelto.Services/IServiceRequestsFactory.cs b/Svelto.Services/IServiceRequestsFactory.cs new file mode 100644 index 0000000..affca17 --- /dev/null +++ b/Svelto.Services/IServiceRequestsFactory.cs @@ -0,0 +1,8 @@ +namespace Svelto.ServiceLayer +{ + public interface IServiceRequestsFactory + { + RequestInterface Create() where RequestInterface:class, IServiceRequest; + } +} + diff --git a/Svelto.Services/ServiceRequestFactoryArgumentException.cs b/Svelto.Services/ServiceRequestFactoryArgumentException.cs new file mode 100644 index 0000000..a304d5f --- /dev/null +++ b/Svelto.Services/ServiceRequestFactoryArgumentException.cs @@ -0,0 +1,10 @@ +using System; + +namespace Svelto.ServiceLayer +{ + public class ServiceRequestFactoryArgumentException: ArgumentException + { + public ServiceRequestFactoryArgumentException(string message):base(message) + {} + } +} \ No newline at end of file diff --git a/Svelto.Services/ServiceRequestsFactory.cs b/Svelto.Services/ServiceRequestsFactory.cs new file mode 100644 index 0000000..e29b1e0 --- /dev/null +++ b/Svelto.Services/ServiceRequestsFactory.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; + +namespace Svelto.ServiceLayer +{ + public abstract class ServiceRequestsFactory : IServiceRequestsFactory + { + public RequestInterface Create() where RequestInterface : class, IServiceRequest + { + var ret = RetrieveObjectType(); + + return ret.CreateInstance() as RequestInterface; + } + + protected void AddRelation() where RequestClass : class, RequestInterface, new() + where RequestInterface : IServiceRequest + + { + _requestMap[typeof(RequestInterface)] = new Value(); + } + + IHoldValue RetrieveObjectType() + { + if (_requestMap.ContainsKey(typeof(RequestInterface)) == false) + throw new ServiceRequestFactoryArgumentException("Request not registered"); + + var ret = _requestMap[typeof(RequestInterface)]; + + if (ret == null) + throw new ServiceRequestFactoryArgumentException("Request not found"); + + return ret; + } + + readonly Dictionary _requestMap = new Dictionary(); + + interface IHoldValue + { + IServiceRequest CreateInstance(); + } + + class Value : IHoldValue where RequestClass : class, IServiceRequest, new() + { + public IServiceRequest CreateInstance() + { + return new RequestClass(); + } + } + } +} diff --git a/Svelto.Services/Svelto.Services.asmdef b/Svelto.Services/Svelto.Services.asmdef new file mode 100644 index 0000000..ab050d2 --- /dev/null +++ b/Svelto.Services/Svelto.Services.asmdef @@ -0,0 +1,17 @@ +{ + "name": "Svelto.Services", + "references": [ + "Svelto.Common", + "Svelto.Tasks", + "Utf8Json" + ], + "optionalUnityReferences": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [] +} \ No newline at end of file diff --git a/Svelto.Services/Unity/Web/IResponseHandler.cs b/Svelto.Services/Unity/Web/IResponseHandler.cs new file mode 100644 index 0000000..f8250b0 --- /dev/null +++ b/Svelto.Services/Unity/Web/IResponseHandler.cs @@ -0,0 +1,19 @@ +namespace Svelto.Services +{ + public interface IResponseHandler : IResponseHandler + { + ResponseType response { get; } + } + + public interface IResponseHandler + { + // Called once per frame when data has been received from the network. + bool ReceiveData(byte[] data, int dataLength); + + // Called when all data has been received from the server and delivered via ReceiveData. + void CompleteContent(); + + // Called when a Content-Length header is received from the server. + void ReceiveContentLength(int contentLength); + } +} \ No newline at end of file diff --git a/Svelto.Services/Unity/Web/StandardWebRequest.cs b/Svelto.Services/Unity/Web/StandardWebRequest.cs new file mode 100644 index 0000000..e024f7c --- /dev/null +++ b/Svelto.Services/Unity/Web/StandardWebRequest.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Net; +using UnityEngine; +using System.Security.Cryptography.X509Certificates; +using System.Net.Security; +using Svelto.Tasks; +using Svelto.Tasks.Enumerators; +using UnityEngine.Networking; + +namespace Svelto.Services +{ + public enum Method + { + GET, + POST + } + + public enum Result + { + Success, + ServerHandledError, + ServerException, + ClientFailure, + } + + public sealed class StandardWebRequest + { + static StandardWebRequest() + { + ServicePointManager.ServerCertificateValidationCallback = CannotVerifyMessageCertificate; + + ServicePointManager.DefaultConnectionLimit = 64; + } + + public Result result { get; private set; } + + public int maxAttempts = 3; + public int waitOnRetrySeconds = 1; + public int timeoutSeconds = 10; + + public Method method = Method.POST; + public Func processStatusCodes = code => false; + public IResponseHandler responseHandler; + public string URL; + + public IEnumerator Execute(byte[] bodyRaw) + { + int attemptNumber = 0; + + do + { + using (UnityWebRequest request = new UnityWebRequest()) + { + switch (method) + { + case Method.GET: + request.method = UnityWebRequest.kHttpVerbGET; + break; + case Method.POST: + request.method = UnityWebRequest.kHttpVerbPOST; + break; + default: + request.method = UnityWebRequest.kHttpVerbPOST; + break; + } + + request.url = URL; + request.uploadHandler = new UploadHandlerRaw(bodyRaw); + request.downloadHandler = new UnityDownloadHandler(responseHandler); + request.SetRequestHeader("Content-Type", "application/json"); + request.timeout = timeoutSeconds; + + AsyncOperation op = request.SendWebRequest(); + + while (op.isDone == false) + { + yield return Yield.It; + } + + if (request.isNetworkError == false) + { + if (ProcessResponse(request) == true) + { + result = Result.Success; + + Svelto.Console.LogDebug("web request completed"); + + yield break; + } + else + { + Svelto.Console.LogDebug("web request completed with failure ", URL); + + try + { + responseHandler?.CompleteContent(); + + result = Result.ServerHandledError; + } + catch + { + result = Result.ServerException; + } + + yield break; //no retry on server error! + } + } + else + if (++attemptNumber < maxAttempts) + { + var wait = new ReusableWaitForSecondsEnumerator(waitOnRetrySeconds); + + while (wait.MoveNext() == true) yield return Yield.It; + + Svelto.Console.LogDebug("web request retry"); + + continue; //retry on client error + } + else + { + result = Result.ClientFailure; + + yield break; + } + } + } + while (true); + } + + bool ProcessResponse(UnityWebRequest request) + { + HttpStatusCode statusCode = (HttpStatusCode) request.responseCode; + + if (statusCode != HttpStatusCode.OK) + if (processStatusCodes(statusCode) == false) + return false; + + return true; + } + + // BOC:: After turning on SSL on the gameserver we started getting certificate problems. + // Found this solution on Stack Overflow with Mike R + // (https://stackoverflow.com/questions/4926676/mono-https-webrequest-fails-with-the-authentication-or-decryption-has-failed) + static bool CannotVerifyMessageCertificate(System.Object sender, X509Certificate certificate, X509Chain chain, + SslPolicyErrors sslPolicyErrors) + { + bool isOk = true; + + // If there are errors in the certificate chain, + // look at each error to determine the cause. + if (sslPolicyErrors != SslPolicyErrors.None) + { + for (int i = 0; i < chain.ChainStatus.Length; i++) + { + if (chain.ChainStatus[i].Status == X509ChainStatusFlags.RevocationStatusUnknown) + continue; + + chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; + chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; + chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 1, 0); + chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; + + bool chainIsValid = chain.Build((X509Certificate2) certificate); + if (!chainIsValid) + { + isOk = false; + break; + } + } + } + + return isOk; + } + } +} \ No newline at end of file diff --git a/Svelto.Services/Unity/Web/UnityDownloadHandler.cs b/Svelto.Services/Unity/Web/UnityDownloadHandler.cs new file mode 100644 index 0000000..60100da --- /dev/null +++ b/Svelto.Services/Unity/Web/UnityDownloadHandler.cs @@ -0,0 +1,61 @@ +using System; +using System.IO; +using System.Text; +using UnityEngine.Networking; + +namespace Svelto.Services +{ + public class UnityDownloadHandler : DownloadHandlerScript + { + public string GetError() + { + if (String.IsNullOrEmpty(_errorString) == true) + { + MemoryStream errorBuffer = new MemoryStream(_dataLength); + + errorBuffer.Write(data, 0, _dataLength); + + _errorString = Encoding.UTF8.GetString(errorBuffer.GetBuffer(), 0, (int) errorBuffer.Length); + + errorBuffer.Dispose(); + } + + return _errorString; + } + + protected override bool ReceiveData(byte[] data, int dataLength) + { + if (data.Length < 1) + return false; // No need to receive data + + _data = data; + _dataLength = dataLength; + + if (_handler == null) + return false; // No need to receive data + + return _handler.ReceiveData(data, dataLength); + } + + public UnityDownloadHandler(IResponseHandler handler) + { + _handler = handler; + } + + protected override byte[] GetData() + { + return _data; + } + + // Called when all data has been received from the server and delivered via ReceiveData. + protected override void CompleteContent() + { + _handler?.CompleteContent(); + } + + readonly IResponseHandler _handler; + byte[] _data; + int _dataLength; + string _errorString; + } +} \ No newline at end of file