diff --git a/Svelto.ECS.Components/Components/ECSQuaternion.cs b/Svelto.ECS.Components/Components/ECSQuaternion.cs new file mode 100644 index 0000000..c88ccb2 --- /dev/null +++ b/Svelto.ECS.Components/Components/ECSQuaternion.cs @@ -0,0 +1,16 @@ +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/Common/Components/ECSRect.cs b/Svelto.ECS.Components/Components/ECSRect.cs similarity index 88% rename from Svelto.ECS/Common/Components/ECSRect.cs rename to Svelto.ECS.Components/Components/ECSRect.cs index 13daa82..bf2fb7c 100644 --- a/Svelto.ECS/Common/Components/ECSRect.cs +++ b/Svelto.ECS.Components/Components/ECSRect.cs @@ -1,7 +1,7 @@ #if UNITY_5 || UNITY_5_3_OR_NEWER using UnityEngine; -namespace Svelto.ECS.Components +namespace Svelto.ECS.Components.Unity { public struct ECSRect { @@ -16,4 +16,4 @@ namespace Svelto.ECS.Components } } } -#endif +#endif \ No newline at end of file diff --git a/Svelto.ECS.Components/Components/ECSVector2.cs b/Svelto.ECS.Components/Components/ECSVector2.cs new file mode 100644 index 0000000..a48a5d4 --- /dev/null +++ b/Svelto.ECS.Components/Components/ECSVector2.cs @@ -0,0 +1,20 @@ +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 new file mode 100644 index 0000000..fea272f --- /dev/null +++ b/Svelto.ECS.Components/Components/ECSVector3.cs @@ -0,0 +1,24 @@ +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/Common/Components/EcsVector4.cs b/Svelto.ECS.Components/Components/ECSVector4.cs similarity index 65% rename from Svelto.ECS/Common/Components/EcsVector4.cs rename to Svelto.ECS.Components/Components/ECSVector4.cs index a5f1c1e..17396fb 100644 --- a/Svelto.ECS/Common/Components/EcsVector4.cs +++ b/Svelto.ECS.Components/Components/ECSVector4.cs @@ -1,10 +1,10 @@ namespace Svelto.ECS.Components { - public struct EcsVector4 + public struct ECSVector4 { public float x, y, z, w; - public EcsVector4(float X, float Y, float Z, float W) + public ECSVector4(float X, float Y, float Z, float W) { x = X; y = Y; diff --git a/Svelto.ECS.Components/Components/ExtensionMethods.cs b/Svelto.ECS.Components/Components/ExtensionMethods.cs new file mode 100644 index 0000000..b3c5b2f --- /dev/null +++ b/Svelto.ECS.Components/Components/ExtensionMethods.cs @@ -0,0 +1,132 @@ +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/ExtensionMethodsUnity.cs b/Svelto.ECS.Components/Components/ExtensionMethodsUnity.cs new file mode 100644 index 0000000..d8d4136 --- /dev/null +++ b/Svelto.ECS.Components/Components/ExtensionMethodsUnity.cs @@ -0,0 +1,44 @@ +#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/ECSResources.cs b/Svelto.ECS.Components/ECSResources/ECSResources.cs new file mode 100644 index 0000000..2ebba29 --- /dev/null +++ b/Svelto.ECS.Components/ECSResources/ECSResources.cs @@ -0,0 +1,50 @@ +using Svelto.DataStructures; + +namespace Svelto.ECS.Experimental +{ + public struct ECSResources + { + internal uint id; + + public static implicit operator T(ECSResources ecsString) { return ResourcesECSDB.FromECS(ecsString.id); } + } + + static class ResourcesECSDB + { + internal static readonly FasterList _resources = new FasterList(); + + internal static uint ToECS(T resource) + { + _resources.Add(resource); + + return (uint)_resources.Count; + } + + public static T FromECS(uint id) + { + if (id - 1 < _resources.Count) + return _resources[(int) id - 1]; + + return default(T); + } + } + + public static class ResourceExtensions + { + public static void Set(ref this ECSResources resource, T newText) + { + if (resource.id != 0) + ResourcesECSDB._resources[(int) 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.Components/ECSResources/StringECSDB.cs b/Svelto.ECS.Components/ECSResources/StringECSDB.cs new file mode 100644 index 0000000..897974e --- /dev/null +++ b/Svelto.ECS.Components/ECSResources/StringECSDB.cs @@ -0,0 +1,12 @@ +namespace Svelto.ECS.Experimental +{ + public struct ECSString + { + internal uint id; + + public static implicit operator string(ECSString ecsString) + { + return ResourcesECSDB.FromECS(ecsString.id); + } + } +} \ No newline at end of file diff --git a/Svelto.ECS.Components/EntityStructs/LocalTransformEntityStruct.cs b/Svelto.ECS.Components/EntityStructs/LocalTransformEntityStruct.cs new file mode 100644 index 0000000..12e6ed8 --- /dev/null +++ b/Svelto.ECS.Components/EntityStructs/LocalTransformEntityStruct.cs @@ -0,0 +1,10 @@ +using Svelto.ECS.Components; + +namespace Svelto.ECS.EntityStructs +{ + public struct LocalTransformEntityStruct : IEntityStruct + { + public ECSVector3 position; + public ECSQuaternion rotation; + } +} diff --git a/Svelto.ECS/Common/EntityStructs/PositionEntityStruct.cs b/Svelto.ECS.Components/EntityStructs/PositionEntityStruct.cs similarity index 81% rename from Svelto.ECS/Common/EntityStructs/PositionEntityStruct.cs rename to Svelto.ECS.Components/EntityStructs/PositionEntityStruct.cs index d0afb46..b86a6ac 100644 --- a/Svelto.ECS/Common/EntityStructs/PositionEntityStruct.cs +++ b/Svelto.ECS.Components/EntityStructs/PositionEntityStruct.cs @@ -5,7 +5,5 @@ namespace Svelto.ECS.EntityStructs public struct PositionEntityStruct : IEntityStruct { public ECSVector3 position; - - public EGID ID { get; set; } } } \ No newline at end of file diff --git a/Svelto.ECS/Common/EntityStructs/RotationEntityStruct.cs b/Svelto.ECS.Components/EntityStructs/RotationEntityStruct.cs similarity index 61% rename from Svelto.ECS/Common/EntityStructs/RotationEntityStruct.cs rename to Svelto.ECS.Components/EntityStructs/RotationEntityStruct.cs index d3e2932..2b30560 100644 --- a/Svelto.ECS/Common/EntityStructs/RotationEntityStruct.cs +++ b/Svelto.ECS.Components/EntityStructs/RotationEntityStruct.cs @@ -4,8 +4,6 @@ namespace Svelto.ECS.EntityStructs { public struct RotationEntityStruct : IEntityStruct { - public EcsVector4 rotation; - - public EGID ID { get; set; } + 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 new file mode 100644 index 0000000..89e535a --- /dev/null +++ b/Svelto.ECS.Components/EntityStructs/ScalingEntityStruct.cs @@ -0,0 +1,9 @@ +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 new file mode 100644 index 0000000..cdf669d --- /dev/null +++ b/Svelto.ECS.Components/Svelto.ECS.Components.asmdef @@ -0,0 +1,23 @@ +{ + "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/.gitignore b/Svelto.ECS/.gitignore deleted file mode 100644 index ac529bb..0000000 --- a/Svelto.ECS/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/EntitySystem/note.txt -/EntitySystem/note.txt.meta -/*.meta -*.meta diff --git a/Svelto.ECS/CheckEntityUtilities.cs b/Svelto.ECS/CheckEntityUtilities.cs index a3516b5..5c26698 100644 --- a/Svelto.ECS/CheckEntityUtilities.cs +++ b/Svelto.ECS/CheckEntityUtilities.cs @@ -15,11 +15,10 @@ namespace Svelto.ECS #endif void CheckRemoveEntityID(EGID entityID, IEntityDescriptor descriptorEntity) { - - Dictionary group; - var descriptorEntitiesToBuild = descriptorEntity.entitiesToBuild; - if (_groupEntityDB.TryGetValue(entityID.groupID, out group)) + var descriptorEntitiesToBuild = descriptorEntity.entitiesToBuild; + + if (_groupEntityDB.TryGetValue(entityID.groupID, out var @group)) { for (int i = 0; i < descriptorEntitiesToBuild.Length; i++) { @@ -38,15 +37,15 @@ namespace Svelto.ECS #if DISABLE_CHECKS [Conditional("_CHECKS_DISABLED")] #endif - void CheckRemoveEntityID(EGID entityID, Type entityType, Dictionary group, string name) + void CheckRemoveEntityID(EGID entityID, Type entityViewType, Dictionary group, string name) { ITypeSafeDictionary entities; - if (group.TryGetValue(entityType, out 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(entityType.ToString()) + .FastConcat(entityViewType.ToString()) .FastConcat(" id: ") .FastConcat(entityID.entityID) .FastConcat(" groupid: ") @@ -56,7 +55,7 @@ namespace Svelto.ECS else { Console.LogError("Entity ".FastConcat(name, " with not found ID is about to be removed: ") - .FastConcat(entityType.ToString()) + .FastConcat(entityViewType.ToString()) .FastConcat(" id: ") .FastConcat(entityID.entityID) .FastConcat(" groupid: ") @@ -69,15 +68,15 @@ namespace Svelto.ECS #endif void CheckAddEntityID(EGID entityID, T descriptorEntity) where T:IEntityDescriptor { - Dictionary group; - var descriptorEntitiesToBuild = descriptorEntity.entitiesToBuild; + var descriptorEntitiesToBuild = descriptorEntity.entitiesToBuild; //these are the entities added in this frame - if (_groupEntityDB.TryGetValue(entityID.groupID, out group)) + if (_groupEntityDB.TryGetValue(entityID.groupID, out var @group)) { for (int i = 0; i < descriptorEntitiesToBuild.Length; i++) { - CheckAddEntityID(entityID, descriptorEntitiesToBuild[i].GetEntityType(), group, descriptorEntity.ToString()); + CheckAddEntityID(entityID, descriptorEntitiesToBuild[i].GetEntityType(), group, + descriptorEntity.ToString()); } } } @@ -85,15 +84,16 @@ namespace Svelto.ECS #if DISABLE_CHECKS [Conditional("_CHECKS_DISABLED")] #endif - static void CheckAddEntityID(EGID entityID, Type entityType, Dictionary group, string name) + static void CheckAddEntityID(EGID entityID, Type entityViewType, Dictionary group, + string name) { ITypeSafeDictionary entities; - if (group.TryGetValue(entityType, out 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(entityType.ToString()) + .FastConcat(entityViewType.ToString()) .FastConcat(" id: ") .FastConcat(entityID.entityID) .FastConcat(" groupid: ") diff --git a/Svelto.ECS/Common/Components/ECSVector2.cs b/Svelto.ECS/Common/Components/ECSVector2.cs deleted file mode 100644 index e5bbf1e..0000000 --- a/Svelto.ECS/Common/Components/ECSVector2.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Svelto.ECS.Components -{ - public struct ECSVector2 - { - public float x, y; - - public ECSVector2(float X, float Y) - { - x = X; - y = Y; - } - } -} \ No newline at end of file diff --git a/Svelto.ECS/Common/Components/ECSVector3.cs b/Svelto.ECS/Common/Components/ECSVector3.cs deleted file mode 100644 index 685b10d..0000000 --- a/Svelto.ECS/Common/Components/ECSVector3.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Svelto.ECS.Components -{ - public struct ECSVector3 - { - public float x, y, z; - - 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/Common/Components/ExtensionMethods.cs b/Svelto.ECS/Common/Components/ExtensionMethods.cs deleted file mode 100644 index 2f2b60c..0000000 --- a/Svelto.ECS/Common/Components/ExtensionMethods.cs +++ /dev/null @@ -1,36 +0,0 @@ -#if UNITY_2018_3_OR_NEWER -using UnityEngine; - -namespace Svelto.ECS.Components.Unity -{ - namespace Svelto.ECS.Components - { - public static class ExtensionMethods - { - public static Vector3 ToVector3(ref this ECSVector3 vector) { return new Vector3(vector.x, vector.y, vector.z); } - - public static void Add(ref this ECSVector3 vector1, ref ECSVector3 vector2) - { - vector1.x += vector2.x; - vector1.y += vector2.y; - vector1.z += vector2.z; - } - - public static void Add(ref this ECSVector3 vector1, float x, float y, float z) - { - vector1.x += x; - vector1.y += y; - vector1.z += z; - } - - public static void Interpolate(ref this ECSVector3 vector, ref ECSVector3 vectorS, - ref 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); - } - } - } -} -#endif \ No newline at end of file diff --git a/Svelto.ECS/Common/EntityStructs/InterpolateVector3EntityStruct.cs b/Svelto.ECS/Common/EntityStructs/InterpolateVector3EntityStruct.cs deleted file mode 100644 index d91a478..0000000 --- a/Svelto.ECS/Common/EntityStructs/InterpolateVector3EntityStruct.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Svelto.ECS.Components; - -namespace Svelto.ECS.EntityStructs -{ - public struct InterpolateVector3EntityStruct : IEntityStruct - { - public ECSVector3 starPos, endPos; - public float time; - - public EGID ID { get; set; } - } -} \ No newline at end of file diff --git a/Svelto.ECS/DBC.cs b/Svelto.ECS/DBC.cs index e9fae93..300cef0 100644 --- a/Svelto.ECS/DBC.cs +++ b/Svelto.ECS/DBC.cs @@ -60,7 +60,7 @@ namespace DBC.ECS /// /// Precondition check. /// -#if DISABLE_DBC +#if DISABLE_CHECKS [Conditional("__NEVER_DEFINED__")] #endif public static void Require(bool assertion, string message) @@ -80,7 +80,7 @@ namespace DBC.ECS /// Precondition check. /// /// -#if DISABLE_DBC +#if DISABLE_CHECKS [Conditional("__NEVER_DEFINED__")] #endif public static void Require(bool assertion, string message, Exception inner) @@ -100,7 +100,7 @@ namespace DBC.ECS /// Precondition check. /// /// -#if DISABLE_DBC +#if DISABLE_CHECKS [Conditional("__NEVER_DEFINED__")] #endif public static void Require(bool assertion) @@ -120,7 +120,7 @@ namespace DBC.ECS /// Postcondition check. /// /// -#if DISABLE_DBC +#if DISABLE_CHECKS [Conditional("__NEVER_DEFINED__")] #endif public static void Ensure(bool assertion, string message) @@ -140,7 +140,7 @@ namespace DBC.ECS /// Postcondition check. /// /// -#if DISABLE_DBC +#if DISABLE_CHECKS [Conditional("__NEVER_DEFINED__")] #endif public static void Ensure(bool assertion, string message, Exception inner) @@ -160,7 +160,7 @@ namespace DBC.ECS /// Postcondition check. /// /// -#if DISABLE_DBC +#if DISABLE_CHECKS [Conditional("__NEVER_DEFINED__")] #endif public static void Ensure(bool assertion) @@ -180,7 +180,7 @@ namespace DBC.ECS /// Invariant check. /// /// -#if DISABLE_DBC +#if DISABLE_CHECKS [Conditional("__NEVER_DEFINED__")] #endif public static void Invariant(bool assertion, string message) @@ -200,7 +200,7 @@ namespace DBC.ECS /// Invariant check. /// /// -#if DISABLE_DBC +#if DISABLE_CHECKS [Conditional("__NEVER_DEFINED__")] #endif public static void Invariant(bool assertion, string message, Exception inner) @@ -220,7 +220,7 @@ namespace DBC.ECS /// Invariant check. /// /// -#if DISABLE_DBC +#if DISABLE_CHECKS [Conditional("__NEVER_DEFINED__")] #endif public static void Invariant(bool assertion) @@ -239,7 +239,7 @@ namespace DBC.ECS /// /// Assertion check. /// -#if DISABLE_DBC +#if DISABLE_CHECKS [Conditional("__NEVER_DEFINED__")] #endif public static void Assert(bool assertion, string message) @@ -259,7 +259,7 @@ namespace DBC.ECS /// Assertion check. /// /// -#if DISABLE_DBC +#if DISABLE_CHECKS [Conditional("__NEVER_DEFINED__")] #endif public static void Assert(bool assertion, string message, Exception inner) @@ -279,7 +279,7 @@ namespace DBC.ECS /// Assertion check. /// /// -#if DISABLE_DBC +#if DISABLE_CHECKS [Conditional("__NEVER_DEFINED__")] #endif public static void Assert(bool assertion) diff --git a/Svelto.ECS/DataStructures/GroupsList.cs b/Svelto.ECS/DataStructures/GroupsList.cs new file mode 100644 index 0000000..932df98 --- /dev/null +++ b/Svelto.ECS/DataStructures/GroupsList.cs @@ -0,0 +1,106 @@ +using System; +using System.Runtime.CompilerServices; +using Svelto.DataStructures; + +namespace Svelto.ECS.Internal +{ + class GroupList + { + public int Count => _list.Count; + + public GroupList() + { + _list = new FasterList(); + } + + public ref T this[uint index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _list[index]; + } + + public GroupListEnumerator GetEnumerator() + { + return new GroupListEnumerator(_list.ToArrayFast(), _list.Count); + } + + public T GetOrAdd(uint location) where TC:class, T, new() + { + if (location >= _list.Count || this[location] == null) + { + var item = new TC(); + _list.Add(location, item); + return item; + } + + return this[location]; + } + + public void Clear() { _list.Clear(); } + public void FastClear() { _list.ResetCountToAvoidGC(); } + + public bool TryGetValue(uint index, out T value) + { + if (default(T) == null) + { + if (index < _list.Count && this[index] != null) + { + value = this[index]; + return true; + } + + value = default(T); + return false; + } + else + { + if (index < _list.Count) + { + value = this[index]; + return true; + } + + value = default(T); + return false; + } + } + + public void Add(uint location, T value) { _list.Add(location, value); } + + readonly FasterList _list; + + } + + public struct GroupListEnumerator + { + public ref readonly T Current => ref _buffer[_counter -1]; + public uint index => _counter - 1; + + public GroupListEnumerator(T[] buffer, int size) + { + _size = size; + _counter = 0; + _buffer = buffer; + } + + public bool MoveNext() + { + if (default(T) == null) + { + while (_counter < _size) + { + if (_buffer[_counter] == null) + _counter++; + else + break; + } + } + + return _counter++ < _size; + } + + readonly T[] _buffer; + uint _counter; + readonly int _size; + } +} \ No newline at end of file diff --git a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs index 6579afc..83b78f4 100644 --- a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs +++ b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs @@ -1,208 +1,234 @@ -using System; +using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Svelto.Common; -using Svelto.DataStructures; + using Svelto.Common.Internal; + using Svelto.DataStructures; using Svelto.DataStructures.Experimental; namespace Svelto.ECS.Internal { public interface ITypeSafeDictionary { + int Count { get; } ITypeSafeDictionary Create(); - - void RemoveEntitiesFromEngines(Dictionary> - entityViewEnginesDB, ref PlatformProfiler profiler); - void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup, - Dictionary> - entityViewEnginesDB, ref PlatformProfiler profiler); - - void FillWithIndexedEntities(ITypeSafeDictionary entities); - void AddEntitiesToEngines(Dictionary> entityViewEnginesDB, - ref PlatformProfiler profiler); - - void AddCapacity(int size); - - int Count { get; } + void RemoveEntitiesFromEngines( + Dictionary> entityViewEnginesDB, + ref PlatformProfiler profiler); + + void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, + Dictionary> engines, + ref PlatformProfiler profiler); + + void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId); + + void AddEntitiesToEngines(Dictionary> entityViewEnginesDb, + ITypeSafeDictionary realDic, ref PlatformProfiler profiler); + + void SetCapacity(uint size); void Trim(); void Clear(); - bool Has(int entityIdEntityId); + bool Has(uint entityIdEntityId); } - class TypeSafeDictionary : FasterDictionary, ITypeSafeDictionary where TValue : IEntityStruct + class TypeSafeDictionary : FasterDictionary, ITypeSafeDictionary where TValue : struct, IEntityStruct { - public TypeSafeDictionary(int size):base(size) - {} - - public TypeSafeDictionary() - {} - - public void FillWithIndexedEntities(ITypeSafeDictionary entities) + 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((uint) size) { } + public TypeSafeDictionary() {} + + public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId) { - int count; - var buffer = (entities as TypeSafeDictionary).GetValuesArray(out count); + var typeSafeDictionary = entitiesToSubmit as TypeSafeDictionary; - for (var i = 0; i < count; i++) + foreach (var tuple in typeSafeDictionary) { - int idEntityId = 0; try { - idEntityId = buffer[i].ID.entityID; - - Add(idEntityId, ref buffer[i]); + 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); } 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(idEntityId), 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); } } } public void AddEntitiesToEngines( - Dictionary> entityViewEnginesDB, + Dictionary> entityViewEnginesDB, + ITypeSafeDictionary realDic, ref PlatformProfiler profiler) + { + foreach (var value in this) + { + var typeSafeDictionary = realDic as TypeSafeDictionary; + + AddEntityViewToEngines(entityViewEnginesDB, ref typeSafeDictionary.GetDirectValue(value.Key), null, + ref profiler); + } + } + + public bool Has(uint entityIdEntityId) { return ContainsKey(entityIdEntityId); } + + public void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID? toEntityID, + ITypeSafeDictionary toGroup, + Dictionary> engines, ref PlatformProfiler profiler) { - var values = GetValuesArray(out var count); - - //pay attention: even if the entity is passed by ref, it won't be saved back in the database when this - //function is called from the building of an entity. This is by design. Entity structs must be initialized - //through the EntityInitializer method and not with an Add callback. - //however the struct can be modified during an add callback if this happens as consequence of a group swap - for (int i = 0; i < count; i++) - AddEntityViewToEngines(entityViewEnginesDB, ref values[i], ref profiler); + var valueIndex = GetValueIndex(fromEntityGid.entityID); + + if (toGroup != null) + { + RemoveEntityViewFromEngines(engines, ref _values[valueIndex], fromEntityGid.groupID, ref 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, + ref profiler); + } + else + RemoveEntityViewFromEngines(engines, ref _values[valueIndex], null, ref profiler); + + + Remove(fromEntityGid.entityID); } - public bool Has(int entityIdEntityId) + public void RemoveEntitiesFromEngines( + Dictionary> entityViewEnginesDB, + ref PlatformProfiler profiler) { - return ContainsKey(entityIdEntityId); + var values = GetValuesArray(out var count); + + for (var i = 0; i < count; i++) + RemoveEntityViewFromEngines(entityViewEnginesDB, ref values[i], null, ref profiler); } - void AddEntityViewToEngines(Dictionary> entityViewEnginesDB, - ref TValue entity, ref PlatformProfiler profiler) + public ITypeSafeDictionary Create() { return new TypeSafeDictionary(); } + + void AddEntityViewToEngines(Dictionary> entityViewEnginesDB, + ref TValue entity, + ExclusiveGroup.ExclusiveGroupStruct? previousGroup, + ref PlatformProfiler profiler) { - FasterList entityViewsEngines; //get all the engines linked to TValue - if (entityViewEnginesDB.TryGetValue(_type, out entityViewsEngines)) - for (int i = 0; i < entityViewsEngines.Count; i++) - { + if (!entityViewEnginesDB.TryGetValue(_type, out var entityViewsEngines)) return; + + if (previousGroup == null) + { + for (var i = 0; i < entityViewsEngines.Count; i++) try { - using (profiler.Sample((entityViewsEngines[i] as EngineInfo).name)) + using (profiler.Sample(entityViewsEngines[i], _typeName)) { - (entityViewsEngines[i] as IHandleEntityStructEngine).AddInternal(ref entity); + (entityViewsEngines[i] as IReactOnAddAndRemove).Add(ref entity); } } catch (Exception e) { - throw new ECSException("Code crashed inside Add callback ". - FastConcat(typeof(TValue).ToString()).FastConcat("id ").FastConcat(entity.ID.entityID), e); + throw new ECSException( + "Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e); } - } - } - - public void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup, - Dictionary> - entityViewEnginesDB, ref PlatformProfiler profiler) - { - int count; - var fasterValuesBuffer = GetValuesArray(out count); - var valueIndex = GetValueIndex(fromEntityGid.entityID); - - if (entityViewEnginesDB != null) - RemoveEntityViewFromEngines(entityViewEnginesDB, ref fasterValuesBuffer[valueIndex], ref profiler); - - if (toGroup != null) - { - var toGroupCasted = toGroup as TypeSafeDictionary; - fasterValuesBuffer[valueIndex].ID = toEntityID; - toGroupCasted.Add(toEntityID.entityID, ref fasterValuesBuffer[valueIndex]); - - if (entityViewEnginesDB != null) - AddEntityViewToEngines(entityViewEnginesDB, ref toGroupCasted.GetValuesArray(out count) - [toGroupCasted.GetValueIndex(toEntityID.entityID)], ref profiler); } - - Remove(fromEntityGid.entityID); - } - - static void RemoveEntityViewFromEngines - (Dictionary> entityViewEnginesDB, ref TValue entity, - ref PlatformProfiler profiler) - { - FasterList entityViewsEngines; - if (entityViewEnginesDB.TryGetValue(_type, out entityViewsEngines)) - for (int i = 0; i < entityViewsEngines.Count; i++) + else + { + for (var i = 0; i < entityViewsEngines.Count; i++) try { - using (profiler.Sample((entityViewsEngines[i] as EngineInfo).name, _typeName)) + using (profiler.Sample(entityViewsEngines[i], _typeName)) { - (entityViewsEngines[i] as IHandleEntityStructEngine).RemoveInternal(ref entity); + (entityViewsEngines[i] as IReactOnSwap).MovedTo(ref entity, previousGroup.Value); } } catch (Exception e) { - throw new ECSException("Code crashed inside Remove callback ". - FastConcat(typeof(TValue).ToString()).FastConcat("id ").FastConcat(entity.ID.entityID), e); + throw new ECSException( + "Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e); } + } } - - public void RemoveEntitiesFromEngines(Dictionary> entityViewEnginesDB, ref PlatformProfiler profiler) - { - int count; - TValue[] values = GetValuesArray(out count); - for (int i = 0; i < count; i++) - RemoveEntityViewFromEngines(entityViewEnginesDB, ref values[i], ref profiler); - } - - public ITypeSafeDictionary Create() + static void RemoveEntityViewFromEngines( + Dictionary> entityViewEnginesDB, ref TValue entity, + ExclusiveGroup.ExclusiveGroupStruct? previousGroup, + ref PlatformProfiler profiler) { - return new TypeSafeDictionary(); - } - - public bool ExecuteOnEntityView(int entityGidEntityId, ref W value, EntityAction action) - { - uint findIndex; - if (FindIndex(entityGidEntityId, out findIndex)) - { - action(ref _values[findIndex], ref value); + if (!entityViewEnginesDB.TryGetValue(_type, out var entityViewsEngines)) return; - return true; + if (previousGroup == null) + { + for (var i = 0; i < entityViewsEngines.Count; i++) + try + { + using (profiler.Sample(entityViewsEngines[i], _typeName)) + (entityViewsEngines[i] as IReactOnAddAndRemove).Remove(ref entity); + } + catch (Exception e) + { + throw new ECSException( + "Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e); + } } - - return false; - } - - public bool ExecuteOnEntityView(int entityGidEntityId, EntityAction action) - { - uint findIndex; - if (FindIndex(entityGidEntityId, out findIndex)) + else { - action(ref _values[findIndex]); - - return true; + for (var i = 0; i < entityViewsEngines.Count; i++) + try + { + using (profiler.Sample(entityViewsEngines[i], _typeName)) + (entityViewsEngines[i] as IReactOnSwap).MovedFrom(ref entity, previousGroup.Value); + } + catch (Exception e) + { + throw new ECSException( + "Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e); + } } - - return false; } - - public uint FindElementIndex(int entityGidEntityId) - { - uint findIndex; - if (FindIndex(entityGidEntityId, out findIndex) == false) - throw new Exception("Entity not found in this group"); - return findIndex; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ref TValue FindElement(uint entityGidEntityId) + { +#if DEBUG && !PROFILER + if (FindIndex(entityGidEntityId, out var findIndex) == false) + throw new Exception("Entity not found in this group ".FastConcat(typeof(TValue).ToString())); +#else + FindIndex(entityGidEntityId, out var findIndex); +#endif + return ref _values[findIndex]; } - - public bool TryFindElementIndex(int entityGidEntityId, out uint index) + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal bool TryFindElementIndex(uint entityGidEntityId, out uint index) { return FindIndex(entityGidEntityId, out index); } - - static readonly Type _type = typeof(TValue); - static readonly string _typeName = _type.Name; } } \ No newline at end of file diff --git a/Svelto.ECS/Dispatcher/DispatchOnChange.cs b/Svelto.ECS/Dispatcher/DispatchOnChange.cs index 6d1761c..cb399ff 100644 --- a/Svelto.ECS/Dispatcher/DispatchOnChange.cs +++ b/Svelto.ECS/Dispatcher/DispatchOnChange.cs @@ -1,20 +1,17 @@ -using System.Collections.Generic; +using System; namespace Svelto.ECS { - public class DispatchOnChange : DispatchOnSet where T:struct + public class DispatchOnChange : DispatchOnSet where T:struct, IEquatable { - public DispatchOnChange(int senderID) : base(senderID) + public DispatchOnChange(EGID senderID) : base(senderID) { } - public DispatchOnChange() - {} - public new T value { set { - if (EqualityComparer.Default.Equals(value, _value) == false) + if (value.Equals(_value) == false) base.value = value; } diff --git a/Svelto.ECS/Dispatcher/DispatchOnSet.cs b/Svelto.ECS/Dispatcher/DispatchOnSet.cs index d149de6..b0f2deb 100644 --- a/Svelto.ECS/Dispatcher/DispatchOnSet.cs +++ b/Svelto.ECS/Dispatcher/DispatchOnSet.cs @@ -5,16 +5,6 @@ namespace Svelto.ECS { public class DispatchOnSet where T:struct { - static ExclusiveGroup OBSOLETE_GROUP = new ExclusiveGroup(); - - public DispatchOnSet(int senderID) - { - Console.LogWarningDebug("This method is obsolete and shouldn't be used anymore"); - - _senderID = new EGID(senderID, OBSOLETE_GROUP); - _subscribers = new WeakEvent(); - } - public DispatchOnSet(EGID senderID) { _subscribers = new WeakEvent(); @@ -22,11 +12,6 @@ namespace Svelto.ECS _senderID = senderID; } - public DispatchOnSet() - { - _subscribers = new WeakEvent(); - } - public T value { set @@ -36,15 +21,12 @@ namespace Svelto.ECS _subscribers.Invoke(_senderID, value); } - get - { - return _value; - } + get => _value; } public void NotifyOnValueSet(Action action) { - _subscribers += action; + _subscribers += action; } public void StopNotify(Action action) @@ -53,8 +35,32 @@ namespace Svelto.ECS } protected T _value; - readonly EGID _senderID; + internal EGID _senderID; WeakEvent _subscribers; } + + public static class DispatchExtensions + { + public static DispatchOnSet Setup(DispatchOnSet dispatcher, EGID entity) where T : struct + { + if (dispatcher == null) + dispatcher = new DispatchOnSet(entity); + else + dispatcher._senderID = entity; + + return dispatcher; + } + + public static DispatchOnChange Setup(DispatchOnChange dispatcher, EGID entity) + where T : struct, IEquatable + { + if (dispatcher == null) + dispatcher = new DispatchOnChange(entity); + else + dispatcher._senderID = entity; + + return dispatcher; + } + } } diff --git a/Svelto.ECS/DynamicEntityDescriptorInfo.cs b/Svelto.ECS/DynamicEntityDescriptorInfo.cs index b6f7335..8cf3ee1 100644 --- a/Svelto.ECS/DynamicEntityDescriptorInfo.cs +++ b/Svelto.ECS/DynamicEntityDescriptorInfo.cs @@ -17,9 +17,9 @@ namespace Svelto.ECS Array.Copy(defaultEntities, 0, entitiesToBuild, 0, length); Array.Copy(extraEntities, 0, entitiesToBuild, length, extraEntities.Length); - var _builder = new EntityBuilder + var _builder = new EntityBuilder { - _initializer = new EntityInfoView + _initializer = new EntityStructInfoView { entitiesToBuild = entitiesToBuild, type = typeof(TType) diff --git a/Svelto.ECS/EGID.cs b/Svelto.ECS/EGID.cs index 8e3272d..c466147 100644 --- a/Svelto.ECS/EGID.cs +++ b/Svelto.ECS/EGID.cs @@ -1,3 +1,4 @@ +#if !REAL_ID using System; using System.Collections.Generic; #pragma warning disable 660,661 @@ -6,17 +7,11 @@ namespace Svelto.ECS { public struct EGID:IEquatable,IEqualityComparer,IComparable { - readonly long _GID; - - public int entityID - { - get { return (int) (_GID & 0xFFFFFFFF); } - } - - public ExclusiveGroup.ExclusiveGroupStruct groupID - { - get { return new ExclusiveGroup.ExclusiveGroupStruct((int) (_GID >> 32)); } - } + readonly ulong _GID; + + public uint entityID => (uint) (_GID & 0xFFFFFFFF); + + public ExclusiveGroup.ExclusiveGroupStruct groupID => new ExclusiveGroup.ExclusiveGroupStruct((uint) (_GID >> 32)); public static bool operator ==(EGID obj1, EGID obj2) { @@ -28,26 +23,24 @@ namespace Svelto.ECS return obj1._GID != obj2._GID; } - public EGID(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) : this() + public EGID(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) : this() { _GID = MAKE_GLOBAL_ID(entityID, groupID); } - static long MAKE_GLOBAL_ID(int entityId, int groupId) + static ulong MAKE_GLOBAL_ID(uint entityId, uint groupId) { - return (long)groupId << 32 | ((long)(uint)entityId & 0xFFFFFFFF); + return (ulong)groupId << 32 | ((ulong)entityId & 0xFFFFFFFF); } - public static explicit operator int(EGID id) + public static explicit operator uint(EGID id) { return id.entityID; } - public static explicit operator long(EGID id) - { - return id._GID; - } - + //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; @@ -68,9 +61,142 @@ namespace Svelto.ECS return _GID.CompareTo(other._GID); } - internal EGID(int entityID, int groupID) : this() + internal EGID(uint entityID, uint groupID) : this() { _GID = MAKE_GLOBAL_ID(entityID, groupID); } } -} \ No newline at end of file +} + +#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) + { + var makeGlobalId = (((ulong)realId & bit21) << (idbits+groupbits)) | (((ulong)groupId & bit20) << idbits) | ((ulong)entityId & bit22); + + return makeGlobalId | (ulong) (hasID << idbits + groupbits + realidbits); + } + + 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); } + } +} +#endif \ No newline at end of file diff --git a/Svelto.ECS/EGIDMapper.cs b/Svelto.ECS/EGIDMapper.cs index 900ac08..3ddfe8e 100644 --- a/Svelto.ECS/EGIDMapper.cs +++ b/Svelto.ECS/EGIDMapper.cs @@ -1,16 +1,16 @@ +using System.Runtime.CompilerServices; using Svelto.ECS.Internal; namespace Svelto.ECS { - public struct EGIDMapper where T : IEntityStruct + public struct EGIDMapper where T : struct, IEntityStruct { internal TypeSafeDictionary map; - public ref T entity(EGID id) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T Entity(EGID id) { - int count; - var index = map.FindElementIndex(id.entityID); - return ref map.GetValuesArray(out count)[index]; + return ref map.FindElement(id.entityID); } } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs b/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs index 286bc90..79972b4 100644 --- a/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs +++ b/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs @@ -1,46 +1,60 @@ -using System; -using System.Collections.Generic; -using Svelto.DataStructures.Experimental; -using Svelto.ECS.Internal; +using Svelto.DataStructures.Experimental; +using EntitiesDB = + Svelto.DataStructures.Experimental.FasterDictionary>; namespace Svelto.ECS { public partial class EnginesRoot { - class DoubleBufferedEntitiesToAdd where T : FasterDictionary>, new() + internal class DoubleBufferedEntitiesToAdd { - readonly T _entityViewsToAddBufferA = new T(); - readonly T _entityViewsToAddBufferB = new T(); - - internal DoubleBufferedEntitiesToAdd() + internal void Swap() { - other = _entityViewsToAddBufferA; - current = _entityViewsToAddBufferB; + Swap(ref current, ref other); + Swap(ref currentEntitiesCreatedPerGroup, ref otherEntitiesCreatedPerGroup); } - internal T other; - internal T current; - - internal void Swap() + void Swap(ref T item1, ref T item2) { - var toSwap = other; - other = current; - current = toSwap; + T toSwap = item2; item2 = item1; item1 = toSwap; } public void ClearOther() { - foreach (var item in other) + //do not clear the groups created so far, they will be reused + foreach (var groups in other) { - foreach (var subitem in item.Value) + //do not remove the dictionaries of entities per type created so far, they will be reused + foreach (var entitiesPerType in groups.Value) { - subitem.Value.Clear(); + //clear the dictionary of entities create do far (it won't allocate though) + entitiesPerType.Value.Clear(); } - - item.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(); + + readonly FasterDictionary _entitiesCreatedPerGroupA = new FasterDictionary(); + readonly FasterDictionary _entitiesCreatedPerGroupB = new FasterDictionary(); + + public DoubleBufferedEntitiesToAdd() + { + currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA; + otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB; - other.Clear(); + current = _entityViewsToAddBufferA; + other = _entityViewsToAddBufferB; } } } diff --git a/Svelto.ECS/EnginesRoot.Engines.cs b/Svelto.ECS/EnginesRoot.Engines.cs index ed19b8b..45c4c50 100644 --- a/Svelto.ECS/EnginesRoot.Engines.cs +++ b/Svelto.ECS/EnginesRoot.Engines.cs @@ -20,22 +20,20 @@ namespace Svelto.ECS /// public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler) { -#if DEBUG && !PROFILER - _entitiesOperationsDebug = new FasterDictionary(); -#endif - _entitiesOperations = new FasterList(); - _entityEngines = new Dictionary>(); + _entitiesOperations = new FasterDictionary(); + _reactiveEnginesAddRemove = new Dictionary>(); + _reactiveEnginesSwap = new Dictionary>(); _enginesSet = new HashSet(); _disposableEngines = new FasterList(); _transientEntitiesOperations = new FasterList(); - _groupEntityDB = new FasterDictionary>(); - _groupsPerEntity = new Dictionary>(); - _groupedEntityToAdd = new DoubleBufferedEntitiesToAdd>>(); - - _entitiesDB = new EntitiesDB(_groupEntityDB, _groupsPerEntity); - _entitiesStream = new EntitiesStream(_entitiesDB); + _groupEntityDB = new FasterDictionary>(); + _groupsPerEntity = new Dictionary>(); + _groupedEntityToAdd = new DoubleBufferedEntitiesToAdd(); + _entitiesStream = new EntitiesStream(); + _entitiesDB = new EntitiesDB(_groupEntityDB, _groupsPerEntity, _entitiesStream); + _scheduler = entityViewScheduler; _scheduler.onTick = new WeakAction(SubmitEntityViews); } @@ -48,18 +46,18 @@ namespace Svelto.ECS try { - var viewEngine = engine as IHandleEntityViewEngineAbstracted; - - if (viewEngine != null) - CheckEntityViewsEngine(viewEngine); + if (engine is IReactOnAddAndRemove viewEngine) + CheckEntityViewsEngine(viewEngine, _reactiveEnginesAddRemove); + + if (engine is IReactOnSwap viewEngineSwap) + CheckEntityViewsEngine(viewEngineSwap, _reactiveEnginesSwap); _enginesSet.Add(engine); if (engine is IDisposable) _disposableEngines.Add(engine as IDisposable); - var queryableEntityViewEngine = engine as IQueryingEntitiesEngine; - if (queryableEntityViewEngine != null) + if (engine is IQueryingEntitiesEngine queryableEntityViewEngine) { queryableEntityViewEngine.entitiesDB = _entitiesDB; queryableEntityViewEngine.Ready(); @@ -75,25 +73,19 @@ namespace Svelto.ECS } } - void CheckEntityViewsEngine(IEngine engine) + void CheckEntityViewsEngine(IEngine engine, Dictionary> engines) { - var baseType = engine.GetType().GetBaseType(); + var interfaces = engine.GetType().GetInterfaces(); - while (baseType != _objectType) + foreach (var interf in interfaces) { - if (baseType.IsGenericTypeEx()) + if (interf.IsGenericTypeEx() && typeof(IReactEngine).IsAssignableFrom(interf)) { - var genericArguments = baseType.GetGenericArgumentsEx(); - - AddEngine(engine as IHandleEntityViewEngineAbstracted, genericArguments, _entityEngines); + var genericArguments = interf.GetGenericArgumentsEx(); - return; + AddEngine(engine, genericArguments, engines); } - - baseType = baseType.GetBaseType(); } - - throw new ArgumentException("Not Supported Engine " + engine); } static void AddEngine(T engine, Type[] entityViewTypes, @@ -109,8 +101,7 @@ namespace Svelto.ECS static void AddEngine(T engine, Dictionary> engines, Type type) where T : IEngine { - FasterList list; - if (engines.TryGetValue(type, out list) == false) + if (engines.TryGetValue(type, out var list) == false) { list = new FasterList(); @@ -120,9 +111,10 @@ namespace Svelto.ECS list.Add(engine); } - readonly Dictionary> _entityEngines; - readonly HashSet _enginesSet; - readonly FasterList _disposableEngines; + readonly Dictionary> _reactiveEnginesAddRemove; + readonly Dictionary> _reactiveEnginesSwap; + readonly HashSet _enginesSet; + readonly FasterList _disposableEngines; //one datastructure rule them all: //split by group @@ -130,12 +122,17 @@ namespace Svelto.ECS //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 - readonly FasterDictionary> _groupEntityDB; - readonly EntitiesDB _entitiesDB; + //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 - readonly Dictionary> _groupsPerEntity; //yes I am being sarcastic + //found indexed by group id + //EntityViewType //groupID //entityID, EntityStruct + readonly Dictionary> _groupsPerEntity; + + readonly EntitiesStream _entitiesStream; + readonly EntitiesDB _entitiesDB; - static readonly Type _objectType = typeof(object); + static readonly Type OBJECT_TYPE = typeof(object); + static readonly Type ENTITY_INFO_VIEW_TYPE = typeof(EntityStructInfoView); } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.Entities.cs b/Svelto.ECS/EnginesRoot.Entities.cs index 25019aa..e25591f 100644 --- a/Svelto.ECS/EnginesRoot.Entities.cs +++ b/Svelto.ECS/EnginesRoot.Entities.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Svelto.Common; using Svelto.DataStructures.Experimental; using Svelto.ECS.Internal; namespace Svelto.ECS { - public partial class EnginesRoot: IDisposable + public partial class EnginesRoot : IDisposable { /// /// Dispose an EngineRoot once not used anymore, so that all the @@ -23,7 +24,7 @@ namespace Svelto.ECS { try { - entityList.Value.RemoveEntitiesFromEngines(_entityEngines, ref profiler); + entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, ref profiler); } catch (Exception e) { @@ -48,17 +49,17 @@ namespace Svelto.ECS ~EnginesRoot() { Console.LogWarning("Engines Root has been garbage collected, don't forget to call Dispose()!"); - + Dispose(); } ///-------------------------------------------- /// - public IEntitiesStream GenerateEntityStream() + public IEntityStreamConsumerFactory GenerateConsumerFactory() { - return new GenericEntitiesStream(new DataStructures.WeakReference(this)); + return new GenericentityStreamConsumerFactory(new DataStructures.WeakReference(this)); } - + public IEntityFactory GenerateEntityFactory() { return new GenericEntityFactory(new DataStructures.WeakReference(this)); @@ -70,155 +71,171 @@ namespace Svelto.ECS } ///-------------------------------------------- - - EntityStructInitializer BuildEntity(EGID entityID, object[] implementors) - where T : IEntityDescriptor, new() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + EntityStructInitializer BuildEntity(EGID entityID, object[] implementors) where T : IEntityDescriptor, new() { return BuildEntity(entityID, EntityDescriptorTemplate.descriptor, implementors); } - EntityStructInitializer BuildEntity(EGID entityID, - T entityDescriptor, - object[] implementors) where T:IEntityDescriptor + [MethodImpl(MethodImplOptions.AggressiveInlining)] + EntityStructInitializer BuildEntity(EGID entityID, T entityDescriptor, object[] implementors) + where T : IEntityDescriptor { var descriptorEntitiesToBuild = entityDescriptor.entitiesToBuild; - + CheckAddEntityID(entityID, entityDescriptor); - var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd.current, + var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, descriptorEntitiesToBuild, implementors); - + return new EntityStructInitializer(entityID, dic); } - - ///-------------------------------------------- - void Preallocate(int groupID, int size) where T : IEntityDescriptor, new() +#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 count = entityViewsToBuild.Length; + var numberOfEntityViews = entityViewsToBuild.Length; //reserve space in the database - Dictionary group; - if (_groupEntityDB.TryGetValue(groupID, out group) == false) + if (_groupEntityDB.TryGetValue(groupID, out var @group) == false) group = _groupEntityDB[groupID] = new Dictionary(); - //reserve space in building buffer - Dictionary groupBuffer; - if (_groupedEntityToAdd.current.TryGetValue(groupID, out groupBuffer) == false) - groupBuffer = _groupedEntityToAdd.current[groupID] = new Dictionary(); - - for (var index = 0; index < count; index++) + for (var index = 0; index < numberOfEntityViews; index++) { var entityViewBuilder = entityViewsToBuild[index]; - var entityViewType = entityViewBuilder.GetEntityType(); + var entityViewType = entityViewBuilder.GetEntityType(); - ITypeSafeDictionary dbList; - if (group.TryGetValue(entityViewType, out dbList) == false) + if (group.TryGetValue(entityViewType, out var dbList) == false) group[entityViewType] = entityViewBuilder.Preallocate(ref dbList, size); else - dbList.AddCapacity(size); - - if (groupBuffer.TryGetValue(entityViewType, out dbList) == false) - groupBuffer[entityViewType] = entityViewBuilder.Preallocate(ref dbList, size); - else - dbList.AddCapacity(size); + dbList.SetCapacity(size); + + if (_groupsPerEntity.TryGetValue(entityViewType, out var groupedGroup) == false) + groupedGroup = _groupsPerEntity[entityViewType] = new FasterDictionary(); + + groupedGroup[groupID] = dbList; } } - + ///-------------------------------------------- /// - void MoveEntity(IEntityBuilder[] entityBuilders, EGID entityGID, Type originalDescriptorType, EGID toEntityGID, - Dictionary toGroup = null) + void MoveEntity(IEntityBuilder[] entityBuilders, EGID fromEntityGID, Type originalDescriptorType, EGID? toEntityGID) { var profiler = new PlatformProfiler(); using (profiler.StartNewSession("Move Entity")) { //for each entity view generated by the entity descriptor - Dictionary fromGroup; - - if (_groupEntityDB.TryGetValue(entityGID.groupID, out fromGroup) == false) - throw new ECSException("from group not found eid: " - .FastConcat(entityGID.entityID).FastConcat(" group: ") - .FastConcat(entityGID.groupID)); - - ITypeSafeDictionary entityInfoViewDic; - EntityInfoView entityInfoView = default; + if (_groupEntityDB.TryGetValue(fromEntityGID.groupID, out var fromGroup) == false) + throw new ECSException("from group not found eid: ".FastConcat(fromEntityGID.entityID) + .FastConcat(" group: ").FastConcat(fromEntityGID.groupID)); //Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor! bool correctEntityDescriptorFound = true; - if (fromGroup.TryGetValue(_entityInfoView, out entityInfoViewDic) - && (entityInfoViewDic as TypeSafeDictionary).TryGetValue - (entityGID.entityID, out entityInfoView) && - (correctEntityDescriptorFound = entityInfoView.type == originalDescriptorType)) + + EntityStructInfoView entityInfoView = default; + if (fromGroup.TryGetValue(ENTITY_INFO_VIEW_TYPE, 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(entityGID, toEntityGID, toGroup, fromGroup, entitiesToMove[i].GetEntityType(), profiler); + MoveEntityView(fromEntityGID, toEntityGID, toGroup, ref fromGroup, + entitiesToMove[i].GetEntityType(), profiler); } //otherwise it's a normal static entity descriptor else { #if DEBUG && !PROFILER if (correctEntityDescriptorFound == false) -#if RELAXED_ECS - Console.LogError(INVALID_DYNAMIC_DESCRIPTOR_ERROR - .FastConcat(" ID ").FastConcat(entityGID.entityID) - .FastConcat(" group ID ").FastConcat(entityGID.groupID).FastConcat( - " descriptor found: ", entityInfoView.type.Name, - " descriptor Excepted ", originalDescriptorType.Name)); -#else - throw new ECSException(INVALID_DYNAMIC_DESCRIPTOR_ERROR.FastConcat(" ID ").FastConcat(entityGID.entityID) - .FastConcat(" group ID ").FastConcat(entityGID.groupID).FastConcat( + 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 #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(entityGID, toEntityGID, toGroup, fromGroup, entityBuilders[i].GetEntityType(), profiler); + MoveEntityView(fromEntityGID, toEntityGID, toGroup, ref fromGroup, + entityBuilders[i].GetEntityType(), profiler); } } } - void MoveEntityView(EGID entityGID, EGID toEntityGID, Dictionary toGroup, - Dictionary fromGroup, Type entityType, PlatformProfiler profiler) + void MoveEntityView(EGID entityGID, EGID? toEntityGID, Dictionary toGroup, + ref Dictionary fromGroup, Type entityViewType, PlatformProfiler profiler) { - ITypeSafeDictionary fromTypeSafeDictionary; - if (fromGroup.TryGetValue(entityType, out fromTypeSafeDictionary) == false) + if (fromGroup.TryGetValue(entityViewType, out var fromTypeSafeDictionary) == false) { - throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID).FastConcat(" group: ").FastConcat(entityGID.groupID)); + throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID) + .FastConcat(" group: ").FastConcat(entityGID.groupID)); } - - ITypeSafeDictionary dictionaryOfEntities         = null; + + ITypeSafeDictionary toEntitiesDictionary = null; //in case we want to move to a new group, otherwise is just a remove - if (toGroup != null) + if (toEntityGID != null) { - if (toGroup.TryGetValue(entityType, out dictionaryOfEntities) == false) + if (toGroup.TryGetValue(entityViewType, out toEntitiesDictionary) == false) { - dictionaryOfEntities = fromTypeSafeDictionary.Create(); - toGroup.Add(entityType, dictionaryOfEntities); + toEntitiesDictionary = fromTypeSafeDictionary.Create(); + toGroup.Add(entityViewType, toEntitiesDictionary); } - FasterDictionary groupedGroup; - if (_groupsPerEntity.TryGetValue(entityType, out groupedGroup) == false) - groupedGroup = _groupsPerEntity[entityType] = new FasterDictionary(); - - groupedGroup[toEntityGID.groupID] = dictionaryOfEntities; + if (_groupsPerEntity.TryGetValue(entityViewType, out var groupedGroup) == false) + groupedGroup = _groupsPerEntity[entityViewType] = new FasterDictionary(); + + groupedGroup[toEntityGID.Value.groupID] = toEntitiesDictionary; } if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) { - throw new EntityNotFoundException(entityGID.entityID, entityGID.groupID, entityType); + throw new EntityNotFoundException(entityGID, entityViewType); } - fromTypeSafeDictionary.MoveEntityFromDictionaryAndEngines(entityGID, toEntityGID, dictionaryOfEntities, - _entityEngines, ref profiler); + + fromTypeSafeDictionary.MoveEntityFromDictionaryAndEngines(entityGID, toEntityGID, + toEntitiesDictionary, + toEntityGID == null ? _reactiveEnginesAddRemove : + _reactiveEnginesSwap, + ref profiler); if (fromTypeSafeDictionary.Count == 0) //clean up { - _groupsPerEntity[entityType].Remove(entityGID.groupID); + _groupsPerEntity[entityViewType].Remove(entityGID.groupID); //I don't remove the group if empty on purpose, in case it needs to be reused however I trim it to save //memory @@ -226,24 +243,25 @@ namespace Svelto.ECS } } - void RemoveGroupAndEntitiesFromDB(int groupID, Type entityDescriptor) + void RemoveGroupAndEntitiesFromDB(uint groupID, Type entityDescriptor) { - /* 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.); - } - } - }*/ + 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.); + } + } + }*/ } - void RemoveGroupAndEntitiesFromDB(int groupID) + void RemoveGroupAndEntitiesFromDB(uint groupID) { var profiler = new PlatformProfiler(); using (profiler.StartNewSession("Remove Group")) @@ -252,7 +270,7 @@ namespace Svelto.ECS foreach (var dictionaryOfEntities in dictionariesOfEntities) { var platformProfiler = profiler; - dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_entityEngines, ref platformProfiler); + dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, ref platformProfiler); var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key]; groupedGroupOfEntities.Remove(groupID); } @@ -264,25 +282,23 @@ namespace Svelto.ECS } ///-------------------------------------------- - - void SwapEntityGroup(IEntityBuilder[] builders, Type originalEntityDescriptor, EGID fromEntityID, EGID toEntityID) + void SwapEntityGroup(IEntityBuilder[] builders, Type originalEntityDescriptor, EGID fromEntityID, + EGID toEntityID) { - DBC.ECS.Check.Require(fromEntityID != toEntityID, "the entity destionation EGID is equal to the source EGID"); - - Dictionary toGroup; + DBC.ECS.Check.Require(fromEntityID.groupID != toEntityID.groupID, + "entity group is the same of the destination group"); - if (_groupEntityDB.TryGetValue(toEntityID.groupID, out toGroup) == false) - toGroup = _groupEntityDB[toEntityID.groupID] = new Dictionary(); + MoveEntity(builders, fromEntityID, originalEntityDescriptor, toEntityID); + } - MoveEntity(builders, fromEntityID, originalEntityDescriptor, toEntityID, toGroup); + internal Consumer GenerateConsumer(string name, int capacity) where T : unmanaged, IEntityStruct + { + return _entitiesStream.GenerateConsumer(name, capacity); } - readonly EntitiesStream _entitiesStream; - - readonly Type _entityInfoView = typeof(EntityInfoView); - 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 "; + 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 "; } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.GenerateEntitiesStream.cs b/Svelto.ECS/EnginesRoot.GenerateEntitiesStream.cs deleted file mode 100644 index ede7b15..0000000 --- a/Svelto.ECS/EnginesRoot.GenerateEntitiesStream.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Svelto.ECS -{ - public partial class EnginesRoot - { - class GenericEntitiesStream : IEntitiesStream - { - public GenericEntitiesStream(DataStructures.WeakReference weakReference) - { - _weakEngine = weakReference; - } - - public Consumer GenerateConsumer(int capacity) where T : unmanaged, IEntityStruct - { - return _weakEngine.Target._entitiesStream.GenerateConsumer(capacity); - } - - public void PublishEntity(EGID id) where T : unmanaged, IEntityStruct - { - _weakEngine.Target._entitiesStream.PublishEntity(id); - } - - readonly DataStructures.WeakReference _weakEngine; - } - } -} \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs b/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs index 1c9fd1a..e3164e5 100644 --- a/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs +++ b/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs @@ -9,9 +9,9 @@ _weakEngine = weakReference; } - public EntityStructInitializer BuildEntity(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, object[] implementors) where T : IEntityDescriptor, new() + public EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, object[] implementors) where T : IEntityDescriptor, new() { - return _weakEngine.Target.BuildEntity(new EGID(entityID, (int)groupStructId), implementors); + return _weakEngine.Target.BuildEntity(new EGID(entityID, groupStructId), implementors); } public EntityStructInitializer BuildEntity(EGID egid, object[] implementors) where T : IEntityDescriptor, new() @@ -19,17 +19,24 @@ return _weakEngine.Target.BuildEntity(egid, implementors); } +#if REAL_ID + public EntityStructInitializer BuildEntity(ExclusiveGroup.ExclusiveGroupStruct groupID, object[] implementors = null) where T : IEntityDescriptor, new() + { + return _weakEngine.Target.BuildEntity(groupID, implementors); + } +#endif + public EntityStructInitializer BuildEntity(EGID egid, T entityDescriptor, object[] implementors) where T:IEntityDescriptor { return _weakEngine.Target.BuildEntity(egid, entityDescriptor, implementors); } - public EntityStructInitializer BuildEntity(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, object[] implementors) where T:IEntityDescriptor + public EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, object[] implementors) where T:IEntityDescriptor { - return _weakEngine.Target.BuildEntity(new EGID(entityID, (int)groupStructId), descriptorEntity, implementors); + return _weakEngine.Target.BuildEntity(new EGID(entityID, groupStructId), descriptorEntity, implementors); } - public void PreallocateEntitySpace(ExclusiveGroup.ExclusiveGroupStruct groupStructId, int size) where T : IEntityDescriptor, new() + public void PreallocateEntitySpace(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) where T : IEntityDescriptor, new() { _weakEngine.Target.Preallocate(groupStructId, size); } diff --git a/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs b/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs index baff736..4f40fad 100644 --- a/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs +++ b/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs @@ -1,11 +1,12 @@ using System; +using System.Runtime.CompilerServices; using Svelto.ECS.Internal; namespace Svelto.ECS { public partial class EnginesRoot { - class GenericEntityFunctions : IEntityFunctions + sealed class GenericEntityFunctions : IEntityFunctions { readonly DataStructures.WeakReference _weakReference; @@ -14,90 +15,98 @@ namespace Svelto.ECS _weakReference = weakReference; } - public void RemoveEntity(int entityID, int groupID) where T : IEntityDescriptor, new() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveEntity(uint entityID, uint groupID) where T : IEntityDescriptor, new() { RemoveEntity(new EGID(entityID, groupID)); } - public void RemoveEntity(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new() { RemoveEntity(new EGID(entityID, groupID)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveEntity(EGID entityEGID) where T : IEntityDescriptor, new() { _weakReference.Target.CheckRemoveEntityID(entityEGID, EntityDescriptorTemplate.descriptor); _weakReference.Target.QueueEntitySubmitOperation( - new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID.entityID, entityEGID.entityID, - entityEGID.groupID, - -1, EntityDescriptorTemplate.descriptor.entitiesToBuild, typeof(T))); + new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID, + EntityDescriptorTemplate.descriptor.entitiesToBuild, typeof(T))); } - public void RemoveEntities(int groupID) where T : IEntityDescriptor, new() + + public void RemoveEntities(uint groupID) where T : IEntityDescriptor, new() { throw new NotImplementedException(); - //_weakReference.Target.QueueEntitySubmitOperation( -// new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, -1, -1, groupID, -1, null, typeof(T))); } public void RemoveEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new() { throw new NotImplementedException(); - //_weakReference.Target.QueueEntitySubmitOperation( - // new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, -1, -1, groupID, -1, null, typeof(T))); } - public void RemoveGroupAndEntities(int groupID) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveGroupAndEntities(uint groupID) { _weakReference.Target.QueueEntitySubmitOperation( - new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, -1, -1, groupID, -1, null, null)); + new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(), new EGID(0, groupID))); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) { - RemoveGroupAndEntities((int)groupID); + RemoveGroupAndEntities((uint)groupID); } - public void SwapEntityGroup(int entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, - ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SwapEntityGroup(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, + ExclusiveGroup.ExclusiveGroupStruct toGroupID) + where T : IEntityDescriptor, new() { SwapEntityGroup(new EGID(entityID, fromGroupID), toGroupID); } - public void SwapEntityGroup(EGID id, ExclusiveGroup.ExclusiveGroupStruct toGroupID) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SwapEntityGroup(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new() { - SwapEntityGroup(id, new EGID(id.entityID, toGroupID)); + SwapEntityGroup(fromID, new EGID(fromID.entityID, (uint)toGroupID)); } - public void SwapEntityGroup(EGID id, ExclusiveGroup.ExclusiveGroupStruct toGroupID - , ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SwapEntityGroup(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID + , ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) + where T : IEntityDescriptor, new() { - if (id.groupID != mustBeFromGroup) + if (fromID.groupID != mustBeFromGroup) throw new ECSException("Entity is not coming from the expected group"); - SwapEntityGroup(id, toGroupID); + SwapEntityGroup(fromID, toGroupID); } - public void SwapEntityGroup(EGID id, EGID toID) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SwapEntityGroup(EGID fromID, EGID toID) where T : IEntityDescriptor, new() { _weakReference.Target.QueueEntitySubmitOperation( new EntitySubmitOperation(EntitySubmitOperationType.Swap, - id.entityID, toID.entityID, id.groupID, toID.groupID, - EntityDescriptorTemplate.descriptor.entitiesToBuild, typeof(T))); + fromID, toID, EntityDescriptorTemplate.descriptor.entitiesToBuild, typeof(T))); } - public void SwapEntityGroup(EGID id, EGID toID - , ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SwapEntityGroup(EGID fromID, EGID toID + , ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) + where T : IEntityDescriptor, new() { - if (id.groupID != mustBeFromGroup) + if (fromID.groupID != mustBeFromGroup) throw new ECSException("Entity is not coming from the expected group"); - SwapEntityGroup(id, toID); + SwapEntityGroup(fromID, toID); } } @@ -106,30 +115,28 @@ namespace Svelto.ECS #if DEBUG && !PROFILER entitySubmitOperation.trace = Environment.StackTrace; #endif - _entitiesOperations.AddRef(ref entitySubmitOperation); + _entitiesOperations.Add((ulong)entitySubmitOperation.fromID, ref entitySubmitOperation); } void QueueEntitySubmitOperation(EntitySubmitOperation entitySubmitOperation) where T:IEntityDescriptor { -#if DEBUG && !PROFILER +#if DEBUG && !PROFILER entitySubmitOperation.trace = Environment.StackTrace; - var egid = new EGID(entitySubmitOperation.ID, entitySubmitOperation.fromGroupID); - if (_entitiesOperationsDebug.ContainsKey((long)egid) == true) - Console.LogError("Only one entity operation per submission is allowed. id: " - .FastConcat(entitySubmitOperation.ID) - .FastConcat(" groupid: ") - .FastConcat(entitySubmitOperation.fromGroupID) - .FastConcat(" entityType: ") - .FastConcat(typeof(T).Name) - .FastConcat(" submission type ", entitySubmitOperation.type.ToString(), - " previous type: ", _entitiesOperationsDebug[(long)egid].ToString())); + + if (_entitiesOperations.TryGetValue((ulong) entitySubmitOperation.fromID, out var entitySubmitedOperation) == true) + { + if (entitySubmitedOperation != entitySubmitOperation) + Console.LogError("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 - _entitiesOperationsDebug[(long)egid] = entitySubmitOperation.type; #endif - _entitiesOperations.AddRef(ref entitySubmitOperation); + _entitiesOperations.Set((ulong)entitySubmitOperation.fromID, ref entitySubmitOperation); } -#if DEBUG && !PROFILER - readonly Svelto.DataStructures.Experimental.FasterDictionary _entitiesOperationsDebug; -#endif } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.Submission.cs b/Svelto.ECS/EnginesRoot.Submission.cs index 82918b3..1ed6b26 100644 --- a/Svelto.ECS/EnginesRoot.Submission.cs +++ b/Svelto.ECS/EnginesRoot.Submission.cs @@ -10,6 +10,8 @@ namespace Svelto.ECS { public partial class EnginesRoot { + readonly FasterList _transientEntitiesOperations; + void SubmitEntityViews() { var profiler = new PlatformProfiler(); @@ -19,11 +21,9 @@ namespace Svelto.ECS { using (profiler.Sample("Remove and Swap operations")) { -#if DEBUG && !PROFILER - _entitiesOperationsDebug.Clear(); -#endif _transientEntitiesOperations.FastClear(); - _transientEntitiesOperations.AddRange(_entitiesOperations); + var entitySubmitOperations = _entitiesOperations.GetValuesArray(out var count); + _transientEntitiesOperations.AddRange(entitySubmitOperations, count); _entitiesOperations.FastClear(); var entitiesOperations = _transientEntitiesOperations.ToArrayFast(); @@ -36,22 +36,19 @@ namespace Svelto.ECS case EntitySubmitOperationType.Swap: SwapEntityGroup(entitiesOperations[i].builders, entitiesOperations[i].entityDescriptor, - new EGID(entitiesOperations[i].ID, - entitiesOperations[i].fromGroupID), - new EGID(entitiesOperations[i].toID, - entitiesOperations[i].toGroupID)); + entitiesOperations[i].fromID, + entitiesOperations[i].toID); break; case EntitySubmitOperationType.Remove: MoveEntity(entitiesOperations[i].builders, - new EGID(entitiesOperations[i].ID, - entitiesOperations[i].fromGroupID), - entitiesOperations[i].entityDescriptor, new EGID()); + entitiesOperations[i].fromID, + entitiesOperations[i].entityDescriptor, null); break; case EntitySubmitOperationType.RemoveGroup: if (entitiesOperations[i].entityDescriptor == null) - RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromGroupID); + RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromID.groupID); else - RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromGroupID, + RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromID.groupID, entitiesOperations[i].entityDescriptor); break; @@ -59,37 +56,27 @@ namespace Svelto.ECS } catch (Exception e) { -#if DEBUG && !PROFILER var str = "Crash while executing Entity Operation" - .FastConcat(entitiesOperations[i].type.ToString()).FastConcat(" id: ") - .FastConcat(entitiesOperations[i].ID).FastConcat(" to id: ") - .FastConcat(entitiesOperations[i].toID).FastConcat(" from groupid: ") - .FastConcat(entitiesOperations[i].fromGroupID).FastConcat(" to groupid: ") - .FastConcat(entitiesOperations[i].toGroupID); -#if RELAXED_ECS - Console.LogException(str.FastConcat(" ", entitiesOperations[i].trace), e); + .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(" ").FastConcat(entitiesOperations[i].trace), e); + throw new ECSException(str.FastConcat(" ") +#if DEBUG && !PROFILER + .FastConcat(entitiesOperations[i].trace) #endif -#else - var str = "Entity Operation is ".FastConcat(entitiesOperations[i].type.ToString()) - .FastConcat(" id: ") - .FastConcat(entitiesOperations[i].ID) - .FastConcat(" to id: ") - .FastConcat(entitiesOperations[i].toID) - .FastConcat(" from groupid: ") - .FastConcat(entitiesOperations[i].fromGroupID) - .FastConcat(" to groupid: ") - .FastConcat(entitiesOperations[i].toGroupID); - - Console.LogException(str, e); + , e); #endif } } } } - if (_groupedEntityToAdd.current.Count > 0) + if (_groupedEntityToAdd.currentEntitiesCreatedPerGroup.Count > 0) { using (profiler.Sample("Add operations")) { @@ -98,18 +85,12 @@ namespace Svelto.ECS 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 it could be complicated as - //callback and database update must be interleaved. - AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd.other, profiler); + //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); } -#if !DEBUG - catch (Exception e) - { - Console.LogException(e); - } -#endif finally { //other can be cleared now, but let's avoid deleting the dictionary every time @@ -120,57 +101,63 @@ namespace Svelto.ECS } } - void AddEntityViewsToTheDBAndSuitableEngines( - FasterDictionary> groupsOfEntitiesToSubmit, - PlatformProfiler profiler) + void AddEntityViewsToTheDBAndSuitableEngines(DoubleBufferedEntitiesToAdd dbgroupsOfEntitiesToSubmit, + 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) - { - Dictionary groupDB; - int groupID = groupOfEntitiesToSubmit.Key; - + { + 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 groupDB) == false) + if (_groupEntityDB.TryGetValue(groupID, out var groupDB) == false) groupDB = _groupEntityDB[groupID] = new Dictionary(); - + //add the entityViews in the group - foreach (var entityViewTypeSafeDictionary in groupOfEntitiesToSubmit.Value) + foreach (var entityViewsToSubmit in groupOfEntitiesToSubmit.Value) { - ITypeSafeDictionary dbDic; - FasterDictionary groupedGroup = null; - if (groupDB.TryGetValue(entityViewTypeSafeDictionary.Key, out dbDic) == false) - dbDic = groupDB[entityViewTypeSafeDictionary.Key] = entityViewTypeSafeDictionary.Value.Create(); - - if (_groupsPerEntity.TryGetValue(entityViewTypeSafeDictionary.Key, out groupedGroup) == false) - groupedGroup = _groupsPerEntity[entityViewTypeSafeDictionary.Key] = - new FasterDictionary(); - + var type = entityViewsToSubmit.Key; + var typeSafeDictionary = entityViewsToSubmit.Value; + + if (groupDB.TryGetValue(type, out var dbDic) == false) + dbDic = groupDB[type] = typeSafeDictionary.Create(); + //Fill the DB with the entity views generate this frame. - dbDic.FillWithIndexedEntities(entityViewTypeSafeDictionary.Value); + dbDic.AddEntitiesFromDictionary(typeSafeDictionary, groupID); + + if (_groupsPerEntity.TryGetValue(type, out var groupedGroup) == false) + groupedGroup = _groupsPerEntity[type] = new FasterDictionary(); + groupedGroup[groupID] = dbDic; } } - //then submit everything in the engines, so that the DB is up to date - //with all the entity views and struct created by the entity built - foreach (var groupToSubmit in groupsOfEntitiesToSubmit) + //then submit everything in the engines, so that the DB is up to date with all the entity views and struct + //created by the entity built + using (profiler.Sample("Add entities to engines")) { - foreach (var entityViewsPerType in groupToSubmit.Value) + foreach (var groupToSubmit in groupsOfEntitiesToSubmit) { - using (profiler.Sample("Add entities to engines")) + var groupID = groupToSubmit.Key; + var groupDB = _groupEntityDB[groupID]; + + foreach (var entityViewsPerType in groupToSubmit.Value) { - entityViewsPerType.Value.AddEntitiesToEngines(_entityEngines, ref profiler); + var realDic = groupDB[entityViewsPerType.Key]; + + entityViewsPerType.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, realDic, ref profiler); } } } } - readonly DoubleBufferedEntitiesToAdd>> - _groupedEntityToAdd; - - readonly IEntitySubmissionScheduler _scheduler; - readonly FasterList _transientEntitiesOperations; - readonly FasterList _entitiesOperations; + readonly 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 8524ec8..737d932 100644 --- a/Svelto.ECS/EntitiesDB.cs +++ b/Svelto.ECS/EntitiesDB.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -using System.Diagnostics; +using System.Runtime.CompilerServices; using Svelto.DataStructures; using Svelto.DataStructures.Experimental; @@ -12,289 +12,235 @@ namespace Svelto.ECS.Internal { partial class EntitiesDB : IEntitiesDB { - internal EntitiesDB(FasterDictionary> groupEntityViewsDB, - Dictionary> groupedGroups) + internal EntitiesDB(FasterDictionary> groupEntityViewsDB, + Dictionary> groupsPerEntity, EntitiesStream entityStream) { _groupEntityViewsDB = groupEntityViewsDB; - _groupedGroups = groupedGroups; + _groupsPerEntity = groupsPerEntity; + _entityStream = entityStream; } - public ReadOnlyCollectionStruct QueryEntityViews(int group) where T:class, IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T QueryUniqueEntity(ExclusiveGroup.ExclusiveGroupStruct @group) where T : struct, IEntityStruct { - if (QueryEntitySafeDictionary(group, out TypeSafeDictionary typeSafeDictionary) == false) - return new ReadOnlyCollectionStruct(RetrieveEmptyEntityViewArray(), 0); + var entities = QueryEntities(@group, out var count); - return typeSafeDictionary.Values; - } - - public ReadOnlyCollectionStruct QueryEntityViews(ExclusiveGroup.ExclusiveGroupStruct group) where T : class, IEntityStruct - { - return QueryEntityViews((int) group); - } - - public T QueryEntityView(int id, ExclusiveGroup.ExclusiveGroupStruct group) where T : class, IEntityStruct - { - return QueryEntityView(new EGID(id, (int) group)); - } - - public ref T QueryUniqueEntity(int @group) where T : IEntityStruct - { - var entities = QueryEntities(group, out var count); - - if (count != 1) throw new ECSException("Unique entities must be unique!".FastConcat(typeof(T).ToString())); - + if (count != 1) throw new ECSException("Unique entities must be unique! ".FastConcat(typeof(T).ToString())); return ref entities[0]; } - - public ref T QueryUniqueEntity(ExclusiveGroup.ExclusiveGroupStruct @group) where T : IEntityStruct - { - return ref QueryUniqueEntity((int) @group); - } - public ref T QueryEntity(EGID entityGID) where T : IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T QueryEntity(EGID entityGID) where T : struct, IEntityStruct { T[] array; if ((array = QueryEntitiesAndIndexInternal(entityGID, out var index)) != null) return ref array[index]; - - throw new EntityNotFoundException(entityGID.entityID, entityGID.groupID, typeof(T)); - } - - public ref T QueryEntity(int id, ExclusiveGroup.ExclusiveGroupStruct group) where T : IEntityStruct - { - return ref QueryEntity(new EGID(id, group)); + + throw new EntityNotFoundException(entityGID, typeof(T)); } - public ref T QueryEntity(int id, int group) where T : 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)); } - public T[] QueryEntities(int group, out int count) where T : IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) where T : struct, IEntityStruct { + uint @group = groupStruct; count = 0; - if (QueryEntitySafeDictionary(group, out TypeSafeDictionary typeSafeDictionary) == false) + if (QueryEntitySafeDictionary(@group, out TypeSafeDictionary typeSafeDictionary) == false) return RetrieveEmptyEntityViewArray(); return typeSafeDictionary.GetValuesArray(out count); } - public T[] QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out int count) where T : IEntityStruct + public EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) + where T : struct, IEntityStruct + { + return new EntityCollection(QueryEntities(groupStruct, out var count), count); + } + + public EntityCollections QueryEntities(ExclusiveGroup[] groups) where T : struct, IEntityStruct { - return QueryEntities((int) groupStruct, out count); + return new EntityCollections(this, groups); } - public (T1[], T2[]) QueryEntities(int @group, out int count) where T1 : IEntityStruct where T2 : IEntityStruct + public EntityCollections QueryEntities(ExclusiveGroup[] groups) + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct { - var T1entities = QueryEntities(group, out var countCheck); - var T2entities = QueryEntities(group, out count); - - if (count != countCheck) + return new EntityCollections(this, groups); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public (T1[], T2[]) QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) + where T1 : struct, IEntityStruct + where T2 : struct, IEntityStruct + { + 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()))); + FastConcat(typeof(T1).ToString()).FastConcat( + "Entity 2: ".FastConcat(typeof(T2).ToString()))); + - return (T1entities, T2entities); } - public (T1[], T2[]) QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out int count) where T1 : IEntityStruct where T2 : IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public (T1[], T2[], T3[]) QueryEntities + (ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct { - return QueryEntities((int) 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())))); + + return (T1entities, T2entities, T3entities); } - public EGIDMapper QueryMappedEntities(int groupID) where T : IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EGIDMapper QueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId) where T : struct, IEntityStruct { - TypeSafeDictionary typeSafeDictionary; - - if (QueryEntitySafeDictionary(groupID, out typeSafeDictionary) == false) - throw new EntityGroupNotFoundException(groupID, typeof(T)); + uint groupId = groupStructId; + if (QueryEntitySafeDictionary(groupId, out TypeSafeDictionary typeSafeDictionary) == false) + throw new EntityGroupNotFoundException(groupId, typeof(T)); EGIDMapper mapper; mapper.map = typeSafeDictionary; - int count; - typeSafeDictionary.GetValuesArray(out count); + typeSafeDictionary.GetValuesArray(out _); return mapper; } - public EGIDMapper QueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId) where T : IEntityStruct - { - return QueryMappedEntities((int) groupStructId); - } - - public T[] QueryEntitiesAndIndex(EGID entityGID, out uint index) where T : IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] QueryEntitiesAndIndex(EGID entityGID, out uint index) where T : struct, IEntityStruct { T[] array; if ((array = QueryEntitiesAndIndexInternal(entityGID, out index)) != null) return array; - - throw new EntityNotFoundException(entityGID.entityID, entityGID.groupID, typeof(T)); + + throw new EntityNotFoundException(entityGID, typeof(T)); } - - public bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) where T : IEntityStruct + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) where T : struct, IEntityStruct { if ((array = QueryEntitiesAndIndexInternal(entityGid, out index)) != null) return true; - - return false; - } - - public T[] QueryEntitiesAndIndex(int id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) where T : IEntityStruct - { - return QueryEntitiesAndIndex(new EGID(id, group), out index); - } - public bool TryQueryEntitiesAndIndex(int id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) where T : IEntityStruct - { - return TryQueryEntitiesAndIndex(new EGID(id, group), out index, out array); + return false; } - public T[] QueryEntitiesAndIndex(int id, int group, out uint index) where T : IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] QueryEntitiesAndIndex(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) where T : struct, IEntityStruct { return QueryEntitiesAndIndex(new EGID(id, group), out index); } - public bool TryQueryEntitiesAndIndex(int id, int group, out uint index, out T[] array) where T : IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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); } - public T QueryEntityView(EGID entityGID) where T : class, IEntityStruct - { - T entityView; - - if (TryQueryEntityViewInGroupInternal(entityGID, out entityView) == false) - throw new EntityNotFoundException(entityGID.entityID, entityGID.groupID, typeof(T)); - - return entityView; - } - - public bool Exists(EGID entityGID) where T : IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Exists(EGID entityGID) where T : struct, IEntityStruct { - TypeSafeDictionary casted; - if (QueryEntitySafeDictionary(entityGID.groupID, out casted) == false) return false; + if (QueryEntitySafeDictionary(entityGID.groupID, out TypeSafeDictionary casted) == false) return false; return casted != null && casted.ContainsKey(entityGID.entityID); } - public bool Exists(int id, int groupid) where T : IEntityStruct - { - return Exists(new EGID(id, groupid)); - } - - //search for the group + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid) { return _groupEntityViewsDB.ContainsKey(gid); } - public bool HasAny(int group) where T : IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool HasAny(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct { - int count; - QueryEntities(group, out count); + QueryEntities(groupStruct, out var count); return count > 0; } - public bool HasAny(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : IEntityStruct - { - return HasAny((int) groupStruct); - } - - public int Count(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : IEntityStruct - { - int count; - QueryEntities(groupStruct, out count); - return count; - } - - public int Count(int groupStruct) where T : IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint Count(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct { - int count; - QueryEntities(groupStruct, out count); + QueryEntities(groupStruct, out var count); return count; } - public bool TryQueryEntityView(EGID entityegid, out T entityView) where T : class, IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PublishEntityChange(EGID egid) where T : unmanaged, IEntityStruct { - return TryQueryEntityViewInGroupInternal(entityegid, out entityView); + _entityStream.PublishEntity(ref QueryEntity(egid)); } - public bool TryQueryEntityView(int id, ExclusiveGroup.ExclusiveGroupStruct group, out T entityView) where T : class, IEntityStruct - { - return TryQueryEntityViewInGroupInternal(new EGID(id, (int) group), out entityView); - } - - bool TryQueryEntityViewInGroupInternal(EGID entityGID, out T entityView) where T:class, IEntityStruct - { - entityView = null; - TypeSafeDictionary safeDictionary; - if (QueryEntitySafeDictionary(entityGID.groupID, out safeDictionary) == false) return false; - - return safeDictionary.TryGetValue(entityGID.entityID, out entityView); - } - - T[] QueryEntitiesAndIndexInternal(EGID entityGID, out uint index) where T : IEntityStruct + [MethodImpl(MethodImplOptions.AggressiveInlining)] + T[] QueryEntitiesAndIndexInternal(EGID entityGID, out uint index) where T : struct, IEntityStruct { - TypeSafeDictionary safeDictionary; index = 0; - if (QueryEntitySafeDictionary(entityGID.groupID, out safeDictionary) == false) + if (QueryEntitySafeDictionary(entityGID.groupID, out TypeSafeDictionary safeDictionary) == false) return null; if (safeDictionary.TryFindElementIndex(entityGID.entityID, out index) == false) return null; - int count; - return safeDictionary.GetValuesArray(out count); + return safeDictionary.GetValuesArray(out _); } - - bool QueryEntitySafeDictionary(int group, out TypeSafeDictionary typeSafeDictionary) where T : IEntityStruct + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + bool QueryEntitySafeDictionary(uint group, out TypeSafeDictionary typeSafeDictionary) where T : struct, IEntityStruct { - Dictionary entitiesInGroupPerType; typeSafeDictionary = null; - //search for the group - if (_groupEntityViewsDB.TryGetValue(group, out entitiesInGroupPerType) == false) + //search for the group + if (_groupEntityViewsDB.TryGetValue(group, out var entitiesInGroupPerType) == false) return false; //search for the indexed entities in the group - ITypeSafeDictionary safeDictionary; - if (entitiesInGroupPerType.TryGetValue(typeof(T), out safeDictionary) == false) + if (entitiesInGroupPerType.TryGetValue(typeof(T), out var safeDictionary) == false) return false; //return the indexes entities if they exist typeSafeDictionary = (safeDictionary as TypeSafeDictionary); - + return true; } - [Conditional("ENABLE_DEBUG_FUNC")] - static void SafetyChecks(TypeSafeDictionary typeSafeDictionary, int count) where T : IEntityStruct - { - if (typeSafeDictionary.Count != count) - throw new ECSException("Entities cannot be swapped or removed during an iteration"); - } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] static ReadOnlyCollectionStruct RetrieveEmptyEntityViewList() { var arrayFast = FasterList.DefaultList.ToArrayFast(); - + return new ReadOnlyCollectionStruct(arrayFast, 0); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] static T[] RetrieveEmptyEntityViewArray() { return FasterList.DefaultList.ToArrayFast(); } - + //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 + //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> _groupEntityViewsDB; //needed to be able to iterate over all the entities of the same type regardless the group //may change in future - readonly Dictionary> _groupedGroups; + readonly Dictionary> _groupsPerEntity; + readonly EntitiesStream _entityStream; } } diff --git a/Svelto.ECS/EntityBuilder.CheckFields.cs b/Svelto.ECS/EntityBuilder.CheckFields.cs index 51cb6e5..118c5fa 100644 --- a/Svelto.ECS/EntityBuilder.CheckFields.cs +++ b/Svelto.ECS/EntityBuilder.CheckFields.cs @@ -7,41 +7,16 @@ using System.Reflection; namespace Svelto.ECS { - public partial class EntityBuilder + public static class EntityBuilderUtilities { #if DISABLE_CHECKS [Conditional("_CHECKS_DISABLED")] #endif - static void CheckFields(Type type, bool needsReflection, bool isRoot) + public static void CheckFields(Type type, bool needsReflection) { - if (ENTITY_VIEW_TYPE == ENTITYINFOVIEW_TYPE || type == EGIDType || type == ECLUSIVEGROUPSTRUCTTYPE) + if (type == ENTITY_STRUCT_INFO_VIEW || type == EGIDType || type == ECLUSIVEGROUPSTRUCTTYPE) return; - { - var methods = type.GetMethods(BindingFlags.Public | - BindingFlags.Instance | BindingFlags.DeclaredOnly); - - var properties = type.GetProperties(BindingFlags.Public | - BindingFlags.Instance | BindingFlags.DeclaredOnly); - - if (isRoot) - { - if (properties.Length > 1) - ProcessError("Entity views cannot have public methods or properties.", type); - - if (methods.Length > properties.Length + 1) - ProcessError("Entity views cannot have public methods or properties.", type); - } - else - { - if (properties.Length > 0) - ProcessError("Entity components fields cannot have public methods or properties.", type); - - if (methods.Length > 0) - ProcessError("Entity components fields cannot have public methods or properties.", type); - } - } - if (needsReflection == false) { if (type.IsClass) @@ -53,6 +28,8 @@ namespace Svelto.ECS { var field = fields[i]; var fieldFieldType = field.FieldType; + + if (fieldFieldType == STRINGTYPE) continue; SubCheckFields(fieldFieldType); } @@ -97,7 +74,7 @@ namespace Svelto.ECS { if (fieldFieldType.IsValueType == true && !fieldFieldType.IsEnum && fieldFieldType.IsPrimitive == false) { - CheckFields(fieldFieldType, false, false); + CheckFields(fieldFieldType, false); } return; @@ -110,6 +87,7 @@ namespace Svelto.ECS static void ProcessError(string message, Type type) { #if !RELAXED_ECS + Type ENTITY_VIEW_TYPE = typeof(Type); throw new EntityStructException(message, ENTITY_VIEW_TYPE, type); #endif } @@ -119,6 +97,8 @@ namespace Svelto.ECS 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); } public class EntityStructException : Exception diff --git a/Svelto.ECS/EntityBuilder.cs b/Svelto.ECS/EntityBuilder.cs index 29f68d0..d8cec9e 100644 --- a/Svelto.ECS/EntityBuilder.cs +++ b/Svelto.ECS/EntityBuilder.cs @@ -7,15 +7,15 @@ using Svelto.Utilities; namespace Svelto.ECS { - public partial class EntityBuilder : IEntityBuilder where T : IEntityStruct, new() + public class EntityBuilder : IEntityBuilder where T : struct, IEntityStruct { public EntityBuilder() { _initializer = DEFAULT_IT; - CheckFields(ENTITY_VIEW_TYPE, NEEDS_REFLECTION, true); + EntityBuilderUtilities.CheckFields(ENTITY_VIEW_TYPE, NEEDS_REFLECTION); - if (NEEDS_REFLECTION == true) + if (NEEDS_REFLECTION) EntityView.InitCache(); } @@ -26,69 +26,63 @@ namespace Svelto.ECS var castedDic = dictionary as TypeSafeDictionary; - if (NEEDS_REFLECTION == true) + 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((long)entityID).FastConcat(" ", ENTITY_VIEW_NAME)); + "building an entity with already used entity id! id: ".FastConcat((ulong) entityID) + .FastConcat(" ", ENTITY_VIEW_NAME)); - T entityView; - EntityView.BuildEntityView(entityID, out entityView); + EntityView.BuildEntityView(out var entityView); - this.FillEntityView(ref entityView, entityViewBlazingFastReflection, implementors, implementorsByType, + this.FillEntityView(ref entityView, entityViewBlazingFastReflection, implementors, implementorsByType, cachedTypes); castedDic.Add(entityID.entityID, ref entityView); } else { - _initializer.ID = entityID; - castedDic.Add(entityID.entityID, _initializer); } } - ITypeSafeDictionary IEntityBuilder.Preallocate(ref ITypeSafeDictionary dictionary, int size) + ITypeSafeDictionary IEntityBuilder.Preallocate(ref ITypeSafeDictionary dictionary, uint size) { return Preallocate(ref dictionary, size); } - public static ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, int size) + static ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size) { if (dictionary == null) dictionary = new TypeSafeDictionary(size); else - dictionary.AddCapacity(size); + dictionary.SetCapacity(size); return dictionary; } - public Type GetEntityType() - { - return ENTITY_VIEW_TYPE; - } - + public Type GetEntityType() { return ENTITY_VIEW_TYPE; } + #if DEBUG && !PROFILER - readonly Dictionary> implementorsByType = new Dictionary>(); + readonly Dictionary> implementorsByType = + new Dictionary>(); #else readonly Dictionary implementorsByType = new Dictionary(); #endif - + //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(); - static FasterList>> entityViewBlazingFastReflection - { - get { return EntityView.cachedFields; } - } - - static readonly Type ENTITY_VIEW_TYPE = typeof(T); - static readonly T DEFAULT_IT = default(T); - static readonly Type ENTITYINFOVIEW_TYPE = typeof(EntityInfoView); - static readonly bool NEEDS_REFLECTION = typeof(IEntityViewStruct).IsAssignableFrom(typeof(T)); - static readonly string ENTITY_VIEW_NAME = ENTITY_VIEW_TYPE.ToString(); + static FasterList>> entityViewBlazingFastReflection => + EntityView.cachedFields; + + internal static readonly Type ENTITY_VIEW_TYPE = typeof(T); + static readonly T DEFAULT_IT = default; + static readonly Type ENTITYINFOVIEW_TYPE = typeof(EntityStructInfoView); + 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 T _initializer; - } } \ No newline at end of file diff --git a/Svelto.ECS/EntityBuilder.cs.rej b/Svelto.ECS/EntityBuilder.cs.rej new file mode 100644 index 0000000..9ea149e --- /dev/null +++ b/Svelto.ECS/EntityBuilder.cs.rej @@ -0,0 +1,8 @@ +diff a/Assets/Svelto/Svelto.ECS/EntityBuilder.cs b/Assets/Svelto/Svelto.ECS/EntityBuilder.cs (rejected hunks) +@@ -1,5 +1,6 @@ + using System; + using System.Collections.Generic; ++using DBC.ECS; + using Svelto.DataStructures; + using Svelto.ECS.Hybrid; + using Svelto.ECS.Internal; diff --git a/Svelto.ECS/EntityCollection.cs b/Svelto.ECS/EntityCollection.cs new file mode 100644 index 0000000..2a8ccc8 --- /dev/null +++ b/Svelto.ECS/EntityCollection.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Svelto.ECS.Internal; + +namespace Svelto.ECS +{ + public struct EntityCollection + { + public EntityCollection(T[] array, uint count) + { + _array = array; + _count = count; + } + + public EntityIterator GetEnumerator() { return new EntityIterator(_array, _count); } + + readonly T[] _array; + readonly uint _count; + } + public struct EntityCollections where T : struct, IEntityStruct + { + public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this() + { + _db = db; + _groups = groups; + } + + public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); } + + readonly IEntitiesDB _db; + readonly ExclusiveGroup[] _groups; + } + + public struct EntityCollections where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct + { + public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this() + { + _db = db; + _groups = groups; + } + + public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); } + + readonly IEntitiesDB _db; + readonly ExclusiveGroup[] _groups; + } + + public struct EntityGroupsIterator : IEnumerator where T : struct, IEntityStruct + { + public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this() + { + _db = db; + _groups = groups; + _indexGroup = -1; + _index = -1; + } + + public bool MoveNext() + { + while (_index + 1 >= _count && ++_indexGroup < _groups.Length) + { + _index = -1; + _array = _db.QueryEntities(_groups[_indexGroup], out _count); + } + + if (++_index < _count) + { + return true; + } + + return false; + } + + public void Reset() + { + _index = -1; + _indexGroup = -1; + _array = _db.QueryEntities(_groups[0], out _count); + } + + public ref T Current => ref _array[_index]; + + T IEnumerator. Current => throw new NotImplementedException(); + object IEnumerator.Current => throw new NotImplementedException(); + public void Dispose() { } + + readonly IEntitiesDB _db; + readonly ExclusiveGroup[] _groups; + T[] _array; + uint _count; + int _index; + int _indexGroup; + } + + public struct ValueRef + { + readonly T1[] array1; + readonly T2[] array2; + + readonly uint index; + + public ValueRef(T1[] entity1, T2[] entity2, uint i):this() + { + array1 = entity1; + array2 = entity2; + index = i; + } + + public ref T1 Item1 => ref array1[index]; + public ref T2 Item2 => ref array2[index]; + } + + public struct EntityGroupsIterator : IEnumerator> where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct + { + public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this() + { + _db = db; + _groups = groups; + _indexGroup = -1; + _index = -1; + } + + public bool MoveNext() + { + while (_index + 1 >= _count && ++_indexGroup < _groups.Length) + { + _index = -1; + _value = new ValueRef(_db.QueryEntities(_groups[_indexGroup], out _count), + _db.QueryEntities(_groups[_indexGroup], out var count1), (uint) _index + 1); + +#if DEBUG && !PROFILER + if (_count != count1) + throw new ECSException("number of entities in group doesn't match"); +#endif + } + + if (++_index < _count) + { + return true; + } + + return false; + } + + public void Reset() + { + _index = -1; + _indexGroup = -1; + _value = new ValueRef(_db.QueryEntities(_groups[_indexGroup], out _count), + _db.QueryEntities(_groups[_indexGroup], out var count1), 0); +#if DEBUG && !PROFILER + if (_count != count1) + throw new ECSException("number of entities in group doesn't match"); +#endif + + } + + public ValueRef Current => _value; + + ValueRef IEnumerator>. Current => throw new NotImplementedException(); + object IEnumerator.Current => throw new NotImplementedException(); + public void Dispose() { } + + readonly IEntitiesDB _db; + readonly ExclusiveGroup[] _groups; + uint _count; + int _index; + int _indexGroup; + ValueRef _value; + } + + public struct EntityIterator : IEnumerator + { + public EntityIterator(T[] array, uint count) : this() + { + _array = array; + _count = count; + _index = -1; + } + + public bool MoveNext() { return ++_index < _count; } + public void Reset() { _index = -1; } + + public ref T Current => ref _array[_index]; + + T IEnumerator. Current => throw new NotImplementedException(); + object IEnumerator.Current => throw new NotImplementedException(); + public void Dispose() { } + + readonly T[] _array; + readonly uint _count; + int _index; + } +} \ No newline at end of file diff --git a/Svelto.ECS/EntityFactory.cs b/Svelto.ECS/EntityFactory.cs index 5956350..42674db 100644 --- a/Svelto.ECS/EntityFactory.cs +++ b/Svelto.ECS/EntityFactory.cs @@ -1,42 +1,42 @@ using System; using System.Collections.Generic; -using Svelto.DataStructures.Experimental; namespace Svelto.ECS.Internal { static class EntityFactory { - internal static Dictionary - BuildGroupedEntities(EGID egid, - FasterDictionary> groupEntityViewsByType, - IEntityBuilder[] entitiesToBuild, - object[] implementors) + internal static Dictionary BuildGroupedEntities(EGID egid, + EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd, IEntityBuilder[] entitiesToBuild, + object[] implementors) { - var @group = FetchEntityGroup(egid.groupID, groupEntityViewsByType); + var @group = FetchEntityGroup(egid.groupID, groupEntitiesToAdd); BuildEntitiesAndAddToGroup(egid, group, entitiesToBuild, implementors); return group; } - static Dictionary FetchEntityGroup(int groupID, - FasterDictionary> groupEntityViewsByType) + static Dictionary FetchEntityGroup(uint groupID, + EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityViewsByType) { - Dictionary group; - - if (groupEntityViewsByType.TryGetValue(groupID, out @group) == false) + if (groupEntityViewsByType.current.TryGetValue(groupID, out Dictionary @group) == + false) { @group = new Dictionary(); - groupEntityViewsByType.Add(groupID, @group); + + groupEntityViewsByType.current.Add(groupID, @group); } + groupEntityViewsByType.currentEntitiesCreatedPerGroup.TryGetValue(groupID, out var value); + groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = value+1; + return @group; } - static void BuildEntitiesAndAddToGroup(EGID entityID, - Dictionary @group, - IEntityBuilder[] entitiesToBuild, - object[] implementors) + static void BuildEntitiesAndAddToGroup(EGID entityID, + Dictionary @group, + IEntityBuilder[] entitiesToBuild, + object[] implementors) { var count = entitiesToBuild.Length; #if DEBUG && !PROFILER @@ -44,35 +44,32 @@ namespace Svelto.ECS.Internal for (var index = 0; index < count; ++index) { - var entityType = entitiesToBuild[index].GetEntityType(); - if (types.Contains(entityType)) + var entityViewType = entitiesToBuild[index].GetEntityType(); + if (types.Contains(entityViewType)) { throw new ECSException("EntityBuilders must be unique inside an EntityDescriptor"); } - types.Add(entityType); + types.Add(entityViewType); } -#endif - +#endif for (var index = 0; index < count; ++index) { var entityViewBuilder = entitiesToBuild[index]; - var entityViewType = entityViewBuilder.GetEntityType(); + var entityViewType = entityViewBuilder.GetEntityType(); BuildEntity(entityID, @group, entityViewType, entityViewBuilder, implementors); } } - static void BuildEntity(EGID entityID, Dictionary @group, - Type entityViewType, IEntityBuilder entityBuilder, object[] implementors) + static void BuildEntity(EGID entityID, Dictionary @group, Type entityViewType, + IEntityBuilder entityBuilder, object[] implementors) { - ITypeSafeDictionary safeDictionary; - - var entityViewsPoolWillBeCreated = @group.TryGetValue(entityViewType, out safeDictionary) == false; + var entityViewsPoolWillBeCreated = @group.TryGetValue(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 type. + //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 + //type. entityBuilder.BuildEntityAndAddToList(ref safeDictionary, entityID, implementors); if (entityViewsPoolWillBeCreated) diff --git a/Svelto.ECS/EntityGroupNotFoundException.cs b/Svelto.ECS/EntityGroupNotFoundException.cs index 8db2ecd..63fb6e6 100644 --- a/Svelto.ECS/EntityGroupNotFoundException.cs +++ b/Svelto.ECS/EntityGroupNotFoundException.cs @@ -4,7 +4,7 @@ namespace Svelto.ECS.Internal { class EntityGroupNotFoundException : Exception { - public EntityGroupNotFoundException(int groupId, Type type) + public EntityGroupNotFoundException(uint groupId, Type type) : base("entity group not found ".FastConcat(type.ToString())) { } diff --git a/Svelto.ECS/EntityHierarchyStruct.cs b/Svelto.ECS/EntityHierarchyStruct.cs new file mode 100644 index 0000000..6bf4f48 --- /dev/null +++ b/Svelto.ECS/EntityHierarchyStruct.cs @@ -0,0 +1,11 @@ +namespace Svelto.ECS +{ + public struct EntityHierarchyStruct: IEntityStruct, INeedEGID + { + public readonly ExclusiveGroup.ExclusiveGroupStruct parentGroup; + + public EntityHierarchyStruct(ExclusiveGroup @group): this() { parentGroup = group; } + + public EGID ID { get; set; } + } +} \ No newline at end of file diff --git a/Svelto.ECS/EntityInfoView.cs b/Svelto.ECS/EntityInfoView.cs index a1f8276..16d2442 100644 --- a/Svelto.ECS/EntityInfoView.cs +++ b/Svelto.ECS/EntityInfoView.cs @@ -2,7 +2,7 @@ using System; namespace Svelto.ECS { - public struct EntityInfoView : IEntityStruct + public struct EntityStructInfoView: IEntityStruct, INeedEGID { public EGID ID { get; set; } public Type type { get; set; } diff --git a/Svelto.ECS/EntityNotFoundException.cs b/Svelto.ECS/EntityNotFoundException.cs index a1d27ca..b8de5e4 100644 --- a/Svelto.ECS/EntityNotFoundException.cs +++ b/Svelto.ECS/EntityNotFoundException.cs @@ -4,7 +4,7 @@ namespace Svelto.ECS { public class EntityNotFoundException : Exception { - public EntityNotFoundException(int entityGidEntityId, int entityGidGroupId, Type type) + public EntityNotFoundException(EGID entityGidEntityId, Type type) : base("entity not found ".FastConcat(type.ToString())) {} } diff --git a/Svelto.ECS/EntityStream.cs b/Svelto.ECS/EntityStream.cs index 127083f..adbfff9 100644 --- a/Svelto.ECS/EntityStream.cs +++ b/Svelto.ECS/EntityStream.cs @@ -1,16 +1,10 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using Svelto.DataStructures; namespace Svelto.ECS { - public interface IEntitiesStream - { - Consumer GenerateConsumer(int capacity) where T : unmanaged, IEntityStruct; - - void PublishEntity(EGID id) where T : unmanaged, IEntityStruct; - } - /// /// Do not use this class in place of a normal polling. /// I eventually realised than in ECS no form of communication other than polling entity components can exist. @@ -22,31 +16,25 @@ namespace Svelto.ECS /// one only /// - you want to communicate between EnginesRoots /// - class EntitiesStream : IEntitiesStream + class EntitiesStream { - public EntitiesStream(IEntitiesDB entitiesDb) - { - _entitiesDB = entitiesDb; - } - - public Consumer GenerateConsumer(int capacity) where T : unmanaged, IEntityStruct + internal Consumer GenerateConsumer(string name, int capacity) where T : unmanaged, IEntityStruct { if (_streams.ContainsKey(typeof(T)) == false) _streams[typeof(T)] = new EntityStream(); - return (_streams[typeof(T)] as EntityStream).GenerateConsumer(capacity); + return (_streams[typeof(T)] as EntityStream).GenerateConsumer(name, capacity); } - public void PublishEntity(EGID id) where T : unmanaged, IEntityStruct + internal void PublishEntity(ref T entity) where T : unmanaged, IEntityStruct { if (_streams.TryGetValue(typeof(T), out var typeSafeStream)) - (typeSafeStream as EntityStream).PublishEntity(ref _entitiesDB.QueryEntity(id)); + (typeSafeStream as EntityStream).PublishEntity(ref entity); else Console.LogWarning("No Consumers are waiting for this entity to change " .FastConcat(typeof(T).ToString())); } - readonly Dictionary _streams = new Dictionary(); - readonly IEntitiesDB _entitiesDB; + readonly ConcurrentDictionary _streams = new ConcurrentDictionary(); } interface ITypeSafeStream @@ -60,46 +48,61 @@ namespace Svelto.ECS _buffers[i].Enqueue(ref entity); } - public Consumer GenerateConsumer(int capacity) + public Consumer GenerateConsumer(string name, int capacity) { - var consumer = new Consumer(capacity); + var consumer = new Consumer(name, capacity, this); _buffers.Add(consumer); return consumer; } + + public void RemoveConsumer(Consumer consumer) + { + _buffers.UnorderedRemove(consumer); + } - readonly FasterList> _buffers = new FasterList>(); + readonly FasterListThreadSafe> _buffers = new FasterListThreadSafe>(); } - public class Consumer where T:unmanaged, IEntityStruct + public struct Consumer: IDisposable where T:unmanaged, IEntityStruct { - public Consumer(int capacity) + internal Consumer(string name, int capacity, EntityStream stream) { _ringBuffer = new RingBuffer(capacity); - _capacity = capacity; + _name = name; + _stream = stream; } - public void Enqueue(ref T entity) + internal void Enqueue(ref T entity) { - if (_ringBuffer.Count >= _capacity) - throw new Exception("EntityStream capacity has been saturated"); - - _ringBuffer.Enqueue(ref entity); + _ringBuffer.Enqueue(ref entity, _name); } - + + /// + /// this can be better, I probably would need to get the group regardless if it supports EGID or not + /// + /// + /// + /// public bool TryDequeue(ExclusiveGroup group, out T entity) { - if (_ringBuffer.TryDequeue(out entity) == true) - return entity.ID.groupID == @group; + if (_ringBuffer.TryDequeue(out entity, _name) == true) + { + if (EntityBuilder.HAS_EGID) + return (entity as INeedEGID).ID.groupID == @group; + + return true; + } return false; } - - public bool TryDequeue(out T entity) { return _ringBuffer.TryDequeue(out entity); } + public bool TryDequeue(out T entity) { return _ringBuffer.TryDequeue(out entity, _name); } public void Flush() { _ringBuffer.Reset(); } + public void Dispose() { _stream.RemoveConsumer(this); } - readonly RingBuffer _ringBuffer; - readonly int _capacity; - + readonly RingBuffer _ringBuffer; + readonly EntityStream _stream; + readonly string _name; + } } \ No newline at end of file diff --git a/Svelto.ECS/EntityStructInitializer.cs b/Svelto.ECS/EntityStructInitializer.cs index fdedb52..90194e5 100644 --- a/Svelto.ECS/EntityStructInitializer.cs +++ b/Svelto.ECS/EntityStructInitializer.cs @@ -6,26 +6,31 @@ namespace Svelto.ECS { public struct EntityStructInitializer { - public EntityStructInitializer(EGID id, Dictionary current) + public EntityStructInitializer(EGID id, Dictionary @group) { - _current = current; - _id = id; + _group = @group; + ID = id; } public void Init(T initializer) where T: struct, IEntityStruct { - DBC.ECS.Check.Require(_current.ContainsKey(typeof(T) )== true, - "Entity to initialise not generated by the EntiyDescriptor"); - - var typeSafeDictionary = (TypeSafeDictionary) _current[typeof(T)]; + if (_group.TryGetValue(EntityBuilder.ENTITY_VIEW_TYPE, out var typeSafeDictionary) == true) + { + var dictionary = typeSafeDictionary as TypeSafeDictionary; - initializer.ID = _id; + if (EntityBuilder.HAS_EGID) + { + var needEgid = ((INeedEGID) initializer); + needEgid.ID = ID; + initializer = (T) needEgid; + } - int count; - typeSafeDictionary.GetValuesArray(out count)[typeSafeDictionary.FindElementIndex(_id.entityID)] = initializer; + if (dictionary.TryFindElementIndex(ID.entityID, out var findElementIndex)) + dictionary.GetValuesArray(out _)[findElementIndex] = initializer; + } } - - readonly Dictionary _current; - readonly EGID _id; + + readonly EGID ID; + readonly Dictionary _group; } } \ No newline at end of file diff --git a/Svelto.ECS/EntitySubmitOperation.cs b/Svelto.ECS/EntitySubmitOperation.cs index c3bb880..7570474 100644 --- a/Svelto.ECS/EntitySubmitOperation.cs +++ b/Svelto.ECS/EntitySubmitOperation.cs @@ -2,41 +2,55 @@ namespace Svelto.ECS { +#pragma warning disable 660,661 struct EntitySubmitOperation +#pragma warning restore 660,661 + : IEquatable { public readonly EntitySubmitOperationType type; public readonly IEntityBuilder[] builders; - public readonly int ID; - public readonly int toID; - public readonly int toGroupID; - public readonly int fromGroupID; + public readonly EGID fromID; + public readonly EGID toID; public readonly Type entityDescriptor; #if DEBUG && !PROFILER public string trace; #endif - public EntitySubmitOperation( - EntitySubmitOperationType operation, int entityId, int toId, int fromGroupId, int toGroupId, - IEntityBuilder[] builders, Type entityDescriptor) + public EntitySubmitOperation(EntitySubmitOperationType operation, EGID from, EGID to, + IEntityBuilder[] builders = null, + Type entityDescriptor = null) { - type = operation; + type = operation; this.builders = builders; - ID = entityId; - toID = toId; + fromID = from; + toID = to; - toGroupID = toGroupId; - fromGroupID = fromGroupId; this.entityDescriptor = entityDescriptor; #if DEBUG && !PROFILER trace = string.Empty; #endif } + + public static bool operator ==(EntitySubmitOperation obj1, EntitySubmitOperation obj2) + { + return obj1.Equals(obj2); + } + + public static bool operator !=(EntitySubmitOperation obj1, EntitySubmitOperation obj2) + { + return obj1.Equals(obj2) == false; + } + + public bool Equals(EntitySubmitOperation other) + { + return type == other.type && fromID == other.fromID && toID == other.toID; + } } enum EntitySubmitOperationType { Swap, Remove, - RemoveGroup, + RemoveGroup } } \ No newline at end of file diff --git a/Svelto.ECS/EntityView.cs b/Svelto.ECS/EntityView.cs index f277c54..f52afa6 100644 --- a/Svelto.ECS/EntityView.cs +++ b/Svelto.ECS/EntityView.cs @@ -6,7 +6,7 @@ using Svelto.Utilities; namespace Svelto.ECS { - static class EntityView where T: IEntityStruct, new() + static class EntityView where T: struct, IEntityStruct { internal static readonly FasterList>> cachedFields; @@ -32,9 +32,9 @@ namespace Svelto.ECS internal static void InitCache() {} - internal static void BuildEntityView(EGID ID, out T entityView) + internal static void BuildEntityView(out T entityView) { - entityView = new T { ID = ID }; + entityView = new T {}; } } } \ No newline at end of file diff --git a/Svelto.ECS/EntityViewUtility.cs b/Svelto.ECS/EntityViewUtility.cs index 949be8f..76190ef 100644 --- a/Svelto.ECS/EntityViewUtility.cs +++ b/Svelto.ECS/EntityViewUtility.cs @@ -38,12 +38,10 @@ namespace Svelto.ECS , Dictionary cachedTypes ) { - int count; - //efficient way to collect the fields of every EntityViewType var setters = FasterList>> - .NoVirt.ToArrayFast(entityViewBlazingFastReflection, out count); + .NoVirt.ToArrayFast(entityViewBlazingFastReflection, out var count); for (var index = 0; index < implementors.Length; index++) { @@ -53,17 +51,14 @@ namespace Svelto.ECS { var type = implementor.GetType(); - Type[] interfaces; - if (cachedTypes.TryGetValue(type, out interfaces) == false) + if (cachedTypes.TryGetValue(type, out var interfaces) == false) interfaces = cachedTypes[type] = type.GetInterfacesEx(); for (var iindex = 0; iindex < interfaces.Length; iindex++) { var componentType = interfaces[iindex]; #if DEBUG && !PROFILER - ECSTuple implementorData; - - if (implementorsByType.TryGetValue(componentType, out implementorData)) + if (implementorsByType.TryGetValue(componentType, out var implementorData)) { implementorData.numberOfImplementations++; implementorsByType[componentType] = implementorData; diff --git a/Svelto.ECS/ExclusiveGroup.cs b/Svelto.ECS/ExclusiveGroup.cs index eccde68..2b4ef91 100644 --- a/Svelto.ECS/ExclusiveGroup.cs +++ b/Svelto.ECS/ExclusiveGroup.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; #pragma warning disable 660,661 @@ -41,12 +41,12 @@ namespace Svelto.ECS return group._group; } - public static explicit operator int(ExclusiveGroup group) + public static explicit operator uint(ExclusiveGroup group) { return group._group; } - public static ExclusiveGroupStruct operator+(ExclusiveGroup a, int b) + public static ExclusiveGroupStruct operator+(ExclusiveGroup a, uint b) { return a._group + b; } @@ -91,7 +91,7 @@ namespace Svelto.ECS { ExclusiveGroupStruct groupStruct; - groupStruct._id = (int) _globalId; + groupStruct._id = _globalId; DBC.ECS.Check.Require(_globalId + 1 < ushort.MaxValue, "too many exclusive groups created"); _globalId++; @@ -103,22 +103,22 @@ namespace Svelto.ECS /// internal ExclusiveGroupStruct(ushort range) { - _id = (int) _globalId; + _id = _globalId; DBC.ECS.Check.Require(_globalId + range < ushort.MaxValue, "too many exclusive groups created"); _globalId += range; } - internal ExclusiveGroupStruct(int groupID) + internal ExclusiveGroupStruct(uint groupID) { _id = groupID; } - public static implicit operator int(ExclusiveGroupStruct groupStruct) + public static implicit operator uint(ExclusiveGroupStruct groupStruct) { return groupStruct._id; } - public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, int b) + public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, uint b) { var group = new ExclusiveGroupStruct(); @@ -127,7 +127,7 @@ namespace Svelto.ECS return group; } - int _id; + uint _id; static uint _globalId; } diff --git a/Svelto.ECS/ExecuteOnEntitiesDB.cs b/Svelto.ECS/ExecuteOnEntitiesDB.cs index c94659b..9b9cb2e 100644 --- a/Svelto.ECS/ExecuteOnEntitiesDB.cs +++ b/Svelto.ECS/ExecuteOnEntitiesDB.cs @@ -1,161 +1,44 @@ +using System; + namespace Svelto.ECS.Internal { partial class EntitiesDB { - public void ExecuteOnEntity(int id, - ExclusiveGroup.ExclusiveGroupStruct groupid, - EntityAction action) where T : IEntityStruct - { - ExecuteOnEntity(id, (int)groupid, action); - } - - public void ExecuteOnEntity(EGID entityGID, ref W value, EntityAction action) where T: IEntityStruct - { - TypeSafeDictionary casted; - if (QueryEntitySafeDictionary(entityGID.groupID, out casted)) - { - if (casted != null) - if (casted.ExecuteOnEntityView(entityGID.entityID, ref value, action)) - return; - } - - throw new EntityNotFoundException(entityGID.entityID, entityGID.groupID, typeof(T)); - } - - public void ExecuteOnEntity(EGID entityGID, EntityAction action) where T : IEntityStruct - { - TypeSafeDictionary casted; - if (QueryEntitySafeDictionary(entityGID.groupID, out casted)) - { - if (casted != null) - if (casted.ExecuteOnEntityView(entityGID.entityID, action)) - return; - } - - throw new EntityNotFoundException(entityGID.entityID, entityGID.groupID, typeof(T)); - } - - public void ExecuteOnEntity(int id, int groupid, EntityAction action) where T : IEntityStruct - { - ExecuteOnEntity(new EGID(id, groupid), action); - } - - public void ExecuteOnEntity(int id, int groupid, ref W value, EntityAction action) - where T : IEntityStruct - { - ExecuteOnEntity(new EGID(id, groupid), ref value, action); - } - - public void ExecuteOnEntity(int id, - ExclusiveGroup.ExclusiveGroupStruct groupid, - ref W value, - EntityAction action) where T : IEntityStruct - { - ExecuteOnEntity(id, (int)groupid, ref value, action); - } - - //---------------------------------------------------------------------------------------------------------- - - public void ExecuteOnEntities(int groupID, EntitiesAction action) where T : IEntityStruct - { - if (QueryEntitySafeDictionary(groupID, out TypeSafeDictionary typeSafeDictionary) == false) return; - - var entities = typeSafeDictionary.GetValuesArray(out var count); - - for (var i = 0; i < count; i++) - action(ref entities[i], new EntityActionData(this, i)); - - SafetyChecks(typeSafeDictionary, count); - } - - public void ExecuteOnEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId, - EntitiesAction action) where T : IEntityStruct - { - ExecuteOnEntities((int)groupStructId, action); - } - - public void ExecuteOnEntities(int groupID, ref W value, EntitiesAction action) where T:IEntityStruct - { - if (QueryEntitySafeDictionary(groupID, out TypeSafeDictionary typeSafeDictionary) == false) return; - - var entities = typeSafeDictionary.GetValuesArray(out var count); - - for (var i = 0; i < count; i++) - action(ref entities[i], ref value, new EntityActionData(this, i)); - - SafetyChecks(typeSafeDictionary, count); - } - - public void ExecuteOnEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId, - ref W value, EntitiesAction action) where T : IEntityStruct - { - ExecuteOnEntities((int)groupStructId, ref value, action); - } - - //----------------------------------------------------------------------------------------------------------- - - public void ExecuteOnAllEntities(AllEntitiesAction action) where T : IEntityStruct + public void ExecuteOnAllEntities(Action action) + where T : struct, IEntityStruct { var type = typeof(T); - if (_groupedGroups.TryGetValue(type, out var dic)) + if (_groupsPerEntity.TryGetValue(type, out var dic)) { - var typeSafeDictionaries = dic.GetValuesArray(out var count); - - for (int j = 0; j < count; j++) + foreach (var pair in dic) { - var typeSafeDictionary = typeSafeDictionaries[j]; - var casted = typeSafeDictionary as TypeSafeDictionary; + var entities = + (pair.Value as TypeSafeDictionary).GetValuesArray(out var innerCount); - var entities = casted.GetValuesArray(out var innerCount); - - for (int i = 0; i < innerCount; i++) - action(ref entities[i], this); - - SafetyChecks(casted, innerCount); + if (innerCount > 0) + action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this); } } } - public void ExecuteOnAllEntities(ref W value, AllEntitiesAction action) where T : IEntityStruct + public void ExecuteOnAllEntities + (ref W value, Action action) + where T : struct, IEntityStruct { var type = typeof(T); - if (_groupedGroups.TryGetValue(type, out var dic)) + if (_groupsPerEntity.TryGetValue(type, out var dic)) { - var typeSafeDictionaries = dic.GetValuesArray(out var count); - - for (int j = 0; j < count; j++) + foreach (var pair in dic) { - var typeSafeDictionary = typeSafeDictionaries[j]; - var casted = typeSafeDictionary as TypeSafeDictionary; - - var entities = casted.GetValuesArray(out var innerCount); + var entities = + (pair.Value as TypeSafeDictionary).GetValuesArray(out var innerCount); - for (int i = 0; i < innerCount; i++) - action(ref entities[i], ref value, this); - - SafetyChecks(casted, innerCount); + if (innerCount > 0) + action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this, value); } } } - - public void ExecuteOnAllEntities(ExclusiveGroup[] groups, EntitiesAction action) where T : IEntityStruct - { - foreach (var group in groups) - { - ExecuteOnEntities(group, action); - } - } - - public void ExecuteOnAllEntities(ExclusiveGroup[] groups, - ref W value, - EntitiesAction action) where T : IEntityStruct - { - foreach (var group in groups) - { - ExecuteOnEntities(group, ref value, action); - } - } } } \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs b/Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs index 6290e3b..85654a4 100644 --- a/Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs +++ b/Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs @@ -3,7 +3,7 @@ using UnityEngine; namespace Svelto.ECS.Unity { - public class GenericEntityDescriptorHolder: + public abstract class GenericEntityDescriptorHolder: MonoBehaviour , IEntityDescriptorHolder where T: IEntityDescriptor, new() { @@ -13,11 +13,12 @@ namespace Svelto.ECS.Unity } public string groupName => _groupName; + public ushort id => _id; - [SerializeField] -#pragma warning disable 649 - string _groupName; -#pragma warning restore 649 +#pragma warning disable 649 + [SerializeField] string _groupName; + [SerializeField] ushort _id = 0; +#pragma warning restore 649 } } #endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/SveltoEntityFactoryForUnity.cs b/Svelto.ECS/Extensions/Unity/SveltoEntityFactoryForUnity.cs deleted file mode 100644 index 71e40c2..0000000 --- a/Svelto.ECS/Extensions/Unity/SveltoEntityFactoryForUnity.cs +++ /dev/null @@ -1,38 +0,0 @@ -#if UNITY_5 || UNITY_5_3_OR_NEWER -using UnityEngine; - -namespace Svelto.ECS.Unity -{ - public static class SveltoEntityFactoryForUnity - { - public static T Create(EGID ID, Transform contextHolder, - IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder - { - var holder = contextHolder.GetComponentInChildren(true); - var implementors = holder.GetComponents(); - - factory.BuildEntity(ID, holder.GetDescriptor(), implementors); - - return holder; - } - - public static void CreateAll(ExclusiveGroup group, Transform contextHolder, - IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder - { - var holders = contextHolder.GetComponentsInChildren(true); - - foreach (var holder in holders) - { - var implementors = holder.GetComponents(); - - ExclusiveGroup.ExclusiveGroupStruct realGroup = group; - - if (string.IsNullOrEmpty( holder.groupName) == false) - realGroup = ExclusiveGroup.Search(holder.groupName); - - factory.BuildEntity(holder.GetInstanceID(), realGroup, holder.GetDescriptor(), implementors); - } - } - } -} -#endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs b/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs new file mode 100644 index 0000000..2f6cc9a --- /dev/null +++ b/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs @@ -0,0 +1,72 @@ +#if UNITY_5 || UNITY_5_3_OR_NEWER +using UnityEngine; + +namespace Svelto.ECS.Unity +{ + public static class SveltoGUIHelper + { + public static T CreateFromPrefab(uint startIndex, Transform contextHolder, IEntityFactory factory, ExclusiveGroup group) where T : MonoBehaviour, IEntityDescriptorHolder + { + var holder = Create(new EGID(startIndex++, group), contextHolder, factory); + + var childs = contextHolder.GetComponentsInChildren(true); + + foreach (var child in childs) + { + if (child.GetType() != typeof(T)) + { + var childImplementors = (child as MonoBehaviour).GetComponents(); + InternalBuildAll(startIndex, child, factory, group, childImplementors); + } + } + + return holder; + } + + public static T Create(EGID ID, Transform contextHolder, + IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder + { + var holder = contextHolder.GetComponentInChildren(true); + var implementors = holder.GetComponents(); + + factory.BuildEntity(ID, holder.GetDescriptor(), implementors); + + return holder; + } + + public static uint CreateAll(uint startIndex, ExclusiveGroup group, Transform contextHolder, + IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder + { + var holders = contextHolder.GetComponentsInChildren(true); + + foreach (var holder in holders) + { + var implementors = holder.GetComponents(); + + InternalBuildAll(startIndex, holder, factory, group, implementors); + } + + return startIndex; + } + + static void InternalBuildAll(uint startIndex, IEntityDescriptorHolder descriptorHolder, IEntityFactory factory, ExclusiveGroup group, IImplementor[] implementors) + { + ExclusiveGroup.ExclusiveGroupStruct realGroup = group; + + if (string.IsNullOrEmpty(descriptorHolder.groupName) == false) + realGroup = ExclusiveGroup.Search(descriptorHolder.groupName); + + EGID egid; + var holderId = descriptorHolder.id; + if (holderId == 0) + egid = new EGID(startIndex++, realGroup); + else + egid = new EGID(holderId, realGroup); + + var init = factory.BuildEntity(egid, descriptorHolder.GetDescriptor(), implementors); + + init.Init(new EntityHierarchyStruct(group)); + } + } +} +#endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs b/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs index 3da402a..2a2ba0a 100644 --- a/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs +++ b/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs @@ -30,13 +30,15 @@ namespace Svelto.ECS.Schedulers.Unity public WeakAction onTick; } + public UnityEntitySubmissionScheduler(string name = "ECSScheduler") { _name = name; } + public WeakAction onTick { set { if (_scheduler == null) { - GameObject go = new GameObject("ECSScheduler"); + GameObject go = new GameObject(_name); _scheduler = go.AddComponent(); } @@ -45,6 +47,7 @@ namespace Svelto.ECS.Schedulers.Unity } Scheduler _scheduler; + string _name; } } #endif \ No newline at end of file diff --git a/Svelto.ECS/GenericEntityDescriptor.cs b/Svelto.ECS/GenericEntityDescriptor.cs index d483450..a8eb7a3 100644 --- a/Svelto.ECS/GenericEntityDescriptor.cs +++ b/Svelto.ECS/GenericEntityDescriptor.cs @@ -1,40 +1,31 @@ namespace Svelto.ECS { - public abstract class GenericEntityDescriptor:IEntityDescriptor where T : IEntityStruct, new() + public abstract class GenericEntityDescriptor : IEntityDescriptor where T : struct, IEntityStruct { - static GenericEntityDescriptor() - { - _entityBuilders = new IEntityBuilder[] { new EntityBuilder() }; - } - - public IEntityBuilder[] entitiesToBuild - { - get { return _entityBuilders; } - } - static readonly IEntityBuilder[] _entityBuilders; + static GenericEntityDescriptor() { _entityBuilders = new IEntityBuilder[] {new EntityBuilder()}; } + + public IEntityBuilder[] entitiesToBuild => _entityBuilders; } - public abstract class GenericEntityDescriptor : IEntityDescriptor where T : IEntityStruct, new() - where U : IEntityStruct, new() + public abstract class GenericEntityDescriptor : IEntityDescriptor + where T : struct, IEntityStruct where U : struct, IEntityStruct { + static readonly IEntityBuilder[] _entityBuilders; + static GenericEntityDescriptor() { _entityBuilders = new IEntityBuilder[] {new EntityBuilder(), new EntityBuilder()}; } - public IEntityBuilder[] entitiesToBuild - { - get { return _entityBuilders; } - } - - static readonly IEntityBuilder[] _entityBuilders; + public IEntityBuilder[] entitiesToBuild => _entityBuilders; } - public abstract class GenericEntityDescriptor : IEntityDescriptor where T : IEntityStruct, new() - where U : IEntityStruct, new() - where V : IEntityStruct, new() + public abstract class GenericEntityDescriptor : IEntityDescriptor + where T : struct, IEntityStruct where U : struct, IEntityStruct where V : struct, IEntityStruct { + static readonly IEntityBuilder[] _entityBuilders; + static GenericEntityDescriptor() { _entityBuilders = new IEntityBuilder[] @@ -45,19 +36,15 @@ }; } - public IEntityBuilder[] entitiesToBuild - { - get { return _entityBuilders; } - } - - static readonly IEntityBuilder[] _entityBuilders; + public IEntityBuilder[] entitiesToBuild => _entityBuilders; } - public abstract class GenericEntityDescriptor : IEntityDescriptor where T : IEntityStruct, new() - where U : IEntityStruct, new() - where V : IEntityStruct, new() - where W : IEntityStruct, new() + public abstract class GenericEntityDescriptor : IEntityDescriptor + where T : struct, IEntityStruct where U : struct, IEntityStruct where V : struct, IEntityStruct + where W : struct, IEntityStruct { + static readonly IEntityBuilder[] _entityBuilders; + static GenericEntityDescriptor() { _entityBuilders = new IEntityBuilder[] @@ -69,20 +56,15 @@ }; } - public IEntityBuilder[] entitiesToBuild - { - get { return _entityBuilders; } - } - - static readonly IEntityBuilder[] _entityBuilders; + public IEntityBuilder[] entitiesToBuild => _entityBuilders; } - public abstract class GenericEntityDescriptor : IEntityDescriptor where T : IEntityStruct, new() - where U : IEntityStruct, new() - where V : IEntityStruct, new() - where W : IEntityStruct, new() - where X : IEntityStruct, new() + public abstract class GenericEntityDescriptor : IEntityDescriptor + where T : struct, IEntityStruct where U : struct, IEntityStruct where V : struct, IEntityStruct + where W : struct, IEntityStruct where X : struct, IEntityStruct { + static readonly IEntityBuilder[] _entityBuilders; + static GenericEntityDescriptor() { _entityBuilders = new IEntityBuilder[] @@ -95,21 +77,15 @@ }; } - public IEntityBuilder[] entitiesToBuild - { - get { return _entityBuilders; } - } - - static readonly IEntityBuilder[] _entityBuilders; + public IEntityBuilder[] entitiesToBuild => _entityBuilders; } - public abstract class GenericEntityDescriptor : IEntityDescriptor where T : IEntityStruct, new() - where U : IEntityStruct, new() - where V : IEntityStruct, new() - where W : IEntityStruct, new() - where X : IEntityStruct, new() - where Y : IEntityStruct, new() + public abstract class GenericEntityDescriptor : IEntityDescriptor + where T : struct, IEntityStruct where U : struct, IEntityStruct where V : struct, IEntityStruct + where W : struct, IEntityStruct where X : struct, IEntityStruct where Y : struct, IEntityStruct { + static readonly IEntityBuilder[] _entityBuilders; + static GenericEntityDescriptor() { _entityBuilders = new IEntityBuilder[] @@ -123,11 +99,6 @@ }; } - public IEntityBuilder[] entitiesToBuild - { - get { return _entityBuilders; } - } - - static readonly IEntityBuilder[] _entityBuilders; + public IEntityBuilder[] entitiesToBuild => _entityBuilders; } -} +} \ No newline at end of file diff --git a/Svelto.ECS/GenericentityStreamConsumerFactory.cs b/Svelto.ECS/GenericentityStreamConsumerFactory.cs new file mode 100644 index 0000000..0aa06d5 --- /dev/null +++ b/Svelto.ECS/GenericentityStreamConsumerFactory.cs @@ -0,0 +1,22 @@ +namespace Svelto.ECS +{ + class GenericentityStreamConsumerFactory : IEntityStreamConsumerFactory + { + public GenericentityStreamConsumerFactory(DataStructures.WeakReference weakReference) + { + _enginesRoot = weakReference; + } + + public Consumer GenerateConsumer(string name, int capacity) where T : unmanaged, IEntityStruct + { + return _enginesRoot.Target.GenerateConsumer(name, capacity); + } + + readonly DataStructures.WeakReference _enginesRoot; + } + + public interface IEntityStreamConsumerFactory + { + Consumer GenerateConsumer(string name, int capacity) where T : unmanaged, IEntityStruct; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Hybrid/IEntityViewStruct.cs b/Svelto.ECS/Hybrid/IEntityViewStruct.cs new file mode 100644 index 0000000..b351a50 --- /dev/null +++ b/Svelto.ECS/Hybrid/IEntityViewStruct.cs @@ -0,0 +1,6 @@ +namespace Svelto.ECS.Hybrid +{ + public interface IEntityViewStruct:IEntityStruct, INeedEGID + {} +} + diff --git a/Svelto.ECS/Extensions/Unity/IImplementor.cs b/Svelto.ECS/Hybrid/IImplementor.cs similarity index 100% rename from Svelto.ECS/Extensions/Unity/IImplementor.cs rename to Svelto.ECS/Hybrid/IImplementor.cs diff --git a/Svelto.ECS/IEngine.cs b/Svelto.ECS/IEngine.cs index cf17f33..b671fd9 100644 --- a/Svelto.ECS/IEngine.cs +++ b/Svelto.ECS/IEngine.cs @@ -1,24 +1,13 @@ namespace Svelto.ECS.Internal { - public interface IHandleEntityViewEngineAbstracted : IEngine + public interface IReactEngine: IEngine {} - public interface IHandleEntityStructEngine : IHandleEntityViewEngineAbstracted - { - void AddInternal(ref T entityView); - void RemoveInternal(ref T entityView); - } - - public class EngineInfo - { -#if ENABLE_PLATFORM_PROFILER - protected EngineInfo() - { - name = GetType().FullName; - } -#endif - internal readonly string name = string.Empty; - } + public interface IReactOnAddAndRemove : IReactEngine + {} + + public interface IReactOnSwap : IReactEngine + {} } namespace Svelto.ECS diff --git a/Svelto.ECS/IEntitiesDB.cs b/Svelto.ECS/IEntitiesDB.cs index 48ac501..33639c3 100644 --- a/Svelto.ECS/IEntitiesDB.cs +++ b/Svelto.ECS/IEntitiesDB.cs @@ -1,26 +1,57 @@ +using System; + namespace Svelto.ECS { - public interface IEntitiesDB: IObsoleteInterfaceDb + public interface IEntitiesDB { /// /// ECS is meant to work on a set of Entities. Working on a single entity is sometime necessary, but using /// the following functions inside a loop would be a mistake as performance can be significantly impacted - /// return the buffer and the index of the entity inside the buffer using the input EGID + /// return the buffer and the index of the entity inside the buffer using the input EGID + /// + /// + /// + /// + /// + bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) where T : struct, IEntityStruct; + + bool TryQueryEntitiesAndIndex + (uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) + where T : struct, IEntityStruct; + + /// + /// ECS is meant to work on a set of Entities. Working on a single entity is sometime necessary, but using + /// the following functions inside a loop would be a mistake as performance can be significantly impacted + /// return the buffer and the index of the entity inside the buffer using the input EGID /// /// /// /// /// - bool TryQueryEntitiesAndIndex(int id, int group, out uint index, out T[] array) where T : IEntityStruct; - bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) where T : IEntityStruct; - bool TryQueryEntitiesAndIndex(int id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) where T : IEntityStruct; - - ref T QueryUniqueEntity(ExclusiveGroup.ExclusiveGroupStruct group) where T : IEntityStruct; - ref T QueryUniqueEntity(int group) where T : IEntityStruct; - - ref T QueryEntity(EGID entityGid) where T : IEntityStruct; - ref T QueryEntity(int id, ExclusiveGroup.ExclusiveGroupStruct group) where T : IEntityStruct; - ref T QueryEntity(int id, int group) where T : IEntityStruct; + 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; + + /// + /// QueryUniqueEntity is a contract method that explicitly declare the intention to have just on entity in a + /// specific group, usually used for GUI elements + /// + /// + /// + /// + ref T QueryUniqueEntity(ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct; + + /// + /// + /// + /// + /// + /// + ref T QueryEntity(EGID entityGid) where T : struct, IEntityStruct; + + ref T QueryEntity(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct; + /// /// Fast and raw (therefore not safe) return of entities buffer /// Modifying a buffer would compromise the integrity of the whole DB @@ -29,13 +60,22 @@ namespace Svelto.ECS /// /// /// - T[] QueryEntities(int group, out int count) where T : IEntityStruct; + T[] QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) + where T : struct, IEntityStruct; + + (T1[], T2[]) QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) + 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; - T[] QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out int count) where T : IEntityStruct; - (T1[], T2[]) QueryEntities(int group, out int count) where T1 : IEntityStruct where T2 : IEntityStruct; + EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) + where T : struct, IEntityStruct; + + EntityCollections QueryEntities(ExclusiveGroup[] groups) where T : struct, IEntityStruct; + EntityCollections QueryEntities(ExclusiveGroup[] groups) + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; - (T1[], T2[]) QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out int count) - where T1 : IEntityStruct where T2 : IEntityStruct; /// /// this version returns a mapped version of the entity array so that is possible to find the /// index of the entity inside the returned buffer through it's EGID @@ -45,79 +85,54 @@ namespace Svelto.ECS /// /// /// - EGIDMapper QueryMappedEntities(int groupID) where T : IEntityStruct; - EGIDMapper QueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId) where T : IEntityStruct; - /// - /// Execute an action on entities. Be sure that the action is not capturing variables - /// otherwise you will allocate memory which will have a great impact on the execution performance. - /// ExecuteOnEntities can be used to iterate safely over entities, several checks are in place - /// to be sure that everything will be done correctly. - /// Cache friendliness is guaranteed if only Entity Structs are used, but - /// - /// - /// - /// - void ExecuteOnEntities(int groupID, EntitiesAction action) where T : IEntityStruct; - void ExecuteOnEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId, EntitiesAction action) where T : IEntityStruct; - void ExecuteOnEntities(int groupID, ref W value, EntitiesAction action) where T : IEntityStruct; - void ExecuteOnEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId, ref W value, EntitiesAction action) where T : IEntityStruct; + EGIDMapper QueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId) + where T : struct, IEntityStruct; + /// /// 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 + /// friendliness even if just EntityStructs are used. + /// Safety checks are in place /// /// /// /// - void ExecuteOnAllEntities(AllEntitiesAction action) where T : IEntityStruct; - void ExecuteOnAllEntities(ref W value, AllEntitiesAction action) where T : IEntityStruct; - void ExecuteOnAllEntities(ExclusiveGroup[] groups, EntitiesAction action) where T : IEntityStruct; - void ExecuteOnAllEntities(ExclusiveGroup[] groups, ref W value, EntitiesAction action) where T : IEntityStruct; + void ExecuteOnAllEntities(Action action) + where T : struct, IEntityStruct; + + void ExecuteOnAllEntities(ref W value, + Action action) + where T : struct, IEntityStruct; + /// - /// ECS is meant to work on a set of Entities. Working on a single entity is sometime necessary, but using - /// the following functions inside a loop would be a mistake as performance can be significantly impacted - /// Execute an action on a specific Entity. Be sure that the action is not capturing variables - /// otherwise you will allocate memory which will have a great impact on the execution performance + /// /// /// - /// /// - void ExecuteOnEntity(EGID egid, EntityAction action) where T : IEntityStruct; - void ExecuteOnEntity(int id, int groupid, EntityAction action) where T : IEntityStruct; - void ExecuteOnEntity(int id, ExclusiveGroup.ExclusiveGroupStruct groupid, EntityAction action) where T : IEntityStruct; - void ExecuteOnEntity(EGID egid, ref W value, EntityAction action) where T : IEntityStruct; - void ExecuteOnEntity(int id, int groupid, ref W value, EntityAction action) where T : IEntityStruct; - void ExecuteOnEntity(int id, ExclusiveGroup.ExclusiveGroupStruct groupid, ref W value, EntityAction action) where T : IEntityStruct; - - bool Exists(EGID egid) where T : IEntityStruct; - bool Exists(int id, int groupid) where T : IEntityStruct; - bool Exists (ExclusiveGroup.ExclusiveGroupStruct gid); - - bool HasAny(int group) where T:IEntityStruct; - bool HasAny(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T:IEntityStruct; - - int Count(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T:IEntityStruct; - int Count(int groupStruct) where T:IEntityStruct; - } + /// + bool Exists(EGID egid) where T : struct, IEntityStruct; + bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid); - public delegate void EntityAction(ref T target, ref W value); - public delegate void EntityAction(ref T target); + /// + /// + /// + /// + /// + /// + bool HasAny(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; - public delegate void AllEntitiesAction(ref T target, ref W value, IEntitiesDB entitiesDb); - public delegate void AllEntitiesAction(ref T target, IEntitiesDB entitiesDb); - - public delegate void EntitiesAction(ref T target, ref W value, EntityActionData extraParams); - public delegate void EntitiesAction(ref T target, EntityActionData extraParams); + /// + /// + /// + /// + /// + /// + uint Count(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; - public struct EntityActionData - { - public readonly IEntitiesDB entitiesDB; - public readonly int entityIndex; - - public EntityActionData(IEntitiesDB entitiesDb, int index) - { - this.entitiesDB = entitiesDb; - entityIndex = index; - } + /// + /// + /// + /// + /// + 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 c3e234d..b349b0b 100644 --- a/Svelto.ECS/IEntityBuilder.cs +++ b/Svelto.ECS/IEntityBuilder.cs @@ -6,7 +6,7 @@ namespace Svelto.ECS public interface IEntityBuilder { void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID entityID, object[] implementors); - ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, int size); + ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size); Type GetEntityType(); } diff --git a/Svelto.ECS/IEntityDescriptorHolder.cs b/Svelto.ECS/IEntityDescriptorHolder.cs index a1937cd..bde2442 100644 --- a/Svelto.ECS/IEntityDescriptorHolder.cs +++ b/Svelto.ECS/IEntityDescriptorHolder.cs @@ -3,7 +3,8 @@ namespace Svelto.ECS public interface IEntityDescriptorHolder { IEntityDescriptor GetDescriptor(); - + string groupName { get; } + ushort id { get; } } } \ No newline at end of file diff --git a/Svelto.ECS/IEntityFactory.cs b/Svelto.ECS/IEntityFactory.cs index 7e24b6b..4d7e8a1 100644 --- a/Svelto.ECS/IEntityFactory.cs +++ b/Svelto.ECS/IEntityFactory.cs @@ -16,13 +16,14 @@ namespace Svelto.ECS /// public interface IEntityFactory { - /// - ///where performance is critical, you may wish to pre allocate the space needed - ///to store the entities - /// - /// - /// - void PreallocateEntitySpace(ExclusiveGroup.ExclusiveGroupStruct groupStructId, int size) + /// + /// where performance is critical, you may wish to pre allocate the space needed + /// to store the entities + /// + /// + /// + /// + void PreallocateEntitySpace(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) where T : IEntityDescriptor, new(); /// @@ -39,11 +40,25 @@ namespace Svelto.ECS /// /// /// - EntityStructInitializer BuildEntity(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, + EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, object[] implementors = null) where T : IEntityDescriptor, new(); EntityStructInitializer BuildEntity(EGID egid, object[] 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. @@ -52,7 +67,7 @@ namespace Svelto.ECS /// /// /// - EntityStructInitializer BuildEntity(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, + EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, object[] implementors = null) where T : IEntityDescriptor; diff --git a/Svelto.ECS/IEntityFunctions.cs b/Svelto.ECS/IEntityFunctions.cs index 6658b0f..3c1ee99 100644 --- a/Svelto.ECS/IEntityFunctions.cs +++ b/Svelto.ECS/IEntityFunctions.cs @@ -5,21 +5,21 @@ namespace Svelto.ECS //being entity ID globally not unique, the group must be specified when //an entity is removed. Not specifying the group will attempt to remove //the entity from the special standard group. - void RemoveEntity(int entityID, int groupID) where T : IEntityDescriptor, new(); - void RemoveEntity(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); + void RemoveEntity(uint entityID, uint groupID) where T : IEntityDescriptor, new(); + void RemoveEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); void RemoveEntity(EGID entityegid) where T : IEntityDescriptor, new(); - void RemoveEntities(int groupID) where T : IEntityDescriptor, new(); + void RemoveEntities(uint groupID) where T : IEntityDescriptor, new(); void RemoveEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); - void RemoveGroupAndEntities(int groupID); + void RemoveGroupAndEntities(uint groupID); void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID); - void SwapEntityGroup(int entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new(); - void SwapEntityGroup(EGID id, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new(); - void SwapEntityGroup(EGID id, ExclusiveGroup.ExclusiveGroupStruct toGroupID, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new(); + void SwapEntityGroup(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new(); + void SwapEntityGroup(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new(); + void SwapEntityGroup(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new(); - void SwapEntityGroup(EGID id, EGID toId) where T : IEntityDescriptor, new(); - void SwapEntityGroup(EGID id, EGID toId, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new(); + void SwapEntityGroup(EGID fromID, EGID toId) where T : IEntityDescriptor, new(); + void SwapEntityGroup(EGID fromID, EGID toId, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new(); } } \ No newline at end of file diff --git a/Svelto.ECS/IEntityStruct.cs b/Svelto.ECS/IEntityStruct.cs index b429fbe..02308a8 100644 --- a/Svelto.ECS/IEntityStruct.cs +++ b/Svelto.ECS/IEntityStruct.cs @@ -2,6 +2,9 @@ namespace Svelto.ECS { ///EntityStruct MUST implement IEntiyStruct public interface IEntityStruct + {} + + public interface INeedEGID { EGID ID { get; set; } } diff --git a/Svelto.ECS/IEntityViewStruct.cs b/Svelto.ECS/IEntityViewStruct.cs deleted file mode 100644 index 09f9abc..0000000 --- a/Svelto.ECS/IEntityViewStruct.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Svelto.ECS.Hybrid -{ - ///EntityViewStructs MUST implement IEntityViewStruct - public interface IEntityViewStruct:IEntityStruct - {} -} - diff --git a/Svelto.ECS/IObsoleteInterfaceDb.cs b/Svelto.ECS/IObsoleteInterfaceDb.cs deleted file mode 100644 index 44a2d5c..0000000 --- a/Svelto.ECS/IObsoleteInterfaceDb.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using Svelto.DataStructures; - -namespace Svelto.ECS -{ - public interface IObsoleteInterfaceDb - { - /// - /// All the EntityView related methods are left for back compatibility, but - /// shouldn't be used anymore. Always pick EntityViewStruct or EntityStruct - /// over EntityView - /// - [Obsolete] - ReadOnlyCollectionStruct QueryEntityViews(int group) where T : class, IEntityStruct; - [Obsolete] - ReadOnlyCollectionStruct QueryEntityViews(ExclusiveGroup.ExclusiveGroupStruct group) where T : class, IEntityStruct; - /// - /// All the EntityView related methods are left for back compatibility, but - /// shouldn't be used anymore. Always pick EntityViewStruct or EntityStruct - /// over EntityView - /// - [Obsolete] - bool TryQueryEntityView(EGID egid, out T entityView) where T : class, IEntityStruct; - [Obsolete] - bool TryQueryEntityView(int id, ExclusiveGroup.ExclusiveGroupStruct group, out T entityView) where T : class, IEntityStruct; - /// - /// All the EntityView related methods are left for back compatibility, but - /// shouldn't be used anymore. Always pick EntityViewStruct or EntityStruct - /// over EntityView - /// - [Obsolete] - T QueryEntityView(EGID egid) where T : class, IEntityStruct; - [Obsolete] - T QueryEntityView(int id, ExclusiveGroup.ExclusiveGroupStruct group) where T : class, IEntityStruct; - /// - /// ECS is meant to work on a set of Entities. Working on a single entity is sometime necessary, but using - /// the following functions inside a loop would be a mistake as performance can be significantly impacted - /// return the buffer and the index of the entity inside the buffer using the input EGID - /// - /// - /// - /// - /// - [Obsolete] - T[] QueryEntitiesAndIndex(EGID entityGid, out uint index) where T : IEntityStruct; - [Obsolete] - T[] QueryEntitiesAndIndex(int id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) where T : IEntityStruct; - [Obsolete] - T[] QueryEntitiesAndIndex(int id, int group, out uint index) where T : IEntityStruct; - } -} \ No newline at end of file diff --git a/Svelto.ECS/IReactOnAddAndRemove.cs b/Svelto.ECS/IReactOnAddAndRemove.cs new file mode 100644 index 0000000..7323984 --- /dev/null +++ b/Svelto.ECS/IReactOnAddAndRemove.cs @@ -0,0 +1,10 @@ +using Svelto.ECS.Internal; + +namespace Svelto.ECS +{ + public interface IReactOnAddAndRemove : IReactOnAddAndRemove where T : IEntityStruct + { + void Add(ref T entityView); + void Remove(ref T entityView); + } + } \ No newline at end of file diff --git a/Svelto.ECS/IReactOnSwap.cs b/Svelto.ECS/IReactOnSwap.cs new file mode 100644 index 0000000..3a5b4cd --- /dev/null +++ b/Svelto.ECS/IReactOnSwap.cs @@ -0,0 +1,10 @@ +using Svelto.ECS.Internal; + +namespace Svelto.ECS +{ + public interface IReactOnSwap : IReactOnSwap where T : IEntityStruct + { + void MovedTo(ref T entityView, ExclusiveGroup.ExclusiveGroupStruct previousGroup); + void MovedFrom(ref T entityView, ExclusiveGroup.ExclusiveGroupStruct previousGroupValue); + } +} \ No newline at end of file diff --git a/Svelto.ECS/MultiEntitiesEngine.cs b/Svelto.ECS/MultiEntitiesEngine.cs deleted file mode 100644 index 701b6f1..0000000 --- a/Svelto.ECS/MultiEntitiesEngine.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Svelto.ECS.Internal; - -namespace Svelto.ECS -{ - public abstract class MultiEntitiesEngine : SingleEntityEngine, IHandleEntityStructEngine - where U : IEntityStruct where T : IEntityStruct - { - public void AddInternal(ref U entityView) - { Add(ref entityView); } - public void RemoveInternal(ref U entityView) - { Remove(ref entityView); } - - protected abstract void Add(ref U entityView); - protected abstract void Remove(ref U entityView); - } - - public abstract class MultiEntitiesEngine : MultiEntitiesEngine, IHandleEntityStructEngine - where V : IEntityStruct where U : IEntityStruct where T : IEntityStruct - { - public void AddInternal(ref V entityView) - { Add(ref entityView); } - public void RemoveInternal(ref V entityView) - { Remove(ref entityView); } - - protected abstract void Add(ref V entityView); - protected abstract void Remove(ref V entityView); - } - - /// - /// Please do not add more MultiEntityViewsEngine if you use more than 4 nodes, your engine has - /// already too many responsibilities. - /// - public abstract class MultiEntitiesEngine : MultiEntitiesEngine, IHandleEntityStructEngine - where W : IEntityStruct where V : IEntityStruct where U : IEntityStruct where T : IEntityStruct - { - public void AddInternal(ref W entityView) - { Add(ref entityView); } - public void RemoveInternal(ref W entityView) - { Remove(ref entityView); } - - protected abstract void Add(ref W entityView); - protected abstract void Remove(ref W entityView); - } -} \ No newline at end of file diff --git a/Svelto.ECS/MultiEntityViewsEngine.cs b/Svelto.ECS/MultiEntityViewsEngine.cs deleted file mode 100644 index fabb009..0000000 --- a/Svelto.ECS/MultiEntityViewsEngine.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Svelto.ECS.Internal; - -namespace Svelto.ECS -{ - public abstract class MultiEntityViewsEngine : SingleEntityViewEngine, IHandleEntityStructEngine - where U : class, IEntityStruct where T : class, IEntityStruct - { - public void AddInternal(ref U entityView) - { Add(entityView); } - public void RemoveInternal(ref U entityView) - { Remove(entityView); } - - protected abstract void Add(U entityView); - protected abstract void Remove(U entityView); - } - - public abstract class MultiEntityViewsEngine : MultiEntityViewsEngine, IHandleEntityStructEngine - where V : class, IEntityStruct where U : class, IEntityStruct where T : class, IEntityStruct - { - public void AddInternal(ref V entityView) - { Add(entityView); } - public void RemoveInternal(ref V entityView) - { Remove(entityView); } - - protected abstract void Add(V entityView); - protected abstract void Remove(V entityView); - } - - /// - /// Please do not add more MultiEntityViewsEngine - /// if you use more than 4 nodes, your engine has - /// already too many responsabilities. - /// - public abstract class MultiEntityViewsEngine : MultiEntityViewsEngine, IHandleEntityStructEngine - where W : class, IEntityStruct where V : class, IEntityStruct where U : class, IEntityStruct where T : class, IEntityStruct - { - public void AddInternal(ref W entityView) - { Add(entityView); } - public void RemoveInternal(ref W entityView) - { Remove(entityView); } - - protected abstract void Add(W entityView); - protected abstract void Remove(W entityView); - } -} \ No newline at end of file diff --git a/Svelto.ECS/Sequencer.cs b/Svelto.ECS/Sequencer.cs index 189eb76..0b0a25d 100644 --- a/Svelto.ECS/Sequencer.cs +++ b/Svelto.ECS/Sequencer.cs @@ -99,7 +99,7 @@ namespace Svelto.ECS /// public class Sequencer where S: Sequencer, new() { - protected void SetSequence(Steps steps) + public void SetSequence(Steps steps) { _steps = steps; } diff --git a/Svelto.ECS/SingleEntityEngine.cs b/Svelto.ECS/SingleEntityEngine.cs deleted file mode 100644 index 5c3d47d..0000000 --- a/Svelto.ECS/SingleEntityEngine.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Svelto.ECS.Internal; - -namespace Svelto.ECS -{ - public abstract class SingleEntityEngine : EngineInfo, IHandleEntityStructEngine where T : IEntityStruct - { - public void AddInternal(ref T entityView) - { Add(ref entityView); } - - public void RemoveInternal(ref T entityView) - { Remove(ref entityView); } - - protected abstract void Add(ref T entityView); - protected abstract void Remove(ref T entityView); - } -} \ No newline at end of file diff --git a/Svelto.ECS/SingleEntityViewEngine.cs b/Svelto.ECS/SingleEntityViewEngine.cs deleted file mode 100644 index 52539d2..0000000 --- a/Svelto.ECS/SingleEntityViewEngine.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Svelto.ECS.Internal; - -namespace Svelto.ECS -{ - public abstract class SingleEntityViewEngine : EngineInfo, IHandleEntityStructEngine where T : class, IEntityStruct - { - public void AddInternal(ref T entityView) - { Add(entityView); } - - public void RemoveInternal(ref T entityView) - { Remove(entityView); } - - protected abstract void Add(T entityView); - protected abstract void Remove(T entityView); - } -} \ No newline at end of file diff --git a/Svelto.ECS/Svelto.ECS.asmdef b/Svelto.ECS/Svelto.ECS.asmdef index df4f03a..49133e0 100644 --- a/Svelto.ECS/Svelto.ECS.asmdef +++ b/Svelto.ECS/Svelto.ECS.asmdef @@ -6,5 +6,10 @@ "optionalUnityReferences": [], "includePlatforms": [], "excludePlatforms": [], - "allowUnsafeCode": false + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [] } \ No newline at end of file diff --git a/Svelto.ECS/Svelto.ECS.csproj b/Svelto.ECS/Svelto.ECS.csproj index 9a62eda..d0f78fe 100644 --- a/Svelto.ECS/Svelto.ECS.csproj +++ b/Svelto.ECS/Svelto.ECS.csproj @@ -1,21 +1,21 @@  Svelto.ECS - 6 + 7.3 netstandard2.0 + + AnyCPU + false + + + AnyCPU + false + - - - - - - - + - - C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.0.0\ref\netcoreapp2.0\System.Reflection.Emit.Lightweight.dll - + \ No newline at end of file