From fb74cb8116087b2614afd05352baa98a11259685 Mon Sep 17 00:00:00 2001 From: sebas77 Date: Sun, 1 Dec 2019 12:23:44 +0000 Subject: [PATCH] Svelto.ECS 2.9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations --- Svelto.Common | 2 +- .../Components/ECSQuaternion.cs | 16 - Svelto.ECS.Components/Components/ECSRect.cs | 19 - .../Components/ECSVector2.cs | 20 - .../Components/ECSVector3.cs | 24 - .../Components/ECSVector4.cs | 15 - .../Components/ExtensionMethods.cs | 131 --- .../Components/ExtensionMethodsUECS.cs | 69 -- .../Components/ExtensionMethodsUnity.cs | 44 -- .../ECSResources/StringECSDB.cs | 22 - .../EntityStructs/PositionEntityStruct.cs | 9 - .../EntityStructs/RotationEntityStruct.cs | 9 - .../EntityStructs/ScalingEntityStruct.cs | 9 - .../Svelto.ECS.Components.asmdef | 23 - Svelto.ECS/AllowMultipleAttribute.cs | 8 + Svelto.ECS/CheckEntityUtilities.cs | 128 ++- Svelto.ECS/DBC.cs | 746 +++++++++--------- .../DataStructures/SetEGIDWithoutBoxing.cs | 37 + .../DataStructures/TypeSafeDictionary.cs | 184 +++-- Svelto.ECS/Dispatcher/DispatchOnChange.cs | 7 +- Svelto.ECS/Dispatcher/DispatchOnSet.cs | 13 +- Svelto.ECS/DynamicEntityDescriptor.cs | 121 ++- Svelto.ECS/ECSException.cs | 8 +- .../ECSResources/ECSResources.cs | 19 +- Svelto.ECS/ECSResources/ECSString.cs | 38 + Svelto.ECS/EGID.cs | 149 +--- Svelto.ECS/EGIDMapper.cs | 37 +- .../EnginesRoot.DoubleBufferedEntityViews.cs | 33 +- Svelto.ECS/EnginesRoot.Engines.cs | 109 +-- Svelto.ECS/EnginesRoot.Entities.cs | 347 ++++---- .../EnginesRoot.GenericEntityFactory.cs | 54 +- .../EnginesRoot.GenericEntityFunctions.cs | 120 +-- Svelto.ECS/EnginesRoot.Submission.cs | 193 +++-- Svelto.ECS/EntitiesDB.cs | 173 ++-- Svelto.ECS/EntityBuilder.CheckFields.cs | 136 ++-- Svelto.ECS/EntityBuilder.cs | 115 ++- Svelto.ECS/EntityCollection.cs | 181 ++++- ...torInfo.cs => EntityDescriptorTemplate.cs} | 8 +- Svelto.ECS/EntityFactory.cs | 72 +- Svelto.ECS/EntityGroup.cs | 29 + Svelto.ECS/EntityHierarchyStruct.cs | 2 +- Svelto.ECS/EntityInfoView.cs | 7 +- Svelto.ECS/EntityNotFoundException.cs | 7 +- Svelto.ECS/EntityStream.cs | 110 ++- Svelto.ECS/EntityStructInitializer.cs | 79 +- Svelto.ECS/EntitySubmissionScheduler.cs | 4 +- Svelto.ECS/EntitySubmitOperation.cs | 13 +- Svelto.ECS/EntityView.cs | 40 - Svelto.ECS/EntityViewUtility.cs | 19 +- Svelto.ECS/ExclusiveGroup.cs | 150 +++- Svelto.ECS/ExecuteOnEntitiesDB.cs | 25 +- Svelto.ECS/ExtendibleEntityDescriptor.cs | 35 +- .../Unity/GenericEntityDescriptorHolder.cs | 2 +- .../Extensions/Unity/SveltoGUIHelper.cs | 41 +- .../Unity/UnityEntitySubmissionScheduler.cs | 43 +- .../GenericentityStreamConsumerFactory.cs | 20 +- Svelto.ECS/IEntitiesDB.cs | 107 ++- Svelto.ECS/IEntityBuilder.cs | 4 +- Svelto.ECS/IEntityFactory.cs | 35 +- Svelto.ECS/IEntityFunctions.cs | 2 - Svelto.ECS/IEntityStruct.cs | 10 +- Svelto.ECS/IReactOnAddAndRemove.cs | 4 +- Svelto.ECS/IReactOnSwap.cs | 6 +- Svelto.ECS/Sequencer.cs | 4 +- .../Serialization/ComposedSerializer.cs | 43 + Svelto.ECS/Serialization/DefaultSerializer.cs | 43 + .../Serialization/DefaultSerializerUtils.cs | 46 ++ .../Serialization/DefaultVersioningFactory.cs | 28 + .../Serialization/DoNotSerializeAttribute.cs | 8 + Svelto.ECS/Serialization/DontSerialize.cs | 17 + .../EnginesRoot.GenericEntitySerialization.cs | 198 +++++ .../EnginesRoot.SerializableEntityHeader.cs | 76 ++ .../Serialization/EntitiesDB.DescriptorMap.cs | 105 +++ Svelto.ECS/Serialization/HashNameAttribute.cs | 15 + .../Serialization/IDeserializationFactory.cs | 9 + .../Serialization/IEntitySerialization.cs | 69 ++ .../ISerializableEntityBuilder.cs | 15 + .../ISerializableEntityDescriptor.cs | 10 + .../Serialization/ISerializationData.cs | 14 + Svelto.ECS/Serialization/ISerializer.cs | 55 ++ Svelto.ECS/Serialization/PartialSerializer.cs | 89 +++ .../SerializableEntityBuilder.cs | 94 +++ .../SerializableEntityDescriptor.cs | 122 +++ .../Serialization/SerializableEntityStruct.cs | 9 + .../SerializationEntityDescriptorTemplate.cs | 16 + .../Serialization/SimpleSerializationData.cs | 37 + Svelto.ECS/Serialization/Unsafe.cs | 15 + .../SimpleSubmissionEntityViewScheduler.cs | 16 +- Svelto.ECS/StaticEntityDescriptorInfo.cs | 13 - Svelto.ECS/Svelto.ECS.asmdef | 2 +- Svelto.ECS/Svelto.ECS.csproj | 3 + Svelto.ECS/TypeCache.cs | 9 + Svelto.ECS/UnsafeStructRef.cs | 28 + Svelto.ECS/Utilities.cs | 14 + Svelto.ECS/WaitForSubmissionEnumerator.cs | 64 ++ .../Debug/netstandard2.0/Svelto.ECS.deps.json | 128 +++ .../netstandard2.0/Svelto.ECS.AssemblyInfo.cs | 23 + .../Svelto.ECS.csproj.CopyComplete | 0 .../Svelto.ECS.csproj.FileListAbsolute.txt | 23 + .../obj/Svelto.ECS.csproj.nuget.dgspec.json | 142 ++++ .../obj/Svelto.ECS.csproj.nuget.g.props | 15 + .../obj/Svelto.ECS.csproj.nuget.g.targets | 9 + Svelto.ECS/obj/project.assets.json | 432 ++++++++++ Svelto.ECS/obj/project.packagespec.json | 66 ++ .../Experimental/IServiceEventContainer.cs | 11 + .../IServiceEventContainerFactory.cs | 8 + .../Experimental/IServiceEventListener.cs | 14 + .../Experimental/ServiceEventContainer.cs | 41 + Svelto.Services/IServiceRequest.cs | 15 + Svelto.Services/IServiceRequestsFactory.cs | 8 + .../ServiceRequestFactoryArgumentException.cs | 10 + Svelto.Services/ServiceRequestsFactory.cs | 50 ++ Svelto.Services/Svelto.Services.asmdef | 17 + Svelto.Services/Unity/Web/IResponseHandler.cs | 19 + .../Unity/Web/StandardWebRequest.cs | 176 +++++ .../Unity/Web/UnityDownloadHandler.cs | 61 ++ 116 files changed, 4729 insertions(+), 2067 deletions(-) delete mode 100644 Svelto.ECS.Components/Components/ECSQuaternion.cs delete mode 100644 Svelto.ECS.Components/Components/ECSRect.cs delete mode 100644 Svelto.ECS.Components/Components/ECSVector2.cs delete mode 100644 Svelto.ECS.Components/Components/ECSVector3.cs delete mode 100644 Svelto.ECS.Components/Components/ECSVector4.cs delete mode 100644 Svelto.ECS.Components/Components/ExtensionMethods.cs delete mode 100644 Svelto.ECS.Components/Components/ExtensionMethodsUECS.cs delete mode 100644 Svelto.ECS.Components/Components/ExtensionMethodsUnity.cs delete mode 100644 Svelto.ECS.Components/ECSResources/StringECSDB.cs delete mode 100644 Svelto.ECS.Components/EntityStructs/PositionEntityStruct.cs delete mode 100644 Svelto.ECS.Components/EntityStructs/RotationEntityStruct.cs delete mode 100644 Svelto.ECS.Components/EntityStructs/ScalingEntityStruct.cs delete mode 100644 Svelto.ECS.Components/Svelto.ECS.Components.asmdef create mode 100644 Svelto.ECS/AllowMultipleAttribute.cs create mode 100644 Svelto.ECS/DataStructures/SetEGIDWithoutBoxing.cs rename {Svelto.ECS.Components => Svelto.ECS}/ECSResources/ECSResources.cs (66%) create mode 100644 Svelto.ECS/ECSResources/ECSString.cs rename Svelto.ECS/{EntityDescriptorInfo.cs => EntityDescriptorTemplate.cs} (58%) create mode 100644 Svelto.ECS/EntityGroup.cs delete mode 100644 Svelto.ECS/EntityView.cs create mode 100644 Svelto.ECS/Serialization/ComposedSerializer.cs create mode 100644 Svelto.ECS/Serialization/DefaultSerializer.cs create mode 100644 Svelto.ECS/Serialization/DefaultSerializerUtils.cs create mode 100644 Svelto.ECS/Serialization/DefaultVersioningFactory.cs create mode 100644 Svelto.ECS/Serialization/DoNotSerializeAttribute.cs create mode 100644 Svelto.ECS/Serialization/DontSerialize.cs create mode 100644 Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs create mode 100644 Svelto.ECS/Serialization/EnginesRoot.SerializableEntityHeader.cs create mode 100644 Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs create mode 100644 Svelto.ECS/Serialization/HashNameAttribute.cs create mode 100644 Svelto.ECS/Serialization/IDeserializationFactory.cs create mode 100644 Svelto.ECS/Serialization/IEntitySerialization.cs create mode 100644 Svelto.ECS/Serialization/ISerializableEntityBuilder.cs create mode 100644 Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs create mode 100644 Svelto.ECS/Serialization/ISerializationData.cs create mode 100644 Svelto.ECS/Serialization/ISerializer.cs create mode 100644 Svelto.ECS/Serialization/PartialSerializer.cs create mode 100644 Svelto.ECS/Serialization/SerializableEntityBuilder.cs create mode 100644 Svelto.ECS/Serialization/SerializableEntityDescriptor.cs create mode 100644 Svelto.ECS/Serialization/SerializableEntityStruct.cs create mode 100644 Svelto.ECS/Serialization/SerializationEntityDescriptorTemplate.cs create mode 100644 Svelto.ECS/Serialization/SimpleSerializationData.cs create mode 100644 Svelto.ECS/Serialization/Unsafe.cs delete mode 100644 Svelto.ECS/StaticEntityDescriptorInfo.cs create mode 100644 Svelto.ECS/TypeCache.cs create mode 100644 Svelto.ECS/UnsafeStructRef.cs create mode 100644 Svelto.ECS/Utilities.cs create mode 100644 Svelto.ECS/WaitForSubmissionEnumerator.cs create mode 100644 Svelto.ECS/bin/Debug/netstandard2.0/Svelto.ECS.deps.json create mode 100644 Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.AssemblyInfo.cs create mode 100644 Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.CopyComplete create mode 100644 Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.FileListAbsolute.txt create mode 100644 Svelto.ECS/obj/Svelto.ECS.csproj.nuget.dgspec.json create mode 100644 Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.props create mode 100644 Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.targets create mode 100644 Svelto.ECS/obj/project.assets.json create mode 100644 Svelto.ECS/obj/project.packagespec.json create mode 100644 Svelto.Services/Experimental/IServiceEventContainer.cs create mode 100644 Svelto.Services/Experimental/IServiceEventContainerFactory.cs create mode 100644 Svelto.Services/Experimental/IServiceEventListener.cs create mode 100644 Svelto.Services/Experimental/ServiceEventContainer.cs create mode 100644 Svelto.Services/IServiceRequest.cs create mode 100644 Svelto.Services/IServiceRequestsFactory.cs create mode 100644 Svelto.Services/ServiceRequestFactoryArgumentException.cs create mode 100644 Svelto.Services/ServiceRequestsFactory.cs create mode 100644 Svelto.Services/Svelto.Services.asmdef create mode 100644 Svelto.Services/Unity/Web/IResponseHandler.cs create mode 100644 Svelto.Services/Unity/Web/StandardWebRequest.cs create mode 100644 Svelto.Services/Unity/Web/UnityDownloadHandler.cs diff --git a/Svelto.Common b/Svelto.Common index 433b39f..0d71df6 160000 --- a/Svelto.Common +++ b/Svelto.Common @@ -1 +1 @@ -Subproject commit 433b39f4a1b9a17301988ecd8a633d049bb4378d +Subproject commit 0d71df6e66f2d0c72d23fa44b740bad4a0ff5e8c diff --git a/Svelto.ECS.Components/Components/ECSQuaternion.cs b/Svelto.ECS.Components/Components/ECSQuaternion.cs deleted file mode 100644 index c88ccb2..0000000 --- a/Svelto.ECS.Components/Components/ECSQuaternion.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Svelto.ECS.Components -{ - public struct ECSQuaternion - { - public static readonly ECSQuaternion identity = new ECSQuaternion(0f, 0f, 0f, 1f); - public float x, y, z, w; - - public ECSQuaternion(float X, float Y, float Z, float W) - { - x = X; - y = Y; - z = Z; - w = W; - } - } -} diff --git a/Svelto.ECS.Components/Components/ECSRect.cs b/Svelto.ECS.Components/Components/ECSRect.cs deleted file mode 100644 index bf2fb7c..0000000 --- a/Svelto.ECS.Components/Components/ECSRect.cs +++ /dev/null @@ -1,19 +0,0 @@ -#if UNITY_5 || UNITY_5_3_OR_NEWER -using UnityEngine; - -namespace Svelto.ECS.Components.Unity -{ - public struct ECSRect - { - public float x, y, width, height; - - public ECSRect(Rect imageUvRect) - { - x = imageUvRect.x; - y = imageUvRect.y; - width = imageUvRect.width; - height = imageUvRect.height; - } - } -} -#endif \ No newline at end of file diff --git a/Svelto.ECS.Components/Components/ECSVector2.cs b/Svelto.ECS.Components/Components/ECSVector2.cs deleted file mode 100644 index a48a5d4..0000000 --- a/Svelto.ECS.Components/Components/ECSVector2.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Svelto.ECS.Components -{ - public struct ECSVector2 - { - public float x, y; - - public static readonly ECSVector2 right = new ECSVector2(1f, 0f); - public static readonly ECSVector2 left = new ECSVector2(-1f, 0f); - public static readonly ECSVector2 down = new ECSVector2(0f, -1f); - public static readonly ECSVector2 up = new ECSVector2(0f, 1f); - public static readonly ECSVector2 one = new ECSVector2(1f, 1f); - public static readonly ECSVector2 zero = new ECSVector2(0f, 0f); - - public ECSVector2(float X, float Y) - { - x = X; - y = Y; - } - } -} \ No newline at end of file diff --git a/Svelto.ECS.Components/Components/ECSVector3.cs b/Svelto.ECS.Components/Components/ECSVector3.cs deleted file mode 100644 index fea272f..0000000 --- a/Svelto.ECS.Components/Components/ECSVector3.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Svelto.ECS.Components -{ - public struct ECSVector3 - { - public float x, y, z; - - public static readonly ECSVector3 forward = new ECSVector3(0f, 0f, 1f); - public static readonly ECSVector3 back = new ECSVector3(0f, 0f, -1f); - public static readonly ECSVector3 right = new ECSVector3(1f, 0f, 0f); - public static readonly ECSVector3 left = new ECSVector3(-1f, 0f, 0f); - public static readonly ECSVector3 up = new ECSVector3(0f, 1f, 0f); - public static readonly ECSVector3 down = new ECSVector3(0f, -1f, 0f); - - public static readonly ECSVector3 zero = new ECSVector3(0f, 0f, 0f); - public static readonly ECSVector3 one = new ECSVector3(1f, 1f, 1f); - - public ECSVector3(float X, float Y, float Z) - { - x = X; - y = Y; - z = Z; - } - } -} \ No newline at end of file diff --git a/Svelto.ECS.Components/Components/ECSVector4.cs b/Svelto.ECS.Components/Components/ECSVector4.cs deleted file mode 100644 index 17396fb..0000000 --- a/Svelto.ECS.Components/Components/ECSVector4.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Svelto.ECS.Components -{ - public struct ECSVector4 - { - public float x, y, z, w; - - public ECSVector4(float X, float Y, float Z, float W) - { - x = X; - y = Y; - z = Z; - w = W; - } - } -} \ No newline at end of file diff --git a/Svelto.ECS.Components/Components/ExtensionMethods.cs b/Svelto.ECS.Components/Components/ExtensionMethods.cs deleted file mode 100644 index ea70e97..0000000 --- a/Svelto.ECS.Components/Components/ExtensionMethods.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using Svelto.ECS.Components; - -public static partial class ExtensionMethods -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float SqrMagnitude(in this ECSVector2 a) { return a.x * a.x + a.y * a.y; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float SqrMagnitude(in this ECSVector3 a) { return a.x * a.x + a.y * a.y + a.z * a.z; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Magnitude(in this ECSVector2 a) { return (float) Math.Sqrt(a.SqrMagnitude()); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Magnitude(in this ECSVector3 a) { return (float) Math.Sqrt(a.SqrMagnitude()); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Add(ref this ECSVector3 vector1, in ECSVector3 vector2) - { - vector1.x += vector2.x; - vector1.y += vector2.y; - vector1.z += vector2.z; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Add(ref this ECSVector3 vector1, float x, float y, float z) - { - vector1.x += x; - vector1.y += y; - vector1.z += z; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Set(ref this ECSVector3 vector1, float x, float y, float z) - { - vector1.x = x; - vector1.y = y; - vector1.z = z; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Zero(ref this ECSVector3 vector1) - { - vector1.x = 0; - vector1.y = 0; - vector1.z = 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Set(ref this ECSQuaternion quaternion, float x, float y, float z, float w) - { - quaternion.x = x; - quaternion.y = y; - quaternion.z = z; - quaternion.w = w; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Interpolate(ref this ECSVector3 vector, in ECSVector3 vectorS, in ECSVector3 vectorE, float time) - { - vector.x = vectorS.x * (1 - time) + vectorE.x * time; - vector.y = vectorS.y * (1 - time) + vectorE.y * time; - vector.z = vectorS.z * (1 - time) + vectorE.z * time; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dot(ref this ECSVector3 vector1, in ECSVector3 vector2) - { - return vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ECSVector3 Cross(ref this ECSVector3 lhs, in ECSVector3 rhs) - { - return new ECSVector3(lhs.y * rhs.z - lhs.z * rhs.y, lhs.z * rhs.x - lhs.x * rhs.z, - lhs.x * rhs.y - lhs.y * rhs.x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ECSVector3 Mul(in this ECSQuaternion rotation, in ECSVector3 point) - { - float x = rotation.x * 2F; - float y = rotation.y * 2F; - float z = rotation.z * 2F; - float xx = rotation.x * x; - float yy = rotation.y * y; - float zz = rotation.z * z; - float xy = rotation.x * y; - float xz = rotation.x * z; - float yz = rotation.y * z; - float wx = rotation.w * x; - float wy = rotation.w * y; - float wz = rotation.w * z; - - return new ECSVector3((1F - (yy + zz)) * point.x + (xy - wz) * point.y + (xz + wy) * point.z, - (xy + wz) * point.x + (1F - (xx + zz)) * point.y + (yz - wx) * point.z, - (xz - wy) * point.x + (yz + wx) * point.y + (1F - (xx + yy)) * point.z); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Swap(ref this ECSVector3 vector, ref ECSVector3 vectorS) - { - float x = vector.x; - float y = vector.y; - float z = vector.z; - - vector.x = vectorS.x; - vector.y = vectorS.y; - vector.z = vectorS.z; - - vectorS.x = x; - vectorS.y = y; - vectorS.z = z; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Sub(ref this ECSVector3 vector1, in ECSVector3 vector2) - { - vector1.x -= vector2.x; - vector1.y -= vector2.y; - vector1.z -= vector2.z; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref ECSVector3 Mul(ref this ECSVector3 vector1, float value) - { - vector1.x *= value; - vector1.y *= value; - vector1.z *= value; - - return ref vector1; - } -} diff --git a/Svelto.ECS.Components/Components/ExtensionMethodsUECS.cs b/Svelto.ECS.Components/Components/ExtensionMethodsUECS.cs deleted file mode 100644 index 2cb943a..0000000 --- a/Svelto.ECS.Components/Components/ExtensionMethodsUECS.cs +++ /dev/null @@ -1,69 +0,0 @@ -#if UNITY_MATHEMATICS -using System.Runtime.CompilerServices; -using UnityEngine; -using Svelto.ECS.Components; -using Unity.Mathematics; - -public static partial class ExtensionMethods -{ - public static float3 ToFloat3(in this ECSVector3 vector) - { - return new float3(vector.x, vector.y, vector.z); - } - - public static quaternion ToQuaternion(in this ECSVector4 vector) - { - return new quaternion(vector.x, vector.y, vector.z, vector.w); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Mul(ref this float3 vector1, float value) - { - vector1.x *= value; - vector1.y *= value; - vector1.z *= value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Div(ref this float3 vector1, float value) - { - vector1.x /= value; - vector1.y /= value; - vector1.z /= value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ProjectOnPlane(ref this float3 vector, in float3 planeNormal) - { - var num1 = math.dot(planeNormal,planeNormal); - var num2 = math.dot(vector,planeNormal) / num1; - - vector.x -= planeNormal.x * num2; - vector.y -= planeNormal.y * num2; - vector.z -= planeNormal.z * num2; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Normalize(ref this float3 x) - { - x.Mul(math.rsqrt(math.dot(x,x))); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void NormalizeSafe(ref this float3 x) - { - var len = math.dot(x,x); - x = len > math.FLT_MIN_NORMAL ? x * math.rsqrt(len) : float3.zero; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Add(ref this float3 a, in float3 b) - { - a.x += b.x; - a.y += b.y; - a.z += b.z; - } - -} - -#endif \ No newline at end of file diff --git a/Svelto.ECS.Components/Components/ExtensionMethodsUnity.cs b/Svelto.ECS.Components/Components/ExtensionMethodsUnity.cs deleted file mode 100644 index d8d4136..0000000 --- a/Svelto.ECS.Components/Components/ExtensionMethodsUnity.cs +++ /dev/null @@ -1,44 +0,0 @@ -#if UNITY_2018_3_OR_NEWER -using Svelto.ECS.Components; -using UnityEngine; - - public static partial class ExtensionMethods - { - public static Vector2 ToVector2(in this ECSVector2 vector) { return new Vector2(vector.x, vector.y); } - public static ECSVector2 ToECSVector2(in this Vector2 vector) { return new ECSVector2(vector.x, vector.y); } - - public static Vector3 ToVector3(in this ECSVector3 vector) - { - return new Vector3(vector.x, vector.y, vector.z); - } - public static ECSVector3 ToECSVector3(in this Vector3 vector) - { - return new ECSVector3(vector.x, vector.y, vector.z); - } - - public static Quaternion ToQuaternion(in this ECSQuaternion quaternion) - { - return new Quaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w); - } - public static ECSQuaternion ToECSQuaternion(in this Quaternion quaternion) - { - return new ECSQuaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w); - } - - public static void Set(ref this ECSQuaternion ecsQuaternion, in Quaternion quaternion) - { - ecsQuaternion.x = quaternion.x; - ecsQuaternion.y = quaternion.y; - ecsQuaternion.z = quaternion.z; - ecsQuaternion.w = quaternion.w; - } - - public static void Set(ref this ECSVector3 ecsVector3, in Vector3 vector3) - { - ecsVector3.x = vector3.x; - ecsVector3.y = vector3.y; - ecsVector3.z = vector3.z; - } - } - -#endif \ No newline at end of file diff --git a/Svelto.ECS.Components/ECSResources/StringECSDB.cs b/Svelto.ECS.Components/ECSResources/StringECSDB.cs deleted file mode 100644 index d977085..0000000 --- a/Svelto.ECS.Components/ECSResources/StringECSDB.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Svelto.ECS.Experimental -{ - public struct ECSString - { - internal uint id; - - ECSString(uint toEcs) - { - id = toEcs; - } - - public static implicit operator string(ECSString ecsString) - { - return ResourcesECSDB.FromECS(ecsString.id); - } - - public static implicit operator ECSString(string text) - { - return new ECSString(ResourcesECSDB.ToECS(text)); - } - } -} \ No newline at end of file diff --git a/Svelto.ECS.Components/EntityStructs/PositionEntityStruct.cs b/Svelto.ECS.Components/EntityStructs/PositionEntityStruct.cs deleted file mode 100644 index b86a6ac..0000000 --- a/Svelto.ECS.Components/EntityStructs/PositionEntityStruct.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Svelto.ECS.Components; - -namespace Svelto.ECS.EntityStructs -{ - public struct PositionEntityStruct : IEntityStruct - { - public ECSVector3 position; - } -} \ No newline at end of file diff --git a/Svelto.ECS.Components/EntityStructs/RotationEntityStruct.cs b/Svelto.ECS.Components/EntityStructs/RotationEntityStruct.cs deleted file mode 100644 index 2b30560..0000000 --- a/Svelto.ECS.Components/EntityStructs/RotationEntityStruct.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Svelto.ECS.Components; - -namespace Svelto.ECS.EntityStructs -{ - public struct RotationEntityStruct : IEntityStruct - { - public ECSQuaternion rotation; - } -} \ No newline at end of file diff --git a/Svelto.ECS.Components/EntityStructs/ScalingEntityStruct.cs b/Svelto.ECS.Components/EntityStructs/ScalingEntityStruct.cs deleted file mode 100644 index 89e535a..0000000 --- a/Svelto.ECS.Components/EntityStructs/ScalingEntityStruct.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Svelto.ECS.Components; - -namespace Svelto.ECS.EntityStructs -{ - public struct ScalingEntityStruct : IEntityStruct - { - public ECSVector3 scale; - } -} diff --git a/Svelto.ECS.Components/Svelto.ECS.Components.asmdef b/Svelto.ECS.Components/Svelto.ECS.Components.asmdef deleted file mode 100644 index cdf669d..0000000 --- a/Svelto.ECS.Components/Svelto.ECS.Components.asmdef +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "Svelto.ECS.Components.Unity", - "references": [ - "Unity.Mathematics", - "Svelto.ECS", - "Svelto.Common" - ], - "optionalUnityReferences": [], - "includePlatforms": [], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": false, - "precompiledReferences": [], - "autoReferenced": true, - "defineConstraints": [], - "versionDefines": [ - { - "name": "com.unity.mathematics", - "expression": "0.0.9", - "define": "UNITY_MATHEMATICS" - } - ] -} \ No newline at end of file diff --git a/Svelto.ECS/AllowMultipleAttribute.cs b/Svelto.ECS/AllowMultipleAttribute.cs new file mode 100644 index 0000000..37c26ec --- /dev/null +++ b/Svelto.ECS/AllowMultipleAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace Svelto.ECS +{ + public class AllowMultipleAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/Svelto.ECS/CheckEntityUtilities.cs b/Svelto.ECS/CheckEntityUtilities.cs index 5c26698..0b061f6 100644 --- a/Svelto.ECS/CheckEntityUtilities.cs +++ b/Svelto.ECS/CheckEntityUtilities.cs @@ -1,105 +1,81 @@ -#if !DEBUG || PROFILER -#define DISABLE_CHECKS +#if DEBUG && !PROFILER +using System.Collections.Generic; +using Svelto.DataStructures; +#else using System.Diagnostics; #endif -using System; -using System.Collections.Generic; -using Svelto.ECS.Internal; namespace Svelto.ECS { public partial class EnginesRoot { -#if DISABLE_CHECKS - [Conditional("_CHECKS_DISABLED")] -#endif - void CheckRemoveEntityID(EGID entityID, IEntityDescriptor descriptorEntity) +#if DEBUG && !PROFILER + void CheckRemoveEntityID(EGID egid) { - - var descriptorEntitiesToBuild = descriptorEntity.entitiesToBuild; - - if (_groupEntityDB.TryGetValue(entityID.groupID, out var @group)) + // Console.LogError("removed".FastConcat(egid.ToString())); + if (_idCheckers.TryGetValue(egid.groupID, out var hash)) { - for (int i = 0; i < descriptorEntitiesToBuild.Length; i++) - { - CheckRemoveEntityID(entityID, descriptorEntitiesToBuild[i].GetEntityType(), group, descriptorEntity.ToString()); - } + if (hash.Contains(egid.entityID) == false) + throw new ECSException("Entity with not found ID is about to be removed: id: " + .FastConcat(egid.entityID) + .FastConcat(" groupid: ") + .FastConcat(egid.groupID)); + + hash.Remove(egid.entityID); + + if (hash.Count == 0) + _idCheckers.Remove(egid.groupID); } else { - Console.LogError("Entity with not found ID is about to be removed: id: " - .FastConcat(entityID.entityID) - .FastConcat(" groupid: ") - .FastConcat(entityID.groupID)); + throw new ECSException("Entity with not found ID is about to be removed: id: " + .FastConcat(egid.entityID) + .FastConcat(" groupid: ") + .FastConcat(egid.groupID)); } } -#if DISABLE_CHECKS - [Conditional("_CHECKS_DISABLED")] -#endif - void CheckRemoveEntityID(EGID entityID, Type entityViewType, Dictionary group, string name) + void CheckAddEntityID(EGID egid) { - ITypeSafeDictionary entities; - if (group.TryGetValue(entityViewType, out entities)) - { - if (entities.Has(entityID.entityID) == false) - { - Console.LogError("Entity ".FastConcat(name, " with not found ID is about to be removed: ") - .FastConcat(entityViewType.ToString()) - .FastConcat(" id: ") - .FastConcat(entityID.entityID) - .FastConcat(" groupid: ") - .FastConcat(entityID.groupID)); - } - } +// Console.LogError("added ".FastConcat(egid.ToString())); + + if (_idCheckers.TryGetValue(egid.groupID, out var hash) == false) + hash = _idCheckers[egid.groupID] = new HashSet(); else { - Console.LogError("Entity ".FastConcat(name, " with not found ID is about to be removed: ") - .FastConcat(entityViewType.ToString()) - .FastConcat(" id: ") - .FastConcat(entityID.entityID) - .FastConcat(" groupid: ") - .FastConcat(entityID.groupID)); + if (hash.Contains(egid.entityID)) + throw new ECSException("Entity with used ID is about to be built: '" + .FastConcat("' id: '") + .FastConcat(egid.entityID) + .FastConcat("' groupid: '") + .FastConcat(egid.groupID) + .FastConcat("'")); } + + hash.Add(egid.entityID); + } + + void RemoveGroupID(ExclusiveGroup.ExclusiveGroupStruct groupID) + { + _idCheckers.Remove(groupID); } -#if DISABLE_CHECKS + readonly FasterDictionary> _idCheckers = new FasterDictionary>(); +#else [Conditional("_CHECKS_DISABLED")] -#endif - void CheckAddEntityID(EGID entityID, T descriptorEntity) where T:IEntityDescriptor + void CheckRemoveEntityID(EGID egid) { - var descriptorEntitiesToBuild = descriptorEntity.entitiesToBuild; - - //these are the entities added in this frame - if (_groupEntityDB.TryGetValue(entityID.groupID, out var @group)) - { - for (int i = 0; i < descriptorEntitiesToBuild.Length; i++) - { - CheckAddEntityID(entityID, descriptorEntitiesToBuild[i].GetEntityType(), group, - descriptorEntity.ToString()); - } - } } -#if DISABLE_CHECKS [Conditional("_CHECKS_DISABLED")] -#endif - static void CheckAddEntityID(EGID entityID, Type entityViewType, Dictionary group, - string name) + void CheckAddEntityID(EGID egid) { - ITypeSafeDictionary entities; - if (group.TryGetValue(entityViewType, out entities)) - { - if (entities.Has(entityID.entityID)) - { - Console.LogError("Entity ".FastConcat(name, " with used ID is about to be built: ") - .FastConcat(entityViewType.ToString()) - .FastConcat(" id: ") - .FastConcat(entityID.entityID) - .FastConcat(" groupid: ") - .FastConcat(entityID.groupID)); - } - } } + + [Conditional("_CHECKS_DISABLED")] + void RemoveGroupID(ExclusiveGroup.ExclusiveGroupStruct groupID) + { + } +#endif } -} \ No newline at end of file +} diff --git a/Svelto.ECS/DBC.cs b/Svelto.ECS/DBC.cs index 300cef0..144c690 100644 --- a/Svelto.ECS/DBC.cs +++ b/Svelto.ECS/DBC.cs @@ -6,339 +6,339 @@ using System; namespace DBC.ECS { - /// - /// Design By Contract Checks. - /// - /// Each method generates an exception or - /// a trace assertion statement if the contract is broken. - /// - /// - /// This example shows how to call the Require method. - /// Assume DBC_CHECK_PRECONDITION is defined. - /// - /// public void Test(int x) - /// { - /// try - /// { - /// Check.Require(x > 1, "x must be > 1"); - /// } - /// catch (System.Exception ex) - /// { - /// Console.WriteLine(ex.ToString()); - /// } - /// } - /// - /// If you wish to use trace assertion statements, intended for Debug scenarios, - /// rather than exception handling then set - /// - /// Check.UseAssertions = true - /// - /// You can specify this in your application entry point and maybe make it - /// dependent on conditional compilation flags or configuration file settings, e.g., - /// - /// #if DBC_USE_ASSERTIONS - /// Check.UseAssertions = true; - /// #endif - /// - /// You can direct output to a Trace listener. For example, you could insert - /// - /// Trace.Listeners.Clear(); - /// Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); - /// - /// - /// or direct output to a file or the Event Log. - /// - /// (Note: For ASP.NET clients use the Listeners collection - /// of the Debug, not the Trace, object and, for a Release build, only exception-handling - /// is possible.) - /// - /// - static class Check - { - #region Interface + /// + /// Design By Contract Checks. + /// + /// Each method generates an exception or + /// a trace assertion statement if the contract is broken. + /// + /// + /// This example shows how to call the Require method. + /// Assume DBC_CHECK_PRECONDITION is defined. + /// + /// public void Test(int x) + /// { + /// try + /// { + /// Check.Require(x > 1, "x must be > 1"); + /// } + /// catch (System.Exception ex) + /// { + /// Console.WriteLine(ex.ToString()); + /// } + /// } + /// + /// If you wish to use trace assertion statements, intended for Debug scenarios, + /// rather than exception handling then set + /// + /// Check.UseAssertions = true + /// + /// You can specify this in your application entry point and maybe make it + /// dependent on conditional compilation flags or configuration file settings, e.g., + /// + /// #if DBC_USE_ASSERTIONS + /// Check.UseAssertions = true; + /// #endif + /// + /// You can direct output to a Trace listener. For example, you could insert + /// + /// Trace.Listeners.Clear(); + /// Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); + /// + /// + /// or direct output to a file or the Event Log. + /// + /// (Note: For ASP.NET clients use the Listeners collection + /// of the Debug, not the Trace, object and, for a Release build, only exception-handling + /// is possible.) + /// + /// + static class Check + { + #region Interface - /// - /// Precondition check. - /// + /// + /// Precondition check. + /// #if DISABLE_CHECKS - [Conditional("__NEVER_DEFINED__")] + [Conditional("__NEVER_DEFINED__")] #endif - public static void Require(bool assertion, string message) - { - if (UseExceptions) - { - if (!assertion) - throw new PreconditionException(message); - } - else - { - Trace.Assert(assertion, "Precondition: " + message); - } - } + public static void Require(bool assertion, string message) + { + if (UseExceptions) + { + if (!assertion) + throw new PreconditionException(message); + } + else + { + Trace.Assert(assertion, "Precondition: " + message); + } + } - /// - /// Precondition check. - /// - /// + /// + /// Precondition check. + /// + /// #if DISABLE_CHECKS - [Conditional("__NEVER_DEFINED__")] + [Conditional("__NEVER_DEFINED__")] #endif - public static void Require(bool assertion, string message, Exception inner) - { - if (UseExceptions) - { - if (!assertion) - throw new PreconditionException(message, inner); - } - else - { - Trace.Assert(assertion, "Precondition: " + message); - } - } + public static void Require(bool assertion, string message, Exception inner) + { + if (UseExceptions) + { + if (!assertion) + throw new PreconditionException(message, inner); + } + else + { + Trace.Assert(assertion, "Precondition: " + message); + } + } - /// - /// Precondition check. - /// - /// + /// + /// Precondition check. + /// + /// #if DISABLE_CHECKS - [Conditional("__NEVER_DEFINED__")] + [Conditional("__NEVER_DEFINED__")] #endif - public static void Require(bool assertion) - { - if (UseExceptions) - { - if (!assertion) - throw new PreconditionException("Precondition failed."); - } - else - { - Trace.Assert(assertion, "Precondition failed."); - } - } - - /// - /// Postcondition check. - /// - /// + public static void Require(bool assertion) + { + if (UseExceptions) + { + if (!assertion) + throw new PreconditionException("Precondition failed."); + } + else + { + Trace.Assert(assertion, "Precondition failed."); + } + } + + /// + /// Postcondition check. + /// + /// #if DISABLE_CHECKS - [Conditional("__NEVER_DEFINED__")] + [Conditional("__NEVER_DEFINED__")] #endif - public static void Ensure(bool assertion, string message) - { - if (UseExceptions) - { - if (!assertion) - throw new PostconditionException(message); - } - else - { - Trace.Assert(assertion, "Postcondition: " + message); - } - } + public static void Ensure(bool assertion, string message) + { + if (UseExceptions) + { + if (!assertion) + throw new PostconditionException(message); + } + else + { + Trace.Assert(assertion, "Postcondition: " + message); + } + } - /// - /// Postcondition check. - /// - /// + /// + /// Postcondition check. + /// + /// #if DISABLE_CHECKS - [Conditional("__NEVER_DEFINED__")] + [Conditional("__NEVER_DEFINED__")] #endif - public static void Ensure(bool assertion, string message, Exception inner) - { - if (UseExceptions) - { - if (!assertion) - throw new PostconditionException(message, inner); - } - else - { - Trace.Assert(assertion, "Postcondition: " + message); - } - } + public static void Ensure(bool assertion, string message, Exception inner) + { + if (UseExceptions) + { + if (!assertion) + throw new PostconditionException(message, inner); + } + else + { + Trace.Assert(assertion, "Postcondition: " + message); + } + } - /// - /// Postcondition check. - /// - /// + /// + /// Postcondition check. + /// + /// #if DISABLE_CHECKS - [Conditional("__NEVER_DEFINED__")] + [Conditional("__NEVER_DEFINED__")] #endif - public static void Ensure(bool assertion) - { - if (UseExceptions) - { - if (!assertion) - throw new PostconditionException("Postcondition failed."); - } - else - { - Trace.Assert(assertion, "Postcondition failed."); - } - } - - /// - /// Invariant check. - /// - /// + public static void Ensure(bool assertion) + { + if (UseExceptions) + { + if (!assertion) + throw new PostconditionException("Postcondition failed."); + } + else + { + Trace.Assert(assertion, "Postcondition failed."); + } + } + + /// + /// Invariant check. + /// + /// #if DISABLE_CHECKS - [Conditional("__NEVER_DEFINED__")] + [Conditional("__NEVER_DEFINED__")] #endif - public static void Invariant(bool assertion, string message) - { - if (UseExceptions) - { - if (!assertion) - throw new InvariantException(message); - } - else - { - Trace.Assert(assertion, "Invariant: " + message); - } - } + public static void Invariant(bool assertion, string message) + { + if (UseExceptions) + { + if (!assertion) + throw new InvariantException(message); + } + else + { + Trace.Assert(assertion, "Invariant: " + message); + } + } - /// - /// Invariant check. - /// - /// + /// + /// Invariant check. + /// + /// #if DISABLE_CHECKS - [Conditional("__NEVER_DEFINED__")] + [Conditional("__NEVER_DEFINED__")] #endif - public static void Invariant(bool assertion, string message, Exception inner) - { - if (UseExceptions) - { - if (!assertion) - throw new InvariantException(message, inner); - } - else - { - Trace.Assert(assertion, "Invariant: " + message); - } - } + public static void Invariant(bool assertion, string message, Exception inner) + { + if (UseExceptions) + { + if (!assertion) + throw new InvariantException(message, inner); + } + else + { + Trace.Assert(assertion, "Invariant: " + message); + } + } - /// - /// Invariant check. - /// - /// + /// + /// Invariant check. + /// + /// #if DISABLE_CHECKS - [Conditional("__NEVER_DEFINED__")] + [Conditional("__NEVER_DEFINED__")] #endif - public static void Invariant(bool assertion) - { - if (UseExceptions) - { - if (!assertion) - throw new InvariantException("Invariant failed."); - } - else - { - Trace.Assert(assertion, "Invariant failed."); - } - } + public static void Invariant(bool assertion) + { + if (UseExceptions) + { + if (!assertion) + throw new InvariantException("Invariant failed."); + } + else + { + Trace.Assert(assertion, "Invariant failed."); + } + } - /// - /// Assertion check. - /// + /// + /// Assertion check. + /// #if DISABLE_CHECKS - [Conditional("__NEVER_DEFINED__")] + [Conditional("__NEVER_DEFINED__")] #endif - public static void Assert(bool assertion, string message) - { - if (UseExceptions) - { - if (!assertion) - throw new AssertionException(message); - } - else - { - Trace.Assert(assertion, "Assertion: " + message); - } - } + public static void Assert(bool assertion, string message) + { + if (UseExceptions) + { + if (!assertion) + throw new AssertionException(message); + } + else + { + Trace.Assert(assertion, "Assertion: " + message); + } + } - /// - /// Assertion check. - /// - /// + /// + /// Assertion check. + /// + /// #if DISABLE_CHECKS - [Conditional("__NEVER_DEFINED__")] + [Conditional("__NEVER_DEFINED__")] #endif - public static void Assert(bool assertion, string message, Exception inner) - { - if (UseExceptions) - { - if (!assertion) - throw new AssertionException(message, inner); - } - else - { - Trace.Assert(assertion, "Assertion: " + message); - } - } + public static void Assert(bool assertion, string message, Exception inner) + { + if (UseExceptions) + { + if (!assertion) + throw new AssertionException(message, inner); + } + else + { + Trace.Assert(assertion, "Assertion: " + message); + } + } - /// - /// Assertion check. - /// - /// + /// + /// Assertion check. + /// + /// #if DISABLE_CHECKS - [Conditional("__NEVER_DEFINED__")] + [Conditional("__NEVER_DEFINED__")] #endif - public static void Assert(bool assertion) - { - if (UseExceptions) - { - if (!assertion) - throw new AssertionException("Assertion failed."); - } - else - { - Trace.Assert(assertion, "Assertion failed."); - } - } + public static void Assert(bool assertion) + { + if (UseExceptions) + { + if (!assertion) + throw new AssertionException("Assertion failed."); + } + else + { + Trace.Assert(assertion, "Assertion failed."); + } + } + + /// + /// Set this if you wish to use Trace Assert statements + /// instead of exception handling. + /// (The Check class uses exception handling by default.) + /// + public static bool UseAssertions + { - /// - /// Set this if you wish to use Trace Assert statements - /// instead of exception handling. - /// (The Check class uses exception handling by default.) - /// - public static bool UseAssertions - { - - get - { - return useAssertions; - } - set - { - useAssertions = value; - } - } - - #endregion // Interface + get + { + return useAssertions; + } + set + { + useAssertions = value; + } + } - #region Implementation + #endregion // Interface - // No creation + #region Implementation - /// - /// Is exception handling being used? - /// - private static bool UseExceptions - { - get - { - return !useAssertions; - } - } + // No creation + + /// + /// Is exception handling being used? + /// + static bool UseExceptions + { + get + { + return !useAssertions; + } + } - // Are trace assertion statements being used? - // Default is to use exception handling. - private static bool useAssertions = false; + // Are trace assertion statements being used? + // Default is to use exception handling. + static bool useAssertions; - #endregion // Implementation + #endregion // Implementation - } // End Check + } // End Check - class Trace + internal class Trace { internal static void Assert(bool assertion, string v) { @@ -346,7 +346,7 @@ namespace DBC.ECS System.Diagnostics.Contracts.Contract.Assert(assertion, v); #else System.Diagnostics.Trace.Assert(assertion, v); -#endif +#endif } } @@ -354,93 +354,93 @@ namespace DBC.ECS /// /// Exception raised when a contract is broken. - /// Catch this exception type if you wish to differentiate between + /// Catch this exception type if you wish to differentiate between /// any DesignByContract exception and other runtime exceptions. - /// + /// /// public class DesignByContractException : Exception - { - protected DesignByContractException() {} - protected DesignByContractException(string message) : base(message) {} - protected DesignByContractException(string message, Exception inner) : base(message, inner) {} - } + { + protected DesignByContractException() {} + protected DesignByContractException(string message) : base(message) {} + protected DesignByContractException(string message, Exception inner) : base(message, inner) {} + } - /// - /// Exception raised when a precondition fails. - /// - public class PreconditionException : DesignByContractException - { - /// - /// Precondition Exception. - /// - public PreconditionException() {} - /// - /// Precondition Exception. - /// - public PreconditionException(string message) : base(message) {} - /// - /// Precondition Exception. - /// - public PreconditionException(string message, Exception inner) : base(message, inner) {} - } - - /// - /// Exception raised when a postcondition fails. - /// - public class PostconditionException : DesignByContractException - { - /// - /// Postcondition Exception. - /// - public PostconditionException() {} - /// - /// Postcondition Exception. - /// - public PostconditionException(string message) : base(message) {} - /// - /// Postcondition Exception. - /// - public PostconditionException(string message, Exception inner) : base(message, inner) {} - } + /// + /// Exception raised when a precondition fails. + /// + public class PreconditionException : DesignByContractException + { + /// + /// Precondition Exception. + /// + public PreconditionException() {} + /// + /// Precondition Exception. + /// + public PreconditionException(string message) : base(message) {} + /// + /// Precondition Exception. + /// + public PreconditionException(string message, Exception inner) : base(message, inner) {} + } - /// - /// Exception raised when an invariant fails. - /// - public class InvariantException : DesignByContractException - { - /// - /// Invariant Exception. - /// - public InvariantException() {} - /// - /// Invariant Exception. - /// - public InvariantException(string message) : base(message) {} - /// - /// Invariant Exception. - /// - public InvariantException(string message, Exception inner) : base(message, inner) {} - } + /// + /// Exception raised when a postcondition fails. + /// + public class PostconditionException : DesignByContractException + { + /// + /// Postcondition Exception. + /// + public PostconditionException() {} + /// + /// Postcondition Exception. + /// + public PostconditionException(string message) : base(message) {} + /// + /// Postcondition Exception. + /// + public PostconditionException(string message, Exception inner) : base(message, inner) {} + } - /// - /// Exception raised when an assertion fails. - /// - public class AssertionException : DesignByContractException - { - /// - /// Assertion Exception. - /// - public AssertionException() {} - /// - /// Assertion Exception. - /// - public AssertionException(string message) : base(message) {} - /// - /// Assertion Exception. - /// - public AssertionException(string message, Exception inner) : base(message, inner) {} - } + /// + /// Exception raised when an invariant fails. + /// + public class InvariantException : DesignByContractException + { + /// + /// Invariant Exception. + /// + public InvariantException() {} + /// + /// Invariant Exception. + /// + public InvariantException(string message) : base(message) {} + /// + /// Invariant Exception. + /// + public InvariantException(string message, Exception inner) : base(message, inner) {} + } + + /// + /// Exception raised when an assertion fails. + /// + public class AssertionException : DesignByContractException + { + /// + /// Assertion Exception. + /// + public AssertionException() {} + /// + /// Assertion Exception. + /// + public AssertionException(string message) : base(message) {} + /// + /// Assertion Exception. + /// + public AssertionException(string message, Exception inner) : base(message, inner) {} + } - #endregion // Exception classes + #endregion // Exception classes } // End Design By Contract diff --git a/Svelto.ECS/DataStructures/SetEGIDWithoutBoxing.cs b/Svelto.ECS/DataStructures/SetEGIDWithoutBoxing.cs new file mode 100644 index 0000000..730fd10 --- /dev/null +++ b/Svelto.ECS/DataStructures/SetEGIDWithoutBoxing.cs @@ -0,0 +1,37 @@ +using System; +using System.Linq.Expressions; +using System.Reflection; + +namespace Svelto.ECS.Internal +{ + internal static class SetEGIDWithoutBoxing where T : struct, IEntityStruct + { + internal delegate void ActionCast(ref T target, EGID egid); + + public static readonly ActionCast SetIDWithoutBoxing = MakeSetter(); + + static ActionCast MakeSetter() + { + if (EntityBuilder.HAS_EGID) + { + Type myTypeA = typeof(T); + PropertyInfo myFieldInfo = myTypeA.GetProperty("ID"); + + ParameterExpression targetExp = Expression.Parameter(typeof(T).MakeByRefType(), "target"); + ParameterExpression valueExp = Expression.Parameter(typeof(EGID), "value"); + MemberExpression fieldExp = Expression.Property(targetExp, myFieldInfo); + BinaryExpression assignExp = Expression.Assign(fieldExp, valueExp); + + var setter = Expression.Lambda(assignExp, targetExp, valueExp).Compile(); + + return setter; + } + + return null; + } + + public static void Warmup() + { + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs index 7a53f93..53a54be 100644 --- a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs +++ b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs @@ -1,27 +1,28 @@ using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; using Svelto.Common; using Svelto.DataStructures; -using Svelto.DataStructures.Experimental; namespace Svelto.ECS.Internal { public interface ITypeSafeDictionary { - int Count { get; } + int Count { get; } ITypeSafeDictionary Create(); - void RemoveEntitiesFromEngines( - Dictionary> entityViewEnginesDB, in PlatformProfiler profiler); + void AddEntitiesToEngines( + FasterDictionary, FasterList> entityViewEnginesDb, + ITypeSafeDictionary realDic, in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group); - void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, - Dictionary> engines, in PlatformProfiler profiler); + void RemoveEntitiesFromEngines(FasterDictionary, FasterList> entityViewEnginesDB, + in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group); void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId); - void AddEntitiesToEngines(Dictionary> entityViewEnginesDb, - ITypeSafeDictionary realDic, in PlatformProfiler profiler); + void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, + FasterDictionary, FasterList> engines, in PlatformProfiler profiler); + + void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup); + void RemoveEntityFromDictionary(EGID fromEntityGid, in PlatformProfiler profiler); void SetCapacity(uint size); void Trim(); @@ -34,111 +35,115 @@ namespace Svelto.ECS.Internal { static readonly Type _type = typeof(TValue); static readonly string _typeName = _type.Name; - static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type); - - public TypeSafeDictionary(uint size) : base(size) { } + static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type); + + public TypeSafeDictionary(uint size) : base(size) {} public TypeSafeDictionary() {} - + public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId) { var typeSafeDictionary = entitiesToSubmit as TypeSafeDictionary; - + foreach (var tuple in typeSafeDictionary) { try { - if (_hasEgid) - { - var needEgid = (INeedEGID)tuple.Value; - needEgid.ID = new EGID(tuple.Key, groupId); - - Add(tuple.Key, (TValue) needEgid); - } - else - Add(tuple.Key, ref tuple.Value); + if (_hasEgid) SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref tuple.Value, new EGID(tuple.Key, groupId)); + + Add(tuple.Key, tuple.Value); } catch (Exception e) { throw new TypeSafeDictionaryException( "trying to add an EntityView with the same ID more than once Entity: " - .FastConcat(typeof(TValue).ToString()).FastConcat("id ").FastConcat(tuple.Key), e); + .FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key), e); } } } public void AddEntitiesToEngines( - Dictionary> entityViewEnginesDB, - ITypeSafeDictionary realDic, in PlatformProfiler profiler) + FasterDictionary, FasterList> entityViewEnginesDB, + ITypeSafeDictionary realDic, in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group) { + var typeSafeDictionary = realDic as TypeSafeDictionary; + + //this can be optimized, should pass all the entities and not restart the process for each one foreach (var value in this) - { - var typeSafeDictionary = realDic as TypeSafeDictionary; - AddEntityViewToEngines(entityViewEnginesDB, ref typeSafeDictionary.GetValueByRef(value.Key), null, - in profiler); - } + in profiler, new EGID(value.Key, group)); } - public bool Has(uint entityIdEntityId) { return ContainsKey(entityIdEntityId); } + public void RemoveEntitiesFromEngines( + FasterDictionary, FasterList> entityViewEnginesDB, + in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group) + { + foreach (var value in this) + RemoveEntityViewFromEngines(entityViewEnginesDB, ref GetValueByRef(value.Key), null, in profiler, + new EGID(value.Key, group)); + } - public void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID? toEntityID, - ITypeSafeDictionary toGroup, Dictionary> engines, - in PlatformProfiler profiler) + public bool Has(uint entityIdEntityId) { - var valueIndex = GetValueIndex(fromEntityGid.entityID); + return ContainsKey(entityIdEntityId); + } + + public void RemoveEntityFromDictionary(EGID fromEntityGid, in PlatformProfiler profiler) + { + Remove(fromEntityGid.entityID); + } + + public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup) + { + var valueIndex = GetIndex(fromEntityGid.entityID); if (toGroup != null) { - RemoveEntityViewFromEngines(engines, ref _values[valueIndex], fromEntityGid.groupID, in profiler); - var toGroupCasted = toGroup as TypeSafeDictionary; - ref var entity = ref _values[valueIndex]; - var previousGroup = fromEntityGid.groupID; - - /// - /// NOTE I WOULD EVENTUALLY NEED TO REUSE THE REAL ID OF THE REMOVING ELEMENT - /// SO THAT I CAN DECREASE THE GLOBAL GROUP COUNT - /// - - // entity.ID = EGID.UPDATE_REAL_ID_AND_GROUP(entity.ID, toEntityID.groupID, entityCount); - if (_hasEgid) - { - var needEgid = (INeedEGID)entity; - needEgid.ID = toEntityID.Value; - entity = (TValue) needEgid; - } - - var index = toGroupCasted.Add(fromEntityGid.entityID, ref entity); - - AddEntityViewToEngines(engines, ref toGroupCasted._values[index], previousGroup, - in profiler); - } - else - RemoveEntityViewFromEngines(engines, ref _values[valueIndex], null, in profiler); + ref var entity = ref valuesArray[valueIndex]; + if (_hasEgid) SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref entity, toEntityID); - Remove(fromEntityGid.entityID); + toGroupCasted.Add(fromEntityGid.entityID, entity); + } } - public void RemoveEntitiesFromEngines( - Dictionary> entityViewEnginesDB, - in PlatformProfiler profiler) + public void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, + FasterDictionary, FasterList> engines, in PlatformProfiler profiler) { - var values = GetValuesArray(out var count); + var valueIndex = GetIndex(fromEntityGid.entityID); + + ref var entity = ref valuesArray[valueIndex]; + + if (toGroup != null) + { + RemoveEntityViewFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler, + fromEntityGid); + + var toGroupCasted = toGroup as TypeSafeDictionary; + var previousGroup = fromEntityGid.groupID; - for (var i = 0; i < count; i++) - RemoveEntityViewFromEngines(entityViewEnginesDB, ref values[i], null, in profiler); + if (_hasEgid) SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref entity, toEntityID.Value); + + var index = toGroupCasted.GetIndex(toEntityID.Value.entityID); + + AddEntityViewToEngines(engines, ref toGroupCasted.valuesArray[index], previousGroup, + in profiler, toEntityID.Value); + } + else + RemoveEntityViewFromEngines(engines, ref entity, null, in profiler, fromEntityGid); } - public ITypeSafeDictionary Create() { return new TypeSafeDictionary(); } + public ITypeSafeDictionary Create() + { + return new TypeSafeDictionary(); + } - void AddEntityViewToEngines(Dictionary> entityViewEnginesDB, - ref TValue entity, - ExclusiveGroup.ExclusiveGroupStruct? previousGroup, - in PlatformProfiler profiler) + void AddEntityViewToEngines(FasterDictionary, FasterList> entityViewEnginesDB, + ref TValue entity, ExclusiveGroup.ExclusiveGroupStruct? previousGroup, + in PlatformProfiler profiler, EGID egid) { //get all the engines linked to TValue - if (!entityViewEnginesDB.TryGetValue(_type, out var entityViewsEngines)) return; + if (!entityViewEnginesDB.TryGetValue(new RefWrapper(_type), out var entityViewsEngines)) return; if (previousGroup == null) { @@ -147,7 +152,7 @@ namespace Svelto.ECS.Internal { using (profiler.Sample(entityViewsEngines[i], _typeName)) { - (entityViewsEngines[i] as IReactOnAddAndRemove).Add(ref entity); + (entityViewsEngines[i] as IReactOnAddAndRemove).Add(ref entity, egid); } } catch (Exception e) @@ -163,22 +168,23 @@ namespace Svelto.ECS.Internal { using (profiler.Sample(entityViewsEngines[i], _typeName)) { - (entityViewsEngines[i] as IReactOnSwap).MovedTo(ref entity, previousGroup.Value); + (entityViewsEngines[i] as IReactOnSwap).MovedTo(ref entity, previousGroup.Value, + egid); } } catch (Exception e) { throw new ECSException( - "Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e); + "Code crashed inside MovedTo callback ".FastConcat(typeof(TValue).ToString()), e); } } } static void RemoveEntityViewFromEngines( - Dictionary> entityViewEnginesDB, ref TValue entity, - ExclusiveGroup.ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler) + FasterDictionary, FasterList> @group, ref TValue entity, + ExclusiveGroup.ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid) { - if (!entityViewEnginesDB.TryGetValue(_type, out var entityViewsEngines)) return; + if (!@group.TryGetValue(new RefWrapper(_type), out var entityViewsEngines)) return; if (previousGroup == null) { @@ -186,7 +192,7 @@ namespace Svelto.ECS.Internal try { using (profiler.Sample(entityViewsEngines[i], _typeName)) - (entityViewsEngines[i] as IReactOnAddAndRemove).Remove(ref entity); + (entityViewsEngines[i] as IReactOnAddAndRemove).Remove(ref entity, egid); } catch (Exception e) { @@ -194,13 +200,14 @@ namespace Svelto.ECS.Internal "Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e); } } +#if SEEMS_UNNECESSARY else { for (var i = 0; i < entityViewsEngines.Count; i++) try { using (profiler.Sample(entityViewsEngines[i], _typeName)) - (entityViewsEngines[i] as IReactOnSwap).MovedFrom(ref entity); + (entityViewsEngines[i] as IReactOnSwap).MovedFrom(ref entity, egid); } catch (Exception e) { @@ -208,18 +215,7 @@ namespace Svelto.ECS.Internal "Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e); } } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ref TValue FindElement(uint entityGidEntityId) - { -#if DEBUG && !PROFILER - if (TryFindIndex(entityGidEntityId, out var findIndex) == false) - throw new Exception("Entity not found in this group ".FastConcat(typeof(TValue).ToString())); -#else - TryFindIndex(entityGidEntityId, out var findIndex); #endif - return ref _values[findIndex]; } } } \ No newline at end of file diff --git a/Svelto.ECS/Dispatcher/DispatchOnChange.cs b/Svelto.ECS/Dispatcher/DispatchOnChange.cs index cb399ff..d1ce756 100644 --- a/Svelto.ECS/Dispatcher/DispatchOnChange.cs +++ b/Svelto.ECS/Dispatcher/DispatchOnChange.cs @@ -2,7 +2,7 @@ using System; namespace Svelto.ECS { - public class DispatchOnChange : DispatchOnSet where T:struct, IEquatable + public class DispatchOnChange : DispatchOnSet where T:IEquatable { public DispatchOnChange(EGID senderID) : base(senderID) { } @@ -15,10 +15,7 @@ namespace Svelto.ECS base.value = value; } - get - { - return _value; - } + get => _value; } } } diff --git a/Svelto.ECS/Dispatcher/DispatchOnSet.cs b/Svelto.ECS/Dispatcher/DispatchOnSet.cs index 120ce32..d407ae8 100644 --- a/Svelto.ECS/Dispatcher/DispatchOnSet.cs +++ b/Svelto.ECS/Dispatcher/DispatchOnSet.cs @@ -1,14 +1,11 @@ using System; -using Svelto.WeakEvents; namespace Svelto.ECS { - public class DispatchOnSet where T:struct + public class DispatchOnSet { public DispatchOnSet(EGID senderID) { - _subscribers = new WeakEvent(); - _senderID = senderID; } @@ -18,8 +15,8 @@ namespace Svelto.ECS { _value = value; - if(_paused == false) - _subscribers.Invoke(_senderID, value); + if (_paused == false) + _subscribers(_senderID, value); } } @@ -39,7 +36,7 @@ namespace Svelto.ECS protected T _value; readonly EGID _senderID; - WeakEvent _subscribers; - bool _paused; + Action _subscribers; + bool _paused; } } diff --git a/Svelto.ECS/DynamicEntityDescriptor.cs b/Svelto.ECS/DynamicEntityDescriptor.cs index 4fcf164..f40d3e7 100644 --- a/Svelto.ECS/DynamicEntityDescriptor.cs +++ b/Svelto.ECS/DynamicEntityDescriptor.cs @@ -1,4 +1,5 @@ using System; +using Svelto.DataStructures; namespace Svelto.ECS { @@ -8,32 +9,120 @@ namespace Svelto.ECS /// This method allocates, so it shouldn't be abused /// /// - public struct DynamicEntityDescriptor:IEntityDescriptor where TType : IEntityDescriptor, new() + public struct DynamicEntityDescriptor : IEntityDescriptor where TType : IEntityDescriptor, new() { - public DynamicEntityDescriptor(IEntityBuilder[] extraEntities) + internal DynamicEntityDescriptor(bool isExtendible) : this() { - DBC.ECS.Check.Require(extraEntities.Length > 0, - "don't use a DynamicEntityDescriptorInfo if you don't need to use extra EntityViews"); - var defaultEntities = EntityDescriptorTemplate.descriptor.entitiesToBuild; var length = defaultEntities.Length; - entitiesToBuild = new IEntityBuilder[length + extraEntities.Length + 1]; + _entitiesToBuild = new IEntityBuilder[length + 1]; - Array.Copy(defaultEntities, 0, entitiesToBuild, 0, length); - Array.Copy(extraEntities, 0, entitiesToBuild, length, extraEntities.Length); + Array.Copy(defaultEntities, 0, _entitiesToBuild, 0, length); + + //assign it after otherwise the previous copy will overwrite the value in case the item + //is already present + _entitiesToBuild[length] = new EntityBuilder + ( + new EntityStructInfoView + { + entitiesToBuild = _entitiesToBuild + } + ); + } + + public DynamicEntityDescriptor(IEntityBuilder[] extraEntityBuilders) : this() + { + var extraEntitiesLength = extraEntityBuilders.Length; + + _entitiesToBuild = Construct(extraEntitiesLength, extraEntityBuilders, + EntityDescriptorTemplate.descriptor.entitiesToBuild); + } + + public DynamicEntityDescriptor(FasterList extraEntityBuilders) : this() + { + var extraEntities = extraEntityBuilders.ToArrayFast(); + var extraEntitiesLength = extraEntityBuilders.Count; + + _entitiesToBuild = Construct(extraEntitiesLength, extraEntities, + EntityDescriptorTemplate.descriptor.entitiesToBuild); + } + + public void ExtendWith() where T : IEntityDescriptor, new() + { + var newEntitiesToBuild = EntityDescriptorTemplate.descriptor.entitiesToBuild; + + _entitiesToBuild = Construct(newEntitiesToBuild.Length, newEntitiesToBuild, _entitiesToBuild); + } + + public void ExtendWith(IEntityBuilder[] extraEntities) + { + _entitiesToBuild = Construct(extraEntities.Length, extraEntities, _entitiesToBuild); + } + + static IEntityBuilder[] Construct(int extraEntitiesLength, IEntityBuilder[] extraEntities, + IEntityBuilder[] startingEntities) + { + IEntityBuilder[] localEntitiesToBuild; + + if (extraEntitiesLength == 0) + { + localEntitiesToBuild = startingEntities; + return localEntitiesToBuild; + } + + var defaultEntities = startingEntities; + var length = defaultEntities.Length; + + var index = SetupSpecialEntityStruct(defaultEntities, out localEntitiesToBuild, extraEntitiesLength); + + Array.Copy(extraEntities, 0, localEntitiesToBuild, length, extraEntitiesLength); + + //assign it after otherwise the previous copy will overwrite the value in case the item + //is already present + localEntitiesToBuild[index] = new EntityBuilder + ( + new EntityStructInfoView + { + entitiesToBuild = localEntitiesToBuild + } + ); + + return localEntitiesToBuild; + } + + static int SetupSpecialEntityStruct(IEntityBuilder[] defaultEntities, out IEntityBuilder[] entitiesToBuild, + int extraLenght) + { + int length = defaultEntities.Length; + int index = -1; - var builder = new EntityBuilder + for (var i = 0; i < length; i++) { - _initializer = new EntityStructInfoView - { - entitiesToBuild = entitiesToBuild, - type = typeof(TType) + //the special entity already exists + if (defaultEntities[i].GetEntityType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) + { + index = i; + break; } - }; - entitiesToBuild[entitiesToBuild.Length - 1] = builder; + } + + if (index == -1) + { + index = length + extraLenght; + entitiesToBuild = new IEntityBuilder[index + 1]; + } + else + entitiesToBuild = new IEntityBuilder[length + extraLenght]; + + Array.Copy(defaultEntities, 0, entitiesToBuild, 0, length); + + return index; } - public IEntityBuilder[] entitiesToBuild { get; } + + public IEntityBuilder[] entitiesToBuild => _entitiesToBuild; + + IEntityBuilder[] _entitiesToBuild; } } \ No newline at end of file diff --git a/Svelto.ECS/ECSException.cs b/Svelto.ECS/ECSException.cs index 712edd6..b815f84 100644 --- a/Svelto.ECS/ECSException.cs +++ b/Svelto.ECS/ECSException.cs @@ -1,13 +1,13 @@ using System; -namespace Svelto.ECS.Internal +namespace Svelto.ECS { - class ECSException : Exception + public class ECSException : Exception { - public ECSException(string message):base(message) + public ECSException(string message):base("".FastConcat(message, "")) {} - public ECSException(string message, Exception innerE):base(message, innerE) + public ECSException(string message, Exception innerE):base("".FastConcat(message, ""), innerE) {} } } \ No newline at end of file diff --git a/Svelto.ECS.Components/ECSResources/ECSResources.cs b/Svelto.ECS/ECSResources/ECSResources.cs similarity index 66% rename from Svelto.ECS.Components/ECSResources/ECSResources.cs rename to Svelto.ECS/ECSResources/ECSResources.cs index 2ebba29..70ba614 100644 --- a/Svelto.ECS.Components/ECSResources/ECSResources.cs +++ b/Svelto.ECS/ECSResources/ECSResources.cs @@ -11,7 +11,12 @@ namespace Svelto.ECS.Experimental static class ResourcesECSDB { - internal static readonly FasterList _resources = new FasterList(); + static readonly FasterList _resources = new FasterList(); + + internal static ref T resources(uint id) + { + return ref _resources[(int) id - 1]; + } internal static uint ToECS(T resource) { @@ -25,7 +30,7 @@ namespace Svelto.ECS.Experimental if (id - 1 < _resources.Count) return _resources[(int) id - 1]; - return default(T); + return default; } } @@ -34,17 +39,9 @@ namespace Svelto.ECS.Experimental public static void Set(ref this ECSResources resource, T newText) { if (resource.id != 0) - ResourcesECSDB._resources[(int) resource.id] = newText; + ResourcesECSDB.resources(resource.id) = newText; else resource.id = ResourcesECSDB.ToECS(newText); } - - public static void Set(ref this ECSString resource, string newText) - { - if (resource.id != 0) - ResourcesECSDB._resources[(int) resource.id] = newText; - else - resource.id = ResourcesECSDB.ToECS(newText); - } } } \ No newline at end of file diff --git a/Svelto.ECS/ECSResources/ECSString.cs b/Svelto.ECS/ECSResources/ECSString.cs new file mode 100644 index 0000000..4eac06c --- /dev/null +++ b/Svelto.ECS/ECSResources/ECSString.cs @@ -0,0 +1,38 @@ +using System; + +namespace Svelto.ECS.Experimental +{ + [Serialization.DoNotSerialize] + public struct ECSString:IEquatable + { + uint id; + + public ECSString(string newText) + { + id = ResourcesECSDB.ToECS(newText); + } + + public static implicit operator string(ECSString ecsString) + { + return ResourcesECSDB.FromECS(ecsString.id); + } + + public void Set(string newText) + { + if (id != 0) + ResourcesECSDB.resources(id) = newText; + else + id = ResourcesECSDB.ToECS(newText); + } + + public bool Equals(ECSString other) + { + return other.id == id; + } + + public override string ToString() + { + return ResourcesECSDB.FromECS(id); + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/EGID.cs b/Svelto.ECS/EGID.cs index c466147..4e7d768 100644 --- a/Svelto.ECS/EGID.cs +++ b/Svelto.ECS/EGID.cs @@ -1,14 +1,15 @@ -#if !REAL_ID using System; using System.Collections.Generic; + #pragma warning disable 660,661 namespace Svelto.ECS { + //todo: add debug map + [Serialization.DoNotSerialize] + [Serializable] public struct EGID:IEquatable,IEqualityComparer,IComparable { - readonly ulong _GID; - public uint entityID => (uint) (_GID & 0xFFFFFFFF); public ExclusiveGroup.ExclusiveGroupStruct groupID => new ExclusiveGroup.ExclusiveGroupStruct((uint) (_GID >> 32)); @@ -17,17 +18,17 @@ namespace Svelto.ECS { return obj1._GID == obj2._GID; } - + public static bool operator !=(EGID obj1, EGID obj2) { return obj1._GID != obj2._GID; } - + public EGID(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) : this() { _GID = MAKE_GLOBAL_ID(entityID, groupID); } - + static ulong MAKE_GLOBAL_ID(uint entityId, uint groupId) { return (ulong)groupId << 32 | ((ulong)entityId & 0xFFFFFFFF); @@ -37,10 +38,10 @@ namespace Svelto.ECS { return id.entityID; } - + //in the way it's used, ulong must be always the same for each id/group public static explicit operator ulong(EGID id) { return id._GID; } - + public bool Equals(EGID other) { return _GID == other._GID; @@ -65,138 +66,12 @@ namespace Svelto.ECS { _GID = MAKE_GLOBAL_ID(entityID, groupID); } - } -} -#else - -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -#pragma warning disable 660,661 - -namespace Svelto.ECS -{ - public struct EGID:IEquatable,IEqualityComparer,IComparable - { - readonly ulong _GID; - const int idbits = 22; //one bit is reserved - const int groupbits = 20; - const int realidbits = 21; - - public EGID(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) : this() - { - DBC.ECS.Check.Require(entityID < bit21, "the entityID value is outside the range, max value: (2^22)-1"); - DBC.ECS.Check.Require(groupID < bit20, "the groupID value is outside the range"); - - _GID = MAKE_GLOBAL_ID(entityID, groupID, 0, 1); - } - - const uint bit21 = 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001_1111_1111_1111_1111_1111; - const uint bit22 = 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0011_1111_1111_1111_1111_1111; - const uint bit20 = 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_1111_1111_1111_1111_1111; - - public uint entityID - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return (uint) (_GID & bit22); } - } - - public ExclusiveGroup.ExclusiveGroupStruct groupID => - new ExclusiveGroup.ExclusiveGroupStruct((uint) ((_GID >> idbits) & bit20)); - -// 1 21 20 1 21 -// | | realid | groupid |R| entityID | - - static ulong MAKE_GLOBAL_ID(uint entityId, uint groupId, uint realId, byte hasID) + public override string ToString() { - var makeGlobalId = (((ulong)realId & bit21) << (idbits+groupbits)) | (((ulong)groupId & bit20) << idbits) | ((ulong)entityId & bit22); - - return makeGlobalId | (ulong) (hasID << idbits + groupbits + realidbits); + return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID); } - public static explicit operator uint(EGID id) - { - return id.entityID; - } - - public static bool operator ==(EGID obj1, EGID obj2) - { - throw new NotSupportedException(); - } - - public static bool operator !=(EGID obj1, EGID obj2) - { - throw new NotSupportedException(); - } - - public bool Equals(EGID other) - { - throw new NotSupportedException(); - } - - public bool Equals(EGID x, EGID y) - { - throw new NotSupportedException(); - } - - public int CompareTo(EGID other) - { - throw new NotSupportedException(); - } - -//in the way it's used, ulong must be always the same for each id/group -public static explicit operator ulong(EGID id) -{ - throw new NotSupportedException(); -} - - public int GetHashCode(EGID egid) - { - throw new NotSupportedException(); - } - - internal EGID(ulong GID) : this() - { - _GID = GID; - } - - internal EGID(uint entityID, uint groupID) : this() - { - _GID = MAKE_GLOBAL_ID(entityID, groupID, 0, 1); - } - - internal static EGID UPDATE_REAL_ID_AND_GROUP(EGID egid, uint toGroupID, uint realID) - { - if (egid.hasID == 0) - return new EGID(MAKE_GLOBAL_ID(SAFE_ID(realID), toGroupID, realID, 0)); - - return new EGID(MAKE_GLOBAL_ID(egid.entityID, toGroupID, realID, 1)); - } - - internal static EGID UPDATE_REAL_ID(EGID egid, uint realID) - { - if (egid.hasID == 0) - return new EGID(MAKE_GLOBAL_ID(SAFE_ID(realID), egid.groupID, realID, 0)); - - return new EGID(MAKE_GLOBAL_ID(egid.entityID, egid.groupID, realID, 1)); - } - - internal static EGID CREATE_WITHOUT_ID(uint toGroupID, uint realID) - { - var _GID = MAKE_GLOBAL_ID(SAFE_ID(realID), toGroupID, realID, 0); - return new EGID(_GID); - } - - public byte hasID { get { return (byte) (_GID >> idbits + groupbits + realidbits); } } - - internal uint realID - { - get { return ((uint)(_GID >> idbits + groupbits)) & bit21; } - } - - static uint SAFE_ID(uint u) { return u | (bit21 + 1); } + readonly ulong _GID; } } -#endif \ No newline at end of file diff --git a/Svelto.ECS/EGIDMapper.cs b/Svelto.ECS/EGIDMapper.cs index 428ba6f..ba2ceb5 100644 --- a/Svelto.ECS/EGIDMapper.cs +++ b/Svelto.ECS/EGIDMapper.cs @@ -1,28 +1,51 @@ +using System; using System.Runtime.CompilerServices; -using Svelto.ECS.Internal; +using System.Runtime.InteropServices; +using Svelto.DataStructures; namespace Svelto.ECS { public struct EGIDMapper where T : struct, IEntityStruct { - internal TypeSafeDictionary map; + internal FasterDictionary map; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Entity(uint entityID) { - return ref map.FindElement(entityID); +#if DEBUG && !PROFILER + if (map.TryFindIndex(entityID, out var findIndex) == false) + throw new Exception("Entity not found in this group ".FastConcat(typeof(T).ToString())); +#else + map.TryFindIndex(entityID, out var findIndex); +#endif + return ref map.valuesArray[findIndex]; } - public bool TryQueryEntity(uint entityID, out T @value) + public bool TryGetEntity(uint entityID, out T value) { if (map.TryFindIndex(entityID, out var index)) { - @value = map.GetDirectValue(index); + value = map.GetDirectValue(index); return true; } - @value = default; + value = default; return false; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeStructRef EntityUnsafeRef(uint entityID) + { +#if DEBUG && !PROFILER + if (map.TryFindIndex(entityID, out var findIndex) == false) + throw new Exception("Entity not found in this group ".FastConcat(typeof(T).ToString())); +#else + map.TryFindIndex(entityID, out var findIndex); +#endif + var alloc = GCHandle.Alloc(map.valuesArray, GCHandleType.Pinned); + + return new UnsafeStructRef(ref map.valuesArray[findIndex], alloc); + } } -} \ No newline at end of file +} + diff --git a/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs b/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs index 79972b4..8625abe 100644 --- a/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs +++ b/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs @@ -1,7 +1,7 @@ -using Svelto.DataStructures.Experimental; -using EntitiesDB = - Svelto.DataStructures.Experimental.FasterDictionary>; +using System; +using System.Collections; +using Svelto.DataStructures; +using Svelto.ECS.Internal; namespace Svelto.ECS { @@ -17,7 +17,9 @@ namespace Svelto.ECS void Swap(ref T item1, ref T item2) { - T toSwap = item2; item2 = item1; item1 = toSwap; + var toSwap = item2; + item2 = item1; + item1 = toSwap; } public void ClearOther() @@ -28,22 +30,27 @@ namespace Svelto.ECS //do not remove the dictionaries of entities per type created so far, they will be reused foreach (var entitiesPerType in groups.Value) { - //clear the dictionary of entities create do far (it won't allocate though) + //clear the dictionary of entities create do far (it won't allocate though) entitiesPerType.Value.Clear(); } } otherEntitiesCreatedPerGroup.Clear(); } - + internal FasterDictionary currentEntitiesCreatedPerGroup; internal FasterDictionary otherEntitiesCreatedPerGroup; - - internal EntitiesDB current; - internal EntitiesDB other; - readonly EntitiesDB _entityViewsToAddBufferA = new EntitiesDB(); - readonly EntitiesDB _entityViewsToAddBufferB = new EntitiesDB(); + internal FasterDictionary, ITypeSafeDictionary>> current; + internal FasterDictionary, ITypeSafeDictionary>> other; + + readonly FasterDictionary, ITypeSafeDictionary>> + _entityViewsToAddBufferA = + new FasterDictionary, ITypeSafeDictionary>>(); + + readonly FasterDictionary, ITypeSafeDictionary>> + _entityViewsToAddBufferB = + new FasterDictionary, ITypeSafeDictionary>>(); readonly FasterDictionary _entitiesCreatedPerGroupA = new FasterDictionary(); readonly FasterDictionary _entitiesCreatedPerGroupB = new FasterDictionary(); @@ -52,7 +59,7 @@ namespace Svelto.ECS { currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA; otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB; - + current = _entityViewsToAddBufferA; other = _entityViewsToAddBufferB; } diff --git a/Svelto.ECS/EnginesRoot.Engines.cs b/Svelto.ECS/EnginesRoot.Engines.cs index 3d0f0ae..8438e5b 100644 --- a/Svelto.ECS/EnginesRoot.Engines.cs +++ b/Svelto.ECS/EnginesRoot.Engines.cs @@ -1,15 +1,28 @@ using System; using System.Collections.Generic; using Svelto.DataStructures; -using Svelto.DataStructures.Experimental; using Svelto.ECS.Internal; using Svelto.ECS.Schedulers; -using Svelto.WeakEvents; namespace Svelto.ECS { public partial class EnginesRoot { + public struct EntitiesSubmitter + { + public EntitiesSubmitter(EnginesRoot enginesRoot) + { + _weakReference = new DataStructures.WeakReference(enginesRoot); + } + + public void Invoke() + { + if (_weakReference.IsValid) + _weakReference.Target.SubmitEntityViews(); + } + + readonly DataStructures.WeakReference _weakReference; + } /// /// Engines root contextualize your engines and entities. You don't need to limit yourself to one EngineRoot /// as multiple engines root could promote separation of scopes. The EntitySubmissionScheduler checks @@ -21,37 +34,47 @@ namespace Svelto.ECS public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler) { _entitiesOperations = new FasterDictionary(); - _reactiveEnginesAddRemove = new Dictionary>(); - _reactiveEnginesSwap = new Dictionary>(); - _enginesSet = new HashSet(); + _reactiveEnginesAddRemove = new FasterDictionary, FasterList>(); + _reactiveEnginesSwap = new FasterDictionary, FasterList>(); + _enginesSet = new FasterList(); + _enginesTypeSet = new HashSet(); _disposableEngines = new FasterList(); _transientEntitiesOperations = new FasterList(); - _groupEntityDB = new FasterDictionary>(); - _groupsPerEntity = new Dictionary>(); + _groupEntityViewsDB = new FasterDictionary, ITypeSafeDictionary>>(); + _groupsPerEntity = new FasterDictionary, FasterDictionary>(); _groupedEntityToAdd = new DoubleBufferedEntitiesToAdd(); _entitiesStream = new EntitiesStream(); - _entitiesDB = new EntitiesDB(_groupEntityDB, _groupsPerEntity, _entitiesStream); - + _entitiesDB = new EntitiesDB(_groupEntityViewsDB, _groupsPerEntity, _entitiesStream); + _scheduler = entityViewScheduler; - _scheduler.onTick = new WeakAction(SubmitEntityViews); + _scheduler.onTick = new EntitiesSubmitter(this); + } + + public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler, bool isDeserializationOnly):this(entityViewScheduler) + { + _isDeserializationOnly = isDeserializationOnly; } public void AddEngine(IEngine engine) { - DBC.ECS.Check.Require(_enginesSet.Contains(engine) == false, - "The same engine has been added more than once " - .FastConcat(engine.ToString())); - + var type = engine.GetType(); + var refWrapper = new RefWrapper(type); + DBC.ECS.Check.Require( + _enginesTypeSet.Contains(refWrapper) == false || + type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true, + "The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute " + .FastConcat(engine.ToString())); try { if (engine is IReactOnAddAndRemove viewEngine) - CheckEntityViewsEngine(viewEngine, _reactiveEnginesAddRemove); - + CheckEntityViewsEngine(viewEngine, _reactiveEnginesAddRemove); + if (engine is IReactOnSwap viewEngineSwap) - CheckEntityViewsEngine(viewEngineSwap, _reactiveEnginesSwap); + CheckEntityViewsEngine(viewEngineSwap, _reactiveEnginesSwap); + _enginesTypeSet.Add(refWrapper); _enginesSet.Add(engine); if (engine is IDisposable) @@ -65,15 +88,12 @@ namespace Svelto.ECS } catch (Exception e) { -#if !DEBUG - throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString()), e); -#else - Console.LogException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString()), e); -#endif + throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " "), e); } } - - void CheckEntityViewsEngine(IEngine engine, Dictionary> engines) + + void CheckEntityViewsEngine(T engine, FasterDictionary, FasterList> engines) + where T : class, IEngine { var interfaces = engine.GetType().GetInterfaces(); @@ -89,9 +109,10 @@ namespace Svelto.ECS } static void AddEngine(T engine, Type[] entityViewTypes, - Dictionary> engines) where T:IEngine + FasterDictionary, FasterList> engines) + where T : class, IEngine { - for (int i = 0; i < entityViewTypes.Length; i++) + for (var i = 0; i < entityViewTypes.Length; i++) { var type = entityViewTypes[i]; @@ -99,40 +120,24 @@ namespace Svelto.ECS } } - static void AddEngine(T engine, Dictionary> engines, Type type) where T : IEngine + static void AddEngine(T engine, FasterDictionary, FasterList> engines, Type type) + where T : class, IEngine { - if (engines.TryGetValue(type, out var list) == false) + if (engines.TryGetValue(new RefWrapper(type), out var list) == false) { - list = new FasterList(); + list = new FasterList(); - engines.Add(type, list); + engines.Add(new RefWrapper(type), list); } list.Add(engine); } - readonly Dictionary> _reactiveEnginesAddRemove; - readonly Dictionary> _reactiveEnginesSwap; - readonly HashSet _enginesSet; - readonly FasterList _disposableEngines; - - //one datastructure rule them all: - //split by group - //split by type per group. It's possible to get all the entities of a give type T per group thanks - //to the FasterDictionary capabilities OR it's possible to get a specific entityView indexed by - //ID. This ID doesn't need to be the EGID, it can be just the entityID - //for each group id, save a dictionary indexed by entity type of entities indexed by id - //ITypeSafeDictionary = Key = entityID, Value = EntityStruct - readonly FasterDictionary> _groupEntityDB; - //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are - //found indexed by group id - //EntityViewType //groupID //entityID, EntityStruct - readonly Dictionary> _groupsPerEntity; - - readonly EntitiesStream _entitiesStream; - readonly EntitiesDB _entitiesDB; + readonly FasterDictionary, FasterList> _reactiveEnginesAddRemove; + readonly FasterDictionary, FasterList> _reactiveEnginesSwap; + readonly FasterList _disposableEngines; - static readonly Type OBJECT_TYPE = typeof(object); - static readonly Type ENTITY_INFO_VIEW_TYPE = typeof(EntityStructInfoView); + readonly FasterList _enginesSet; + readonly HashSet _enginesTypeSet; } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.Entities.cs b/Svelto.ECS/EnginesRoot.Entities.cs index e34914a..518ac13 100644 --- a/Svelto.ECS/EnginesRoot.Entities.cs +++ b/Svelto.ECS/EnginesRoot.Entities.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using Svelto.Common; -using Svelto.DataStructures.Experimental; +using Svelto.DataStructures; using Svelto.ECS.Internal; namespace Svelto.ECS @@ -18,28 +18,35 @@ namespace Svelto.ECS { using (var profiler = new PlatformProfiler("Final Dispose")) { - foreach (var groups in _groupEntityDB) + foreach (var groups in _groupEntityViewsDB) + { foreach (var entityList in groups.Value) { - try - { - entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler); - } - catch (Exception e) - { - Console.LogException(e); - } + entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, + profiler, new ExclusiveGroup.ExclusiveGroupStruct(groups.Key)); } + } + + _groupEntityViewsDB.Clear(); + _groupsPerEntity.Clear(); foreach (var engine in _disposableEngines) - try - { engine.Dispose(); - } - catch (Exception e) - { - Console.LogException(e); - } + + _disposableEngines.Clear(); + _enginesSet.Clear(); + _enginesTypeSet.Clear(); + _reactiveEnginesSwap.Clear(); + _reactiveEnginesAddRemove.Clear(); + + _entitiesOperations.Clear(); + _transientEntitiesOperations.Clear(); +#if DEBUG && !PROFILER + _idCheckers.Clear(); +#endif + _groupedEntityToAdd = null; + + _entitiesStream.Dispose(); } GC.SuppressFinalize(this); @@ -56,252 +63,242 @@ namespace Svelto.ECS /// public IEntityStreamConsumerFactory GenerateConsumerFactory() { - return new GenericentityStreamConsumerFactory(new DataStructures.WeakReference(this)); + return new GenericEntityStreamConsumerFactory(this); } public IEntityFactory GenerateEntityFactory() { - return new GenericEntityFactory(new DataStructures.WeakReference(this)); + return new GenericEntityFactory(this); } public IEntityFunctions GenerateEntityFunctions() { - return new GenericEntityFunctions(new DataStructures.WeakReference(this)); + return new GenericEntityFunctions(this); } ///-------------------------------------------- [MethodImpl(MethodImplOptions.AggressiveInlining)] - EntityStructInitializer BuildEntity(EGID entityID, object[] implementors) where T : IEntityDescriptor, new() + EntityStructInitializer BuildEntity(EGID entityID, IEntityBuilder[] entitiesToBuild, + IEnumerable implementors = null) { - return BuildEntity(entityID, EntityDescriptorTemplate.descriptor, implementors); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - EntityStructInitializer BuildEntity(EGID entityID, T entityDescriptor, object[] implementors) - where T : IEntityDescriptor - { - var descriptorEntitiesToBuild = entityDescriptor.entitiesToBuild; - - CheckAddEntityID(entityID, entityDescriptor); + CheckAddEntityID(entityID); var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, - descriptorEntitiesToBuild, implementors); + entitiesToBuild, implementors); return new EntityStructInitializer(entityID, dic); } -#if REAL_ID - [MethodImpl(MethodImplOptions.AggressiveInlining)] - EntityStructInitializer BuildEntity(ExclusiveGroup.ExclusiveGroupStruct groupID, object[] implementors) where T : IEntityDescriptor, new() - { - //temporary egid, will change during the real submission, needed for the initialization look up - var egid = EGID.CREATE_WITHOUT_ID(groupID, _groupedEntityToAdd.entitiesBuiltThisSubmission++); - - var dic = EntityFactory.BuildGroupedEntities(egid, _groupedEntityToAdd.current, - EntityDescriptorTemplate.descriptor.entitiesToBuild, implementors); - - return new EntityStructInitializer(egid, dic.groups); - } -#endif - ///-------------------------------------------- void Preallocate(uint groupID, uint size) where T : IEntityDescriptor, new() { var entityViewsToBuild = EntityDescriptorTemplate.descriptor.entitiesToBuild; - var numberOfEntityViews = entityViewsToBuild.Length; - + var numberOfEntityViews = entityViewsToBuild.Length; + //reserve space in the database - if (_groupEntityDB.TryGetValue(groupID, out var @group) == false) - group = _groupEntityDB[groupID] = new Dictionary(); + if (_groupEntityViewsDB.TryGetValue(groupID, out var group) == false) + group = _groupEntityViewsDB[groupID] = new FasterDictionary, ITypeSafeDictionary>(); for (var index = 0; index < numberOfEntityViews; index++) { var entityViewBuilder = entityViewsToBuild[index]; var entityViewType = entityViewBuilder.GetEntityType(); - if (group.TryGetValue(entityViewType, out var dbList) == false) - group[entityViewType] = entityViewBuilder.Preallocate(ref dbList, size); + var refWrapper = new RefWrapper(entityViewType); + if (group.TryGetValue(refWrapper, out var dbList) == false) + group[refWrapper] = entityViewBuilder.Preallocate(ref dbList, size); else dbList.SetCapacity(size); - if (_groupsPerEntity.TryGetValue(entityViewType, out var groupedGroup) == false) - groupedGroup = _groupsPerEntity[entityViewType] = new FasterDictionary(); + if (_groupsPerEntity.TryGetValue(refWrapper, out var groupedGroup) == false) + groupedGroup = _groupsPerEntity[refWrapper] = + new FasterDictionary(); groupedGroup[groupID] = dbList; } } ///-------------------------------------------- - /// - void MoveEntity(IEntityBuilder[] entityBuilders, EGID fromEntityGID, Type originalDescriptorType, EGID? toEntityGID) + /// + void MoveEntityFromAndToEngines(IEntityBuilder[] entityBuilders, EGID fromEntityGID, EGID? toEntityGID) { - using (var sampler = new PlatformProfiler("Move Entity")) + using (var sampler = new PlatformProfiler("Move Entity From Engines")) { //for each entity view generated by the entity descriptor - if (_groupEntityDB.TryGetValue(fromEntityGID.groupID, out var fromGroup) == false) + if (_groupEntityViewsDB.TryGetValue(fromEntityGID.groupID, out var fromGroup) == false) throw new ECSException("from group not found eid: ".FastConcat(fromEntityGID.entityID) - .FastConcat(" group: ").FastConcat(fromEntityGID.groupID)); + .FastConcat(" group: ").FastConcat(fromEntityGID.groupID)); //Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor! - bool correctEntityDescriptorFound = true; - - EntityStructInfoView entityInfoView = default; - if (fromGroup.TryGetValue(ENTITY_INFO_VIEW_TYPE, out var entityInfoViewDic) && + if (fromGroup.TryGetValue(new RefWrapper(EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW), + out var entityInfoViewDic) && (entityInfoViewDic as TypeSafeDictionary).TryGetValue( - fromEntityGID.entityID, out entityInfoView) && (correctEntityDescriptorFound = - entityInfoView.type == originalDescriptorType)) - { - var entitiesToMove = entityInfoView.entitiesToBuild; - - Dictionary toGroup = null; - - if (toEntityGID != null) - { - var toGroupID = toEntityGID.Value.groupID; - - if (_groupEntityDB.TryGetValue(toGroupID, out toGroup) == false) - toGroup = _groupEntityDB[toGroupID] = new Dictionary(); - } - - for (int i = 0; i < entitiesToMove.Length; i++) - MoveEntityView(fromEntityGID, toEntityGID, toGroup, ref fromGroup, - entitiesToMove[i].GetEntityType(), sampler); - } + fromEntityGID.entityID, out var entityInfoView)) + MoveEntities(fromEntityGID, toEntityGID, entityInfoView.entitiesToBuild, fromGroup, sampler); //otherwise it's a normal static entity descriptor else - { -#if DEBUG && !PROFILER - if (correctEntityDescriptorFound == false) - throw new ECSException(INVALID_DYNAMIC_DESCRIPTOR_ERROR.FastConcat(" ID ").FastConcat(fromEntityGID.entityID) - .FastConcat(" group ID ").FastConcat(fromEntityGID.groupID).FastConcat( - " descriptor found: ", entityInfoView.type.Name, " descriptor Excepted ", - originalDescriptorType.Name)); -#endif - - Dictionary toGroup = null; - if (toEntityGID != null) - { - var toGroupID = toEntityGID.Value.groupID; - - if (_groupEntityDB.TryGetValue(toGroupID, out toGroup) == false) - toGroup = _groupEntityDB[toGroupID] = new Dictionary(); - } - - for (var i = 0; i < entityBuilders.Length; i++) - MoveEntityView(fromEntityGID, toEntityGID, toGroup, ref fromGroup, - entityBuilders[i].GetEntityType(), sampler); - } + MoveEntities(fromEntityGID, toEntityGID, entityBuilders, fromGroup, sampler); } } - void MoveEntityView(EGID entityGID, EGID? toEntityGID, Dictionary toGroup, - ref Dictionary fromGroup, Type entityViewType, in PlatformProfiler profiler) + void MoveEntities(EGID fromEntityGID, EGID? toEntityGID, IEntityBuilder[] entitiesToMove, + FasterDictionary, ITypeSafeDictionary> fromGroup, PlatformProfiler sampler) { - if (fromGroup.TryGetValue(entityViewType, out var fromTypeSafeDictionary) == false) + FasterDictionary, ITypeSafeDictionary> toGroup = null; + + if (toEntityGID != null) { - throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID) - .FastConcat(" group: ").FastConcat(entityGID.groupID)); + var toGroupID = toEntityGID.Value.groupID; + + if (_groupEntityViewsDB.TryGetValue(toGroupID, out toGroup) == false) + toGroup = _groupEntityViewsDB[toGroupID] = new FasterDictionary, ITypeSafeDictionary>(); + + //Add all the entities to the dictionary + for (var i = 0; i < entitiesToMove.Length; i++) + CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup, + entitiesToMove[i].GetEntityType()); } - ITypeSafeDictionary toEntitiesDictionary = null; + //call all the callbacks + for (var i = 0; i < entitiesToMove.Length; i++) + MoveEntityViewFromAndToEngines(fromEntityGID, toEntityGID, fromGroup, toGroup, + entitiesToMove[i].GetEntityType(), sampler); - //in case we want to move to a new group, otherwise is just a remove - if (toEntityGID != null) - { - if (toGroup.TryGetValue(entityViewType, out toEntitiesDictionary) == false) - { - toEntitiesDictionary = fromTypeSafeDictionary.Create(); - toGroup.Add(entityViewType, toEntitiesDictionary); - } + //then remove all the entities from the dictionary + for (var i = 0; i < entitiesToMove.Length; i++) + RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityType(), sampler); + } - if (_groupsPerEntity.TryGetValue(entityViewType, out var groupedGroup) == false) - groupedGroup = _groupsPerEntity[entityViewType] = new FasterDictionary(); + void CopyEntityToDictionary(EGID entityGID, EGID toEntityGID, + FasterDictionary, ITypeSafeDictionary> fromGroup, + FasterDictionary, ITypeSafeDictionary> toGroup, Type entityViewType) + { + var wrapper = new RefWrapper(entityViewType); - groupedGroup[toEntityGID.Value.groupID] = toEntitiesDictionary; + if (fromGroup.TryGetValue(wrapper, out var fromTypeSafeDictionary) == false) + { + throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID) + .FastConcat(" group: ").FastConcat(entityGID.groupID)); } +#if DEBUG && !PROFILER if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) { throw new EntityNotFoundException(entityGID, entityViewType); } +#endif + if (toGroup.TryGetValue(wrapper, out var toEntitiesDictionary) == false) + { + toEntitiesDictionary = fromTypeSafeDictionary.Create(); + toGroup.Add(wrapper, toEntitiesDictionary); + } - fromTypeSafeDictionary.MoveEntityFromDictionaryAndEngines(entityGID, toEntityGID, - toEntitiesDictionary, - toEntityGID == null ? _reactiveEnginesAddRemove : - _reactiveEnginesSwap, - in profiler); + //todo: this must be unit tested properly + if (_groupsPerEntity.TryGetValue(wrapper, out var groupedGroup) == false) + groupedGroup = _groupsPerEntity[wrapper] = + new FasterDictionary(); - if (fromTypeSafeDictionary.Count == 0) //clean up - { - _groupsPerEntity[entityViewType].Remove(entityGID.groupID); + groupedGroup[toEntityGID.groupID] = toEntitiesDictionary; - //I don't remove the group if empty on purpose, in case it needs to be reused however I trim it to save - //memory - fromTypeSafeDictionary.Trim(); - } + fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary); } - void RemoveGroupAndEntitiesFromDB(uint groupID, Type entityDescriptor) + void MoveEntityViewFromAndToEngines(EGID entityGID, EGID? toEntityGID, + FasterDictionary, ITypeSafeDictionary> fromGroup, + FasterDictionary, ITypeSafeDictionary> toGroup, Type entityViewType, + in PlatformProfiler profiler) { - throw new NotImplementedException(); - /* var profiler = new PlatformProfiler(); - using (profiler.StartNewSession("Remove Group Of Entities")) - { - FasterDictionary @group; - if (_groupsPerEntity.TryGetValue(entityDescriptor, out group)) - { - if (group.TryGetValue()) - foreach (var entity in group) - { - MoveEntity(entity.); - } - } - }*/ + //add all the entities + var refWrapper = new RefWrapper(entityViewType); + if (fromGroup.TryGetValue(refWrapper, out var fromTypeSafeDictionary) == false) + { + throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID) + .FastConcat(" group: ").FastConcat(entityGID.groupID)); + } + + ITypeSafeDictionary toEntitiesDictionary = null; + if (toGroup != null) + toEntitiesDictionary = toGroup[refWrapper]; //this is guaranteed to exist by AddEntityToDictionary + +#if DEBUG && !PROFILER + if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) + throw new EntityNotFoundException(entityGID, entityViewType); +#endif + fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID, + toEntitiesDictionary, toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap, + in profiler); } - void RemoveGroupAndEntitiesFromDB(uint groupID) + void RemoveEntityFromDictionary(EGID entityGID, + FasterDictionary, ITypeSafeDictionary> fromGroup, Type entityViewType, + in PlatformProfiler profiler) { - using (var profiler = new PlatformProfiler("Remove Group")) + var refWrapper = new RefWrapper(entityViewType); + if (fromGroup.TryGetValue(refWrapper, out var fromTypeSafeDictionary) == false) { - var dictionariesOfEntities = _groupEntityDB[groupID]; - foreach (var dictionaryOfEntities in dictionariesOfEntities) - { - var platformProfiler = profiler; - dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, platformProfiler); - var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key]; - groupedGroupOfEntities.Remove(groupID); - } + throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID) + .FastConcat(" group: ").FastConcat(entityGID.groupID)); + } - //careful, in this case I assume you really don't want to use this group anymore - //so I remove it from the database - _groupEntityDB.Remove(groupID); + fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID, profiler); + + if (fromTypeSafeDictionary.Count == 0) //clean up + { + //todo: this must be unit tested properly + _groupsPerEntity[refWrapper].Remove(entityGID.groupID); + //I don't remove the group if empty on purpose, in case it needs to be reused } } - ///-------------------------------------------- - void SwapEntityGroup(IEntityBuilder[] builders, Type originalEntityDescriptor, EGID fromEntityID, - EGID toEntityID) + /// + /// Todo: I should keep the group, but I need to mark the group as deleted for the Exist function to work + /// + /// + /// + void RemoveGroupAndEntitiesFromDB(uint groupID, in PlatformProfiler profiler) { - DBC.ECS.Check.Require(fromEntityID.groupID != toEntityID.groupID, - "entity group is the same of the destination group"); + var dictionariesOfEntities = _groupEntityViewsDB[groupID]; + + foreach (var dictionaryOfEntities in dictionariesOfEntities) + { + dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler, + new ExclusiveGroup.ExclusiveGroupStruct(groupID)); + var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key]; + groupedGroupOfEntities.Remove(groupID); + } - MoveEntity(builders, fromEntityID, originalEntityDescriptor, toEntityID); + //careful, in this case I assume you really don't want to use this group anymore + //so I remove it from the database + _groupEntityViewsDB.Remove(groupID); } - internal Consumer GenerateConsumer(string name, int capacity) where T : unmanaged, IEntityStruct + internal Consumer GenerateConsumer(string name, uint capacity) where T : unmanaged, IEntityStruct { return _entitiesStream.GenerateConsumer(name, capacity); } - - public Consumer GenerateConsumer(ExclusiveGroup @group, string name, int capacity) where T : unmanaged, + + public Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) where T : unmanaged, IEntityStruct { - return _entitiesStream.GenerateConsumer(@group, name, capacity); + return _entitiesStream.GenerateConsumer(group, name, capacity); } - const string INVALID_DYNAMIC_DESCRIPTOR_ERROR = - "Found an entity requesting an invalid dynamic descriptor, this " + - "can happen only if you are building different entities with the " + - "same ID in the same group! The operation will continue using" + "the base descriptor only "; + + //one datastructure rule them all: + //split by group + //split by type per group. It's possible to get all the entities of a give type T per group thanks + //to the FasterDictionary capabilities OR it's possible to get a specific entityView indexed by + //ID. This ID doesn't need to be the EGID, it can be just the entityID + //for each group id, save a dictionary indexed by entity type of entities indexed by id + //ITypeSafeDictionary = Key = entityID, Value = EntityStruct + readonly FasterDictionary, ITypeSafeDictionary>> _groupEntityViewsDB; + + //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are + //found indexed by group id + //EntityViewType //groupID //entityID, EntityStruct + readonly FasterDictionary, FasterDictionary> _groupsPerEntity; + + readonly EntitiesDB _entitiesDB; + readonly EntitiesStream _entitiesStream; } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs b/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs index e3164e5..8308e86 100644 --- a/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs +++ b/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs @@ -1,47 +1,57 @@ -namespace Svelto.ECS +using System.Collections.Generic; +using Svelto.DataStructures; + +namespace Svelto.ECS { public partial class EnginesRoot { class GenericEntityFactory : IEntityFactory { - public GenericEntityFactory(DataStructures.WeakReference weakReference) + public GenericEntityFactory(EnginesRoot weakReference) { - _weakEngine = weakReference; + _enginesRoot = new WeakReference(weakReference); } - public EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, object[] implementors) where T : IEntityDescriptor, new() + public EntityStructInitializer BuildEntity(uint entityID, + ExclusiveGroup.ExclusiveGroupStruct groupStructId, IEnumerable implementors = null) + where T : IEntityDescriptor, new() { - return _weakEngine.Target.BuildEntity(new EGID(entityID, groupStructId), implementors); + return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId), + EntityDescriptorTemplate.descriptor.entitiesToBuild, implementors); } - public EntityStructInitializer BuildEntity(EGID egid, object[] implementors) where T : IEntityDescriptor, new() + public EntityStructInitializer BuildEntity(EGID egid, IEnumerable implementors = null) + where T : IEntityDescriptor, new() { - return _weakEngine.Target.BuildEntity(egid, implementors); + return _enginesRoot.Target.BuildEntity(egid, + EntityDescriptorTemplate.descriptor.entitiesToBuild, implementors); } -#if REAL_ID - public EntityStructInitializer BuildEntity(ExclusiveGroup.ExclusiveGroupStruct groupID, object[] implementors = null) where T : IEntityDescriptor, new() + public EntityStructInitializer BuildEntity(EGID egid, T entityDescriptor, + IEnumerable implementors) + where T : IEntityDescriptor { - return _weakEngine.Target.BuildEntity(groupID, implementors); + return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.entitiesToBuild, implementors); } -#endif - public EntityStructInitializer BuildEntity(EGID egid, T entityDescriptor, object[] implementors) where T:IEntityDescriptor + public EntityStructInitializer BuildEntity(uint entityID, + ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, IEnumerable implementors) + where T : IEntityDescriptor { - return _weakEngine.Target.BuildEntity(egid, entityDescriptor, implementors); + return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId), + descriptorEntity.entitiesToBuild, + implementors); } - public EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, object[] implementors) where T:IEntityDescriptor - { - return _weakEngine.Target.BuildEntity(new EGID(entityID, groupStructId), descriptorEntity, implementors); - } - - public void PreallocateEntitySpace(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) where T : IEntityDescriptor, new() + public void PreallocateEntitySpace(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) + where T : IEntityDescriptor, new() { - _weakEngine.Target.Preallocate(groupStructId, size); + _enginesRoot.Target.Preallocate(groupStructId, size); } - - readonly DataStructures.WeakReference _weakEngine; + + //enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside +//engines of other enginesRoot + readonly WeakReference _enginesRoot; } } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs b/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs index 111dccc..d5a2cbd 100644 --- a/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs +++ b/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs @@ -1,22 +1,24 @@ -using System; +using System.Diagnostics; using System.Runtime.CompilerServices; -using Svelto.ECS.Internal; +using Svelto.DataStructures; namespace Svelto.ECS { public partial class EnginesRoot { + /// + /// todo: EnginesRoot was a weakreference to give the change to inject + /// entityfunctions from other engines root. It probably should be reverted + /// sealed class GenericEntityFunctions : IEntityFunctions { - readonly DataStructures.WeakReference _weakReference; - - internal GenericEntityFunctions(DataStructures.WeakReference weakReference) + internal GenericEntityFunctions(EnginesRoot weakReference) { - _weakReference = weakReference; + _enginesRoot = new WeakReference(weakReference); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RemoveEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : + public void RemoveEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new() { RemoveEntity(new EGID(entityID, groupID)); @@ -25,46 +27,40 @@ namespace Svelto.ECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveEntity(EGID entityEGID) where T : IEntityDescriptor, new() { - _weakReference.Target.CheckRemoveEntityID(entityEGID, EntityDescriptorTemplate.descriptor); + _enginesRoot.Target.CheckRemoveEntityID(entityEGID); - _weakReference.Target.QueueEntitySubmitOperation( - new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID, - EntityDescriptorTemplate.descriptor.entitiesToBuild, typeof(T))); - } - - - public void RemoveEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) - where T : IEntityDescriptor, new() - { - throw new NotImplementedException(); + _enginesRoot.Target.QueueEntitySubmitOperation( + new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID, + EntityDescriptorTemplate.descriptor.entitiesToBuild)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) { - _weakReference.Target.QueueEntitySubmitOperation( - new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(), new EGID(0, groupID))); + _enginesRoot.Target.RemoveGroupID(groupID); + _enginesRoot.Target.QueueEntitySubmitOperation( + new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(0, groupID), new EGID())); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SwapEntityGroup(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, - ExclusiveGroup.ExclusiveGroupStruct toGroupID) + public void SwapEntityGroup(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, + ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new() { SwapEntityGroup(new EGID(entityID, fromGroupID), toGroupID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SwapEntityGroup(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) + public void SwapEntityGroup(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new() { - SwapEntityGroup(fromID, new EGID(fromID.entityID, (uint)toGroupID)); + SwapEntityGroup(fromID, new EGID(fromID.entityID, (uint) toGroupID)); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SwapEntityGroup(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID - , ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) + public void SwapEntityGroup(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID + , ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new() { if (fromID.groupID != mustBeFromGroup) @@ -72,55 +68,63 @@ namespace Svelto.ECS SwapEntityGroup(fromID, toGroupID); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SwapEntityGroup(EGID fromID, EGID toID) - where T : IEntityDescriptor, new() - { - _weakReference.Target.QueueEntitySubmitOperation( - new EntitySubmitOperation(EntitySubmitOperationType.Swap, - fromID, toID, EntityDescriptorTemplate.descriptor.entitiesToBuild, typeof(T))); - } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SwapEntityGroup(EGID fromID, EGID toID - , ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) + , ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new() { if (fromID.groupID != mustBeFromGroup) throw new ECSException("Entity is not coming from the expected group"); - + SwapEntityGroup(fromID, toID); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SwapEntityGroup(EGID fromID, EGID toID) + where T : IEntityDescriptor, new() + { + _enginesRoot.Target.CheckRemoveEntityID(fromID); + _enginesRoot.Target.CheckAddEntityID(toID); + + _enginesRoot.Target.QueueEntitySubmitOperation( + new EntitySubmitOperation(EntitySubmitOperationType.Swap, + fromID, toID, EntityDescriptorTemplate.descriptor.entitiesToBuild)); + } + + //enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside +//engines of other enginesRoot + readonly WeakReference _enginesRoot; } - + void QueueEntitySubmitOperation(EntitySubmitOperation entitySubmitOperation) { -#if DEBUG && !PROFILER - entitySubmitOperation.trace = Environment.StackTrace; -#endif - _entitiesOperations.Add((ulong)entitySubmitOperation.fromID, ref entitySubmitOperation); +#if DEBUG && !PROFILER + entitySubmitOperation.trace = new StackFrame(1, true); +#endif + _entitiesOperations.Add((ulong) entitySubmitOperation.fromID, entitySubmitOperation); } - void QueueEntitySubmitOperation(EntitySubmitOperation entitySubmitOperation) where T:IEntityDescriptor + void QueueEntitySubmitOperation(EntitySubmitOperation entitySubmitOperation) where T : IEntityDescriptor { -#if DEBUG && !PROFILER - entitySubmitOperation.trace = Environment.StackTrace; - - if (_entitiesOperations.TryGetValue((ulong) entitySubmitOperation.fromID, out var entitySubmitedOperation) == true) +#if DEBUG && !PROFILER + entitySubmitOperation.trace = new StackFrame(1, true); + + if (_entitiesOperations.TryGetValue((ulong) entitySubmitOperation.fromID, out var entitySubmitedOperation)) { if (entitySubmitedOperation != entitySubmitOperation) - throw new ECSException("Only one entity operation per submission is allowed".FastConcat(" entityViewType: ") - .FastConcat(typeof(T).Name) - .FastConcat(" submission type ", entitySubmitOperation.type.ToString(), - " from ID: ", entitySubmitOperation.fromID.entityID.ToString()) - .FastConcat( " previous operation type: ", - _entitiesOperations[(ulong) entitySubmitOperation.fromID].type - .ToString())); + throw new ECSException("Only one entity operation per submission is allowed" + .FastConcat(" entityViewType: ") + .FastConcat(typeof(T).Name) + .FastConcat(" submission type ", entitySubmitOperation.type.ToString(), + " from ID: ", entitySubmitOperation.fromID.entityID.ToString()) + .FastConcat(" previous operation type: ", + _entitiesOperations[(ulong) entitySubmitOperation.fromID].type + .ToString())); } else -#endif - _entitiesOperations.Set((ulong)entitySubmitOperation.fromID, ref entitySubmitOperation); +#endif + _entitiesOperations.Set((ulong) entitySubmitOperation.fromID, entitySubmitOperation); } } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.Submission.cs b/Svelto.ECS/EnginesRoot.Submission.cs index fa1daeb..8ecaa15 100644 --- a/Svelto.ECS/EnginesRoot.Submission.cs +++ b/Svelto.ECS/EnginesRoot.Submission.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using Svelto.Common; using Svelto.DataStructures; -using Svelto.DataStructures.Experimental; using Svelto.ECS.Internal; using Svelto.ECS.Schedulers; @@ -11,125 +9,120 @@ namespace Svelto.ECS public partial class EnginesRoot { readonly FasterList _transientEntitiesOperations; - + void SubmitEntityViews() { using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission")) { - if (_entitiesOperations.Count > 0) + int iterations = 0; + do { - using (profiler.Sample("Remove and Swap operations")) - { - _transientEntitiesOperations.FastClear(); - var entitySubmitOperations = _entitiesOperations.GetValuesArray(out var count); - _transientEntitiesOperations.AddRange(entitySubmitOperations, count); - _entitiesOperations.FastClear(); + SingleSubmission(profiler); + } while ((_groupedEntityToAdd.currentEntitiesCreatedPerGroup.Count > 0 || + _entitiesOperations.Count > 0) && ++iterations < 5); + +#if DEBUG && !PROFILER + if (iterations == 5) + throw new ECSException("possible circular submission detected"); +#endif + } + } + + void SingleSubmission(in PlatformProfiler profiler) + { + if (_entitiesOperations.Count > 0) + { + using (profiler.Sample("Remove and Swap operations")) + { + _transientEntitiesOperations.FastClear(); + var entitySubmitOperations = _entitiesOperations.GetValuesArray(out var count); + _transientEntitiesOperations.AddRange(entitySubmitOperations, count); + _entitiesOperations.FastClear(); - var entitiesOperations = _transientEntitiesOperations.ToArrayFast(); - for (var i = 0; i < _transientEntitiesOperations.Count; i++) + var entitiesOperations = _transientEntitiesOperations.ToArrayFast(); + for (var i = 0; i < _transientEntitiesOperations.Count; i++) + { + try { - try + switch (entitiesOperations[i].type) { - switch (entitiesOperations[i].type) - { - case EntitySubmitOperationType.Swap: - SwapEntityGroup(entitiesOperations[i].builders, - entitiesOperations[i].entityDescriptor, - entitiesOperations[i].fromID, - entitiesOperations[i].toID); - break; - case EntitySubmitOperationType.Remove: - MoveEntity(entitiesOperations[i].builders, - entitiesOperations[i].fromID, - entitiesOperations[i].entityDescriptor, null); - break; - case EntitySubmitOperationType.RemoveGroup: - if (entitiesOperations[i].entityDescriptor == null) - RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromID.groupID); - else - RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromID.groupID, - entitiesOperations[i].entityDescriptor); - - break; - } + case EntitySubmitOperationType.Swap: + MoveEntityFromAndToEngines(entitiesOperations[i].builders, + entitiesOperations[i].fromID, + entitiesOperations[i].toID); + break; + case EntitySubmitOperationType.Remove: + MoveEntityFromAndToEngines(entitiesOperations[i].builders, + entitiesOperations[i].fromID, null); + break; + case EntitySubmitOperationType.RemoveGroup: + RemoveGroupAndEntitiesFromDB( + entitiesOperations[i].fromID.groupID, profiler); + break; } - catch (Exception e) - { - var str = "Crash while executing Entity Operation " - .FastConcat(entitiesOperations[i].type.ToString()); -#if RELAXED_ECS && !PROFILER - Console.LogException(str.FastConcat(" " -#if DEBUG && !PROFILER - , entitiesOperations[i].trace -#endif - ), e); -#else - throw new ECSException(str.FastConcat(" ") -#if DEBUG && !PROFILER - .FastConcat(entitiesOperations[i].trace) -#endif - , e); + } + catch (Exception e) + { + var str = "Crash while executing Entity Operation " + .FastConcat(entitiesOperations[i].type.ToString()); + + throw new ECSException(str.FastConcat(" ") +#if DEBUG && !PROFILER + .FastConcat(entitiesOperations[i].trace.ToString()) #endif - } + , e); } } } + } - if (_groupedEntityToAdd.currentEntitiesCreatedPerGroup.Count > 0) + _groupedEntityToAdd.Swap(); + + if (_groupedEntityToAdd.otherEntitiesCreatedPerGroup.Count > 0) + { + using (profiler.Sample("Add operations")) { - using (profiler.Sample("Add operations")) + try { - //use other as source from now on current will be use to write new entityViews - _groupedEntityToAdd.Swap(); - - try - { - //Note: if N entity of the same type are added on the same frame the Add callback is called - //N times on the same frame. if the Add callback builds a new entity, that entity will not - //be available in the database until the N callbacks are done. Solving this could be - //complicated as callback and database update must be interleaved. - AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd, profiler); - } - finally - { - //other can be cleared now, but let's avoid deleting the dictionary every time - _groupedEntityToAdd.ClearOther(); - } + AddEntityViewsToTheDBAndSuitableEngines(profiler); + } + finally + { + //other can be cleared now, but let's avoid deleting the dictionary every time + _groupedEntityToAdd.ClearOther(); } } } } - void AddEntityViewsToTheDBAndSuitableEngines(DoubleBufferedEntitiesToAdd dbgroupsOfEntitiesToSubmit, - in PlatformProfiler profiler) + void AddEntityViewsToTheDBAndSuitableEngines(in PlatformProfiler profiler) { //each group is indexed by entity view type. for each type there is a dictionary indexed by entityID - var groupsOfEntitiesToSubmit = dbgroupsOfEntitiesToSubmit.other; - foreach (var groupOfEntitiesToSubmit in groupsOfEntitiesToSubmit) - { + foreach (var groupOfEntitiesToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup) + { var groupID = groupOfEntitiesToSubmit.Key; - - if (dbgroupsOfEntitiesToSubmit.otherEntitiesCreatedPerGroup.ContainsKey(groupID) == false) continue; - + //if the group doesn't exist in the current DB let's create it first - if (_groupEntityDB.TryGetValue(groupID, out var groupDB) == false) - groupDB = _groupEntityDB[groupID] = new Dictionary(); - + if (_groupEntityViewsDB.TryGetValue(groupID, out var groupDB) == false) + groupDB = _groupEntityViewsDB[groupID] = + new FasterDictionary, ITypeSafeDictionary>(); + //add the entityViews in the group - foreach (var entityViewsToSubmit in groupOfEntitiesToSubmit.Value) + foreach (var entityViewsToSubmit in _groupedEntityToAdd.other[groupID]) { - var type = entityViewsToSubmit.Key; + var type = entityViewsToSubmit.Key; var typeSafeDictionary = entityViewsToSubmit.Value; - - if (groupDB.TryGetValue(type, out var dbDic) == false) - dbDic = groupDB[type] = typeSafeDictionary.Create(); - + + var wrapper = new RefWrapper(type); + if (groupDB.TryGetValue(wrapper, out var dbDic) == false) + dbDic = groupDB[wrapper] = typeSafeDictionary.Create(); + //Fill the DB with the entity views generate this frame. dbDic.AddEntitiesFromDictionary(typeSafeDictionary, groupID); - if (_groupsPerEntity.TryGetValue(type, out var groupedGroup) == false) - groupedGroup = _groupsPerEntity[type] = new FasterDictionary(); - + if (_groupsPerEntity.TryGetValue(wrapper, out var groupedGroup) == false) + groupedGroup = _groupsPerEntity[wrapper] = new FasterDictionary(); + groupedGroup[groupID] = dbDic; } } @@ -138,25 +131,25 @@ namespace Svelto.ECS //created by the entity built using (profiler.Sample("Add entities to engines")) { - foreach (var groupToSubmit in groupsOfEntitiesToSubmit) + foreach (var groupToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup) { var groupID = groupToSubmit.Key; - var groupDB = _groupEntityDB[groupID]; - - foreach (var entityViewsPerType in groupToSubmit.Value) + + var groupDB = _groupEntityViewsDB[groupID]; + + foreach (var entityViewsToSubmit in _groupedEntityToAdd.other[groupID]) { - var realDic = groupDB[entityViewsPerType.Key]; - - entityViewsPerType.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, realDic, in profiler); + var realDic = groupDB[new RefWrapper(entityViewsToSubmit.Key)]; + + entityViewsToSubmit.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, realDic, in profiler, + new ExclusiveGroup.ExclusiveGroupStruct(groupToSubmit.Key)); } } } } - readonly DoubleBufferedEntitiesToAdd _groupedEntityToAdd; + DoubleBufferedEntitiesToAdd _groupedEntityToAdd; readonly IEntitySubmissionScheduler _scheduler; readonly FasterDictionary _entitiesOperations; - - //temp } } \ No newline at end of file diff --git a/Svelto.ECS/EntitiesDB.cs b/Svelto.ECS/EntitiesDB.cs index 490d25c..fb49c7a 100644 --- a/Svelto.ECS/EntitiesDB.cs +++ b/Svelto.ECS/EntitiesDB.cs @@ -3,17 +3,17 @@ #endif using System; -using System.Collections.Generic; using System.Runtime.CompilerServices; using Svelto.DataStructures; -using Svelto.DataStructures.Experimental; namespace Svelto.ECS.Internal { partial class EntitiesDB : IEntitiesDB { - internal EntitiesDB(FasterDictionary> groupEntityViewsDB, - Dictionary> groupsPerEntity, EntitiesStream entityStream) + internal EntitiesDB( + FasterDictionary, ITypeSafeDictionary>> groupEntityViewsDB, + FasterDictionary, FasterDictionary> groupsPerEntity, + EntitiesStream entityStream) { _groupEntityViewsDB = groupEntityViewsDB; _groupsPerEntity = groupsPerEntity; @@ -21,35 +21,40 @@ namespace Svelto.ECS.Internal } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T QueryUniqueEntity(ExclusiveGroup.ExclusiveGroupStruct @group) where T : struct, IEntityStruct + public ref T QueryUniqueEntity(ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct { - var entities = QueryEntities(@group, out var count); + var entities = QueryEntities(group, out var count); - if (count != 1) throw new ECSException("Unique entities must be unique! ".FastConcat(typeof(T).ToString())); + if (count == 0) + throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'")); + if (count != 1) + throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString()) + .FastConcat("'")); return ref entities[0]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T QueryEntity(EGID entityGID) where T : struct, IEntityStruct { - T[] array; + T[] array; if ((array = QueryEntitiesAndIndexInternal(entityGID, out var index)) != null) return ref array[index]; throw new EntityNotFoundException(entityGID, typeof(T)); } - public ref T QueryEntity(uint id, ExclusiveGroup.ExclusiveGroupStruct @group) where T : struct, IEntityStruct + public ref T QueryEntity(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct { - return ref QueryEntity(new EGID(id, @group)); + return ref QueryEntity(new EGID(id, group)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) where T : struct, IEntityStruct + public T[] QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) + where T : struct, IEntityStruct { - uint @group = groupStruct; + uint group = groupStruct; count = 0; - if (QueryEntitySafeDictionary(@group, out TypeSafeDictionary typeSafeDictionary) == false) + if (SafeQueryEntityDictionary(group, out TypeSafeDictionary typeSafeDictionary) == false) return RetrieveEmptyEntityViewArray(); return typeSafeDictionary.GetValuesArray(out count); @@ -61,6 +66,18 @@ namespace Svelto.ECS.Internal return new EntityCollection(QueryEntities(groupStruct, out var count), count); } + public EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct + { + return new EntityCollection(QueryEntities(groupStruct, out var count), count); + } + + public EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct + { + return new EntityCollection(QueryEntities(groupStruct, out var count), count); + } + public EntityCollections QueryEntities(ExclusiveGroup[] groups) where T : struct, IEntityStruct { return new EntityCollections(this, groups); @@ -74,17 +91,21 @@ namespace Svelto.ECS.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public (T1[], T2[]) QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) - where T1 : struct, IEntityStruct + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct { - var T1entities = QueryEntities(@groupStruct, out var countCheck); - var T2entities = QueryEntities(@groupStruct, out count); + var T1entities = QueryEntities(groupStruct, out var countCheck); + var T2entities = QueryEntities(groupStruct, out count); if (count != countCheck) - throw new ECSException("Entity views count do not match in group. Entity 1: ". - FastConcat(typeof(T1).ToString()).FastConcat( - "Entity 2: ".FastConcat(typeof(T2).ToString()))); - + { + throw new ECSException("Entity views count do not match in group. Entity 1: ' count: " + .FastConcat(countCheck) + .FastConcat(typeof(T1).ToString()) + .FastConcat("'. Entity 2: ' count: ".FastConcat(count) + .FastConcat(typeof(T2).ToString()) + .FastConcat("'"))); + } return (T1entities, T2entities); } @@ -94,34 +115,48 @@ namespace Svelto.ECS.Internal (ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct { - var T1entities = QueryEntities(@groupStruct, out var countCheck1); - var T2entities = QueryEntities(@groupStruct, out var countCheck2); - var T3entities = QueryEntities(@groupStruct, out count); + var T1entities = QueryEntities(groupStruct, out var countCheck1); + var T2entities = QueryEntities(groupStruct, out var countCheck2); + var T3entities = QueryEntities(groupStruct, out count); if (count != countCheck1 || count != countCheck2) - throw new ECSException("Entity views count do not match in group. Entity 1: ". - FastConcat(typeof(T1).ToString()). - FastConcat(" Entity 2: ".FastConcat(typeof(T2).ToString()). - FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())))); + throw new ECSException("Entity views count do not match in group. Entity 1: " + .FastConcat(typeof(T1).ToString()).FastConcat(" count: ").FastConcat(countCheck1).FastConcat( + " Entity 2: ".FastConcat(typeof(T2).ToString()) + .FastConcat(" count: ").FastConcat(countCheck2) + .FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())).FastConcat(" count: ") + .FastConcat(count))); return (T1entities, T2entities, T3entities); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EGIDMapper QueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId) where T : struct, IEntityStruct + public EGIDMapper QueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId) + where T : struct, IEntityStruct { - uint groupId = groupStructId; - if (QueryEntitySafeDictionary(groupId, out TypeSafeDictionary typeSafeDictionary) == false) - throw new EntityGroupNotFoundException(groupId, typeof(T)); + if (SafeQueryEntityDictionary(groupStructId, out TypeSafeDictionary typeSafeDictionary) == false) + throw new EntityGroupNotFoundException(groupStructId, typeof(T)); EGIDMapper mapper; mapper.map = typeSafeDictionary; - typeSafeDictionary.GetValuesArray(out _); - return mapper; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryQueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId, + out EGIDMapper mapper) + where T : struct, IEntityStruct + { + mapper = default; + if (SafeQueryEntityDictionary(groupStructId, out TypeSafeDictionary typeSafeDictionary) == false) + return false; + + mapper.map = typeSafeDictionary; + + return true; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T[] QueryEntitiesAndIndex(EGID entityGID, out uint index) where T : struct, IEntityStruct { @@ -133,7 +168,8 @@ namespace Svelto.ECS.Internal } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) where T : struct, IEntityStruct + public bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) + where T : struct, IEntityStruct { if ((array = QueryEntitiesAndIndexInternal(entityGid, out index)) != null) return true; @@ -142,13 +178,15 @@ namespace Svelto.ECS.Internal } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] QueryEntitiesAndIndex(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) where T : struct, IEntityStruct + public T[] QueryEntitiesAndIndex(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) + where T : struct, IEntityStruct { return QueryEntitiesAndIndex(new EGID(id, group), out index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryQueryEntitiesAndIndex(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) where T : struct, IEntityStruct + public bool TryQueryEntitiesAndIndex(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, + out T[] array) where T : struct, IEntityStruct { return TryQueryEntitiesAndIndex(new EGID(id, group), out index, out array); } @@ -156,11 +194,19 @@ namespace Svelto.ECS.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(EGID entityGID) where T : struct, IEntityStruct { - if (QueryEntitySafeDictionary(entityGID.groupID, out TypeSafeDictionary casted) == false) return false; + if (SafeQueryEntityDictionary(entityGID.groupID, out TypeSafeDictionary casted) == false) return false; return casted != null && casted.ContainsKey(entityGID.entityID); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Exists(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct + { + if (SafeQueryEntityDictionary(group, out TypeSafeDictionary casted) == false) return false; + + return casted != null && casted.ContainsKey(id); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid) { @@ -184,14 +230,14 @@ namespace Svelto.ECS.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PublishEntityChange(EGID egid) where T : unmanaged, IEntityStruct { - _entityStream.PublishEntity(ref QueryEntity(egid)); + _entityStream.PublishEntity(ref QueryEntity(egid), egid); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] T[] QueryEntitiesAndIndexInternal(EGID entityGID, out uint index) where T : struct, IEntityStruct { index = 0; - if (QueryEntitySafeDictionary(entityGID.groupID, out TypeSafeDictionary safeDictionary) == false) + if (SafeQueryEntityDictionary(entityGID.groupID, out TypeSafeDictionary safeDictionary) == false) return null; if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false) @@ -201,46 +247,55 @@ namespace Svelto.ECS.Internal } [MethodImpl(MethodImplOptions.AggressiveInlining)] - bool QueryEntitySafeDictionary(uint group, out TypeSafeDictionary typeSafeDictionary) where T : struct, IEntityStruct + bool SafeQueryEntityDictionary(uint group, out TypeSafeDictionary typeSafeDictionary) + where T : struct, IEntityStruct { - typeSafeDictionary = null; - - //search for the group - if (_groupEntityViewsDB.TryGetValue(group, out var entitiesInGroupPerType) == false) - return false; - - //search for the indexed entities in the group - if (entitiesInGroupPerType.TryGetValue(typeof(T), out var safeDictionary) == false) + if (UnsafeQueryEntityDictionary(group, TypeCache.type, out var safeDictionary) == false) + { + typeSafeDictionary = default; return false; + } //return the indexes entities if they exist - typeSafeDictionary = (safeDictionary as TypeSafeDictionary); + typeSafeDictionary = safeDictionary as TypeSafeDictionary; return true; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - static ReadOnlyCollectionStruct RetrieveEmptyEntityViewList() + internal bool UnsafeQueryEntityDictionary(uint group, Type type, out ITypeSafeDictionary typeSafeDictionary) { - var arrayFast = FasterList.DefaultList.ToArrayFast(); + //search for the group + if (_groupEntityViewsDB.TryGetValue(group, out var entitiesInGroupPerType) == false) + { + typeSafeDictionary = null; + return false; + } - return new ReadOnlyCollectionStruct(arrayFast, 0); + //search for the indexed entities in the group + return entitiesInGroupPerType.TryGetValue(new RefWrapper(type), out typeSafeDictionary); } [MethodImpl(MethodImplOptions.AggressiveInlining)] static T[] RetrieveEmptyEntityViewArray() { - return FasterList.DefaultList.ToArrayFast(); + return EmptyList.emptyArray; } //grouped set of entity views, this is the standard way to handle entity views entity views are grouped per //group, then indexable per type, then indexable per EGID. however the TypeSafeDictionary can return an array of //values directly, that can be iterated over, so that is possible to iterate over all the entity views of //a specific type inside a specific group. - readonly FasterDictionary> _groupEntityViewsDB; + readonly FasterDictionary, ITypeSafeDictionary>> _groupEntityViewsDB; + //needed to be able to iterate over all the entities of the same type regardless the group //may change in future - readonly Dictionary> _groupsPerEntity; - readonly EntitiesStream _entityStream; + readonly FasterDictionary, FasterDictionary> _groupsPerEntity; + readonly EntitiesStream _entityStream; + + static class EmptyList + { + internal static readonly T[] emptyArray = new T[0]; + } } -} +} \ No newline at end of file diff --git a/Svelto.ECS/EntityBuilder.CheckFields.cs b/Svelto.ECS/EntityBuilder.CheckFields.cs index c733561..99b7d0e 100644 --- a/Svelto.ECS/EntityBuilder.CheckFields.cs +++ b/Svelto.ECS/EntityBuilder.CheckFields.cs @@ -1,108 +1,138 @@ #if !DEBUG || PROFILER #define DISABLE_CHECKS using System.Diagnostics; -#endif +#endif using System; using System.Reflection; namespace Svelto.ECS { - public static class EntityBuilderUtilities + internal static class EntityBuilderUtilities { -#if DISABLE_CHECKS + const string MSG = "Entity Structs field and Entity View Struct components must hold value types."; + + +#if DISABLE_CHECKS [Conditional("_CHECKS_DISABLED")] #endif - public static void CheckFields(Type type, bool needsReflection) + public static void CheckFields(Type entityStructType, bool needsReflection) { - if (type == ENTITY_STRUCT_INFO_VIEW || type == EGIDType || type == ECLUSIVEGROUPSTRUCTTYPE) + if (entityStructType == ENTITY_STRUCT_INFO_VIEW || + entityStructType == EGIDType || + entityStructType == EXCLUSIVEGROUPSTRUCTTYPE || + entityStructType == SERIALIZABLE_ENTITY_STRUCT) + { return; + } if (needsReflection == false) { - if (type.IsClass) - throw new EntityStructException("EntityStructs must be structs.", ENTITY_VIEW_TYPE, type); + if (entityStructType.IsClass) + { + throw new EntityStructException("EntityStructs must be structs.", entityStructType); + } - var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); + FieldInfo[] fields = entityStructType.GetFields(BindingFlags.Public | BindingFlags.Instance); - for (int i = fields.Length - 1; i >= 0; --i) + for (var i = fields.Length - 1; i >= 0; --i) { - var field = fields[i]; - var fieldFieldType = field.FieldType; - - SubCheckFields(fieldFieldType); + FieldInfo fieldInfo = fields[i]; + Type fieldType = fieldInfo.FieldType; + + SubCheckFields(fieldType, entityStructType); } } else { - var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); + FieldInfo[] fields = entityStructType.GetFields(BindingFlags.Public | BindingFlags.Instance); if (fields.Length < 1) - ProcessError("Entity View Structs must hold only entity components interfaces.", type); - + { + ProcessError("Entity View Structs must hold only entity components interfaces.", entityStructType); + } + for (int i = fields.Length - 1; i >= 0; --i) { - var field = fields[i]; - - if (field.FieldType.IsInterfaceEx() == false) - ProcessError("Entity View Structs must hold only entity components interfaces.", type); - - var properties = field.FieldType.GetProperties(BindingFlags.Public | - BindingFlags.Instance | BindingFlags.DeclaredOnly); + FieldInfo fieldInfo = fields[i]; + + if (fieldInfo.FieldType.IsInterfaceEx() == false) + { + ProcessError("Entity View Structs must hold only entity components interfaces.", + entityStructType); + } + + PropertyInfo[] properties = fieldInfo.FieldType.GetProperties( + BindingFlags.Public | + BindingFlags.Instance | + BindingFlags.DeclaredOnly); for (int j = properties.Length - 1; j >= 0; --j) { - if (properties[j].PropertyType.IsGenericType == true) + if (properties[j].PropertyType.IsGenericType) { - var genericTypeDefinition = properties[j].PropertyType.GetGenericTypeDefinition(); + Type genericTypeDefinition = properties[j].PropertyType.GetGenericTypeDefinition(); if (genericTypeDefinition == DISPATCHONSETTYPE || - genericTypeDefinition == DISPATCHONCHANGETYPE) continue; + genericTypeDefinition == DISPATCHONCHANGETYPE) + { + continue; + } } - var propertyType = properties[j].PropertyType; + Type propertyType = properties[j].PropertyType; if (propertyType != STRINGTYPE) - SubCheckFields(propertyType); + { + SubCheckFields(propertyType, entityStructType); + } } } } } - static void SubCheckFields(Type fieldFieldType) + static void SubCheckFields(Type fieldType, Type entityStructType) { - if (fieldFieldType.IsPrimitive == true || fieldFieldType.IsValueType == true) + if (fieldType.IsPrimitive || fieldType.IsValueType) { - if (fieldFieldType.IsValueType == true && !fieldFieldType.IsEnum && fieldFieldType.IsPrimitive == false) + if (fieldType.IsValueType && !fieldType.IsEnum && fieldType.IsPrimitive == false) { - CheckFields(fieldFieldType, false); + CheckFields(fieldType, false); } return; } - - ProcessError("Entity Structs field and Entity View Struct components must hold value types.", - fieldFieldType); + + ProcessError(MSG, entityStructType, fieldType); } - - static void ProcessError(string message, Type type) + + static void ProcessError(string message, Type entityViewType, Type fieldType = null) { -#if !RELAXED_ECS - Type ENTITY_VIEW_TYPE = typeof(Type); - throw new EntityStructException(message, ENTITY_VIEW_TYPE, type); -#endif + if (fieldType != null) + { + throw new EntityStructException(message, entityViewType, fieldType); + } + + throw new EntityStructException(message, entityViewType); } - - static readonly Type EGIDType = typeof(EGID); - static readonly Type ECLUSIVEGROUPSTRUCTTYPE = typeof(ExclusiveGroup.ExclusiveGroupStruct); - static readonly Type DISPATCHONSETTYPE = typeof(DispatchOnSet<>); - static readonly Type DISPATCHONCHANGETYPE = typeof(DispatchOnChange<>); - static readonly Type STRINGTYPE = typeof(String); - static readonly Type ENTITY_VIEW_TYPE = typeof(Type); - static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityStructInfoView); + + static readonly Type DISPATCHONCHANGETYPE = typeof(DispatchOnChange<>); + static readonly Type DISPATCHONSETTYPE = typeof(DispatchOnSet<>); + static readonly Type EGIDType = typeof(EGID); + static readonly Type EXCLUSIVEGROUPSTRUCTTYPE = typeof(ExclusiveGroup.ExclusiveGroupStruct); + static readonly Type SERIALIZABLE_ENTITY_STRUCT = typeof(SerializableEntityStruct); + static readonly Type STRINGTYPE = typeof(string); + + internal static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityStructInfoView); } - + public class EntityStructException : Exception { - public EntityStructException(string message, Type entityViewType, Type type): - base(message.FastConcat(" entity view: ", entityViewType.ToString(), " field: ", type.ToString())) - {} + public EntityStructException(string message, Type entityViewType, Type type) : + base(message.FastConcat(" entity view: '", entityViewType.ToString(), "', field: '", type.ToString())) + { + } + + public EntityStructException(string message, Type entityViewType) : + base(message.FastConcat(" entity view: ", entityViewType.ToString())) + { + } } } \ No newline at end of file diff --git a/Svelto.ECS/EntityBuilder.cs b/Svelto.ECS/EntityBuilder.cs index b0de643..78aaffa 100644 --- a/Svelto.ECS/EntityBuilder.cs +++ b/Svelto.ECS/EntityBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using Svelto.DataStructures; using Svelto.ECS.Hybrid; using Svelto.ECS.Internal; @@ -9,6 +10,51 @@ namespace Svelto.ECS { public class EntityBuilder : IEntityBuilder where T : struct, IEntityStruct { + static class EntityView + { + internal static readonly FasterList>> cachedFields; + internal static readonly Dictionary cachedTypes; +#if DEBUG && !PROFILER + internal static readonly Dictionary> implementorsByType; +#else + internal static readonly Dictionary implementorsByType; +#endif + static EntityView() + { + cachedFields = new FasterList>>(); + + var type = typeof(T); + + var fields = type.GetFields(BindingFlags.Public | + BindingFlags.Instance); + + for (var i = fields.Length - 1; i >= 0; --i) + { + var field = fields[i]; + + var setter = FastInvoke.MakeSetter(field); + + cachedFields.Add(new KeyValuePair>(field.FieldType, setter)); + } + + cachedTypes = new Dictionary(); + +#if DEBUG && !PROFILER + implementorsByType = new Dictionary>(); +#else + implementorsByType = new Dictionary(); +#endif + } + + internal static void InitCache() + {} + + internal static void BuildEntityView(out T entityView) + { + entityView = new T(); + } + } + public EntityBuilder() { _initializer = DEFAULT_IT; @@ -16,10 +62,16 @@ namespace Svelto.ECS EntityBuilderUtilities.CheckFields(ENTITY_VIEW_TYPE, NEEDS_REFLECTION); if (NEEDS_REFLECTION) - EntityView.InitCache(); + EntityView.InitCache(); } - public void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID entityID, object[] implementors) + public EntityBuilder(in T initializer) : this() + { + _initializer = initializer; + } + + public void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID egid, + IEnumerable implementors) { if (dictionary == null) dictionary = new TypeSafeDictionary(); @@ -28,21 +80,24 @@ namespace Svelto.ECS if (NEEDS_REFLECTION) { - DBC.ECS.Check.Require(implementors != null, "Implementors not found while building an EntityView"); - DBC.ECS.Check.Require(castedDic.ContainsKey(entityID.entityID) == false, - "building an entity with already used entity id! id: ".FastConcat((ulong) entityID) - .FastConcat(" ", ENTITY_VIEW_NAME)); + DBC.ECS.Check.Require(implementors != null, + $"Implementors not found while building an EntityView `{typeof(T)}`"); + DBC.ECS.Check.Require(castedDic.ContainsKey(egid.entityID) == false, + $"building an entity with already used entity id! id: '{(ulong) egid}', {ENTITY_VIEW_NAME}"); + + EntityView.BuildEntityView(out var entityView); - EntityView.BuildEntityView(out var entityView); + this.FillEntityView(ref entityView, entityViewBlazingFastReflection, implementors, + EntityView.implementorsByType, EntityView.cachedTypes); - this.FillEntityView(ref entityView, entityViewBlazingFastReflection, implementors, implementorsByType, - cachedTypes); - - castedDic.Add(entityID.entityID, ref entityView); + castedDic.Add(egid.entityID, entityView); } else { - castedDic.Add(entityID.entityID, _initializer); + DBC.ECS.Check.Require(!castedDic.ContainsKey(egid.entityID), + $"building an entity with already used entity id! id: '{egid.entityID}'"); + + castedDic.Add(egid.entityID, _initializer); } } @@ -61,27 +116,31 @@ namespace Svelto.ECS return dictionary; } - public Type GetEntityType() { return ENTITY_VIEW_TYPE; } + public Type GetEntityType() + { + return ENTITY_VIEW_TYPE; + } -#if DEBUG && !PROFILER - readonly Dictionary> implementorsByType = - new Dictionary>(); -#else - readonly Dictionary implementorsByType = new Dictionary(); -#endif + static EntityBuilder() + { + ENTITY_VIEW_TYPE = typeof(T); + DEFAULT_IT = default; + NEEDS_REFLECTION = typeof(IEntityViewStruct).IsAssignableFrom(ENTITY_VIEW_TYPE); + HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_VIEW_TYPE); + ENTITY_VIEW_NAME = ENTITY_VIEW_TYPE.ToString(); + SetEGIDWithoutBoxing.Warmup(); + } - //this is used to avoid newing a dictionary every time, but it's used locally only and it's cleared for each use - readonly Dictionary cachedTypes = new Dictionary(); + readonly T _initializer; static FasterList>> entityViewBlazingFastReflection => - EntityView.cachedFields; + EntityView.cachedFields; - internal static readonly Type ENTITY_VIEW_TYPE = typeof(T); - static readonly T DEFAULT_IT = default; - static readonly bool NEEDS_REFLECTION = typeof(IEntityViewStruct).IsAssignableFrom(typeof(T)); - static readonly string ENTITY_VIEW_NAME = ENTITY_VIEW_TYPE.ToString(); - internal static readonly bool HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_VIEW_TYPE); + internal static readonly Type ENTITY_VIEW_TYPE; + public static readonly bool HAS_EGID; - internal T _initializer; + static readonly T DEFAULT_IT; + static readonly bool NEEDS_REFLECTION; + static readonly string ENTITY_VIEW_NAME; } } \ No newline at end of file diff --git a/Svelto.ECS/EntityCollection.cs b/Svelto.ECS/EntityCollection.cs index 2521610..355e0e8 100644 --- a/Svelto.ECS/EntityCollection.cs +++ b/Svelto.ECS/EntityCollection.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using Svelto.ECS.Internal; namespace Svelto.ECS { @@ -52,7 +51,103 @@ namespace Svelto.ECS int _index; } } + + public struct EntityCollection + { + public EntityCollection(in (T1[], T2[]) array, uint count) + { + _array = array; + _count = count; + } + + public EntityIterator GetEnumerator() + { + return new EntityIterator(_array, _count); + } + + readonly (T1[], T2[]) _array; + readonly uint _count; + + public struct EntityIterator : IEnumerator> + { + public EntityIterator((T1[], T2[]) array, uint count) : this() + { + _array = array; + _count = count; + _index = -1; + } + + public bool MoveNext() + { + return ++_index < _count; + } + + public void Reset() + { + _index = -1; + } + + public ValueRef Current => new ValueRef(_array, (uint) _index); + + ValueRef IEnumerator>. Current => throw new NotImplementedException(); + object IEnumerator.Current => throw new NotImplementedException(); + + public void Dispose() {} + + readonly (T1[], T2[]) _array; + readonly uint _count; + int _index; + } + } + + public struct EntityCollection + { + public EntityCollection(in (T1[], T2[], T3[]) array, uint count) + { + _array = array; + _count = count; + } + + public EntityIterator GetEnumerator() + { + return new EntityIterator(_array, _count); + } + readonly (T1[], T2[], T3[]) _array; + readonly uint _count; + + public struct EntityIterator : IEnumerator> + { + public EntityIterator((T1[], T2[], T3[]) array, uint count) : this() + { + _array = array; + _count = count; + _index = -1; + } + + public bool MoveNext() + { + return ++_index < _count; + } + + public void Reset() + { + _index = -1; + } + + public ValueRef Current => new ValueRef(_array, (uint) _index); + + ValueRef IEnumerator>.Current => throw new NotImplementedException(); + object IEnumerator. Current => throw new NotImplementedException(); + + public void Dispose() {} + + readonly (T1[], T2[], T3[]) _array; + readonly uint _count; + int _index; + } + } + public struct EntityCollections where T : struct, IEntityStruct { public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this() @@ -94,7 +189,7 @@ namespace Svelto.ECS { _index = -1; _indexGroup = -1; - _array = _db.QueryEntities(_groups[0], out _count); + _count = 0; } public ref T Current => ref _array[_index]; @@ -130,7 +225,7 @@ namespace Svelto.ECS readonly IEntitiesDB _db; readonly ExclusiveGroup[] _groups; - public struct EntityGroupsIterator : IEnumerator + public struct EntityGroupsIterator : IEnumerator> { public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this() { @@ -145,8 +240,9 @@ namespace Svelto.ECS while (_index + 1 >= _count && ++_indexGroup < _groups.Length) { _index = -1; - _array1 = _db.QueryEntities(_groups[_indexGroup], out _count); - _array2 = _db.QueryEntities(_groups[_indexGroup], out var count1); + var array1 = _db.QueryEntities(_groups[_indexGroup], out _count); + var array2 = _db.QueryEntities(_groups[_indexGroup], out var count1); + _array = (array1, array2); #if DEBUG && !PROFILER if (_count != count1) @@ -161,47 +257,70 @@ namespace Svelto.ECS { _index = -1; _indexGroup = -1; - - _array1 = _db.QueryEntities(_groups[0], out _count); - _array2 = _db.QueryEntities(_groups[0], out var count1); + + var array1 = _db.QueryEntities(_groups[0], out _count); + var array2 = _db.QueryEntities(_groups[0], out var count1); + _array = (array1, array2); #if DEBUG && !PROFILER if (_count != count1) throw new ECSException("number of entities in group doesn't match"); #endif } - public ValueRef Current => new ValueRef(_array1, _array2, (uint) _index); + public ValueRef Current + { + get + { + var valueRef = new ValueRef(_array, (uint) _index); + return valueRef; + } + } - ValueRef IEnumerator.Current => throw new NotImplementedException(); + ValueRef IEnumerator>.Current => throw new NotImplementedException(); object IEnumerator.Current => throw new NotImplementedException(); public void Dispose() {} - readonly IEntitiesDB _db; + readonly IEntitiesDB _db; readonly ExclusiveGroup[] _groups; - uint _count; - int _index; - int _indexGroup; - T2[] _array2; - T1[] _array1; + uint _count; + int _index; + int _indexGroup; + (T1[], T2[]) _array; + } + } + + public struct ValueRef + { + readonly (T1[], T2[]) array; - public struct ValueRef - { - readonly T1[] array1; - readonly T2[] array2; + readonly uint index; - readonly uint index; + public ValueRef(in (T1[], T2[]) entity1, uint i) + { + array = entity1; + index = i; + } - public ValueRef(T1[] entity1, T2[] entity2, uint i) : this() - { - array1 = entity1; - array2 = entity2; - index = i; - } + public ref T1 entityStructA => ref array.Item1[index]; + public ref T2 entityStructB => ref array.Item2[index]; + } + + public struct ValueRef + { + readonly (T1[], T2[], T3[]) array; - public ref T1 Item1 => ref array1[index]; - public ref T2 Item2 => ref array2[index]; - } + readonly uint index; + + public ValueRef(in (T1[], T2[], T3[]) entity1, uint i) + { + array = entity1; + index = i; } + + public ref T1 entityStructA => ref array.Item1[index]; + public ref T2 entityStructB => ref array.Item2[index]; + public ref T3 entityStructC => ref array.Item3[index]; + } -} \ No newline at end of file +} diff --git a/Svelto.ECS/EntityDescriptorInfo.cs b/Svelto.ECS/EntityDescriptorTemplate.cs similarity index 58% rename from Svelto.ECS/EntityDescriptorInfo.cs rename to Svelto.ECS/EntityDescriptorTemplate.cs index 72000aa..b96e3bd 100644 --- a/Svelto.ECS/EntityDescriptorInfo.cs +++ b/Svelto.ECS/EntityDescriptorTemplate.cs @@ -7,7 +7,11 @@ namespace Svelto.ECS static class EntityDescriptorTemplate where TType : IEntityDescriptor, new() { - internal static readonly StaticEntityDescriptorInfo descriptor - = new StaticEntityDescriptorInfo(new TType()); + static EntityDescriptorTemplate() + { + descriptor = new TType(); + } + + public static IEntityDescriptor descriptor { get; } } } diff --git a/Svelto.ECS/EntityFactory.cs b/Svelto.ECS/EntityFactory.cs index 42674db..3556ae8 100644 --- a/Svelto.ECS/EntityFactory.cs +++ b/Svelto.ECS/EntityFactory.cs @@ -1,71 +1,89 @@ using System; using System.Collections.Generic; +using Svelto.DataStructures; namespace Svelto.ECS.Internal { static class EntityFactory { - internal static Dictionary BuildGroupedEntities(EGID egid, - EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd, IEntityBuilder[] entitiesToBuild, - object[] implementors) + public static FasterDictionary, ITypeSafeDictionary> BuildGroupedEntities(EGID egid, + EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd, + IEntityBuilder[] entitiesToBuild, + IEnumerable implementors) { - var @group = FetchEntityGroup(egid.groupID, groupEntitiesToAdd); + var group = FetchEntityGroup(egid.groupID, groupEntitiesToAdd); BuildEntitiesAndAddToGroup(egid, group, entitiesToBuild, implementors); return group; } - static Dictionary FetchEntityGroup(uint groupID, + static FasterDictionary, ITypeSafeDictionary> FetchEntityGroup(uint groupID, EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityViewsByType) { - if (groupEntityViewsByType.current.TryGetValue(groupID, out Dictionary @group) == - false) + if (groupEntityViewsByType.current.TryGetValue(groupID, out var group) == false) { - @group = new Dictionary(); + group = new FasterDictionary, ITypeSafeDictionary>(); - groupEntityViewsByType.current.Add(groupID, @group); + groupEntityViewsByType.current.Add(groupID, group); } - groupEntityViewsByType.currentEntitiesCreatedPerGroup.TryGetValue(groupID, out var value); - groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = value+1; + if (groupEntityViewsByType.currentEntitiesCreatedPerGroup.TryGetValue(groupID, out var value) == false) + groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = 0; + else + groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = value+1; - return @group; + return group; } - static void BuildEntitiesAndAddToGroup(EGID entityID, - Dictionary @group, - IEntityBuilder[] entitiesToBuild, - object[] implementors) + static void BuildEntitiesAndAddToGroup(EGID entityID, + FasterDictionary, ITypeSafeDictionary> group, + IEntityBuilder[] entityBuilders, IEnumerable implementors) { - var count = entitiesToBuild.Length; #if DEBUG && !PROFILER HashSet types = new HashSet(); - +#endif + InternalBuild(entityID, group, entityBuilders, implementors +#if DEBUG && !PROFILER + , types +#endif + ); + } + + static void InternalBuild(EGID entityID, FasterDictionary, ITypeSafeDictionary> group, + IEntityBuilder[] entityBuilders, IEnumerable implementors +#if DEBUG && !PROFILER + , HashSet types +#endif + ) + { + var count = entityBuilders.Length; +#if DEBUG && !PROFILER for (var index = 0; index < count; ++index) { - var entityViewType = entitiesToBuild[index].GetEntityType(); + var entityViewType = entityBuilders[index].GetEntityType(); if (types.Contains(entityViewType)) { throw new ECSException("EntityBuilders must be unique inside an EntityDescriptor"); } - + types.Add(entityViewType); } #endif for (var index = 0; index < count; ++index) { - var entityViewBuilder = entitiesToBuild[index]; - var entityViewType = entityViewBuilder.GetEntityType(); + var entityStructBuilder = entityBuilders[index]; + var entityViewType = entityStructBuilder.GetEntityType(); - BuildEntity(entityID, @group, entityViewType, entityViewBuilder, implementors); + BuildEntity(entityID, group, entityViewType, entityStructBuilder, implementors); } } - static void BuildEntity(EGID entityID, Dictionary @group, Type entityViewType, - IEntityBuilder entityBuilder, object[] implementors) + static void BuildEntity(EGID entityID, FasterDictionary, ITypeSafeDictionary> group, + Type entityViewType, IEntityBuilder entityBuilder, IEnumerable implementors) { - var entityViewsPoolWillBeCreated = @group.TryGetValue(entityViewType, out var safeDictionary) == false; + var entityViewsPoolWillBeCreated = + group.TryGetValue(new RefWrapper(entityViewType), out var safeDictionary) == false; //passing the undefined entityViewsByType inside the entityViewBuilder will allow it to be created with the //correct type and casted back to the undefined list. that's how the list will be eventually of the target @@ -73,7 +91,7 @@ namespace Svelto.ECS.Internal entityBuilder.BuildEntityAndAddToList(ref safeDictionary, entityID, implementors); if (entityViewsPoolWillBeCreated) - @group.Add(entityViewType, safeDictionary); + group.Add(new RefWrapper(entityViewType), safeDictionary); } } } \ No newline at end of file diff --git a/Svelto.ECS/EntityGroup.cs b/Svelto.ECS/EntityGroup.cs new file mode 100644 index 0000000..b12c1a9 --- /dev/null +++ b/Svelto.ECS/EntityGroup.cs @@ -0,0 +1,29 @@ +using System; +using Svelto.DataStructures; +using Svelto.ECS.Internal; + +namespace Svelto.ECS +{ + public struct EntityGroup + { + internal EntityGroup(FasterDictionary, ITypeSafeDictionary> entitiesInGroupPerType, uint _groupID) + { + _group = entitiesInGroupPerType; + groupID = _groupID; + } + + public ref T QueryEntity(uint entityGidEntityId) where T : struct, IEntityStruct + { + return ref (_group[new RefWrapper(typeof(T))] as TypeSafeDictionary).GetValueByRef( + entityGidEntityId); + } + + public bool Exists(uint entityGidEntityId) where T : struct, IEntityStruct + { + return (_group[new RefWrapper(typeof(T))] as TypeSafeDictionary).ContainsKey(entityGidEntityId); + } + + readonly FasterDictionary, ITypeSafeDictionary> _group; + public uint groupID; + } +} \ No newline at end of file diff --git a/Svelto.ECS/EntityHierarchyStruct.cs b/Svelto.ECS/EntityHierarchyStruct.cs index 6bf4f48..c0fe044 100644 --- a/Svelto.ECS/EntityHierarchyStruct.cs +++ b/Svelto.ECS/EntityHierarchyStruct.cs @@ -4,7 +4,7 @@ namespace Svelto.ECS { public readonly ExclusiveGroup.ExclusiveGroupStruct parentGroup; - public EntityHierarchyStruct(ExclusiveGroup @group): this() { parentGroup = group; } + public EntityHierarchyStruct(ExclusiveGroup group): this() { parentGroup = group; } public EGID ID { get; set; } } diff --git a/Svelto.ECS/EntityInfoView.cs b/Svelto.ECS/EntityInfoView.cs index 16d2442..98711f8 100644 --- a/Svelto.ECS/EntityInfoView.cs +++ b/Svelto.ECS/EntityInfoView.cs @@ -1,12 +1,7 @@ -using System; - namespace Svelto.ECS { - public struct EntityStructInfoView: IEntityStruct, INeedEGID + struct EntityStructInfoView: IEntityStruct { - public EGID ID { get; set; } - public Type type { get; set; } - public IEntityBuilder[] entitiesToBuild; } } \ No newline at end of file diff --git a/Svelto.ECS/EntityNotFoundException.cs b/Svelto.ECS/EntityNotFoundException.cs index b8de5e4..e2e4fda 100644 --- a/Svelto.ECS/EntityNotFoundException.cs +++ b/Svelto.ECS/EntityNotFoundException.cs @@ -4,8 +4,9 @@ namespace Svelto.ECS { public class EntityNotFoundException : Exception { - public EntityNotFoundException(EGID entityGidEntityId, Type type) - : base("entity not found ".FastConcat(type.ToString())) - {} + public EntityNotFoundException(EGID entityEGID, Type entityType) : base( + $"entity of type '{entityType}' with ID '{entityEGID.entityID}', group '{(uint) entityEGID.groupID}' not found!") + { + } } } \ No newline at end of file diff --git a/Svelto.ECS/EntityStream.cs b/Svelto.ECS/EntityStream.cs index 14484ec..948d4d7 100644 --- a/Svelto.ECS/EntityStream.cs +++ b/Svelto.ECS/EntityStream.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using Svelto.DataStructures; -using Svelto.ECS.Internal; namespace Svelto.ECS { @@ -17,70 +15,78 @@ namespace Svelto.ECS /// one only /// - you want to communicate between EnginesRoots /// - class EntitiesStream + class EntitiesStream : IDisposable { - internal Consumer GenerateConsumer(string name, int capacity) where T : unmanaged, IEntityStruct + internal Consumer GenerateConsumer(string name, uint capacity) where T : unmanaged, IEntityStruct { if (_streams.ContainsKey(typeof(T)) == false) _streams[typeof(T)] = new EntityStream(); return (_streams[typeof(T)] as EntityStream).GenerateConsumer(name, capacity); } - - public Consumer GenerateConsumer(ExclusiveGroup @group, string name, int capacity) where T : unmanaged, IEntityStruct + + public Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) + where T : unmanaged, IEntityStruct { if (_streams.ContainsKey(typeof(T)) == false) _streams[typeof(T)] = new EntityStream(); - return (_streams[typeof(T)] as EntityStream).GenerateConsumer( @group, name, capacity); + return (_streams[typeof(T)] as EntityStream).GenerateConsumer(group, name, capacity); } - internal void PublishEntity(ref T entity) where T : unmanaged, IEntityStruct + internal void PublishEntity(ref T entity, EGID egid) where T : unmanaged, IEntityStruct { if (_streams.TryGetValue(typeof(T), out var typeSafeStream)) - (typeSafeStream as EntityStream).PublishEntity(ref entity); + (typeSafeStream as EntityStream).PublishEntity(ref entity, egid); else - Console.LogWarningDebug("No Consumers are waiting for this entity to change ", typeof(T)); + Console.LogDebug("No Consumers are waiting for this entity to change ", typeof(T)); } readonly ConcurrentDictionary _streams = new ConcurrentDictionary(); + + public void Dispose() + { + _streams.Clear(); + } } interface ITypeSafeStream {} - class EntityStream:ITypeSafeStream where T:unmanaged, IEntityStruct + class EntityStream : ITypeSafeStream where T : unmanaged, IEntityStruct { - public void PublishEntity(ref T entity) + public void PublishEntity(ref T entity, EGID egid) { for (int i = 0; i < _consumers.Count; i++) { if (_consumers[i]._hasGroup) { - if (EntityBuilder.HAS_EGID && (entity as INeedEGID).ID.groupID == _consumers[i]._group) + if (egid.groupID == _consumers[i]._group) { - _consumers[i].Enqueue(ref entity); + _consumers[i].Enqueue(entity, egid); } } else - _consumers[i].Enqueue(ref entity); + { + _consumers[i].Enqueue(entity, egid); + } } } - public Consumer GenerateConsumer(string name, int capacity) + public Consumer GenerateConsumer(string name, uint capacity) { var consumer = new Consumer(name, capacity, this); - + _consumers.Add(consumer); - + return consumer; } - - public Consumer GenerateConsumer(ExclusiveGroup @group, string name, int capacity) + + public Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) { var consumer = new Consumer(group, name, capacity, this); - + _consumers.Add(consumer); - + return consumer; } @@ -92,34 +98,66 @@ namespace Svelto.ECS readonly FasterListThreadSafe> _consumers = new FasterListThreadSafe>(); } - public struct Consumer: IDisposable where T:unmanaged, IEntityStruct + public struct Consumer : IDisposable where T : unmanaged, IEntityStruct { - internal Consumer(string name, int capacity, EntityStream stream): this() + internal Consumer(string name, uint capacity, EntityStream stream):this() { - _ringBuffer = new RingBuffer(capacity); +#if DEBUG && !PROFILER _name = name; +#endif + _ringBuffer = new RingBuffer>((int) capacity, +#if DEBUG && !PROFILER + _name +#else + string.Empty +#endif + ); + _stream = stream; } - - internal Consumer(ExclusiveGroup @group, string name, int capacity, EntityStream stream):this(name, capacity, stream) + + internal Consumer(ExclusiveGroup group, string name, uint capacity, EntityStream stream) : this(name, + capacity, stream) { _group = group; _hasGroup = true; } - internal void Enqueue(ref T entity) + internal void Enqueue(in T entity, in EGID egid) { - _ringBuffer.Enqueue(ref entity, _name); + _ringBuffer.Enqueue((entity, egid)); + } + + public bool TryDequeue(out T entity) + { + var tryDequeue = _ringBuffer.TryDequeue(out var values); + + entity = values.Item1; + + return tryDequeue; + } + + public bool TryDequeue(out T entity, out EGID id) + { + var tryDequeue = _ringBuffer.TryDequeue(out var values); + + entity = values.Item1; + id = values.Item2; + + return tryDequeue; } - - public bool TryDequeue(out T entity) { return _ringBuffer.TryDequeue(out entity, _name); } public void Flush() { _ringBuffer.Reset(); } public void Dispose() { _stream.RemoveConsumer(this); } + public uint Count() { return (uint) _ringBuffer.Count; } - readonly RingBuffer _ringBuffer; - readonly EntityStream _stream; - readonly string _name; + readonly RingBuffer> _ringBuffer; + readonly EntityStream _stream; + internal readonly ExclusiveGroup _group; - internal readonly bool _hasGroup; + internal readonly bool _hasGroup; + +#if DEBUG && !PROFILER + readonly string _name; +#endif } -} \ No newline at end of file +} diff --git a/Svelto.ECS/EntityStructInitializer.cs b/Svelto.ECS/EntityStructInitializer.cs index 0b9e88b..ed85d53 100644 --- a/Svelto.ECS/EntityStructInitializer.cs +++ b/Svelto.ECS/EntityStructInitializer.cs @@ -1,36 +1,75 @@ using System; -using System.Collections.Generic; +using Svelto.DataStructures; using Svelto.ECS.Internal; namespace Svelto.ECS { - public struct EntityStructInitializer + public ref struct EntityStructInitializer { - public EntityStructInitializer(EGID id, Dictionary @group) + public EntityStructInitializer(EGID id, FasterDictionary, ITypeSafeDictionary> group) { - _group = @group; - ID = id; + _group = group; + _ID = id; } - public void Init(T initializer) where T: struct, IEntityStruct + public void Init(T initializer) where T : struct, IEntityStruct { - if (_group.TryGetValue(EntityBuilder.ENTITY_VIEW_TYPE, out var typeSafeDictionary) == true) - { - var dictionary = typeSafeDictionary as TypeSafeDictionary; + if (_group.TryGetValue(new RefWrapper(EntityBuilder.ENTITY_VIEW_TYPE), + out var typeSafeDictionary) == false) return; - if (EntityBuilder.HAS_EGID) - { - var needEgid = ((INeedEGID) initializer); - needEgid.ID = ID; - initializer = (T) needEgid; - } + var dictionary = (TypeSafeDictionary) typeSafeDictionary; - if (dictionary.TryFindIndex(ID.entityID, out var findElementIndex)) - dictionary.GetDirectValue(findElementIndex) = initializer; - } + if (EntityBuilder.HAS_EGID) + SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref initializer, _ID); + + if (dictionary.TryFindIndex(_ID.entityID, out var findElementIndex)) + dictionary.GetDirectValue(findElementIndex) = initializer; } - readonly EGID ID; - readonly Dictionary _group; + public void CopyFrom(T initializer) where T : struct, IEntityStruct + { + var dictionary = (TypeSafeDictionary) _group[new RefWrapper(EntityBuilder.ENTITY_VIEW_TYPE)]; + + if (EntityBuilder.HAS_EGID) + SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref initializer, _ID); + + dictionary[_ID.entityID] = initializer; + } + + public ref T GetOrCreate() where T : struct, IEntityStruct + { + ref var entityDictionary = ref _group.GetOrCreate(new RefWrapper(EntityBuilder.ENTITY_VIEW_TYPE) + , () => new TypeSafeDictionary()); + var dictionary = (TypeSafeDictionary) entityDictionary; + + return ref dictionary.GetOrCreate(_ID.entityID); + } + + public T Get() where T : struct, IEntityStruct + { + return (_group[new RefWrapper(EntityBuilder.ENTITY_VIEW_TYPE)] as TypeSafeDictionary)[_ID.entityID]; + } + + public bool Has() where T : struct, IEntityStruct + { + if (_group.TryGetValue(new RefWrapper(EntityBuilder.ENTITY_VIEW_TYPE), + out var typeSafeDictionary)) + { + var dictionary = (TypeSafeDictionary) typeSafeDictionary; + + if (dictionary.ContainsKey(_ID.entityID)) + return true; + } + + return false; + } + + public static EntityStructInitializer CreateEmptyInitializer() + { + return new EntityStructInitializer(new EGID(), new FasterDictionary, ITypeSafeDictionary>()); + } + + readonly EGID _ID; + readonly FasterDictionary, ITypeSafeDictionary> _group; } } \ No newline at end of file diff --git a/Svelto.ECS/EntitySubmissionScheduler.cs b/Svelto.ECS/EntitySubmissionScheduler.cs index 93e4b4c..5ddc91a 100644 --- a/Svelto.ECS/EntitySubmissionScheduler.cs +++ b/Svelto.ECS/EntitySubmissionScheduler.cs @@ -1,9 +1,7 @@ -using Svelto.WeakEvents; - namespace Svelto.ECS.Schedulers { public interface IEntitySubmissionScheduler { - WeakAction onTick { set; } + EnginesRoot.EntitiesSubmitter onTick { set; } } } \ No newline at end of file diff --git a/Svelto.ECS/EntitySubmitOperation.cs b/Svelto.ECS/EntitySubmitOperation.cs index 7570474..57299be 100644 --- a/Svelto.ECS/EntitySubmitOperation.cs +++ b/Svelto.ECS/EntitySubmitOperation.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; namespace Svelto.ECS { @@ -11,23 +12,19 @@ namespace Svelto.ECS public readonly IEntityBuilder[] builders; public readonly EGID fromID; public readonly EGID toID; - public readonly Type entityDescriptor; #if DEBUG && !PROFILER - public string trace; + public StackFrame trace; #endif public EntitySubmitOperation(EntitySubmitOperationType operation, EGID from, EGID to, - IEntityBuilder[] builders = null, - Type entityDescriptor = null) + IEntityBuilder[] builders = null) { type = operation; this.builders = builders; - fromID = from; + fromID = from; toID = to; - - this.entityDescriptor = entityDescriptor; #if DEBUG && !PROFILER - trace = string.Empty; + trace = default; #endif } diff --git a/Svelto.ECS/EntityView.cs b/Svelto.ECS/EntityView.cs deleted file mode 100644 index f52afa6..0000000 --- a/Svelto.ECS/EntityView.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using Svelto.DataStructures; -using Svelto.Utilities; - -namespace Svelto.ECS -{ - static class EntityView where T: struct, IEntityStruct - { - internal static readonly FasterList>> cachedFields; - - static EntityView() - { - cachedFields = new FasterList>>(); - - var type = typeof(T); - - var fields = type.GetFields(BindingFlags.Public | - BindingFlags.Instance); - - for (int i = fields.Length - 1; i >= 0; --i) - { - var field = fields[i]; - - ActionCast setter = FastInvoke.MakeSetter(field); - - cachedFields.Add(new KeyValuePair>(field.FieldType, setter)); - } - } - - internal static void InitCache() - {} - - internal static void BuildEntityView(out T entityView) - { - entityView = new T {}; - } - } -} \ No newline at end of file diff --git a/Svelto.ECS/EntityViewUtility.cs b/Svelto.ECS/EntityViewUtility.cs index 76190ef..8e4be9b 100644 --- a/Svelto.ECS/EntityViewUtility.cs +++ b/Svelto.ECS/EntityViewUtility.cs @@ -6,7 +6,6 @@ using Svelto.Utilities; namespace Svelto.ECS { - #if DEBUG && !PROFILER struct ECSTuple { @@ -21,7 +20,6 @@ namespace Svelto.ECS } #endif - static class EntityViewUtility { @@ -29,7 +27,7 @@ namespace Svelto.ECS , ref T entityView , FasterList>> entityViewBlazingFastReflection - , object[] implementors, + , IEnumerable implementors, #if DEBUG && !PROFILER Dictionary> implementorsByType #else @@ -40,13 +38,10 @@ namespace Svelto.ECS { //efficient way to collect the fields of every EntityViewType var setters = - FasterList>> - .NoVirt.ToArrayFast(entityViewBlazingFastReflection, out var count); + FasterList>>.NoVirt.ToArrayFast(entityViewBlazingFastReflection, out var count); - for (var index = 0; index < implementors.Length; index++) + foreach (var implementor in implementors) { - var implementor = implementors[index]; - if (implementor != null) { var type = implementor.GetType(); @@ -73,7 +68,7 @@ namespace Svelto.ECS #if DEBUG && !PROFILER else { - Svelto.Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityView ", + Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityView ", entityBuilder.GetEntityType().ToString())); } #endif @@ -118,13 +113,13 @@ namespace Svelto.ECS } const string DUPLICATE_IMPLEMENTOR_ERROR = - "Svelto.ECS the same component is implemented with more than one implementor. This is " + + "Svelto.ECS the same component is implemented with more than one implementor. This is " + "considered an error and MUST be fixed. "; const string NULL_IMPLEMENTOR_ERROR = - "Svelto.ECS Null implementor, please be careful about the implementors passed to avoid " + + "Svelto.ECS Null implementor, please be careful about the implementors passed to avoid " + "performance loss "; - const string NOT_FOUND_EXCEPTION = "Svelto.ECS Implementor not found for an EntityView. "; + const string NOT_FOUND_EXCEPTION = "Svelto.ECS Implementor not found for an EntityView. "; } } \ No newline at end of file diff --git a/Svelto.ECS/ExclusiveGroup.cs b/Svelto.ECS/ExclusiveGroup.cs index 2b4ef91..959f7cc 100644 --- a/Svelto.ECS/ExclusiveGroup.cs +++ b/Svelto.ECS/ExclusiveGroup.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; + #pragma warning disable 660,661 namespace Svelto.ECS @@ -17,42 +18,68 @@ namespace Svelto.ECS /// } /// /// + + ///use this like: + /// public class TriggersGroup : ExclusiveGroup {} + public abstract class NamedExclusiveGroup:ExclusiveGroup + { + public static ExclusiveGroup Group = new ExclusiveGroup(); + public static string name = typeof(T).FullName; + + public NamedExclusiveGroup() { } + + public NamedExclusiveGroup(string recognizeAs) : base(recognizeAs) + {} + + public NamedExclusiveGroup(ushort range) : base(range) + {} + } + public class ExclusiveGroup { public ExclusiveGroup() { _group = ExclusiveGroupStruct.Generate(); } - + public ExclusiveGroup(string recognizeAs) { _group = ExclusiveGroupStruct.Generate(); - + _serialisedGroups.Add(recognizeAs, _group); } - + public ExclusiveGroup(ushort range) { _group = new ExclusiveGroupStruct(range); +#if DEBUG + _range = range; +#endif } - + public static implicit operator ExclusiveGroupStruct(ExclusiveGroup group) { return group._group; } - - public static explicit operator uint(ExclusiveGroup group) + + public static explicit operator uint(ExclusiveGroup group) { return group._group; } public static ExclusiveGroupStruct operator+(ExclusiveGroup a, uint b) { +#if DEBUG + if (a._range == 0) + throw new ECSException("adding values to a not ranged ExclusiveGroup"); + if (b >= a._range) + throw new ECSException("Using out of range group"); +#endif return a._group + b; } readonly ExclusiveGroupStruct _group; - + //I use this as parameter because it must not be possible to pass null Exclusive Groups. public struct ExclusiveGroupStruct : IEquatable, IComparable, IEqualityComparer @@ -107,17 +134,29 @@ namespace Svelto.ECS DBC.ECS.Check.Require(_globalId + range < ushort.MaxValue, "too many exclusive groups created"); _globalId += range; } - + internal ExclusiveGroupStruct(uint groupID) { _id = groupID; } + public ExclusiveGroupStruct(byte[] data, uint pos) + { + _id = (uint)( + data[pos++] + | data[pos++] << 8 + | data[pos++] << 16 + | data[pos++] << 24 + ); + + DBC.ECS.Check.Ensure(_id < _globalId, "Invalid group ID deserialiased"); + } + public static implicit operator uint(ExclusiveGroupStruct groupStruct) { return groupStruct._id; } - + public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, uint b) { var group = new ExclusiveGroupStruct(); @@ -127,19 +166,100 @@ namespace Svelto.ECS return group; } - uint _id; + uint _id; static uint _globalId; } public static ExclusiveGroupStruct Search(string holderGroupName) { if (_serialisedGroups.ContainsKey(holderGroupName) == false) - throw new Exception("Serialized Group Not Found ".FastConcat(holderGroupName)); - + throw new Exception("Named Group Not Found ".FastConcat(holderGroupName)); + return _serialisedGroups[holderGroupName]; } - static readonly Dictionary _serialisedGroups = new Dictionary(); + static readonly Dictionary _serialisedGroups = new Dictionary(); +#if DEBUG + readonly ushort _range; +#endif } -} \ No newline at end of file +} + +#if future + public static void ConstructStaticGroups() + { + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + // Assemblies or types aren't guaranteed to be returned in the same order, + // and I couldn't find proof that `GetTypes()` returns them in fixed order either, + // even for builds made with the exact same source code. + // So will sort reflection results by name before constructing groups. + var groupFields = new List>(); + + foreach (Assembly assembly in assemblies) + { + Type[] types = GetTypesSafe(assembly); + + foreach (Type type in types) + { + if (type == null || !type.IsClass) + { + continue; + } + + // Groups defined as static members in static classes + if (type.IsSealed && type.IsAbstract) + { + FieldInfo[] fields = type.GetFields(); + foreach(var field in fields) + { + if (field.IsStatic && typeof(ExclusiveGroup).IsAssignableFrom(field.FieldType)) + { + groupFields.Add(new KeyValuePair($"{type.FullName}.{field.Name}", field)); + } + } + } + // Groups defined as classes + else if (type.BaseType != null + && type.BaseType.IsGenericType + && type.BaseType.GetGenericTypeDefinition() == typeof(ExclusiveGroup<>)) + { + FieldInfo field = type.GetField("Group", + BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy); + + groupFields.Add(new KeyValuePair(type.FullName, field)); + } + } + } + + groupFields.Sort((a, b) => string.CompareOrdinal(a.Key, b.Key)); + + for (int i = 0; i < groupFields.Count; ++i) + { + groupFields[i].Value.GetValue(null); +#if DEBUG + var group = (ExclusiveGroup) groupFields[i].Value.GetValue(null); + groupNames[(uint) group] = groupFields[i].Key; +#endif + } + } + + static Type[] GetTypesSafe(Assembly assembly) + { + try + { + Type[] types = assembly.GetTypes(); + + return types; + } + catch (ReflectionTypeLoadException e) + { + return e.Types; + } + } + +#if DEBUG + static string[] groupNames = new string[ushort.MaxValue]; +#endif +#endif \ No newline at end of file diff --git a/Svelto.ECS/ExecuteOnEntitiesDB.cs b/Svelto.ECS/ExecuteOnEntitiesDB.cs index de3a6b0..375dfb4 100644 --- a/Svelto.ECS/ExecuteOnEntitiesDB.cs +++ b/Svelto.ECS/ExecuteOnEntitiesDB.cs @@ -1,4 +1,5 @@ using System; +using Svelto.DataStructures; namespace Svelto.ECS.Internal { @@ -9,9 +10,9 @@ namespace Svelto.ECS.Internal { var type = typeof(T); - if (_groupsPerEntity.TryGetValue(type, out var dic)) + if (_groupsPerEntity.TryGetValue(new RefWrapper(type), out var dictionary)) { - foreach (var pair in dic) + foreach (var pair in dictionary) { var entities = (pair.Value as TypeSafeDictionary).GetValuesArray(out var innerCount); @@ -27,7 +28,7 @@ namespace Svelto.ECS.Internal { var type = typeof(T); - if (_groupsPerEntity.TryGetValue(type, out var dic)) + if (_groupsPerEntity.TryGetValue(new RefWrapper(type), out var dic)) { foreach (var pair in dic) { @@ -38,5 +39,23 @@ namespace Svelto.ECS.Internal } } } + + public void ExecuteOnAllEntities + (ref W value, ExecuteOnAllEntitiesAction action) + where T : struct, IEntityStruct + { + var type = typeof(T); + + if (_groupsPerEntity.TryGetValue(new RefWrapper(type), out var dic)) + { + foreach (var pair in dic) + { + var entities = (pair.Value as TypeSafeDictionary).GetValuesArray(out var innerCount); + + if (innerCount > 0) + action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this, ref value); + } + } + } } } \ No newline at end of file diff --git a/Svelto.ECS/ExtendibleEntityDescriptor.cs b/Svelto.ECS/ExtendibleEntityDescriptor.cs index 170f8a8..3b420fd 100644 --- a/Svelto.ECS/ExtendibleEntityDescriptor.cs +++ b/Svelto.ECS/ExtendibleEntityDescriptor.cs @@ -1,3 +1,6 @@ +using System; +using Svelto.ECS.Serialization; + namespace Svelto.ECS { /// @@ -5,15 +8,41 @@ namespace Svelto.ECS /// to swap and remove specialized entities from abstract engines /// /// - public abstract class ExtendibleEntityDescriptor:IEntityDescriptor where TType : IEntityDescriptor, new() + public class ExtendibleEntityDescriptor : IEntityDescriptor where TType : IEntityDescriptor, new() { - protected ExtendibleEntityDescriptor(IEntityBuilder[] extraEntities) + static ExtendibleEntityDescriptor() + { + if (typeof(ISerializableEntityDescriptor).IsAssignableFrom(typeof(TType))) + throw new Exception( + $"SerializableEntityDescriptors cannot be used as base entity descriptor: {typeof(TType)}"); + } + + public ExtendibleEntityDescriptor(IEntityBuilder[] extraEntities) { _dynamicDescriptor = new DynamicEntityDescriptor(extraEntities); } + public ExtendibleEntityDescriptor() + { + _dynamicDescriptor = new DynamicEntityDescriptor(true); + } + + public ExtendibleEntityDescriptor ExtendWith() where T : IEntityDescriptor, new() + { + _dynamicDescriptor.ExtendWith(); + + return this; + } + + public ExtendibleEntityDescriptor ExtendWith(IEntityBuilder[] extraEntities) + { + _dynamicDescriptor.ExtendWith(extraEntities); + + return this; + } + public IEntityBuilder[] entitiesToBuild => _dynamicDescriptor.entitiesToBuild; - readonly DynamicEntityDescriptor _dynamicDescriptor; + DynamicEntityDescriptor _dynamicDescriptor; } } \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs b/Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs index 85654a4..28b6898 100644 --- a/Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs +++ b/Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs @@ -17,7 +17,7 @@ namespace Svelto.ECS.Unity #pragma warning disable 649 [SerializeField] string _groupName; - [SerializeField] ushort _id = 0; + [SerializeField] ushort _id; #pragma warning restore 649 } } diff --git a/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs b/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs index 61167f1..160615d 100644 --- a/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs +++ b/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs @@ -5,7 +5,8 @@ namespace Svelto.ECS.Unity { public static class SveltoGUIHelper { - public static T CreateFromPrefab(ref uint startIndex, Transform contextHolder, IEntityFactory factory, ExclusiveGroup group) where T : MonoBehaviour, IEntityDescriptorHolder + public static T CreateFromPrefab(ref uint startIndex, Transform contextHolder, IEntityFactory factory, + ExclusiveGroup group, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder { var holder = Create(new EGID(startIndex++, group), contextHolder, factory); var childs = contextHolder.GetComponentsInChildren(true); @@ -14,18 +15,28 @@ namespace Svelto.ECS.Unity { if (child.GetType() != typeof(T)) { - var childImplementors = (child as MonoBehaviour).GetComponents(); - startIndex = InternalBuildAll(startIndex, child, factory, group, childImplementors); + var monoBehaviour = child as MonoBehaviour; + var childImplementors = monoBehaviour.GetComponents(); + startIndex = InternalBuildAll( + startIndex, + child, + factory, + group, + childImplementors, + groupNamePostfix); } } return holder; } - public static T Create(EGID ID, Transform contextHolder, - IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder + public static T Create(EGID ID, Transform contextHolder, IEntityFactory factory) + where T : MonoBehaviour, IEntityDescriptorHolder { var holder = contextHolder.GetComponentInChildren(true); + DBC.ECS.Check.Assert(holder != null, $"`{nameof(holder)}` is null! No component of type " + + $"`{typeof(T)}` was found between its children."); + var implementors = holder.GetComponents(); factory.BuildEntity(ID, holder.GetDescriptor(), implementors); @@ -34,7 +45,8 @@ namespace Svelto.ECS.Unity } public static EntityStructInitializer CreateWithEntity(EGID ID, Transform contextHolder, - IEntityFactory factory, out T holder) where T : MonoBehaviour, IEntityDescriptorHolder + IEntityFactory factory, out T holder) + where T : MonoBehaviour, IEntityDescriptorHolder { holder = contextHolder.GetComponentInChildren(true); var implementors = holder.GetComponents(); @@ -42,8 +54,8 @@ namespace Svelto.ECS.Unity return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); } - public static uint CreateAll(uint startIndex, ExclusiveGroup group, Transform contextHolder, - IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder + public static uint CreateAll(uint startIndex, ExclusiveGroup group, + Transform contextHolder, IEntityFactory factory, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder { var holders = contextHolder.GetComponentsInChildren(true); @@ -51,18 +63,23 @@ namespace Svelto.ECS.Unity { var implementors = holder.GetComponents(); - startIndex = InternalBuildAll(startIndex, holder, factory, group, implementors); + startIndex = InternalBuildAll(startIndex, holder, factory, group, implementors, groupNamePostfix); } return startIndex; } - static uint InternalBuildAll(uint startIndex, IEntityDescriptorHolder descriptorHolder, IEntityFactory factory, ExclusiveGroup group, IImplementor[] implementors) + static uint InternalBuildAll(uint startIndex, IEntityDescriptorHolder descriptorHolder, + IEntityFactory factory, ExclusiveGroup group, IImplementor[] implementors, string groupNamePostfix) { ExclusiveGroup.ExclusiveGroupStruct realGroup = group; if (string.IsNullOrEmpty(descriptorHolder.groupName) == false) - realGroup = ExclusiveGroup.Search(descriptorHolder.groupName); + { + realGroup = ExclusiveGroup.Search(!string.IsNullOrEmpty(groupNamePostfix) + ? $"{descriptorHolder.groupName}{groupNamePostfix}" + : descriptorHolder.groupName); + } EGID egid; var holderId = descriptorHolder.id; @@ -79,4 +96,4 @@ namespace Svelto.ECS.Unity } } } -#endif \ No newline at end of file +#endif diff --git a/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs b/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs index 2a2ba0a..be60f78 100644 --- a/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs +++ b/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs @@ -1,53 +1,62 @@ #if UNITY_5 || UNITY_5_3_OR_NEWER +using Object = UnityEngine.Object; +using System; using System.Collections; -using Svelto.WeakEvents; using UnityEngine; namespace Svelto.ECS.Schedulers.Unity { //The EntitySubmissionScheduler has been introduced to make the entity views submission logic platform independent //You can customize the scheduler if you wish - public class UnityEntitySubmissionScheduler : IEntitySubmissionScheduler + public class UnityEntitySubmissionScheduler : IEntitySubmissionScheduler, IDisposable { class Scheduler : MonoBehaviour { - IEnumerator Start() + public Scheduler() + { + _coroutine = Coroutine(); + } + + void Update() + { + _coroutine.MoveNext(); + } + + IEnumerator Coroutine() { while (true) { yield return _wait; - if (onTick.IsValid) - onTick.Invoke(); - else - yield break; - + onTick.Invoke(); } } readonly WaitForEndOfFrame _wait = new WaitForEndOfFrame(); + readonly IEnumerator _coroutine; - public WeakAction onTick; + public EnginesRoot.EntitiesSubmitter onTick; } public UnityEntitySubmissionScheduler(string name = "ECSScheduler") { _name = name; } + + public void Dispose() + { + Object.Destroy(_scheduler.gameObject); + } - public WeakAction onTick + public EnginesRoot.EntitiesSubmitter onTick { set { - if (_scheduler == null) - { - GameObject go = new GameObject(_name); - - _scheduler = go.AddComponent(); - } + if (_scheduler == null) _scheduler = new GameObject(_name).AddComponent(); + _scheduler.onTick = value; } } Scheduler _scheduler; - string _name; + readonly string _name; } } #endif \ No newline at end of file diff --git a/Svelto.ECS/GenericentityStreamConsumerFactory.cs b/Svelto.ECS/GenericentityStreamConsumerFactory.cs index 45369b2..4fc0269 100644 --- a/Svelto.ECS/GenericentityStreamConsumerFactory.cs +++ b/Svelto.ECS/GenericentityStreamConsumerFactory.cs @@ -1,29 +1,33 @@ +using Svelto.DataStructures; + namespace Svelto.ECS { - class GenericentityStreamConsumerFactory : IEntityStreamConsumerFactory + class GenericEntityStreamConsumerFactory : IEntityStreamConsumerFactory { - public GenericentityStreamConsumerFactory(DataStructures.WeakReference weakReference) + public GenericEntityStreamConsumerFactory(EnginesRoot weakReference) { - _enginesRoot = weakReference; + _enginesRoot = new WeakReference(weakReference); } - public Consumer GenerateConsumer(string name, int capacity) where T : unmanaged, IEntityStruct + public Consumer GenerateConsumer(string name, uint capacity) where T : unmanaged, IEntityStruct { return _enginesRoot.Target.GenerateConsumer(name, capacity); } - public Consumer GenerateConsumer(ExclusiveGroup group, string name, int capacity) where T : unmanaged, IEntityStruct + public Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) where T : unmanaged, IEntityStruct { return _enginesRoot.Target.GenerateConsumer(group, name, capacity); } - readonly DataStructures.WeakReference _enginesRoot; +//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside +//engines of other enginesRoot + readonly WeakReference _enginesRoot; } public interface IEntityStreamConsumerFactory { - Consumer GenerateConsumer(string name, int capacity) where T : unmanaged, IEntityStruct; - Consumer GenerateConsumer(ExclusiveGroup group, string name, int capacity) + Consumer GenerateConsumer(string name, uint capacity) where T : unmanaged, IEntityStruct; + Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) where T : unmanaged, IEntityStruct; } } \ No newline at end of file diff --git a/Svelto.ECS/IEntitiesDB.cs b/Svelto.ECS/IEntitiesDB.cs index ca5b7c1..b2734a2 100644 --- a/Svelto.ECS/IEntitiesDB.cs +++ b/Svelto.ECS/IEntitiesDB.cs @@ -2,17 +2,21 @@ using System; namespace Svelto.ECS { + public delegate void ExecuteOnAllEntitiesAction(T[] entities, ExclusiveGroup.ExclusiveGroupStruct group, + uint count, IEntitiesDB db, ref W value); + public interface IEntitiesDB { /////////////////////////////////////////////////// - /// Query entities - /// ECS systems are meant to work on a set of Entities. These methods allow to iterate over entity - /// structs inside a given group or an array of groups + // Query entities + // ECS systems are meant to work on a set of Entities. These methods allow to iterate over entity + // structs inside a given group or an array of groups /////////////////////////////////////////////////// - + /// - /// Fast and raw return of entities buffer. + /// Fast and raw return of entities buffer. /// + /// /// /// /// @@ -22,7 +26,7 @@ namespace Svelto.ECS where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; (T1[], T2[], T3[]) QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct; - + /// /// return entities that can be iterated through the EntityCollection iterator /// @@ -31,29 +35,33 @@ namespace Svelto.ECS /// EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; - + EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; + EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) + where T1 : struct, IEntityStruct + where T2 : struct, IEntityStruct + where T3 : struct, IEntityStruct; + /// /// return entities found in multiple groups, that can be iterated through the EntityCollection iterator /// This method is useful to write abstracted engines /// - /// /// /// EntityCollections QueryEntities(ExclusiveGroup[] groups) where T : struct, IEntityStruct; EntityCollections QueryEntities(ExclusiveGroup[] groups) where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; - + /////////////////////////////////////////////////// - /// Query entities regardless the group - /// these methods are necessary to create abstracted engines. Engines that can iterate over entities regardless - /// the group + // Query entities regardless the group + // these methods are necessary to create abstracted engines. Engines that can iterate over entities regardless + // the group /////////////////////////////////////////////////// /// /// Execute an action on ALL the entities regardless the group. This function doesn't guarantee cache /// friendliness even if just EntityStructs are used. Safety checks are in place, /// - /// /// /// void ExecuteOnAllEntities(Action action) @@ -66,18 +74,20 @@ namespace Svelto.ECS /// /// /// - void ExecuteOnAllEntities(W value, - Action action) + void ExecuteOnAllEntities(ref W value, ExecuteOnAllEntitiesAction action) + where T : struct, IEntityStruct; + + void ExecuteOnAllEntities(W value, Action action) where T : struct, IEntityStruct; /////////////////////////////////////////////////// - /// Query single entities - /// ECS systems are meant to work on a set of Entities. Working on a single entity is sometime necessary, hence - /// the following methods - /// However Because of the double hashing required to identify a specific entity, these function are slower than - /// other query methods when used multiple times! + // Query single entities + // ECS systems are meant to work on a set of Entities. Working on a single entity is sometime necessary, hence + // the following methods + // However Because of the double hashing required to identify a specific entity, these function are slower than + // other query methods when used multiple times! /////////////////////////////////////////////////// - + /// /// QueryUniqueEntity is a contract method that explicitly declare the intention to have just on entity in a /// specific group, usually used for GUI elements @@ -86,7 +96,7 @@ namespace Svelto.ECS /// /// ref T QueryUniqueEntity(ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct; - + /// /// return a specific entity by reference. /// @@ -99,8 +109,8 @@ namespace Svelto.ECS /// /// ///QueryEntitiesAndIndex is useful to optimize cases when multiple entity structs from the same entity must - /// be queried. This is the use case: - /// + /// be queried. This is the use case: + /// ///ref var ghostPosition = ref entitiesDB.QueryEntitiesAndIndex /// (MockupRenderingGroups.GhostCubeID, out var index)[index]; ///ref var ghostScaling = ref entitiesDB.QueryEntities @@ -118,9 +128,9 @@ namespace Svelto.ECS T[] QueryEntitiesAndIndex(EGID entityGid, out uint index) where T : struct, IEntityStruct; T[] QueryEntitiesAndIndex(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) where T : struct, IEntityStruct; - + /// - /// Like QueryEntitiesAndIndex and only way to get an index only if exists + /// Like QueryEntitiesAndIndex and only way to get an index only if exists /// /// /// @@ -130,24 +140,24 @@ namespace Svelto.ECS bool TryQueryEntitiesAndIndex (uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) where T : struct, IEntityStruct; - + /// /// this method returns a mapped version of the entity array so that is possible to work on multiple entities /// inside the group through their EGID. This version skip a level of indirection so it's a bit faster than /// using QueryEntity multiple times (with different EGIDs). /// However mapping can be slow so it must be used for not performance critical paths /// - /// - /// /// /// EGIDMapper QueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId) where T : struct, IEntityStruct; - + bool TryQueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId, out EGIDMapper mapper) + where T : struct, IEntityStruct; + /////////////////////////////////////////////////// - /// Utility methods + // Utility methods /////////////////////////////////////////////////// - + /// /// check if a specific entity exists /// @@ -155,12 +165,13 @@ namespace Svelto.ECS /// /// bool Exists(EGID egid) where T : struct, IEntityStruct; + bool Exists(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct; bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid); /// /// know if there is any entity struct in a specific group /// - /// + /// /// /// bool HasAny(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; @@ -172,39 +183,11 @@ namespace Svelto.ECS /// /// uint Count(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; - - /////////////////////////////////////////////////// - /// Publish entities in the entity stream - /// The Entity Stream is a communication mean based on the Publisher/Consumer model. The pattern is - /// 1) changing an entity - /// 2) publish the entity modified - /////////////////////////////////////////////////// /// - /// Publish an Entity Change, through the Entity Stream linked to this database - /// Use case: - /// public void Add(ref ButtonEntityViewStruct buttonEntityViewStruct) -/// { -/// buttonEntityViewStruct.buttonClick.buttonEvent = new DispatchOnSet(buttonEntityViewStruct.ID); -/// -/// buttonEntityViewStruct.buttonClick.buttonEvent.NotifyOnValueSet(_enqueueButtonChange); -/// } -/// -/// public void Remove(ref ButtonEntityViewStruct buttonEntityViewStruct) -/// { -/// buttonEntityViewStruct.buttonClick.buttonEvent.StopNotify(_enqueueButtonChange); -/// } -/// -/// void EnqueueButtonChange(EGID egid, ButtonEvents value) -/// { -/// entitiesDB.QueryEntity(egid) = new ButtonEntityStruct(egid, value); -/// -/// entitiesDB.PublishEntityChange(egid); -/// } /// /// /// void PublishEntityChange(EGID egid) where T : unmanaged, IEntityStruct; - } -} \ No newline at end of file +} diff --git a/Svelto.ECS/IEntityBuilder.cs b/Svelto.ECS/IEntityBuilder.cs index b349b0b..da7b527 100644 --- a/Svelto.ECS/IEntityBuilder.cs +++ b/Svelto.ECS/IEntityBuilder.cs @@ -1,11 +1,13 @@ using System; +using System.Collections.Generic; using Svelto.ECS.Internal; namespace Svelto.ECS { public interface IEntityBuilder { - void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID entityID, object[] implementors); + void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID egid, + IEnumerable implementors); ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size); Type GetEntityType(); diff --git a/Svelto.ECS/IEntityFactory.cs b/Svelto.ECS/IEntityFactory.cs index 4d7e8a1..14b7e89 100644 --- a/Svelto.ECS/IEntityFactory.cs +++ b/Svelto.ECS/IEntityFactory.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace Svelto.ECS { /// @@ -12,7 +14,7 @@ namespace Svelto.ECS ///{ /// public static readonly ExclusiveGroups PlayerEntitiesGroup = new ExclusiveGroups(); ///} - /// + /// /// public interface IEntityFactory { @@ -25,7 +27,7 @@ namespace Svelto.ECS /// void PreallocateEntitySpace(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) where T : IEntityDescriptor, new(); - + /// /// The EntityDescriptor doesn't need to be ever instantiated. It just describes the Entity /// itself in terms of EntityViews to build. The Implementors are passed to fill the @@ -41,24 +43,12 @@ namespace Svelto.ECS /// /// EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, - object[] implementors = null) + IEnumerable implementors = null) where T : IEntityDescriptor, new(); - EntityStructInitializer BuildEntity(EGID egid, object[] implementors = null) + + EntityStructInitializer BuildEntity(EGID egid, IEnumerable implementors = null) where T:IEntityDescriptor, new(); -#if REAL_ID - /// - /// BuildEntity version without specifying the entity ID. The complete EGID will be found inside - /// the EntityStructInitializer and/or the single entity components - /// - /// - /// - /// - /// - EntityStructInitializer BuildEntity(ExclusiveGroup.ExclusiveGroupStruct groupID, object[] implementors = null) - where T : IEntityDescriptor, new(); -#endif - /// /// When the type of the entity is not known (this is a special case!) an EntityDescriptorInfo /// can be built in place of the generic parameter T. @@ -66,12 +56,11 @@ namespace Svelto.ECS /// /// /// - /// - EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, - T descriptorEntity, - object[] implementors = null) - where T : IEntityDescriptor; - EntityStructInitializer BuildEntity(EGID egid, T entityDescriptor, object[] implementors = null) + /// + EntityStructInitializer BuildEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, + T descriptorEntity, IEnumerable implementors = null) where T : IEntityDescriptor; + + EntityStructInitializer BuildEntity(EGID egid, T entityDescriptor, IEnumerable implementors = null) where T : IEntityDescriptor; } } diff --git a/Svelto.ECS/IEntityFunctions.cs b/Svelto.ECS/IEntityFunctions.cs index 49c28ac..9d04e0d 100644 --- a/Svelto.ECS/IEntityFunctions.cs +++ b/Svelto.ECS/IEntityFunctions.cs @@ -8,8 +8,6 @@ namespace Svelto.ECS void RemoveEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); void RemoveEntity(EGID entityegid) where T : IEntityDescriptor, new(); - void RemoveEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); - void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID); void SwapEntityGroup(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new(); diff --git a/Svelto.ECS/IEntityStruct.cs b/Svelto.ECS/IEntityStruct.cs index 02308a8..379b4b4 100644 --- a/Svelto.ECS/IEntityStruct.cs +++ b/Svelto.ECS/IEntityStruct.cs @@ -1,9 +1,13 @@ namespace Svelto.ECS { - ///EntityStruct MUST implement IEntiyStruct + ///EntityStruct MUST implement IEntityStruct public interface IEntityStruct - {} - + { + } + + /// + /// use INeedEGID on an IEntityStruct only if you need the EGID + /// public interface INeedEGID { EGID ID { get; set; } diff --git a/Svelto.ECS/IReactOnAddAndRemove.cs b/Svelto.ECS/IReactOnAddAndRemove.cs index 7323984..0e35eb7 100644 --- a/Svelto.ECS/IReactOnAddAndRemove.cs +++ b/Svelto.ECS/IReactOnAddAndRemove.cs @@ -4,7 +4,7 @@ namespace Svelto.ECS { public interface IReactOnAddAndRemove : IReactOnAddAndRemove where T : IEntityStruct { - void Add(ref T entityView); - void Remove(ref T entityView); + void Add(ref T entityView, EGID egid); + void Remove(ref T entityView, EGID egid); } } \ No newline at end of file diff --git a/Svelto.ECS/IReactOnSwap.cs b/Svelto.ECS/IReactOnSwap.cs index 0d5722c..e9d0f0d 100644 --- a/Svelto.ECS/IReactOnSwap.cs +++ b/Svelto.ECS/IReactOnSwap.cs @@ -4,7 +4,9 @@ namespace Svelto.ECS { public interface IReactOnSwap : IReactOnSwap where T : IEntityStruct { - void MovedTo(ref T entityView, ExclusiveGroup.ExclusiveGroupStruct previousGroup); - void MovedFrom(ref T entityView); + void MovedTo(ref T entityView, ExclusiveGroup.ExclusiveGroupStruct previousGroup, EGID egid); +#if SEEMS_UNNECESSARY + void MovedFrom(ref T entityView, EGID egid); +#endif } } \ No newline at end of file diff --git a/Svelto.ECS/Sequencer.cs b/Svelto.ECS/Sequencer.cs index 0b0a25d..da67c6f 100644 --- a/Svelto.ECS/Sequencer.cs +++ b/Svelto.ECS/Sequencer.cs @@ -12,7 +12,7 @@ namespace Svelto.ECS { _steps = new Dictionary(); - for (int i = 0; i < values.Length; i++) + for (var i = 0; i < values.Length; i++) _steps.Add(values[i].from, values[i].to); } } @@ -106,7 +106,7 @@ namespace Svelto.ECS public void Next(IEngine engine, C condition, EGID id) where C:struct, IConvertible { - C branch = condition; + var branch = condition; var to = (_steps._steps[engine] as To); var steps = to._tos[branch]; diff --git a/Svelto.ECS/Serialization/ComposedSerializer.cs b/Svelto.ECS/Serialization/ComposedSerializer.cs new file mode 100644 index 0000000..c78bdf3 --- /dev/null +++ b/Svelto.ECS/Serialization/ComposedSerializer.cs @@ -0,0 +1,43 @@ +using System; + +namespace Svelto.ECS.Serialization +{ + public class ComposedSerializer : ISerializer + where T : unmanaged, IEntityStruct + where X : class, ISerializer, new() + where Y : class, ISerializer, new() + { + public ComposedSerializer() + { + _serializers = new ISerializer[2]; + _serializers[0] = new X(); + _serializers[1] = new Y(); + } + + public bool Serialize(in T value, ISerializationData serializationData) + { + foreach (ISerializer s in _serializers) + { + serializationData.data.ExpandBy(s.size); + if (s.SerializeSafe(value, serializationData)) + return true; + } + + throw new Exception($"ComposedSerializer for {typeof(T)} did not serialize any data!"); + } + + public bool Deserialize(ref T value, ISerializationData serializationData) + { + foreach (ISerializer s in _serializers) + { + if (s.DeserializeSafe(ref value, serializationData)) + return true; + } + + throw new Exception($"ComposedSerializer for {typeof(T)} did not deserialize any data!"); + } + + public uint size => 0; + ISerializer[] _serializers; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/DefaultSerializer.cs b/Svelto.ECS/Serialization/DefaultSerializer.cs new file mode 100644 index 0000000..38e905f --- /dev/null +++ b/Svelto.ECS/Serialization/DefaultSerializer.cs @@ -0,0 +1,43 @@ +using Svelto.Common; + +namespace Svelto.ECS.Serialization +{ + public class DefaultSerializer : ISerializer where T : unmanaged, IEntityStruct + { + public static readonly uint SIZEOFT = SerializableEntityBuilder.SIZE; + + public uint size => SIZEOFT; + + static DefaultSerializer() + { + var _type = typeof(T); + + foreach (var field in _type.GetFields()) + { + if (field.FieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) && field.IsPrivate == false) + throw new ECSException("field cannot be serialised ".FastConcat(_type.FullName)); + } + + if (_type.GetProperties().Length > (EntityBuilder.HAS_EGID ? 1 : 0)) + throw new ECSException("serializable entity struct must be property less ".FastConcat(_type.FullName)); + } + + public bool Serialize(in T value, ISerializationData serializationData) + { + DefaultSerializerUtils.CopyToByteArray(value, serializationData.data.ToArrayFast(), serializationData.dataPos); + + serializationData.dataPos += SIZEOFT; + + return true; + } + + public bool Deserialize(ref T value, ISerializationData serializationData) + { + value = DefaultSerializerUtils.CopyFromByteArray(serializationData.data.ToArrayFast(), serializationData.dataPos); + + serializationData.dataPos += SIZEOFT; + + return true; + } + } +} diff --git a/Svelto.ECS/Serialization/DefaultSerializerUtils.cs b/Svelto.ECS/Serialization/DefaultSerializerUtils.cs new file mode 100644 index 0000000..fcc600d --- /dev/null +++ b/Svelto.ECS/Serialization/DefaultSerializerUtils.cs @@ -0,0 +1,46 @@ +using System; +using Svelto.ECS; + +public static class DefaultSerializerUtils +{ + public static unsafe void CopyToByteArray(in T src, byte[] data, uint offsetDst) where T : unmanaged, IEntityStruct + { + fixed (void* dstPtr = data) + { + void* dstOffsetPtr; + if (IntPtr.Size == sizeof(int)) + { + dstOffsetPtr = (void*) (((IntPtr) dstPtr).ToInt32() + ((IntPtr) offsetDst).ToInt32()); + } + else + { + dstOffsetPtr = (void*) (((IntPtr) dstPtr).ToInt64() + ((IntPtr) offsetDst).ToInt64()); + } + + *(T*) dstOffsetPtr = src; + } + } + + public static unsafe T CopyFromByteArray(byte[] data, uint offsetSrc) where T : unmanaged, IEntityStruct + { + T dst = new T(); + + void* dstPtr = &dst; + fixed (void* srcPtr = data) + { + void* srcOffsetPtr; + if (IntPtr.Size == sizeof(int)) + { + srcOffsetPtr = (void*) (((IntPtr) srcPtr).ToInt32() + ((IntPtr) offsetSrc).ToInt32()); + } + else + { + srcOffsetPtr = (void*) (((IntPtr) srcPtr).ToInt64() + ((IntPtr) offsetSrc).ToInt64()); + } + + *(T*) dstPtr = *(T*) srcOffsetPtr; + } + + return dst; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/DefaultVersioningFactory.cs b/Svelto.ECS/Serialization/DefaultVersioningFactory.cs new file mode 100644 index 0000000..8af213d --- /dev/null +++ b/Svelto.ECS/Serialization/DefaultVersioningFactory.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace Svelto.ECS.Serialization +{ + public class DefaultVersioningFactory : IDeserializationFactory where T : IEntityDescriptor, new() + { + public EntityStructInitializer BuildDeserializedEntity(EGID egid, + ISerializationData serializationData, + ISerializableEntityDescriptor entityDescriptor, + SerializationType serializationType, + IEntitySerialization entitySerialization) + { + var initializer = _factory.BuildEntity(egid, _implementors); + + entitySerialization.DeserializeEntityStructs(serializationData, entityDescriptor, initializer, SerializationType.Storage); + + return initializer; + } + + public DefaultVersioningFactory(IEntityFactory factory) { _factory = factory; } + public DefaultVersioningFactory(IEntityFactory factory, IEnumerable implementors) { _factory = factory; + _implementors = implementors; + } + + readonly IEntityFactory _factory; + readonly IEnumerable _implementors; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/DoNotSerializeAttribute.cs b/Svelto.ECS/Serialization/DoNotSerializeAttribute.cs new file mode 100644 index 0000000..b74485a --- /dev/null +++ b/Svelto.ECS/Serialization/DoNotSerializeAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace Svelto.ECS.Serialization +{ + public class DoNotSerializeAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/DontSerialize.cs b/Svelto.ECS/Serialization/DontSerialize.cs new file mode 100644 index 0000000..f5cba8a --- /dev/null +++ b/Svelto.ECS/Serialization/DontSerialize.cs @@ -0,0 +1,17 @@ +namespace Svelto.ECS.Serialization +{ + public class DontSerialize : ISerializer where T : unmanaged, IEntityStruct + { + public uint size => 0; + + public bool Serialize(in T value, ISerializationData serializationData) + { + return false; + } + + public bool Deserialize(ref T value, ISerializationData serializationData) + { + return false; + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs b/Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs new file mode 100644 index 0000000..011d5a9 --- /dev/null +++ b/Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs @@ -0,0 +1,198 @@ +using System; +using Svelto.Common; +using Svelto.ECS.Internal; +using Svelto.ECS.Serialization; + +namespace Svelto.ECS +{ + //todo: this should not be at framework level + public enum SerializationType + { + Network, + Storage, + + Length + } + + public partial class EnginesRoot + { + readonly bool _isDeserializationOnly; + + sealed class EntitySerialization : IEntitySerialization + { + public void SerializeEntity(EGID egid, ISerializationData serializationData, + SerializationType serializationType) + { + var entitiesDb = _enginesRoot._entitiesDB; + + //needs to retrieve the meta data associated with the entity + ref var serializableEntityStruct = ref entitiesDb.QueryEntity(egid); + uint descriptorHash = serializableEntityStruct.descriptorHash; + + SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; + var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); + var entityStructsToSerialise = entityDescriptor.entitiesToSerialize; + + var header = + new SerializableEntityHeader(descriptorHash, egid, (byte) entityStructsToSerialise.Length); + header.Copy(serializationData); + + for (int index = 0; index < entityStructsToSerialise.Length; index++) + { + var entityBuilder = entityStructsToSerialise[index]; + + serializationData.BeginNextEntityStruct(); + SerializeEntityStruct(egid, entityBuilder, serializationData, serializationType); + } + } + + public EntityStructInitializer DeserializeNewEntity(EGID egid, ISerializationData serializationData, + SerializationType serializationType) + { + //todo: SerializableEntityHeader may be needed to be customizable + var serializableEntityHeader = new SerializableEntityHeader(serializationData); + + uint descriptorHash = serializableEntityHeader.descriptorHash; + SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; + var factory = serializationDescriptorMap.GetSerializationFactory(descriptorHash); + var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); + + //default factory + if (factory == null) + { + var initializer = _enginesRoot.BuildEntity(egid, + _enginesRoot._isDeserializationOnly + ? entityDescriptor.entitiesToSerialize + : entityDescriptor.entitiesToBuild); + + DeserializeEntityStructs(serializationData, entityDescriptor, initializer, serializationType); + + return initializer; + } + + //custom factory + return factory.BuildDeserializedEntity(egid, serializationData, entityDescriptor, serializationType, + this); + } + + public void DeserializeEntity(ISerializationData serializationData, SerializationType serializationType) + { + var serializableEntityHeader = new SerializableEntityHeader(serializationData); + + EGID egid = serializableEntityHeader.egid; + + DeserializeEntityInternal(serializationData, egid, serializableEntityHeader, serializationType); + } + + public void DeserializeEntity(EGID egid, ISerializationData serializationData, + SerializationType serializationType) + { + var serializableEntityHeader = new SerializableEntityHeader(serializationData); + + DeserializeEntityInternal(serializationData, egid, serializableEntityHeader, serializationType); + } + + public void DeserializeEntityStructs(ISerializationData serializationData, + ISerializableEntityDescriptor entityDescriptor, + in EntityStructInitializer initializer, SerializationType serializationType) + { + foreach (var serializableEntityBuilder in entityDescriptor.entitiesToSerialize) + { + serializationData.BeginNextEntityStruct(); + serializableEntityBuilder.Deserialize(serializationData, initializer, serializationType); + } + } + + public void DeserializeEntityToSwap(EGID localEgid, EGID toEgid) + { + EntitiesDB entitiesDb = _enginesRoot._entitiesDB; + ref var serializableEntityStruct = ref entitiesDb.QueryEntity(localEgid); + + SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; + uint descriptorHash = serializableEntityStruct.descriptorHash; + var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); + + var entitySubmitOperation = new EntitySubmitOperation( + EntitySubmitOperationType.Swap, + localEgid, + toEgid, + entityDescriptor.entitiesToBuild); + + _enginesRoot.CheckRemoveEntityID(localEgid); + _enginesRoot.CheckAddEntityID(toEgid); + + _enginesRoot.QueueEntitySubmitOperation(entitySubmitOperation); + } + + public void DeserializeEntityToDelete(EGID egid) + { + EntitiesDB entitiesDB = _enginesRoot._entitiesDB; + ref var serializableEntityStruct = ref entitiesDB.QueryEntity(egid); + uint descriptorHash = serializableEntityStruct.descriptorHash; + + SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; + var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); + + _enginesRoot.CheckRemoveEntityID(egid); + + var entitySubmitOperation = new EntitySubmitOperation( + EntitySubmitOperationType.Remove, + egid, + egid, + entityDescriptor.entitiesToBuild); + + _enginesRoot.QueueEntitySubmitOperation(entitySubmitOperation); + } + + public void RegisterSerializationFactory(IDeserializationFactory deserializationFactory) + where T : ISerializableEntityDescriptor, new() + { + SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; + serializationDescriptorMap.RegisterSerializationFactory(deserializationFactory); + } + + internal EntitySerialization(EnginesRoot enginesRoot) + { + _enginesRoot = enginesRoot; + } + + void SerializeEntityStruct(EGID entityGID, ISerializableEntityBuilder entityBuilder, + ISerializationData serializationData, SerializationType serializationType) + { + uint groupId = entityGID.groupID; + Type entityType = entityBuilder.GetEntityType(); + if (!_enginesRoot._entitiesDB.UnsafeQueryEntityDictionary(groupId, entityType, + out var safeDictionary)) + { + throw new Exception("Entity Serialization failed"); + } + + entityBuilder.Serialize(entityGID.entityID, safeDictionary, serializationData, serializationType); + } + + void DeserializeEntityInternal(ISerializationData serializationData, EGID egid, + SerializableEntityHeader serializableEntityHeader, SerializationType serializationType) + { + SerializationDescriptorMap descriptorMap = _enginesRoot.serializationDescriptorMap; + var entityDescriptor = descriptorMap.GetDescriptorFromHash(serializableEntityHeader.descriptorHash); + + foreach (var serializableEntityBuilder in entityDescriptor.entitiesToSerialize) + { + _enginesRoot._entitiesDB.UnsafeQueryEntityDictionary(egid.groupID, + serializableEntityBuilder.GetEntityType(), out var safeDictionary); + + serializationData.BeginNextEntityStruct(); + serializableEntityBuilder.Deserialize(egid.entityID, safeDictionary, serializationData, + serializationType); + } + } + + readonly EnginesRoot _enginesRoot; + } + + public IEntitySerialization GenerateEntitySerializer() + { + return new EntitySerialization(this); + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/EnginesRoot.SerializableEntityHeader.cs b/Svelto.ECS/Serialization/EnginesRoot.SerializableEntityHeader.cs new file mode 100644 index 0000000..32ccc67 --- /dev/null +++ b/Svelto.ECS/Serialization/EnginesRoot.SerializableEntityHeader.cs @@ -0,0 +1,76 @@ +using Svelto.DataStructures; + +namespace Svelto.ECS +{ + public partial class EnginesRoot + { + struct SerializableEntityHeader + { + public readonly uint descriptorHash; + public readonly byte entityStructsCount; + + const uint SIZE = 4 + 4 + 4 + 1; + + internal SerializableEntityHeader(uint descriptorHash_, EGID egid_, byte entityStructsCount_) + { + entityStructsCount = entityStructsCount_; + descriptorHash = descriptorHash_; + egid = egid_; + } + + internal SerializableEntityHeader(ISerializationData serializationData) + { + descriptorHash = (uint) + (serializationData.data[serializationData.dataPos++] + | serializationData.data[serializationData.dataPos++] << 8 + | serializationData.data[serializationData.dataPos++] << 16 + | serializationData.data[serializationData.dataPos++] << 24); + + uint entityID = (uint) + (serializationData.data[serializationData.dataPos++] + | serializationData.data[serializationData.dataPos++] << 8 + | serializationData.data[serializationData.dataPos++] << 16 + | serializationData.data[serializationData.dataPos++] << 24); + + uint groupID = (uint) + (serializationData.data[serializationData.dataPos++] + | serializationData.data[serializationData.dataPos++] << 8 + | serializationData.data[serializationData.dataPos++] << 16 + | serializationData.data[serializationData.dataPos++] << 24); + + entityStructsCount = serializationData.data[serializationData.dataPos++]; + + egid = new EGID(entityID, new ExclusiveGroup.ExclusiveGroupStruct(groupID)); + } + + internal void Copy(ISerializationData serializationData) + { + serializationData.data.ExpandBy(SIZE); + + // Splitting the descriptorHash_ (uint, 32 bit) into four bytes. + serializationData.data[serializationData.dataPos++] = (byte) (descriptorHash & 0xff); + serializationData.data[serializationData.dataPos++] = (byte) ((descriptorHash >> 8) & 0xff); + serializationData.data[serializationData.dataPos++] = (byte) ((descriptorHash >> 16) & 0xff); + serializationData.data[serializationData.dataPos++] = (byte) ((descriptorHash >> 24) & 0xff); + + // Splitting the entityID (uint, 32 bit) into four bytes. + uint entityID = egid.entityID; + serializationData.data[serializationData.dataPos++] = (byte) (entityID & 0xff); + serializationData.data[serializationData.dataPos++] = (byte) ((entityID >> 8) & 0xff); + serializationData.data[serializationData.dataPos++] = (byte) ((entityID >> 16) & 0xff); + serializationData.data[serializationData.dataPos++] = (byte) ((entityID >> 24) & 0xff); + + // Splitting the groupID (uint, 32 bit) into four bytes. + uint groupID = egid.groupID; + serializationData.data[serializationData.dataPos++] = (byte) (groupID & 0xff); + serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 8) & 0xff); + serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 16) & 0xff); + serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 24) & 0xff); + + serializationData.data[serializationData.dataPos++] = entityStructsCount; + } + + internal readonly EGID egid; //this can't be used safely! + } + } +} diff --git a/Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs b/Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs new file mode 100644 index 0000000..6e23c57 --- /dev/null +++ b/Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Svelto.ECS.Serialization; + +namespace Svelto.ECS +{ + partial class EnginesRoot + { + sealed class SerializationDescriptorMap + { + /// + /// Here we want to register all the EntityDescriptors that need to be serialized for network play. + /// + /// Remember! This needs to in sync across different clients and server as the values are serialized across + /// the network also want this to not change so we can save to a DB + /// + internal SerializationDescriptorMap() + { + _descriptors = new Dictionary(); + _factories = new Dictionary(); + + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly assembly in assemblies) + { + foreach (Type type in GetTypesSafe(assembly)) + { + if (type != null && type.IsClass && type.GetConstructor(Type.EmptyTypes) != null && + typeof(ISerializableEntityDescriptor).IsAssignableFrom(type)) + { + var descriptor = Activator.CreateInstance(type) as ISerializableEntityDescriptor; + + RegisterEntityDescriptor(descriptor); + } + } + } + } + + static IEnumerable GetTypesSafe(Assembly assembly) + { + try + { + Type[] types = assembly.GetTypes(); + + return types; + } + catch (ReflectionTypeLoadException e) + { + return e.Types; + } + } + + void RegisterEntityDescriptor(ISerializableEntityDescriptor descriptor) + { + if (descriptor == null) + { + return; + } + + uint descriptorHash = descriptor.hash; + +#if DEBUG && !PROFILER + if (_descriptors.ContainsKey(descriptorHash)) + { + throw new Exception($"Hash Collision of '{descriptor.GetType()}' against " + + $"'{_descriptors[descriptorHash]} ::: {descriptorHash}'"); + } +#endif + + _descriptors[descriptorHash] = descriptor; + } + + public ISerializableEntityDescriptor GetDescriptorFromHash(uint descriptorID) + { +#if DEBUG && !PROFILER + DBC.ECS.Check.Require(_descriptors.ContainsKey(descriptorID), + $"Could not find descriptor with ID '{descriptorID}'!"); +#endif + + return _descriptors[descriptorID]; + } + + public IDeserializationFactory GetSerializationFactory(uint descriptorID) + { + return _factories.TryGetValue(descriptorID, out var factory) ? factory : null; + } + + public void RegisterSerializationFactory(IDeserializationFactory deserializationFactory) + where Descriptor : ISerializableEntityDescriptor, new() + { + _factories.Add(SerializationEntityDescriptorTemplate.hash, deserializationFactory); + } + + + readonly Dictionary _descriptors; + readonly Dictionary _factories; + } + + /// + /// The map of serializable entity hashes to the serializable entity builders (to know the entity structs + /// to serialize) + /// + SerializationDescriptorMap serializationDescriptorMap { get; } = new SerializationDescriptorMap(); + } +} diff --git a/Svelto.ECS/Serialization/HashNameAttribute.cs b/Svelto.ECS/Serialization/HashNameAttribute.cs new file mode 100644 index 0000000..f2c6ed2 --- /dev/null +++ b/Svelto.ECS/Serialization/HashNameAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Svelto.ECS.Serialization +{ + [AttributeUsage(AttributeTargets.Class)] + public class HashNameAttribute:Attribute + { + public HashNameAttribute(string name) + { + _name = name; + } + + internal string _name; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/IDeserializationFactory.cs b/Svelto.ECS/Serialization/IDeserializationFactory.cs new file mode 100644 index 0000000..1045f71 --- /dev/null +++ b/Svelto.ECS/Serialization/IDeserializationFactory.cs @@ -0,0 +1,9 @@ +namespace Svelto.ECS.Serialization +{ + public interface IDeserializationFactory + { + EntityStructInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, + ISerializableEntityDescriptor entityDescriptor, SerializationType serializationType, + IEntitySerialization entitySerialization); + } +} diff --git a/Svelto.ECS/Serialization/IEntitySerialization.cs b/Svelto.ECS/Serialization/IEntitySerialization.cs new file mode 100644 index 0000000..d143280 --- /dev/null +++ b/Svelto.ECS/Serialization/IEntitySerialization.cs @@ -0,0 +1,69 @@ +namespace Svelto.ECS.Serialization +{ + public interface IEntitySerialization + { + /// + /// Fill the serializationData of the entitiesToSerialize of this descriptor + /// + /// + /// + /// + /// Size in bytes of the newly instantiated entity + void SerializeEntity(EGID egid, ISerializationData serializationData, SerializationType serializationType); + + /// + /// Deserialize a serializationData and copy directly onto the appropriate entities + /// + /// + /// + /// + void DeserializeEntity(ISerializationData serializationData, SerializationType serializationType); + + /// + /// Deserialize a serializationData and copy directly onto the appropriate entities with explicit EGID + /// + /// + /// + /// + /// + void DeserializeEntity(EGID egid, ISerializationData serializationData, SerializationType serializationType); + + /// + /// Deserialize a serializationData and copy directly to an previously created EntityStructInitializer + /// + /// + /// + /// + /// + void DeserializeEntityStructs(ISerializationData serializationData, + ISerializableEntityDescriptor entityDescriptor, + in EntityStructInitializer initializer, SerializationType serializationType); + + /// + /// Contrary to the other Deserialize methods that assume that the entity exists, this method is used to deserialise + /// a new Entity + /// + /// + /// + /// + /// + EntityStructInitializer DeserializeNewEntity(EGID egid, ISerializationData serializationData, + SerializationType serializationType); + + /// + /// Special Entity Swap method that works without knowing the EntityDescriptor to swap + /// + /// + /// + void DeserializeEntityToSwap(EGID localEgid, EGID toEgid); + + /// + /// Special Entity delete method that works without knowing the EntityDescriptor to delete + /// + /// + void DeserializeEntityToDelete(EGID egid); + + void RegisterSerializationFactory(IDeserializationFactory deserializationFactory) + where T : ISerializableEntityDescriptor, new(); + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/ISerializableEntityBuilder.cs b/Svelto.ECS/Serialization/ISerializableEntityBuilder.cs new file mode 100644 index 0000000..bc7197e --- /dev/null +++ b/Svelto.ECS/Serialization/ISerializableEntityBuilder.cs @@ -0,0 +1,15 @@ +using Svelto.ECS.Internal; + +namespace Svelto.ECS.Serialization +{ + public interface ISerializableEntityBuilder : IEntityBuilder + { + void Serialize(uint id, ITypeSafeDictionary dictionary, ISerializationData serializationData, SerializationType serializationType); + + void Deserialize(uint id, ITypeSafeDictionary dictionary, ISerializationData serializationData, SerializationType serializationType); + + void Deserialize(ISerializationData serializationData, in EntityStructInitializer initializer, SerializationType serializationType); + + void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer, in EntityStructInitializer destinationInitializer); + } +} diff --git a/Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs b/Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs new file mode 100644 index 0000000..6d23dd1 --- /dev/null +++ b/Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs @@ -0,0 +1,10 @@ +namespace Svelto.ECS.Serialization +{ + public interface ISerializableEntityDescriptor : IEntityDescriptor + { + uint hash { get; } + ISerializableEntityBuilder[] entitiesToSerialize { get; } + + void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer, in EntityStructInitializer destinationInitializer); + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/ISerializationData.cs b/Svelto.ECS/Serialization/ISerializationData.cs new file mode 100644 index 0000000..852d43a --- /dev/null +++ b/Svelto.ECS/Serialization/ISerializationData.cs @@ -0,0 +1,14 @@ +using Svelto.DataStructures; + +namespace Svelto.ECS +{ + public interface ISerializationData + { + uint dataPos { get; set; } + FasterList data { get; } + + void ReuseAsNew(); + void Reset(); + void BeginNextEntityStruct(); + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/ISerializer.cs b/Svelto.ECS/Serialization/ISerializer.cs new file mode 100644 index 0000000..39fa588 --- /dev/null +++ b/Svelto.ECS/Serialization/ISerializer.cs @@ -0,0 +1,55 @@ +#if DEBUG && !PROFILER +using System; +#endif + +namespace Svelto.ECS.Serialization +{ + public interface ISerializer + where T : unmanaged, IEntityStruct + { + bool Serialize(in T value, ISerializationData serializationData); + bool Deserialize(ref T value, ISerializationData serializationData); + + uint size { get; } + } + + public static class SerializerExt + { + public static bool SerializeSafe(this ISerializer serializer, in T value, ISerializationData serializationData) + where T : unmanaged, IEntityStruct + { +#if DEBUG && !PROFILER + uint posBefore = serializationData.dataPos; +#endif + bool res = serializer.Serialize(value, serializationData); +#if DEBUG && !PROFILER + // size == 0 is a special case when we don't know the size in advance + if (serializer.size != 0 && serializationData.dataPos != posBefore + serializer.size) + { + throw new IndexOutOfRangeException( + $"Size mismatch when serializing {typeof(T).FullName} using {serializer.GetType().FullName}, " + + $"expected offset {posBefore + serializer.size}, got {serializationData.dataPos}"); + } +#endif + return res; + } + + public static bool DeserializeSafe(this ISerializer serializer, ref T value, ISerializationData serializationData) + where T : unmanaged, IEntityStruct + { +#if DEBUG && !PROFILER + uint posBefore = serializationData.dataPos; +#endif + bool res = serializer.Deserialize(ref value, serializationData); +#if DEBUG && !PROFILER + if (serializer.size != 0 && serializationData.dataPos != posBefore + serializer.size) + { + throw new IndexOutOfRangeException( + $"Size mismatch when deserializing {typeof(T).FullName} using {serializer.GetType().FullName}, " + + $"expected offset {posBefore + serializer.size}, got {serializationData.dataPos}"); + } +#endif + return res; + } + } +} diff --git a/Svelto.ECS/Serialization/PartialSerializer.cs b/Svelto.ECS/Serialization/PartialSerializer.cs new file mode 100644 index 0000000..a21348f --- /dev/null +++ b/Svelto.ECS/Serialization/PartialSerializer.cs @@ -0,0 +1,89 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using Svelto.DataStructures; +using Attribute = System.Attribute; + +namespace Svelto.ECS.Serialization +{ + [AttributeUsage(AttributeTargets.Field)] + public class PartialSerializerFieldAttribute : Attribute + {} + + public class PartialSerializer : ISerializer + where T : unmanaged, IEntityStruct + { + static PartialSerializer() + { + Type myType = typeof(T); + FieldInfo[] myMembers = myType.GetFields(); + + for (int i = 0; i < myMembers.Length; i++) + { + Object[] myAttributes = myMembers[i].GetCustomAttributes(true); + for (int j = 0; j < myAttributes.Length; j++) + { + if (myAttributes[j] is PartialSerializerFieldAttribute) + { + if (myMembers[i].FieldType == typeof(EGID)) + throw new ECSException("EGID fields cannot be serialised ".FastConcat(myType.FullName)); + + var offset = Marshal.OffsetOf(myMembers[i].Name); + var sizeOf = (uint)Marshal.SizeOf(myMembers[i].FieldType); + offsets.Add(((uint) offset.ToInt32(), sizeOf)); + totalSize += sizeOf; + } + } + } + + if (myType.GetProperties().Length > (EntityBuilder.HAS_EGID ? 1 : 0)) + throw new ECSException("serializable entity struct must be property less ".FastConcat(myType.FullName)); + } + + public bool Serialize(in T value, ISerializationData serializationData) + { + unsafe + { + fixed (byte* dataptr = serializationData.data.ToArrayFast()) + { + var entityStruct = value; + foreach ((uint offset, uint size) offset in offsets) + { + byte* srcPtr = (byte*) &entityStruct + offset.offset; + //todo move to Unsafe Copy when available as it is faster + Buffer.MemoryCopy(srcPtr, dataptr + serializationData.dataPos, + serializationData.data.Count - serializationData.dataPos, offset.size); + serializationData.dataPos += offset.size; + } + } + } + + return true; + } + + public bool Deserialize(ref T value, ISerializationData serializationData) + { + unsafe + { + T tempValue = value; //todo: temporary solution I want to get rid of this copy + fixed (byte* dataptr = serializationData.data.ToArrayFast()) + foreach ((uint offset, uint size) offset in offsets) + { + byte* dstPtr = (byte*) &tempValue + offset.offset; + //todo move to Unsafe Copy when available as it is faster + Buffer.MemoryCopy(dataptr + serializationData.dataPos, dstPtr, offset.size, offset.size); + serializationData.dataPos += offset.size; + } + + value = tempValue; //todo: temporary solution I want to get rid of this copy + } + + return true; + } + + public uint size => totalSize; + + static readonly FasterList<(uint, uint)> offsets = new FasterList<(uint, uint)>(); + static readonly uint totalSize; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/SerializableEntityBuilder.cs b/Svelto.ECS/Serialization/SerializableEntityBuilder.cs new file mode 100644 index 0000000..b5cdaea --- /dev/null +++ b/Svelto.ECS/Serialization/SerializableEntityBuilder.cs @@ -0,0 +1,94 @@ +using System; +using Svelto.Common; +using Svelto.ECS.Internal; + +namespace Svelto.ECS.Serialization +{ + public class SerializableEntityBuilder : EntityBuilder, ISerializableEntityBuilder + where T : unmanaged, IEntityStruct + { + public static readonly uint SIZE = UnsafeUtils.SizeOf(); + + static SerializableEntityBuilder() + {} + + public SerializableEntityBuilder() + { + _serializers = new ISerializer[(int) SerializationType.Length]; + for (int i = 0; i < (int) SerializationType.Length; i++) + { + _serializers[i] = new DefaultSerializer(); + } + } + + public SerializableEntityBuilder(params ValueTuple>[] serializers) + { + _serializers = new ISerializer[(int) SerializationType.Length]; + for (int i = 0; i < serializers.Length; i++) + { + ref (SerializationType, ISerializer) s = ref serializers[i]; + _serializers[(int) s.Item1] = s.Item2; + } + + // Just in case the above are the same type + for (int i = 0; i < (int) SerializationType.Length; i++) + { + if (_serializers[i] == null) _serializers[i] = new DontSerialize(); + } + } + + public void Serialize(uint entityID, ITypeSafeDictionary dictionary, + ISerializationData serializationData, SerializationType serializationType) + { + ISerializer serializer = _serializers[(int)serializationType]; + + var safeDictionary = (TypeSafeDictionary) dictionary; + if (safeDictionary.TryFindIndex(entityID, out uint index) == false) + { + throw new ECSException("Entity Serialization failed"); + } + + T[] values = safeDictionary.GetValuesArray(out _); + ref T val = ref values[index]; + + serializationData.dataPos = (uint) serializationData.data.Count; + + serializationData.data.ExpandBy(serializer.size); + serializer.SerializeSafe(val, serializationData); + } + + public void Deserialize(uint entityID, ITypeSafeDictionary dictionary, + ISerializationData serializationData, SerializationType serializationType) + { + ISerializer serializer = _serializers[(int) serializationType]; + + // Handle the case when an entity struct is gone + var safeDictionary = (TypeSafeDictionary) dictionary; + if (safeDictionary.TryFindIndex(entityID, out uint index) == false) + { + throw new ECSException("Entity Deserialization failed"); + } + + T[] values = safeDictionary.GetValuesArray(out _); + ref T val = ref values[index]; + + serializer.DeserializeSafe(ref val, serializationData); + } + + public void Deserialize(ISerializationData serializationData + , in EntityStructInitializer initializer, SerializationType serializationType) + { + ISerializer serializer = _serializers[(int) serializationType]; + + serializer.DeserializeSafe(ref initializer.GetOrCreate(), serializationData); + } + + public void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer, + in EntityStructInitializer destinationInitializer) + { + destinationInitializer.CopyFrom(sourceInitializer.Get()); + } + + readonly ISerializer[] _serializers; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/SerializableEntityDescriptor.cs b/Svelto.ECS/Serialization/SerializableEntityDescriptor.cs new file mode 100644 index 0000000..9efb21b --- /dev/null +++ b/Svelto.ECS/Serialization/SerializableEntityDescriptor.cs @@ -0,0 +1,122 @@ +using System; +using System.Reflection; +using System.Text; +using Svelto.DataStructures; +using Svelto.Utilities; + +namespace Svelto.ECS.Serialization +{ + public static class DesignatedHash + { + public static readonly Func Hash = Murmur3.MurmurHash3_x86_32; + } + + public abstract class SerializableEntityDescriptor : ISerializableEntityDescriptor + where TType : IEntityDescriptor, new() + { + static SerializableEntityDescriptor() + { + IEntityBuilder[] defaultEntities = EntityDescriptorTemplate.descriptor.entitiesToBuild; + + var hashNameAttribute = _type.GetCustomAttribute(); + if (hashNameAttribute == null) + { + throw new Exception("HashName attribute not found on the serializable type ".FastConcat(_type.FullName)); + } + + _hash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(hashNameAttribute._name)); + + var (index, dynamicIndex) = SetupSpecialEntityStruct(defaultEntities, out _entitiesToBuild); + if (index == -1) + { + index = _entitiesToBuild.Length - 1; + } + + // Stores the hash of this EntityDescriptor + _entitiesToBuild[index] = new EntityBuilder + ( + new SerializableEntityStruct + { + descriptorHash = _hash + } + ); + + // If the current serializable is an ExtendibleDescriptor, I have to update it. + if (dynamicIndex != -1) + { + _entitiesToBuild[dynamicIndex] = new EntityBuilder + ( + new EntityStructInfoView + { + entitiesToBuild = _entitiesToBuild + } + ); + } + + ///// + var entitiesToSerialize = new FasterList(); + _entitiesToSerializeMap = new FasterDictionary, ISerializableEntityBuilder>(); + foreach (IEntityBuilder e in defaultEntities) + { + if (e is ISerializableEntityBuilder serializableEntityBuilder) + { + var entityType = serializableEntityBuilder.GetEntityType(); + _entitiesToSerializeMap[new RefWrapper(entityType)] = serializableEntityBuilder; + entitiesToSerialize.Add(serializableEntityBuilder); + } + } + + _entitiesToSerialize = entitiesToSerialize.ToArray(); + } + + static (int indexSerial, int indexDynamic) SetupSpecialEntityStruct(IEntityBuilder[] defaultEntities, + out IEntityBuilder[] entitiesToBuild) + { + int length = defaultEntities.Length; + int newLenght = length + 1; + + int indexSerial = -1; + int indexDynamic = -1; + + for (var i = 0; i < length; ++i) + { + if (defaultEntities[i].GetEntityType() == _serializableStructType) + { + indexSerial = i; + --newLenght; + } + + if (defaultEntities[i].GetEntityType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) + { + indexDynamic = i; + } + } + + entitiesToBuild = new IEntityBuilder[newLenght]; + + Array.Copy(defaultEntities, 0, entitiesToBuild, 0, length); + + return (indexSerial, indexDynamic); + } + + public void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer, in EntityStructInitializer destinationInitializer) + { + foreach (ISerializableEntityBuilder e in entitiesToSerialize) + { + e.CopySerializedEntityStructs(sourceInitializer, destinationInitializer); + } + } + + public IEntityBuilder[] entitiesToBuild => _entitiesToBuild; + public uint hash => _hash; + public ISerializableEntityBuilder[] entitiesToSerialize => _entitiesToSerialize; + + static readonly IEntityBuilder[] _entitiesToBuild; + static readonly FasterDictionary, ISerializableEntityBuilder> _entitiesToSerializeMap; + static readonly ISerializableEntityBuilder[] _entitiesToSerialize; + + static readonly uint _hash; + static readonly Type _serializableStructType = typeof(SerializableEntityStruct); + static readonly Type _type = typeof(TType); + } +} diff --git a/Svelto.ECS/Serialization/SerializableEntityStruct.cs b/Svelto.ECS/Serialization/SerializableEntityStruct.cs new file mode 100644 index 0000000..1e10932 --- /dev/null +++ b/Svelto.ECS/Serialization/SerializableEntityStruct.cs @@ -0,0 +1,9 @@ +namespace Svelto.ECS +{ + internal struct SerializableEntityStruct : IEntityStruct, INeedEGID + { + public uint descriptorHash; + + public EGID ID { get; set; } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/SerializationEntityDescriptorTemplate.cs b/Svelto.ECS/Serialization/SerializationEntityDescriptorTemplate.cs new file mode 100644 index 0000000..1b1de3a --- /dev/null +++ b/Svelto.ECS/Serialization/SerializationEntityDescriptorTemplate.cs @@ -0,0 +1,16 @@ +namespace Svelto.ECS.Serialization +{ + public static class SerializationEntityDescriptorTemplate where TType : ISerializableEntityDescriptor, new() + { + static SerializationEntityDescriptorTemplate() + { + var serializableEntityDescriptor = new TType(); + hash = serializableEntityDescriptor.hash; + + entityDescriptor = (ISerializableEntityDescriptor) EntityDescriptorTemplate.descriptor; + } + + public static uint hash { get; } + public static ISerializableEntityDescriptor entityDescriptor { get; } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/SimpleSerializationData.cs b/Svelto.ECS/Serialization/SimpleSerializationData.cs new file mode 100644 index 0000000..71c7d54 --- /dev/null +++ b/Svelto.ECS/Serialization/SimpleSerializationData.cs @@ -0,0 +1,37 @@ +using Svelto.DataStructures; + +namespace Svelto.ECS +{ + public class SimpleSerializationData : ISerializationData + { + public uint dataPos { get; set; } + public FasterList data { get; set; } + + public SimpleSerializationData(FasterList d) + { + data = d; + } + + public void ResetWithNewData(FasterList newData) + { + dataPos = 0; + + data = newData; + } + + public void ReuseAsNew() + { + dataPos = 0; + + data.ResetToReuse(); + } + + public void Reset() + { + dataPos = 0; + } + + public void BeginNextEntityStruct() + {} + } +} \ No newline at end of file diff --git a/Svelto.ECS/Serialization/Unsafe.cs b/Svelto.ECS/Serialization/Unsafe.cs new file mode 100644 index 0000000..dd6ea52 --- /dev/null +++ b/Svelto.ECS/Serialization/Unsafe.cs @@ -0,0 +1,15 @@ +using Svelto.ECS; + +namespace Svelto.Common +{ + public class UnsafeUtils + { + public static uint SizeOf() where T : unmanaged, IEntityStruct + { + unsafe + { + return (uint) sizeof(T); + } + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs b/Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs index d316c5c..7735e1c 100644 --- a/Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs +++ b/Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs @@ -1,17 +1,21 @@ -using Svelto.ECS.Schedulers; -using Svelto.WeakEvents; +using System; +using Svelto.ECS.Schedulers; namespace Svelto.ECS { - //This scheduler shouldn't be used in production and it's meant to be - //used for Unit Tests only + //This scheduler shouldn't be used in production and it's meant to be used for Unit Tests only public class SimpleSubmissionEntityViewScheduler : IEntitySubmissionScheduler { public void SubmitEntities() { - onTick.Invoke(); + _onTick.Invoke(); } - public WeakAction onTick { set; private get; } + EnginesRoot.EntitiesSubmitter IEntitySubmissionScheduler.onTick + { + set => _onTick = value; + } + + EnginesRoot.EntitiesSubmitter _onTick; } } \ No newline at end of file diff --git a/Svelto.ECS/StaticEntityDescriptorInfo.cs b/Svelto.ECS/StaticEntityDescriptorInfo.cs deleted file mode 100644 index 3acc6df..0000000 --- a/Svelto.ECS/StaticEntityDescriptorInfo.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Svelto.ECS -{ - class StaticEntityDescriptorInfo: IEntityDescriptor where TType : IEntityDescriptor - { - internal StaticEntityDescriptorInfo(TType descriptor) - { - entitiesToBuild = descriptor.entitiesToBuild; - } - - public IEntityBuilder[] entitiesToBuild { get; } - } -} - diff --git a/Svelto.ECS/Svelto.ECS.asmdef b/Svelto.ECS/Svelto.ECS.asmdef index 49133e0..d561d80 100644 --- a/Svelto.ECS/Svelto.ECS.asmdef +++ b/Svelto.ECS/Svelto.ECS.asmdef @@ -6,7 +6,7 @@ "optionalUnityReferences": [], "includePlatforms": [], "excludePlatforms": [], - "allowUnsafeCode": false, + "allowUnsafeCode": true, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, diff --git a/Svelto.ECS/Svelto.ECS.csproj b/Svelto.ECS/Svelto.ECS.csproj index d0f78fe..9fdf31b 100644 --- a/Svelto.ECS/Svelto.ECS.csproj +++ b/Svelto.ECS/Svelto.ECS.csproj @@ -7,15 +7,18 @@ AnyCPU false + true AnyCPU false + true + \ No newline at end of file diff --git a/Svelto.ECS/TypeCache.cs b/Svelto.ECS/TypeCache.cs new file mode 100644 index 0000000..bd10908 --- /dev/null +++ b/Svelto.ECS/TypeCache.cs @@ -0,0 +1,9 @@ +using System; + +namespace Svelto.ECS.Internal +{ + public class TypeCache + { + public static Type type = typeof(T); + } +} \ No newline at end of file diff --git a/Svelto.ECS/UnsafeStructRef.cs b/Svelto.ECS/UnsafeStructRef.cs new file mode 100644 index 0000000..7ace92e --- /dev/null +++ b/Svelto.ECS/UnsafeStructRef.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Svelto.ECS +{ + public struct UnsafeStructRef: IDisposable + { + readonly unsafe void* pointer; + GCHandle _handle; + + public UnsafeStructRef(ref T entityStruct, GCHandle handle) + { + unsafe + { + _handle = handle; + pointer = Unsafe.AsPointer(ref entityStruct); + } + } + + public unsafe ref T refvalue => ref Unsafe.AsRef(pointer); + + public void Dispose() + { + _handle.Free(); + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Utilities.cs b/Svelto.ECS/Utilities.cs new file mode 100644 index 0000000..3229dd9 --- /dev/null +++ b/Svelto.ECS/Utilities.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Svelto.ECS +{ + public static class Utilities + { + public static UnsafeStructRef ToUnsafeRef(this T[] entityStructs, uint index) where T : struct, IEntityStruct + { + var alloc = GCHandle.Alloc(entityStructs, GCHandleType.Pinned); + + return new UnsafeStructRef(ref entityStructs[index], alloc); + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/WaitForSubmissionEnumerator.cs b/Svelto.ECS/WaitForSubmissionEnumerator.cs new file mode 100644 index 0000000..d5a1732 --- /dev/null +++ b/Svelto.ECS/WaitForSubmissionEnumerator.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections; + +namespace Svelto.ECS +{ + public class WaitForSubmissionEnumerator : IEnumerator + { + class SubmissionEntityDescriptor : GenericEntityDescriptor + { + internal static readonly ExclusiveGroup SubmissionGroup = new ExclusiveGroup(); + } + + readonly IEntityFactory _entityFactory; + readonly IEntitiesDB _entitiesDB; + readonly IEntityFunctions _entityFunctions; + + int _state; + + public WaitForSubmissionEnumerator(IEntityFunctions entityFunctions, IEntityFactory entityFactory, + IEntitiesDB entitiesDb) + { + _entityFactory = entityFactory; + _entityFunctions = entityFunctions; + _entitiesDB = entitiesDb; + } + + public bool MoveNext() + { + switch (_state) + { + case 0: + _counter = _COUNTER++; + _entityFactory.BuildEntity(new EGID((uint) _counter, + SubmissionEntityDescriptor.SubmissionGroup)); + _state = 1; + return true; + case 1: + if (_entitiesDB.Exists(new EGID((uint) _counter, + SubmissionEntityDescriptor.SubmissionGroup)) == false) + return true; + + _entityFunctions.RemoveEntity(new EGID((uint) _counter, + SubmissionEntityDescriptor.SubmissionGroup)); + _state = 0; + return false; + } + + throw new Exception("something is wrong"); + } + + void IEnumerator.Reset() + { + throw new NotImplementedException(); + } + + public object Current { get; } + + struct SubmissionSignalStruct : IEntityStruct + {} + + int _counter; + static int _COUNTER; + } +} \ No newline at end of file diff --git a/Svelto.ECS/bin/Debug/netstandard2.0/Svelto.ECS.deps.json b/Svelto.ECS/bin/Debug/netstandard2.0/Svelto.ECS.deps.json new file mode 100644 index 0000000..ff5c143 --- /dev/null +++ b/Svelto.ECS/bin/Debug/netstandard2.0/Svelto.ECS.deps.json @@ -0,0 +1,128 @@ +{ + "runtimeTarget": { + "name": ".NETStandard,Version=v2.0/", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETStandard,Version=v2.0": {}, + ".NETStandard,Version=v2.0/": { + "Svelto.ECS/1.0.0": { + "dependencies": { + "NETStandard.Library": "2.0.3", + "Svelto.Common": "1.0.0", + "System.Memory": "4.5.2", + "System.Runtime.CompilerServices.Unsafe": "4.6.0-preview8.19405.3" + }, + "runtime": { + "Svelto.ECS.dll": {} + } + }, + "Microsoft.NETCore.Platforms/1.1.0": {}, + "NETStandard.Library/2.0.3": { + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "System.Buffers/4.4.0": { + "runtime": { + "lib/netstandard2.0/System.Buffers.dll": { + "assemblyVersion": "4.0.2.0", + "fileVersion": "4.6.25519.3" + } + } + }, + "System.Memory/4.5.2": { + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.6.0-preview8.19405.3" + }, + "runtime": { + "lib/netstandard2.0/System.Memory.dll": { + "assemblyVersion": "4.0.1.0", + "fileVersion": "4.6.27129.4" + } + } + }, + "System.Numerics.Vectors/4.4.0": { + "runtime": { + "lib/netstandard2.0/System.Numerics.Vectors.dll": { + "assemblyVersion": "4.1.3.0", + "fileVersion": "4.6.25519.3" + } + } + }, + "System.Runtime.CompilerServices.Unsafe/4.6.0-preview8.19405.3": { + "runtime": { + "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": { + "assemblyVersion": "4.0.5.0", + "fileVersion": "4.700.19.40503" + } + } + }, + "Svelto.Common/1.0.0": { + "dependencies": { + "System.Memory": "4.5.2" + }, + "runtime": { + "Svelto.Common.dll": {} + } + } + } + }, + "libraries": { + "Svelto.ECS/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Microsoft.NETCore.Platforms/1.1.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==", + "path": "microsoft.netcore.platforms/1.1.0", + "hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512" + }, + "NETStandard.Library/2.0.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-548M6mnBSJWxsIlkQHfbzoYxpiYFXZZSL00p4GHYv8PkiqFBnnT68mW5mGEsA/ch9fDO9GkPgkFQpWiXZN7mAQ==", + "path": "netstandard.library/2.0.3", + "hashPath": "netstandard.library.2.0.3.nupkg.sha512" + }, + "System.Buffers/4.4.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Ii2bedd4HVzddupdU35n3ygohUPlNn7MDimBOYcwWNce2NizQ1fCSaQJY1Tzv80aMqOGpVcU4wZr/Xe50xcTwg==", + "path": "system.buffers/4.4.0", + "hashPath": "system.buffers.4.4.0.nupkg.sha512" + }, + "System.Memory/4.5.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Ohf9h6em5CjPaAzupGtW/TWMzX6aVfIGPh7bq5KQmfvYJr+DqvOa0cRS9ggFKGIuvKTModCQEacGC/CwE8j7cg==", + "path": "system.memory/4.5.2", + "hashPath": "system.memory.4.5.2.nupkg.sha512" + }, + "System.Numerics.Vectors/4.4.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-gdRrUJs1RrjW3JB5p82hYjA67xoeFLvh0SdSIWjTiN8qExlbFt/RtXwVYNc5BukJ/f9OKzQQS6gakzbJeHTqHg==", + "path": "system.numerics.vectors/4.4.0", + "hashPath": "system.numerics.vectors.4.4.0.nupkg.sha512" + }, + "System.Runtime.CompilerServices.Unsafe/4.6.0-preview8.19405.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-1EHM+Tp0/RlDu4cQpYl/+OsikV6DqVK9WXLZenKA0jNVgAvmKG4eD7czEIDR1w3IQjqywhIdIrqm1C4wJutVnA==", + "path": "system.runtime.compilerservices.unsafe/4.6.0-preview8.19405.3", + "hashPath": "system.runtime.compilerservices.unsafe.4.6.0-preview8.19405.3.nupkg.sha512" + }, + "Svelto.Common/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.AssemblyInfo.cs b/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.AssemblyInfo.cs new file mode 100644 index 0000000..45755d3 --- /dev/null +++ b/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.AssemblyInfo.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; +using System.Reflection; + +[assembly: System.Reflection.AssemblyCompanyAttribute("Svelto.ECS")] +[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] +[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] +[assembly: System.Reflection.AssemblyProductAttribute("Svelto.ECS")] +[assembly: System.Reflection.AssemblyTitleAttribute("Svelto.ECS")] +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] + +// Generated by the MSBuild WriteCodeFragment class. + diff --git a/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.CopyComplete b/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.CopyComplete new file mode 100644 index 0000000..e69de29 diff --git a/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.FileListAbsolute.txt b/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..aad0fa2 --- /dev/null +++ b/Svelto.ECS/obj/Debug/netstandard2.0/Svelto.ECS.csproj.FileListAbsolute.txt @@ -0,0 +1,23 @@ +E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.ECS.deps.json +E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.ECS.dll +E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.ECS.pdb +E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.Common.dll +E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.Common.pdb +E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.csprojAssemblyReference.cache +E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.csproj.CoreCompileInputs.cache +E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.AssemblyInfoInputs.cache +E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.AssemblyInfo.cs +E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.csproj.CopyComplete +E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.dll +E:\Prgdir\unity\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.pdb +E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.ECS.deps.json +E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.ECS.dll +E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.ECS.pdb +E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.Common.dll +E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\bin\Debug\netstandard2.0\Svelto.Common.pdb +E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.csprojAssemblyReference.cache +E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.AssemblyInfoInputs.cache +E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.AssemblyInfo.cs +E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.csproj.CopyComplete +E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.dll +E:\Archive\Prgdir\Svelto\Svelto.ECS\Svelto.ECS.Tests\Svelto.ECS\obj\Debug\netstandard2.0\Svelto.ECS.pdb diff --git a/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.dgspec.json b/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.dgspec.json new file mode 100644 index 0000000..a446841 --- /dev/null +++ b/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.dgspec.json @@ -0,0 +1,142 @@ +{ + "format": 1, + "restore": { + "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\Svelto.ECS.csproj": {} + }, + "projects": { + "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj", + "projectName": "Svelto.Common", + "projectPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj", + "packagesPath": "C:\\Users\\Seb-e-Sam\\.nuget\\packages\\", + "outputPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder" + ], + "configFilePaths": [ + "C:\\Users\\Seb-e-Sam\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "netstandard2.0" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "C:\\Users\\Seb-e-Sam\\AppData\\Local\\Xenko\\NugetDev": {}, + "https://api.nuget.org/v3/index.json": {}, + "https://packages.xenko.com/nuget": {} + }, + "frameworks": { + "netstandard2.0": { + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + } + }, + "frameworks": { + "netstandard2.0": { + "dependencies": { + "NETStandard.Library": { + "suppressParent": "All", + "target": "Package", + "version": "[2.0.3, )", + "autoReferenced": true + }, + "System.Memory": { + "target": "Package", + "version": "[4.5.2, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48" + ], + "assetTargetFallback": true, + "warn": true + } + } + }, + "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\Svelto.ECS.csproj": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\Svelto.ECS.csproj", + "projectName": "Svelto.ECS", + "projectPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\Svelto.ECS.csproj", + "packagesPath": "C:\\Users\\Seb-e-Sam\\.nuget\\packages\\", + "outputPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder" + ], + "configFilePaths": [ + "C:\\Users\\Seb-e-Sam\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "netstandard2.0" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "C:\\Users\\Seb-e-Sam\\AppData\\Local\\Xenko\\NugetDev": {}, + "https://api.nuget.org/v3/index.json": {}, + "https://packages.xenko.com/nuget": {} + }, + "frameworks": { + "netstandard2.0": { + "projectReferences": { + "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj": { + "projectPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj" + } + } + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + } + }, + "frameworks": { + "netstandard2.0": { + "dependencies": { + "NETStandard.Library": { + "suppressParent": "All", + "target": "Package", + "version": "[2.0.3, )", + "autoReferenced": true + }, + "System.Memory": { + "target": "Package", + "version": "[4.5.2, )" + }, + "System.Runtime.CompilerServices.Unsafe": { + "target": "Package", + "version": "[4.6.0-preview8.19405.3, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48" + ], + "assetTargetFallback": true, + "warn": true + } + } + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.props b/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.props new file mode 100644 index 0000000..7539763 --- /dev/null +++ b/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.props @@ -0,0 +1,15 @@ + + + + True + NuGet + $(MSBuildThisFileDirectory)project.assets.json + $(UserProfile)\.nuget\packages\ + C:\Users\Seb-e-Sam\.nuget\packages\;C:\Program Files\dotnet\sdk\NuGetFallbackFolder + PackageReference + 5.3.0 + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + \ No newline at end of file diff --git a/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.targets b/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.targets new file mode 100644 index 0000000..8f2d2d6 --- /dev/null +++ b/Svelto.ECS/obj/Svelto.ECS.csproj.nuget.g.targets @@ -0,0 +1,9 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + \ No newline at end of file diff --git a/Svelto.ECS/obj/project.assets.json b/Svelto.ECS/obj/project.assets.json new file mode 100644 index 0000000..1d27c5b --- /dev/null +++ b/Svelto.ECS/obj/project.assets.json @@ -0,0 +1,432 @@ +{ + "version": 3, + "targets": { + ".NETStandard,Version=v2.0": { + "Microsoft.NETCore.Platforms/1.1.0": { + "type": "package", + "compile": { + "lib/netstandard1.0/_._": {} + }, + "runtime": { + "lib/netstandard1.0/_._": {} + } + }, + "NETStandard.Library/2.0.3": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + }, + "compile": { + "lib/netstandard1.0/_._": {} + }, + "runtime": { + "lib/netstandard1.0/_._": {} + }, + "build": { + "build/netstandard2.0/NETStandard.Library.targets": {} + } + }, + "System.Buffers/4.4.0": { + "type": "package", + "compile": { + "ref/netstandard2.0/System.Buffers.dll": {} + }, + "runtime": { + "lib/netstandard2.0/System.Buffers.dll": {} + } + }, + "System.Memory/4.5.2": { + "type": "package", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + }, + "compile": { + "lib/netstandard2.0/System.Memory.dll": {} + }, + "runtime": { + "lib/netstandard2.0/System.Memory.dll": {} + } + }, + "System.Numerics.Vectors/4.4.0": { + "type": "package", + "compile": { + "ref/netstandard2.0/System.Numerics.Vectors.dll": {} + }, + "runtime": { + "lib/netstandard2.0/System.Numerics.Vectors.dll": {} + } + }, + "System.Runtime.CompilerServices.Unsafe/4.6.0-preview8.19405.3": { + "type": "package", + "compile": { + "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": {} + }, + "runtime": { + "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": {} + } + }, + "Svelto.Common/1.0.0": { + "type": "project", + "framework": ".NETStandard,Version=v2.0", + "dependencies": { + "System.Memory": "4.5.2" + }, + "compile": { + "bin/placeholder/Svelto.Common.dll": {} + }, + "runtime": { + "bin/placeholder/Svelto.Common.dll": {} + } + } + } + }, + "libraries": { + "Microsoft.NETCore.Platforms/1.1.0": { + "sha512": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==", + "type": "package", + "path": "microsoft.netcore.platforms/1.1.0", + "files": [ + ".nupkg.metadata", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/netstandard1.0/_._", + "microsoft.netcore.platforms.1.1.0.nupkg.sha512", + "microsoft.netcore.platforms.nuspec", + "runtime.json" + ] + }, + "NETStandard.Library/2.0.3": { + "sha512": "548M6mnBSJWxsIlkQHfbzoYxpiYFXZZSL00p4GHYv8PkiqFBnnT68mW5mGEsA/ch9fDO9GkPgkFQpWiXZN7mAQ==", + "type": "package", + "path": "netstandard.library/2.0.3", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "build/netstandard2.0/NETStandard.Library.targets", + "build/netstandard2.0/ref/Microsoft.Win32.Primitives.dll", + "build/netstandard2.0/ref/System.AppContext.dll", + "build/netstandard2.0/ref/System.Collections.Concurrent.dll", + "build/netstandard2.0/ref/System.Collections.NonGeneric.dll", + "build/netstandard2.0/ref/System.Collections.Specialized.dll", + "build/netstandard2.0/ref/System.Collections.dll", + "build/netstandard2.0/ref/System.ComponentModel.Composition.dll", + "build/netstandard2.0/ref/System.ComponentModel.EventBasedAsync.dll", + "build/netstandard2.0/ref/System.ComponentModel.Primitives.dll", + "build/netstandard2.0/ref/System.ComponentModel.TypeConverter.dll", + "build/netstandard2.0/ref/System.ComponentModel.dll", + "build/netstandard2.0/ref/System.Console.dll", + "build/netstandard2.0/ref/System.Core.dll", + "build/netstandard2.0/ref/System.Data.Common.dll", + "build/netstandard2.0/ref/System.Data.dll", + "build/netstandard2.0/ref/System.Diagnostics.Contracts.dll", + "build/netstandard2.0/ref/System.Diagnostics.Debug.dll", + "build/netstandard2.0/ref/System.Diagnostics.FileVersionInfo.dll", + "build/netstandard2.0/ref/System.Diagnostics.Process.dll", + "build/netstandard2.0/ref/System.Diagnostics.StackTrace.dll", + "build/netstandard2.0/ref/System.Diagnostics.TextWriterTraceListener.dll", + "build/netstandard2.0/ref/System.Diagnostics.Tools.dll", + "build/netstandard2.0/ref/System.Diagnostics.TraceSource.dll", + "build/netstandard2.0/ref/System.Diagnostics.Tracing.dll", + "build/netstandard2.0/ref/System.Drawing.Primitives.dll", + "build/netstandard2.0/ref/System.Drawing.dll", + "build/netstandard2.0/ref/System.Dynamic.Runtime.dll", + "build/netstandard2.0/ref/System.Globalization.Calendars.dll", + "build/netstandard2.0/ref/System.Globalization.Extensions.dll", + "build/netstandard2.0/ref/System.Globalization.dll", + "build/netstandard2.0/ref/System.IO.Compression.FileSystem.dll", + "build/netstandard2.0/ref/System.IO.Compression.ZipFile.dll", + "build/netstandard2.0/ref/System.IO.Compression.dll", + "build/netstandard2.0/ref/System.IO.FileSystem.DriveInfo.dll", + "build/netstandard2.0/ref/System.IO.FileSystem.Primitives.dll", + "build/netstandard2.0/ref/System.IO.FileSystem.Watcher.dll", + "build/netstandard2.0/ref/System.IO.FileSystem.dll", + "build/netstandard2.0/ref/System.IO.IsolatedStorage.dll", + "build/netstandard2.0/ref/System.IO.MemoryMappedFiles.dll", + "build/netstandard2.0/ref/System.IO.Pipes.dll", + "build/netstandard2.0/ref/System.IO.UnmanagedMemoryStream.dll", + "build/netstandard2.0/ref/System.IO.dll", + "build/netstandard2.0/ref/System.Linq.Expressions.dll", + "build/netstandard2.0/ref/System.Linq.Parallel.dll", + "build/netstandard2.0/ref/System.Linq.Queryable.dll", + "build/netstandard2.0/ref/System.Linq.dll", + "build/netstandard2.0/ref/System.Net.Http.dll", + "build/netstandard2.0/ref/System.Net.NameResolution.dll", + "build/netstandard2.0/ref/System.Net.NetworkInformation.dll", + "build/netstandard2.0/ref/System.Net.Ping.dll", + "build/netstandard2.0/ref/System.Net.Primitives.dll", + "build/netstandard2.0/ref/System.Net.Requests.dll", + "build/netstandard2.0/ref/System.Net.Security.dll", + "build/netstandard2.0/ref/System.Net.Sockets.dll", + "build/netstandard2.0/ref/System.Net.WebHeaderCollection.dll", + "build/netstandard2.0/ref/System.Net.WebSockets.Client.dll", + "build/netstandard2.0/ref/System.Net.WebSockets.dll", + "build/netstandard2.0/ref/System.Net.dll", + "build/netstandard2.0/ref/System.Numerics.dll", + "build/netstandard2.0/ref/System.ObjectModel.dll", + "build/netstandard2.0/ref/System.Reflection.Extensions.dll", + "build/netstandard2.0/ref/System.Reflection.Primitives.dll", + "build/netstandard2.0/ref/System.Reflection.dll", + "build/netstandard2.0/ref/System.Resources.Reader.dll", + "build/netstandard2.0/ref/System.Resources.ResourceManager.dll", + "build/netstandard2.0/ref/System.Resources.Writer.dll", + "build/netstandard2.0/ref/System.Runtime.CompilerServices.VisualC.dll", + "build/netstandard2.0/ref/System.Runtime.Extensions.dll", + "build/netstandard2.0/ref/System.Runtime.Handles.dll", + "build/netstandard2.0/ref/System.Runtime.InteropServices.RuntimeInformation.dll", + "build/netstandard2.0/ref/System.Runtime.InteropServices.dll", + "build/netstandard2.0/ref/System.Runtime.Numerics.dll", + "build/netstandard2.0/ref/System.Runtime.Serialization.Formatters.dll", + "build/netstandard2.0/ref/System.Runtime.Serialization.Json.dll", + "build/netstandard2.0/ref/System.Runtime.Serialization.Primitives.dll", + "build/netstandard2.0/ref/System.Runtime.Serialization.Xml.dll", + "build/netstandard2.0/ref/System.Runtime.Serialization.dll", + "build/netstandard2.0/ref/System.Runtime.dll", + "build/netstandard2.0/ref/System.Security.Claims.dll", + "build/netstandard2.0/ref/System.Security.Cryptography.Algorithms.dll", + "build/netstandard2.0/ref/System.Security.Cryptography.Csp.dll", + "build/netstandard2.0/ref/System.Security.Cryptography.Encoding.dll", + "build/netstandard2.0/ref/System.Security.Cryptography.Primitives.dll", + "build/netstandard2.0/ref/System.Security.Cryptography.X509Certificates.dll", + "build/netstandard2.0/ref/System.Security.Principal.dll", + "build/netstandard2.0/ref/System.Security.SecureString.dll", + "build/netstandard2.0/ref/System.ServiceModel.Web.dll", + "build/netstandard2.0/ref/System.Text.Encoding.Extensions.dll", + "build/netstandard2.0/ref/System.Text.Encoding.dll", + "build/netstandard2.0/ref/System.Text.RegularExpressions.dll", + "build/netstandard2.0/ref/System.Threading.Overlapped.dll", + "build/netstandard2.0/ref/System.Threading.Tasks.Parallel.dll", + "build/netstandard2.0/ref/System.Threading.Tasks.dll", + "build/netstandard2.0/ref/System.Threading.Thread.dll", + "build/netstandard2.0/ref/System.Threading.ThreadPool.dll", + "build/netstandard2.0/ref/System.Threading.Timer.dll", + "build/netstandard2.0/ref/System.Threading.dll", + "build/netstandard2.0/ref/System.Transactions.dll", + "build/netstandard2.0/ref/System.ValueTuple.dll", + "build/netstandard2.0/ref/System.Web.dll", + "build/netstandard2.0/ref/System.Windows.dll", + "build/netstandard2.0/ref/System.Xml.Linq.dll", + "build/netstandard2.0/ref/System.Xml.ReaderWriter.dll", + "build/netstandard2.0/ref/System.Xml.Serialization.dll", + "build/netstandard2.0/ref/System.Xml.XDocument.dll", + "build/netstandard2.0/ref/System.Xml.XPath.XDocument.dll", + "build/netstandard2.0/ref/System.Xml.XPath.dll", + "build/netstandard2.0/ref/System.Xml.XmlDocument.dll", + "build/netstandard2.0/ref/System.Xml.XmlSerializer.dll", + "build/netstandard2.0/ref/System.Xml.dll", + "build/netstandard2.0/ref/System.dll", + "build/netstandard2.0/ref/mscorlib.dll", + "build/netstandard2.0/ref/netstandard.dll", + "build/netstandard2.0/ref/netstandard.xml", + "lib/netstandard1.0/_._", + "netstandard.library.2.0.3.nupkg.sha512", + "netstandard.library.nuspec" + ] + }, + "System.Buffers/4.4.0": { + "sha512": "Ii2bedd4HVzddupdU35n3ygohUPlNn7MDimBOYcwWNce2NizQ1fCSaQJY1Tzv80aMqOGpVcU4wZr/Xe50xcTwg==", + "type": "package", + "path": "system.buffers/4.4.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/netcoreapp2.0/_._", + "lib/netstandard1.1/System.Buffers.dll", + "lib/netstandard1.1/System.Buffers.xml", + "lib/netstandard2.0/System.Buffers.dll", + "lib/netstandard2.0/System.Buffers.xml", + "ref/netcoreapp2.0/_._", + "ref/netstandard1.1/System.Buffers.dll", + "ref/netstandard1.1/System.Buffers.xml", + "ref/netstandard2.0/System.Buffers.dll", + "ref/netstandard2.0/System.Buffers.xml", + "system.buffers.4.4.0.nupkg.sha512", + "system.buffers.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "System.Memory/4.5.2": { + "sha512": "Ohf9h6em5CjPaAzupGtW/TWMzX6aVfIGPh7bq5KQmfvYJr+DqvOa0cRS9ggFKGIuvKTModCQEacGC/CwE8j7cg==", + "type": "package", + "path": "system.memory/4.5.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/netcoreapp2.1/_._", + "lib/netstandard1.1/System.Memory.dll", + "lib/netstandard1.1/System.Memory.xml", + "lib/netstandard2.0/System.Memory.dll", + "lib/netstandard2.0/System.Memory.xml", + "ref/netcoreapp2.1/_._", + "system.memory.4.5.2.nupkg.sha512", + "system.memory.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "System.Numerics.Vectors/4.4.0": { + "sha512": "gdRrUJs1RrjW3JB5p82hYjA67xoeFLvh0SdSIWjTiN8qExlbFt/RtXwVYNc5BukJ/f9OKzQQS6gakzbJeHTqHg==", + "type": "package", + "path": "system.numerics.vectors/4.4.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.Numerics.Vectors.dll", + "lib/net46/System.Numerics.Vectors.xml", + "lib/netcoreapp2.0/_._", + "lib/netstandard1.0/System.Numerics.Vectors.dll", + "lib/netstandard1.0/System.Numerics.Vectors.xml", + "lib/netstandard2.0/System.Numerics.Vectors.dll", + "lib/netstandard2.0/System.Numerics.Vectors.xml", + "lib/portable-net45+win8+wp8+wpa81/System.Numerics.Vectors.dll", + "lib/portable-net45+win8+wp8+wpa81/System.Numerics.Vectors.xml", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.Numerics.Vectors.dll", + "ref/net46/System.Numerics.Vectors.xml", + "ref/netcoreapp2.0/_._", + "ref/netstandard1.0/System.Numerics.Vectors.dll", + "ref/netstandard1.0/System.Numerics.Vectors.xml", + "ref/netstandard2.0/System.Numerics.Vectors.dll", + "ref/netstandard2.0/System.Numerics.Vectors.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.numerics.vectors.4.4.0.nupkg.sha512", + "system.numerics.vectors.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "System.Runtime.CompilerServices.Unsafe/4.6.0-preview8.19405.3": { + "sha512": "1EHM+Tp0/RlDu4cQpYl/+OsikV6DqVK9WXLZenKA0jNVgAvmKG4eD7czEIDR1w3IQjqywhIdIrqm1C4wJutVnA==", + "type": "package", + "path": "system.runtime.compilerservices.unsafe/4.6.0-preview8.19405.3", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll", + "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.xml", + "lib/netstandard1.0/System.Runtime.CompilerServices.Unsafe.dll", + "lib/netstandard1.0/System.Runtime.CompilerServices.Unsafe.xml", + "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll", + "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml", + "ref/netstandard1.0/System.Runtime.CompilerServices.Unsafe.dll", + "ref/netstandard1.0/System.Runtime.CompilerServices.Unsafe.xml", + "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll", + "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml", + "system.runtime.compilerservices.unsafe.4.6.0-preview8.19405.3.nupkg.sha512", + "system.runtime.compilerservices.unsafe.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "Svelto.Common/1.0.0": { + "type": "project", + "path": "../Svelto.Common/Svelto.Common.csproj", + "msbuildProject": "../Svelto.Common/Svelto.Common.csproj" + } + }, + "projectFileDependencyGroups": { + ".NETStandard,Version=v2.0": [ + "NETStandard.Library >= 2.0.3", + "Svelto.Common >= 1.0.0", + "System.Memory >= 4.5.2", + "System.Runtime.CompilerServices.Unsafe >= 4.6.0-preview8.19405.3" + ] + }, + "packageFolders": { + "C:\\Users\\Seb-e-Sam\\.nuget\\packages\\": {}, + "C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder": {} + }, + "project": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\Svelto.ECS.csproj", + "projectName": "Svelto.ECS", + "projectPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\Svelto.ECS.csproj", + "packagesPath": "C:\\Users\\Seb-e-Sam\\.nuget\\packages\\", + "outputPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder" + ], + "configFilePaths": [ + "C:\\Users\\Seb-e-Sam\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "netstandard2.0" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "C:\\Users\\Seb-e-Sam\\AppData\\Local\\Xenko\\NugetDev": {}, + "https://api.nuget.org/v3/index.json": {}, + "https://packages.xenko.com/nuget": {} + }, + "frameworks": { + "netstandard2.0": { + "projectReferences": { + "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj": { + "projectPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj" + } + } + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + } + }, + "frameworks": { + "netstandard2.0": { + "dependencies": { + "NETStandard.Library": { + "suppressParent": "All", + "target": "Package", + "version": "[2.0.3, )", + "autoReferenced": true + }, + "System.Memory": { + "target": "Package", + "version": "[4.5.2, )" + }, + "System.Runtime.CompilerServices.Unsafe": { + "target": "Package", + "version": "[4.6.0-preview8.19405.3, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48" + ], + "assetTargetFallback": true, + "warn": true + } + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/obj/project.packagespec.json b/Svelto.ECS/obj/project.packagespec.json new file mode 100644 index 0000000..d048127 --- /dev/null +++ b/Svelto.ECS/obj/project.packagespec.json @@ -0,0 +1,66 @@ +{ + "version": "1.0.0", + "restore": { + "projectUniqueName": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\Svelto.ECS.csproj", + "projectName": "Svelto.ECS", + "projectPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\Svelto.ECS.csproj", + "outputPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.ECS\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder" + ], + "originalTargetFrameworks": [ + "netstandard2.0" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "C:\\Users\\Seb-e-Sam\\AppData\\Local\\Xenko\\NugetDev": {}, + "https://api.nuget.org/v3/index.json": {}, + "https://packages.xenko.com/nuget": {} + }, + "frameworks": { + "netstandard2.0": { + "projectReferences": { + "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj": { + "projectPath": "E:\\Archive\\Prgdir\\Svelto\\Svelto.ECS\\Svelto.ECS.Tests\\Svelto.Common\\Svelto.Common.csproj" + } + } + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + } + }, + "frameworks": { + "netstandard2.0": { + "dependencies": { + "NETStandard.Library": { + "suppressParent": "All", + "target": "Package", + "version": "[2.0.3, )", + "autoReferenced": true + }, + "System.Memory": { + "target": "Package", + "version": "[4.5.2, )" + }, + "System.Runtime.CompilerServices.Unsafe": { + "target": "Package", + "version": "[4.6.0-preview8.19405.3, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48" + ], + "assetTargetFallback": true, + "warn": true + } + } +} \ No newline at end of file diff --git a/Svelto.Services/Experimental/IServiceEventContainer.cs b/Svelto.Services/Experimental/IServiceEventContainer.cs new file mode 100644 index 0000000..cca960d --- /dev/null +++ b/Svelto.Services/Experimental/IServiceEventContainer.cs @@ -0,0 +1,11 @@ +using System; + +namespace Svelto.ServiceLayer.Experimental +{ + public interface IServiceEventContainer : IDisposable + { + //Delegate constraints to store delegates without needing a signature + void ListenTo(TDelegate callBack) + where TListener : class, IServiceEventListener where TDelegate : Delegate; + } +} diff --git a/Svelto.Services/Experimental/IServiceEventContainerFactory.cs b/Svelto.Services/Experimental/IServiceEventContainerFactory.cs new file mode 100644 index 0000000..801263e --- /dev/null +++ b/Svelto.Services/Experimental/IServiceEventContainerFactory.cs @@ -0,0 +1,8 @@ + +namespace Svelto.ServiceLayer.Experimental +{ + interface IServiceEventContainerFactory + { + IServiceEventContainer Create(); + } +} diff --git a/Svelto.Services/Experimental/IServiceEventListener.cs b/Svelto.Services/Experimental/IServiceEventListener.cs new file mode 100644 index 0000000..694da39 --- /dev/null +++ b/Svelto.Services/Experimental/IServiceEventListener.cs @@ -0,0 +1,14 @@ +using System; + +namespace Svelto.ServiceLayer.Experimental +{ + public interface IServiceEventListener : IServiceEventListenerBase where TDelegate : Delegate + { + void SetCallback(TDelegate callback); + } + + // This interface exists so we can use one type which can represent any of the interfaces above + public interface IServiceEventListenerBase : IDisposable + { + } +} diff --git a/Svelto.Services/Experimental/ServiceEventContainer.cs b/Svelto.Services/Experimental/ServiceEventContainer.cs new file mode 100644 index 0000000..29e3423 --- /dev/null +++ b/Svelto.Services/Experimental/ServiceEventContainer.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; + +namespace Svelto.ServiceLayer.Experimental +{ + public abstract class ServiceEventContainer : IServiceEventContainer + { + public void Dispose() + { + foreach (var listener in _listeners) + { + listener.Dispose(); + } + + _listeners.Clear(); + } + + protected ServiceEventContainer() + { + //call all the AddRelation in the implementation if you wish + } + + public void ListenTo(TDelegate callBack) + where TListener : class, IServiceEventListener where TDelegate : Delegate + { + var concreteType = _registeredTypes[typeof(TListener)]; + var listener = (TListener)Activator.CreateInstance(concreteType); + listener.SetCallback(callBack); + _listeners.Add(listener); + } + + protected void AddRelation() where TInterface : IServiceEventListenerBase + where TConcrete : TInterface + { + _registeredTypes.Add(typeof(TInterface), typeof(TConcrete)); + } + + readonly List _listeners = new List(); + readonly Dictionary _registeredTypes = new Dictionary(); + } +} \ No newline at end of file diff --git a/Svelto.Services/IServiceRequest.cs b/Svelto.Services/IServiceRequest.cs new file mode 100644 index 0000000..23b7bd9 --- /dev/null +++ b/Svelto.Services/IServiceRequest.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using Svelto.Tasks; + +namespace Svelto.ServiceLayer +{ + public interface IServiceRequest + { + IEnumerator Execute(); + } + + public interface IServiceRequest: IServiceRequest + { + IServiceRequest Inject(TDependency registerData); + } +} diff --git a/Svelto.Services/IServiceRequestsFactory.cs b/Svelto.Services/IServiceRequestsFactory.cs new file mode 100644 index 0000000..affca17 --- /dev/null +++ b/Svelto.Services/IServiceRequestsFactory.cs @@ -0,0 +1,8 @@ +namespace Svelto.ServiceLayer +{ + public interface IServiceRequestsFactory + { + RequestInterface Create() where RequestInterface:class, IServiceRequest; + } +} + diff --git a/Svelto.Services/ServiceRequestFactoryArgumentException.cs b/Svelto.Services/ServiceRequestFactoryArgumentException.cs new file mode 100644 index 0000000..a304d5f --- /dev/null +++ b/Svelto.Services/ServiceRequestFactoryArgumentException.cs @@ -0,0 +1,10 @@ +using System; + +namespace Svelto.ServiceLayer +{ + public class ServiceRequestFactoryArgumentException: ArgumentException + { + public ServiceRequestFactoryArgumentException(string message):base(message) + {} + } +} \ No newline at end of file diff --git a/Svelto.Services/ServiceRequestsFactory.cs b/Svelto.Services/ServiceRequestsFactory.cs new file mode 100644 index 0000000..e29b1e0 --- /dev/null +++ b/Svelto.Services/ServiceRequestsFactory.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; + +namespace Svelto.ServiceLayer +{ + public abstract class ServiceRequestsFactory : IServiceRequestsFactory + { + public RequestInterface Create() where RequestInterface : class, IServiceRequest + { + var ret = RetrieveObjectType(); + + return ret.CreateInstance() as RequestInterface; + } + + protected void AddRelation() where RequestClass : class, RequestInterface, new() + where RequestInterface : IServiceRequest + + { + _requestMap[typeof(RequestInterface)] = new Value(); + } + + IHoldValue RetrieveObjectType() + { + if (_requestMap.ContainsKey(typeof(RequestInterface)) == false) + throw new ServiceRequestFactoryArgumentException("Request not registered"); + + var ret = _requestMap[typeof(RequestInterface)]; + + if (ret == null) + throw new ServiceRequestFactoryArgumentException("Request not found"); + + return ret; + } + + readonly Dictionary _requestMap = new Dictionary(); + + interface IHoldValue + { + IServiceRequest CreateInstance(); + } + + class Value : IHoldValue where RequestClass : class, IServiceRequest, new() + { + public IServiceRequest CreateInstance() + { + return new RequestClass(); + } + } + } +} diff --git a/Svelto.Services/Svelto.Services.asmdef b/Svelto.Services/Svelto.Services.asmdef new file mode 100644 index 0000000..ab050d2 --- /dev/null +++ b/Svelto.Services/Svelto.Services.asmdef @@ -0,0 +1,17 @@ +{ + "name": "Svelto.Services", + "references": [ + "Svelto.Common", + "Svelto.Tasks", + "Utf8Json" + ], + "optionalUnityReferences": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [] +} \ No newline at end of file diff --git a/Svelto.Services/Unity/Web/IResponseHandler.cs b/Svelto.Services/Unity/Web/IResponseHandler.cs new file mode 100644 index 0000000..f8250b0 --- /dev/null +++ b/Svelto.Services/Unity/Web/IResponseHandler.cs @@ -0,0 +1,19 @@ +namespace Svelto.Services +{ + public interface IResponseHandler : IResponseHandler + { + ResponseType response { get; } + } + + public interface IResponseHandler + { + // Called once per frame when data has been received from the network. + bool ReceiveData(byte[] data, int dataLength); + + // Called when all data has been received from the server and delivered via ReceiveData. + void CompleteContent(); + + // Called when a Content-Length header is received from the server. + void ReceiveContentLength(int contentLength); + } +} \ No newline at end of file diff --git a/Svelto.Services/Unity/Web/StandardWebRequest.cs b/Svelto.Services/Unity/Web/StandardWebRequest.cs new file mode 100644 index 0000000..e024f7c --- /dev/null +++ b/Svelto.Services/Unity/Web/StandardWebRequest.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Net; +using UnityEngine; +using System.Security.Cryptography.X509Certificates; +using System.Net.Security; +using Svelto.Tasks; +using Svelto.Tasks.Enumerators; +using UnityEngine.Networking; + +namespace Svelto.Services +{ + public enum Method + { + GET, + POST + } + + public enum Result + { + Success, + ServerHandledError, + ServerException, + ClientFailure, + } + + public sealed class StandardWebRequest + { + static StandardWebRequest() + { + ServicePointManager.ServerCertificateValidationCallback = CannotVerifyMessageCertificate; + + ServicePointManager.DefaultConnectionLimit = 64; + } + + public Result result { get; private set; } + + public int maxAttempts = 3; + public int waitOnRetrySeconds = 1; + public int timeoutSeconds = 10; + + public Method method = Method.POST; + public Func processStatusCodes = code => false; + public IResponseHandler responseHandler; + public string URL; + + public IEnumerator Execute(byte[] bodyRaw) + { + int attemptNumber = 0; + + do + { + using (UnityWebRequest request = new UnityWebRequest()) + { + switch (method) + { + case Method.GET: + request.method = UnityWebRequest.kHttpVerbGET; + break; + case Method.POST: + request.method = UnityWebRequest.kHttpVerbPOST; + break; + default: + request.method = UnityWebRequest.kHttpVerbPOST; + break; + } + + request.url = URL; + request.uploadHandler = new UploadHandlerRaw(bodyRaw); + request.downloadHandler = new UnityDownloadHandler(responseHandler); + request.SetRequestHeader("Content-Type", "application/json"); + request.timeout = timeoutSeconds; + + AsyncOperation op = request.SendWebRequest(); + + while (op.isDone == false) + { + yield return Yield.It; + } + + if (request.isNetworkError == false) + { + if (ProcessResponse(request) == true) + { + result = Result.Success; + + Svelto.Console.LogDebug("web request completed"); + + yield break; + } + else + { + Svelto.Console.LogDebug("web request completed with failure ", URL); + + try + { + responseHandler?.CompleteContent(); + + result = Result.ServerHandledError; + } + catch + { + result = Result.ServerException; + } + + yield break; //no retry on server error! + } + } + else + if (++attemptNumber < maxAttempts) + { + var wait = new ReusableWaitForSecondsEnumerator(waitOnRetrySeconds); + + while (wait.MoveNext() == true) yield return Yield.It; + + Svelto.Console.LogDebug("web request retry"); + + continue; //retry on client error + } + else + { + result = Result.ClientFailure; + + yield break; + } + } + } + while (true); + } + + bool ProcessResponse(UnityWebRequest request) + { + HttpStatusCode statusCode = (HttpStatusCode) request.responseCode; + + if (statusCode != HttpStatusCode.OK) + if (processStatusCodes(statusCode) == false) + return false; + + return true; + } + + // BOC:: After turning on SSL on the gameserver we started getting certificate problems. + // Found this solution on Stack Overflow with Mike R + // (https://stackoverflow.com/questions/4926676/mono-https-webrequest-fails-with-the-authentication-or-decryption-has-failed) + static bool CannotVerifyMessageCertificate(System.Object sender, X509Certificate certificate, X509Chain chain, + SslPolicyErrors sslPolicyErrors) + { + bool isOk = true; + + // If there are errors in the certificate chain, + // look at each error to determine the cause. + if (sslPolicyErrors != SslPolicyErrors.None) + { + for (int i = 0; i < chain.ChainStatus.Length; i++) + { + if (chain.ChainStatus[i].Status == X509ChainStatusFlags.RevocationStatusUnknown) + continue; + + chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; + chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; + chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 1, 0); + chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; + + bool chainIsValid = chain.Build((X509Certificate2) certificate); + if (!chainIsValid) + { + isOk = false; + break; + } + } + } + + return isOk; + } + } +} \ No newline at end of file diff --git a/Svelto.Services/Unity/Web/UnityDownloadHandler.cs b/Svelto.Services/Unity/Web/UnityDownloadHandler.cs new file mode 100644 index 0000000..60100da --- /dev/null +++ b/Svelto.Services/Unity/Web/UnityDownloadHandler.cs @@ -0,0 +1,61 @@ +using System; +using System.IO; +using System.Text; +using UnityEngine.Networking; + +namespace Svelto.Services +{ + public class UnityDownloadHandler : DownloadHandlerScript + { + public string GetError() + { + if (String.IsNullOrEmpty(_errorString) == true) + { + MemoryStream errorBuffer = new MemoryStream(_dataLength); + + errorBuffer.Write(data, 0, _dataLength); + + _errorString = Encoding.UTF8.GetString(errorBuffer.GetBuffer(), 0, (int) errorBuffer.Length); + + errorBuffer.Dispose(); + } + + return _errorString; + } + + protected override bool ReceiveData(byte[] data, int dataLength) + { + if (data.Length < 1) + return false; // No need to receive data + + _data = data; + _dataLength = dataLength; + + if (_handler == null) + return false; // No need to receive data + + return _handler.ReceiveData(data, dataLength); + } + + public UnityDownloadHandler(IResponseHandler handler) + { + _handler = handler; + } + + protected override byte[] GetData() + { + return _data; + } + + // Called when all data has been received from the server and delivered via ReceiveData. + protected override void CompleteContent() + { + _handler?.CompleteContent(); + } + + readonly IResponseHandler _handler; + byte[] _data; + int _dataLength; + string _errorString; + } +} \ No newline at end of file