@@ -0,0 +1,16 @@ | |||
namespace Svelto.ECS.Components | |||
{ | |||
public struct ECSQuaternion | |||
{ | |||
public static readonly ECSQuaternion identity = new ECSQuaternion(0f, 0f, 0f, 1f); | |||
public float x, y, z, w; | |||
public ECSQuaternion(float X, float Y, float Z, float W) | |||
{ | |||
x = X; | |||
y = Y; | |||
z = Z; | |||
w = W; | |||
} | |||
} | |||
} |
@@ -1,7 +1,7 @@ | |||
#if UNITY_5 || UNITY_5_3_OR_NEWER | |||
using UnityEngine; | |||
namespace Svelto.ECS.Components | |||
namespace Svelto.ECS.Components.Unity | |||
{ | |||
public struct ECSRect | |||
{ | |||
@@ -16,4 +16,4 @@ namespace Svelto.ECS.Components | |||
} | |||
} | |||
} | |||
#endif | |||
#endif |
@@ -0,0 +1,20 @@ | |||
namespace Svelto.ECS.Components | |||
{ | |||
public struct ECSVector2 | |||
{ | |||
public float x, y; | |||
public static readonly ECSVector2 right = new ECSVector2(1f, 0f); | |||
public static readonly ECSVector2 left = new ECSVector2(-1f, 0f); | |||
public static readonly ECSVector2 down = new ECSVector2(0f, -1f); | |||
public static readonly ECSVector2 up = new ECSVector2(0f, 1f); | |||
public static readonly ECSVector2 one = new ECSVector2(1f, 1f); | |||
public static readonly ECSVector2 zero = new ECSVector2(0f, 0f); | |||
public ECSVector2(float X, float Y) | |||
{ | |||
x = X; | |||
y = Y; | |||
} | |||
} | |||
} |
@@ -0,0 +1,24 @@ | |||
namespace Svelto.ECS.Components | |||
{ | |||
public struct ECSVector3 | |||
{ | |||
public float x, y, z; | |||
public static readonly ECSVector3 forward = new ECSVector3(0f, 0f, 1f); | |||
public static readonly ECSVector3 back = new ECSVector3(0f, 0f, -1f); | |||
public static readonly ECSVector3 right = new ECSVector3(1f, 0f, 0f); | |||
public static readonly ECSVector3 left = new ECSVector3(-1f, 0f, 0f); | |||
public static readonly ECSVector3 up = new ECSVector3(0f, 1f, 0f); | |||
public static readonly ECSVector3 down = new ECSVector3(0f, -1f, 0f); | |||
public static readonly ECSVector3 zero = new ECSVector3(0f, 0f, 0f); | |||
public static readonly ECSVector3 one = new ECSVector3(1f, 1f, 1f); | |||
public ECSVector3(float X, float Y, float Z) | |||
{ | |||
x = X; | |||
y = Y; | |||
z = Z; | |||
} | |||
} | |||
} |
@@ -1,10 +1,10 @@ | |||
namespace Svelto.ECS.Components | |||
{ | |||
public struct EcsVector4 | |||
public struct ECSVector4 | |||
{ | |||
public float x, y, z, w; | |||
public EcsVector4(float X, float Y, float Z, float W) | |||
public ECSVector4(float X, float Y, float Z, float W) | |||
{ | |||
x = X; | |||
y = Y; |
@@ -0,0 +1,132 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.ECS.Components; | |||
public static partial class ExtensionMethods | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static float SqrMagnitude(in this ECSVector2 a) { return a.x * a.x + a.y * a.y; } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static float SqrMagnitude(in this ECSVector3 a) { return a.x * a.x + a.y * a.y + a.z * a.z; } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static float Magnitude(in this ECSVector2 a) { return (float) Math.Sqrt(a.SqrMagnitude()); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static float Magnitude(in this ECSVector3 a) { return (float) Math.Sqrt(a.SqrMagnitude()); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Add(ref this ECSVector3 vector1, in ECSVector3 vector2) | |||
{ | |||
vector1.x += vector2.x; | |||
vector1.y += vector2.y; | |||
vector1.z += vector2.z; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Add(ref this ECSVector3 vector1, float x, float y, float z) | |||
{ | |||
vector1.x += x; | |||
vector1.y += y; | |||
vector1.z += z; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Set(ref this ECSVector3 vector1, float x, float y, float z) | |||
{ | |||
vector1.x = x; | |||
vector1.y = y; | |||
vector1.z = z; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Zero(ref this ECSVector3 vector1) | |||
{ | |||
vector1.x = 0; | |||
vector1.y = 0; | |||
vector1.z = 0; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Set(ref this ECSQuaternion quaternion, float x, float y, float z, float w) | |||
{ | |||
quaternion.x = x; | |||
quaternion.y = y; | |||
quaternion.z = z; | |||
quaternion.w = w; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Interpolate(ref this ECSVector3 vector, in ECSVector3 vectorS, in ECSVector3 vectorE, float time) | |||
{ | |||
vector.x = vectorS.x * (1 - time) + vectorE.x * time; | |||
vector.y = vectorS.y * (1 - time) + vectorE.y * time; | |||
vector.z = vectorS.z * (1 - time) + vectorE.z * time; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static float Dot(ref this ECSVector3 vector1, in ECSVector3 vector2) | |||
{ | |||
return vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static ECSVector3 Cross(ref this ECSVector3 lhs, in ECSVector3 rhs) | |||
{ | |||
return new ECSVector3(lhs.y * rhs.z - lhs.z * rhs.y, lhs.z * rhs.x - lhs.x * rhs.z, | |||
lhs.x * rhs.y - lhs.y * rhs.x); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static ECSVector3 Mul(in this ECSQuaternion rotation, in ECSVector3 point) | |||
{ | |||
float x = rotation.x * 2F; | |||
float y = rotation.y * 2F; | |||
float z = rotation.z * 2F; | |||
float xx = rotation.x * x; | |||
float yy = rotation.y * y; | |||
float zz = rotation.z * z; | |||
float xy = rotation.x * y; | |||
float xz = rotation.x * z; | |||
float yz = rotation.y * z; | |||
float wx = rotation.w * x; | |||
float wy = rotation.w * y; | |||
float wz = rotation.w * z; | |||
return new ECSVector3((1F - (yy + zz)) * point.x + (xy - wz) * point.y + (xz + wy) * point.z, | |||
(xy + wz) * point.x + (1F - (xx + zz)) * point.y + (yz - wx) * point.z, | |||
(xz - wy) * point.x + (yz + wx) * point.y + (1F - (xx + yy)) * point.z); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Swap(ref this ECSVector3 vector, ref ECSVector3 vectorS) | |||
{ | |||
float x = vector.x; | |||
float y = vector.y; | |||
float z = vector.z; | |||
vector.x = vectorS.x; | |||
vector.y = vectorS.y; | |||
vector.z = vectorS.z; | |||
vectorS.x = x; | |||
vectorS.y = y; | |||
vectorS.z = z; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Sub(ref this ECSVector3 vector1, in ECSVector3 vector2) | |||
{ | |||
vector1.x -= vector2.x; | |||
vector1.y -= vector2.y; | |||
vector1.z -= vector2.z; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static ref ECSVector3 Mul(ref this ECSVector3 vector1, float value) | |||
{ | |||
vector1.x *= value; | |||
vector1.y *= value; | |||
vector1.z *= value; | |||
return ref vector1; | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
#if UNITY_2018_3_OR_NEWER | |||
using Svelto.ECS.Components; | |||
using UnityEngine; | |||
public static partial class ExtensionMethods | |||
{ | |||
public static Vector2 ToVector2(in this ECSVector2 vector) { return new Vector2(vector.x, vector.y); } | |||
public static ECSVector2 ToECSVector2(in this Vector2 vector) { return new ECSVector2(vector.x, vector.y); } | |||
public static Vector3 ToVector3(in this ECSVector3 vector) | |||
{ | |||
return new Vector3(vector.x, vector.y, vector.z); | |||
} | |||
public static ECSVector3 ToECSVector3(in this Vector3 vector) | |||
{ | |||
return new ECSVector3(vector.x, vector.y, vector.z); | |||
} | |||
public static Quaternion ToQuaternion(in this ECSQuaternion quaternion) | |||
{ | |||
return new Quaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w); | |||
} | |||
public static ECSQuaternion ToECSQuaternion(in this Quaternion quaternion) | |||
{ | |||
return new ECSQuaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w); | |||
} | |||
public static void Set(ref this ECSQuaternion ecsQuaternion, in Quaternion quaternion) | |||
{ | |||
ecsQuaternion.x = quaternion.x; | |||
ecsQuaternion.y = quaternion.y; | |||
ecsQuaternion.z = quaternion.z; | |||
ecsQuaternion.w = quaternion.w; | |||
} | |||
public static void Set(ref this ECSVector3 ecsVector3, in Vector3 vector3) | |||
{ | |||
ecsVector3.x = vector3.x; | |||
ecsVector3.y = vector3.y; | |||
ecsVector3.z = vector3.z; | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,50 @@ | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS.Experimental | |||
{ | |||
public struct ECSResources<T> | |||
{ | |||
internal uint id; | |||
public static implicit operator T(ECSResources<T> ecsString) { return ResourcesECSDB<T>.FromECS(ecsString.id); } | |||
} | |||
static class ResourcesECSDB<T> | |||
{ | |||
internal static readonly FasterList<T> _resources = new FasterList<T>(); | |||
internal static uint ToECS(T resource) | |||
{ | |||
_resources.Add(resource); | |||
return (uint)_resources.Count; | |||
} | |||
public static T FromECS(uint id) | |||
{ | |||
if (id - 1 < _resources.Count) | |||
return _resources[(int) id - 1]; | |||
return default(T); | |||
} | |||
} | |||
public static class ResourceExtensions | |||
{ | |||
public static void Set<T>(ref this ECSResources<T> resource, T newText) | |||
{ | |||
if (resource.id != 0) | |||
ResourcesECSDB<T>._resources[(int) resource.id] = newText; | |||
else | |||
resource.id = ResourcesECSDB<T>.ToECS(newText); | |||
} | |||
public static void Set(ref this ECSString resource, string newText) | |||
{ | |||
if (resource.id != 0) | |||
ResourcesECSDB<string>._resources[(int) resource.id] = newText; | |||
else | |||
resource.id = ResourcesECSDB<string>.ToECS(newText); | |||
} | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
namespace Svelto.ECS.Experimental | |||
{ | |||
public struct ECSString | |||
{ | |||
internal uint id; | |||
public static implicit operator string(ECSString ecsString) | |||
{ | |||
return ResourcesECSDB<string>.FromECS(ecsString.id); | |||
} | |||
} | |||
} |
@@ -0,0 +1,10 @@ | |||
using Svelto.ECS.Components; | |||
namespace Svelto.ECS.EntityStructs | |||
{ | |||
public struct LocalTransformEntityStruct : IEntityStruct | |||
{ | |||
public ECSVector3 position; | |||
public ECSQuaternion rotation; | |||
} | |||
} |
@@ -5,7 +5,5 @@ namespace Svelto.ECS.EntityStructs | |||
public struct PositionEntityStruct : IEntityStruct | |||
{ | |||
public ECSVector3 position; | |||
public EGID ID { get; set; } | |||
} | |||
} |
@@ -4,8 +4,6 @@ namespace Svelto.ECS.EntityStructs | |||
{ | |||
public struct RotationEntityStruct : IEntityStruct | |||
{ | |||
public EcsVector4 rotation; | |||
public EGID ID { get; set; } | |||
public ECSQuaternion rotation; | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
using Svelto.ECS.Components; | |||
namespace Svelto.ECS.EntityStructs | |||
{ | |||
public struct ScalingEntityStruct : IEntityStruct | |||
{ | |||
public ECSVector3 scale; | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
{ | |||
"name": "Svelto.ECS.Components.Unity", | |||
"references": [ | |||
"Unity.Mathematics", | |||
"Svelto.ECS", | |||
"Svelto.Common" | |||
], | |||
"optionalUnityReferences": [], | |||
"includePlatforms": [], | |||
"excludePlatforms": [], | |||
"allowUnsafeCode": false, | |||
"overrideReferences": false, | |||
"precompiledReferences": [], | |||
"autoReferenced": true, | |||
"defineConstraints": [], | |||
"versionDefines": [ | |||
{ | |||
"name": "com.unity.mathematics", | |||
"expression": "0.0.9", | |||
"define": "UNITY_MATHEMATICS" | |||
} | |||
] | |||
} |
@@ -1,4 +0,0 @@ | |||
/EntitySystem/note.txt | |||
/EntitySystem/note.txt.meta | |||
/*.meta | |||
*.meta |
@@ -15,11 +15,10 @@ namespace Svelto.ECS | |||
#endif | |||
void CheckRemoveEntityID(EGID entityID, IEntityDescriptor descriptorEntity) | |||
{ | |||
Dictionary<Type, ITypeSafeDictionary> group; | |||
var descriptorEntitiesToBuild = descriptorEntity.entitiesToBuild; | |||
if (_groupEntityDB.TryGetValue(entityID.groupID, out group)) | |||
var descriptorEntitiesToBuild = descriptorEntity.entitiesToBuild; | |||
if (_groupEntityDB.TryGetValue(entityID.groupID, out var @group)) | |||
{ | |||
for (int i = 0; i < descriptorEntitiesToBuild.Length; i++) | |||
{ | |||
@@ -38,15 +37,15 @@ namespace Svelto.ECS | |||
#if DISABLE_CHECKS | |||
[Conditional("_CHECKS_DISABLED")] | |||
#endif | |||
void CheckRemoveEntityID(EGID entityID, Type entityType, Dictionary<Type, ITypeSafeDictionary> group, string name) | |||
void CheckRemoveEntityID(EGID entityID, Type entityViewType, Dictionary<Type, ITypeSafeDictionary> group, string name) | |||
{ | |||
ITypeSafeDictionary entities; | |||
if (group.TryGetValue(entityType, out entities)) | |||
if (group.TryGetValue(entityViewType, out entities)) | |||
{ | |||
if (entities.Has(entityID.entityID) == false) | |||
{ | |||
Console.LogError("Entity ".FastConcat(name, " with not found ID is about to be removed: ") | |||
.FastConcat(entityType.ToString()) | |||
.FastConcat(entityViewType.ToString()) | |||
.FastConcat(" id: ") | |||
.FastConcat(entityID.entityID) | |||
.FastConcat(" groupid: ") | |||
@@ -56,7 +55,7 @@ namespace Svelto.ECS | |||
else | |||
{ | |||
Console.LogError("Entity ".FastConcat(name, " with not found ID is about to be removed: ") | |||
.FastConcat(entityType.ToString()) | |||
.FastConcat(entityViewType.ToString()) | |||
.FastConcat(" id: ") | |||
.FastConcat(entityID.entityID) | |||
.FastConcat(" groupid: ") | |||
@@ -69,15 +68,15 @@ namespace Svelto.ECS | |||
#endif | |||
void CheckAddEntityID<T>(EGID entityID, T descriptorEntity) where T:IEntityDescriptor | |||
{ | |||
Dictionary<Type, ITypeSafeDictionary> group; | |||
var descriptorEntitiesToBuild = descriptorEntity.entitiesToBuild; | |||
var descriptorEntitiesToBuild = descriptorEntity.entitiesToBuild; | |||
//these are the entities added in this frame | |||
if (_groupEntityDB.TryGetValue(entityID.groupID, out group)) | |||
if (_groupEntityDB.TryGetValue(entityID.groupID, out var @group)) | |||
{ | |||
for (int i = 0; i < descriptorEntitiesToBuild.Length; i++) | |||
{ | |||
CheckAddEntityID(entityID, descriptorEntitiesToBuild[i].GetEntityType(), group, descriptorEntity.ToString()); | |||
CheckAddEntityID(entityID, descriptorEntitiesToBuild[i].GetEntityType(), group, | |||
descriptorEntity.ToString()); | |||
} | |||
} | |||
} | |||
@@ -85,15 +84,16 @@ namespace Svelto.ECS | |||
#if DISABLE_CHECKS | |||
[Conditional("_CHECKS_DISABLED")] | |||
#endif | |||
static void CheckAddEntityID(EGID entityID, Type entityType, Dictionary<Type, ITypeSafeDictionary> group, string name) | |||
static void CheckAddEntityID(EGID entityID, Type entityViewType, Dictionary<Type, ITypeSafeDictionary> group, | |||
string name) | |||
{ | |||
ITypeSafeDictionary entities; | |||
if (group.TryGetValue(entityType, out entities)) | |||
if (group.TryGetValue(entityViewType, out entities)) | |||
{ | |||
if (entities.Has(entityID.entityID)) | |||
{ | |||
Console.LogError("Entity ".FastConcat(name, " with used ID is about to be built: ") | |||
.FastConcat(entityType.ToString()) | |||
.FastConcat(entityViewType.ToString()) | |||
.FastConcat(" id: ") | |||
.FastConcat(entityID.entityID) | |||
.FastConcat(" groupid: ") | |||
@@ -1,13 +0,0 @@ | |||
namespace Svelto.ECS.Components | |||
{ | |||
public struct ECSVector2 | |||
{ | |||
public float x, y; | |||
public ECSVector2(float X, float Y) | |||
{ | |||
x = X; | |||
y = Y; | |||
} | |||
} | |||
} |
@@ -1,14 +0,0 @@ | |||
namespace Svelto.ECS.Components | |||
{ | |||
public struct ECSVector3 | |||
{ | |||
public float x, y, z; | |||
public ECSVector3(float X, float Y, float Z) | |||
{ | |||
x = X; | |||
y = Y; | |||
z = Z; | |||
} | |||
} | |||
} |
@@ -1,36 +0,0 @@ | |||
#if UNITY_2018_3_OR_NEWER | |||
using UnityEngine; | |||
namespace Svelto.ECS.Components.Unity | |||
{ | |||
namespace Svelto.ECS.Components | |||
{ | |||
public static class ExtensionMethods | |||
{ | |||
public static Vector3 ToVector3(ref this ECSVector3 vector) { return new Vector3(vector.x, vector.y, vector.z); } | |||
public static void Add(ref this ECSVector3 vector1, ref ECSVector3 vector2) | |||
{ | |||
vector1.x += vector2.x; | |||
vector1.y += vector2.y; | |||
vector1.z += vector2.z; | |||
} | |||
public static void Add(ref this ECSVector3 vector1, float x, float y, float z) | |||
{ | |||
vector1.x += x; | |||
vector1.y += y; | |||
vector1.z += z; | |||
} | |||
public static void Interpolate(ref this ECSVector3 vector, ref ECSVector3 vectorS, | |||
ref ECSVector3 vectorE, float time) | |||
{ | |||
vector.x = vectorS.x * (1 - time) + vectorE.x * (time); | |||
vector.y = vectorS.y * (1 - time) + vectorE.y * (time); | |||
vector.z = vectorS.z * (1 - time) + vectorE.z * (time); | |||
} | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,12 +0,0 @@ | |||
using Svelto.ECS.Components; | |||
namespace Svelto.ECS.EntityStructs | |||
{ | |||
public struct InterpolateVector3EntityStruct : IEntityStruct | |||
{ | |||
public ECSVector3 starPos, endPos; | |||
public float time; | |||
public EGID ID { get; set; } | |||
} | |||
} |
@@ -60,7 +60,7 @@ namespace DBC.ECS | |||
/// <summary> | |||
/// Precondition check. | |||
/// </summary> | |||
#if DISABLE_DBC | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Require(bool assertion, string message) | |||
@@ -80,7 +80,7 @@ namespace DBC.ECS | |||
/// Precondition check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_DBC | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Require(bool assertion, string message, Exception inner) | |||
@@ -100,7 +100,7 @@ namespace DBC.ECS | |||
/// Precondition check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_DBC | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Require(bool assertion) | |||
@@ -120,7 +120,7 @@ namespace DBC.ECS | |||
/// Postcondition check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_DBC | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Ensure(bool assertion, string message) | |||
@@ -140,7 +140,7 @@ namespace DBC.ECS | |||
/// Postcondition check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_DBC | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Ensure(bool assertion, string message, Exception inner) | |||
@@ -160,7 +160,7 @@ namespace DBC.ECS | |||
/// Postcondition check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_DBC | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Ensure(bool assertion) | |||
@@ -180,7 +180,7 @@ namespace DBC.ECS | |||
/// Invariant check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_DBC | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Invariant(bool assertion, string message) | |||
@@ -200,7 +200,7 @@ namespace DBC.ECS | |||
/// Invariant check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_DBC | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Invariant(bool assertion, string message, Exception inner) | |||
@@ -220,7 +220,7 @@ namespace DBC.ECS | |||
/// Invariant check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_DBC | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Invariant(bool assertion) | |||
@@ -239,7 +239,7 @@ namespace DBC.ECS | |||
/// <summary> | |||
/// Assertion check. | |||
/// </summary> | |||
#if DISABLE_DBC | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Assert(bool assertion, string message) | |||
@@ -259,7 +259,7 @@ namespace DBC.ECS | |||
/// Assertion check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_DBC | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Assert(bool assertion, string message, Exception inner) | |||
@@ -279,7 +279,7 @@ namespace DBC.ECS | |||
/// Assertion check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_DBC | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Assert(bool assertion) | |||
@@ -0,0 +1,106 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
class GroupList<T> | |||
{ | |||
public int Count => _list.Count; | |||
public GroupList() | |||
{ | |||
_list = new FasterList<T>(); | |||
} | |||
public ref T this[uint index] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref _list[index]; | |||
} | |||
public GroupListEnumerator<T> GetEnumerator() | |||
{ | |||
return new GroupListEnumerator<T>(_list.ToArrayFast(), _list.Count); | |||
} | |||
public T GetOrAdd<TC>(uint location) where TC:class, T, new() | |||
{ | |||
if (location >= _list.Count || this[location] == null) | |||
{ | |||
var item = new TC(); | |||
_list.Add(location, item); | |||
return item; | |||
} | |||
return this[location]; | |||
} | |||
public void Clear() { _list.Clear(); } | |||
public void FastClear() { _list.ResetCountToAvoidGC(); } | |||
public bool TryGetValue(uint index, out T value) | |||
{ | |||
if (default(T) == null) | |||
{ | |||
if (index < _list.Count && this[index] != null) | |||
{ | |||
value = this[index]; | |||
return true; | |||
} | |||
value = default(T); | |||
return false; | |||
} | |||
else | |||
{ | |||
if (index < _list.Count) | |||
{ | |||
value = this[index]; | |||
return true; | |||
} | |||
value = default(T); | |||
return false; | |||
} | |||
} | |||
public void Add(uint location, T value) { _list.Add(location, value); } | |||
readonly FasterList<T> _list; | |||
} | |||
public struct GroupListEnumerator<T> | |||
{ | |||
public ref readonly T Current => ref _buffer[_counter -1]; | |||
public uint index => _counter - 1; | |||
public GroupListEnumerator(T[] buffer, int size) | |||
{ | |||
_size = size; | |||
_counter = 0; | |||
_buffer = buffer; | |||
} | |||
public bool MoveNext() | |||
{ | |||
if (default(T) == null) | |||
{ | |||
while (_counter < _size) | |||
{ | |||
if (_buffer[_counter] == null) | |||
_counter++; | |||
else | |||
break; | |||
} | |||
} | |||
return _counter++ < _size; | |||
} | |||
readonly T[] _buffer; | |||
uint _counter; | |||
readonly int _size; | |||
} | |||
} |
@@ -1,208 +1,234 @@ | |||
using System; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.Common.Internal; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Experimental; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
public interface ITypeSafeDictionary | |||
{ | |||
int Count { get; } | |||
ITypeSafeDictionary Create(); | |||
void RemoveEntitiesFromEngines(Dictionary<Type, FasterList<IHandleEntityViewEngineAbstracted>> | |||
entityViewEnginesDB, ref PlatformProfiler profiler); | |||
void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup, | |||
Dictionary<Type, FasterList<IHandleEntityViewEngineAbstracted>> | |||
entityViewEnginesDB, ref PlatformProfiler profiler); | |||
void FillWithIndexedEntities(ITypeSafeDictionary entities); | |||
void AddEntitiesToEngines(Dictionary<Type, FasterList<IHandleEntityViewEngineAbstracted>> entityViewEnginesDB, | |||
ref PlatformProfiler profiler); | |||
void AddCapacity(int size); | |||
int Count { get; } | |||
void RemoveEntitiesFromEngines( | |||
Dictionary<Type, FasterList<IEngine>> entityViewEnginesDB, | |||
ref PlatformProfiler profiler); | |||
void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, | |||
Dictionary<Type, FasterList<IEngine>> engines, | |||
ref PlatformProfiler profiler); | |||
void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId); | |||
void AddEntitiesToEngines(Dictionary<Type, FasterList<IEngine>> entityViewEnginesDb, | |||
ITypeSafeDictionary realDic, ref PlatformProfiler profiler); | |||
void SetCapacity(uint size); | |||
void Trim(); | |||
void Clear(); | |||
bool Has(int entityIdEntityId); | |||
bool Has(uint entityIdEntityId); | |||
} | |||
class TypeSafeDictionary<TValue> : FasterDictionary<int, TValue>, ITypeSafeDictionary where TValue : IEntityStruct | |||
class TypeSafeDictionary<TValue> : FasterDictionary<uint, TValue>, ITypeSafeDictionary where TValue : struct, IEntityStruct | |||
{ | |||
public TypeSafeDictionary(int size):base(size) | |||
{} | |||
public TypeSafeDictionary() | |||
{} | |||
public void FillWithIndexedEntities(ITypeSafeDictionary entities) | |||
static readonly Type _type = typeof(TValue); | |||
static readonly string _typeName = _type.Name; | |||
static readonly bool HasEgid = typeof(INeedEGID).IsAssignableFrom(_type); | |||
public TypeSafeDictionary(uint size) : base((uint) size) { } | |||
public TypeSafeDictionary() {} | |||
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId) | |||
{ | |||
int count; | |||
var buffer = (entities as TypeSafeDictionary<TValue>).GetValuesArray(out count); | |||
var typeSafeDictionary = entitiesToSubmit as TypeSafeDictionary<TValue>; | |||
for (var i = 0; i < count; i++) | |||
foreach (var tuple in typeSafeDictionary) | |||
{ | |||
int idEntityId = 0; | |||
try | |||
{ | |||
idEntityId = buffer[i].ID.entityID; | |||
Add(idEntityId, ref buffer[i]); | |||
if (HasEgid) | |||
{ | |||
var needEgid = (INeedEGID)tuple.Value; | |||
needEgid.ID = new EGID(tuple.Key, groupId); | |||
Add(tuple.Key, (TValue) needEgid); | |||
} | |||
else | |||
Add(tuple.Key, ref tuple.Value); | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new TypeSafeDictionaryException("trying to add an EntityView with the same ID more than once Entity: ". | |||
FastConcat(typeof(TValue).ToString()).FastConcat("id ").FastConcat(idEntityId), e); | |||
throw new TypeSafeDictionaryException( | |||
"trying to add an EntityView with the same ID more than once Entity: " | |||
.FastConcat(typeof(TValue).ToString()).FastConcat("id ").FastConcat(tuple.Key), e); | |||
} | |||
} | |||
} | |||
public void AddEntitiesToEngines( | |||
Dictionary<Type, FasterList<IHandleEntityViewEngineAbstracted>> entityViewEnginesDB, | |||
Dictionary<Type, FasterList<IEngine>> entityViewEnginesDB, | |||
ITypeSafeDictionary realDic, ref PlatformProfiler profiler) | |||
{ | |||
foreach (var value in this) | |||
{ | |||
var typeSafeDictionary = realDic as TypeSafeDictionary<TValue>; | |||
AddEntityViewToEngines(entityViewEnginesDB, ref typeSafeDictionary.GetDirectValue(value.Key), null, | |||
ref profiler); | |||
} | |||
} | |||
public bool Has(uint entityIdEntityId) { return ContainsKey(entityIdEntityId); } | |||
public void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID? toEntityID, | |||
ITypeSafeDictionary toGroup, | |||
Dictionary<Type, FasterList<IEngine>> engines, | |||
ref PlatformProfiler profiler) | |||
{ | |||
var values = GetValuesArray(out var count); | |||
//pay attention: even if the entity is passed by ref, it won't be saved back in the database when this | |||
//function is called from the building of an entity. This is by design. Entity structs must be initialized | |||
//through the EntityInitializer method and not with an Add callback. | |||
//however the struct can be modified during an add callback if this happens as consequence of a group swap | |||
for (int i = 0; i < count; i++) | |||
AddEntityViewToEngines(entityViewEnginesDB, ref values[i], ref profiler); | |||
var valueIndex = GetValueIndex(fromEntityGid.entityID); | |||
if (toGroup != null) | |||
{ | |||
RemoveEntityViewFromEngines(engines, ref _values[valueIndex], fromEntityGid.groupID, ref profiler); | |||
var toGroupCasted = toGroup as TypeSafeDictionary<TValue>; | |||
ref var entity = ref _values[valueIndex]; | |||
var previousGroup = fromEntityGid.groupID; | |||
/// | |||
/// NOTE I WOULD EVENTUALLY NEED TO REUSE THE REAL ID OF THE REMOVING ELEMENT | |||
/// SO THAT I CAN DECREASE THE GLOBAL GROUP COUNT | |||
/// | |||
// entity.ID = EGID.UPDATE_REAL_ID_AND_GROUP(entity.ID, toEntityID.groupID, entityCount); | |||
if (HasEgid) | |||
{ | |||
var needEgid = (INeedEGID)entity; | |||
needEgid.ID = toEntityID.Value; | |||
entity = (TValue) needEgid; | |||
} | |||
var index = toGroupCasted.Add(fromEntityGid.entityID, ref entity); | |||
AddEntityViewToEngines(engines, ref toGroupCasted._values[index], previousGroup, | |||
ref profiler); | |||
} | |||
else | |||
RemoveEntityViewFromEngines(engines, ref _values[valueIndex], null, ref profiler); | |||
Remove(fromEntityGid.entityID); | |||
} | |||
public bool Has(int entityIdEntityId) | |||
public void RemoveEntitiesFromEngines( | |||
Dictionary<Type, FasterList<IEngine>> entityViewEnginesDB, | |||
ref PlatformProfiler profiler) | |||
{ | |||
return ContainsKey(entityIdEntityId); | |||
var values = GetValuesArray(out var count); | |||
for (var i = 0; i < count; i++) | |||
RemoveEntityViewFromEngines(entityViewEnginesDB, ref values[i], null, ref profiler); | |||
} | |||
void AddEntityViewToEngines(Dictionary<Type, FasterList<IHandleEntityViewEngineAbstracted>> entityViewEnginesDB, | |||
ref TValue entity, ref PlatformProfiler profiler) | |||
public ITypeSafeDictionary Create() { return new TypeSafeDictionary<TValue>(); } | |||
void AddEntityViewToEngines(Dictionary<Type, FasterList<IEngine>> entityViewEnginesDB, | |||
ref TValue entity, | |||
ExclusiveGroup.ExclusiveGroupStruct? previousGroup, | |||
ref PlatformProfiler profiler) | |||
{ | |||
FasterList<IHandleEntityViewEngineAbstracted> entityViewsEngines; | |||
//get all the engines linked to TValue | |||
if (entityViewEnginesDB.TryGetValue(_type, out entityViewsEngines)) | |||
for (int i = 0; i < entityViewsEngines.Count; i++) | |||
{ | |||
if (!entityViewEnginesDB.TryGetValue(_type, out var entityViewsEngines)) return; | |||
if (previousGroup == null) | |||
{ | |||
for (var i = 0; i < entityViewsEngines.Count; i++) | |||
try | |||
{ | |||
using (profiler.Sample((entityViewsEngines[i] as EngineInfo).name)) | |||
using (profiler.Sample(entityViewsEngines[i], _typeName)) | |||
{ | |||
(entityViewsEngines[i] as IHandleEntityStructEngine<TValue>).AddInternal(ref entity); | |||
(entityViewsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new ECSException("Code crashed inside Add callback ". | |||
FastConcat(typeof(TValue).ToString()).FastConcat("id ").FastConcat(entity.ID.entityID), e); | |||
throw new ECSException( | |||
"Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e); | |||
} | |||
} | |||
} | |||
public void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup, | |||
Dictionary<Type, FasterList<IHandleEntityViewEngineAbstracted>> | |||
entityViewEnginesDB, ref PlatformProfiler profiler) | |||
{ | |||
int count; | |||
var fasterValuesBuffer = GetValuesArray(out count); | |||
var valueIndex = GetValueIndex(fromEntityGid.entityID); | |||
if (entityViewEnginesDB != null) | |||
RemoveEntityViewFromEngines(entityViewEnginesDB, ref fasterValuesBuffer[valueIndex], ref profiler); | |||
if (toGroup != null) | |||
{ | |||
var toGroupCasted = toGroup as TypeSafeDictionary<TValue>; | |||
fasterValuesBuffer[valueIndex].ID = toEntityID; | |||
toGroupCasted.Add(toEntityID.entityID, ref fasterValuesBuffer[valueIndex]); | |||
if (entityViewEnginesDB != null) | |||
AddEntityViewToEngines(entityViewEnginesDB, ref toGroupCasted.GetValuesArray(out count) | |||
[toGroupCasted.GetValueIndex(toEntityID.entityID)], ref profiler); | |||
} | |||
Remove(fromEntityGid.entityID); | |||
} | |||
static void RemoveEntityViewFromEngines | |||
(Dictionary<Type, FasterList<IHandleEntityViewEngineAbstracted>> entityViewEnginesDB, ref TValue entity, | |||
ref PlatformProfiler profiler) | |||
{ | |||
FasterList<IHandleEntityViewEngineAbstracted> entityViewsEngines; | |||
if (entityViewEnginesDB.TryGetValue(_type, out entityViewsEngines)) | |||
for (int i = 0; i < entityViewsEngines.Count; i++) | |||
else | |||
{ | |||
for (var i = 0; i < entityViewsEngines.Count; i++) | |||
try | |||
{ | |||
using (profiler.Sample((entityViewsEngines[i] as EngineInfo).name, _typeName)) | |||
using (profiler.Sample(entityViewsEngines[i], _typeName)) | |||
{ | |||
(entityViewsEngines[i] as IHandleEntityStructEngine<TValue>).RemoveInternal(ref entity); | |||
(entityViewsEngines[i] as IReactOnSwap<TValue>).MovedTo(ref entity, previousGroup.Value); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new ECSException("Code crashed inside Remove callback ". | |||
FastConcat(typeof(TValue).ToString()).FastConcat("id ").FastConcat(entity.ID.entityID), e); | |||
throw new ECSException( | |||
"Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e); | |||
} | |||
} | |||
} | |||
public void RemoveEntitiesFromEngines(Dictionary<Type, | |||
FasterList<IHandleEntityViewEngineAbstracted>> entityViewEnginesDB, ref PlatformProfiler profiler) | |||
{ | |||
int count; | |||
TValue[] values = GetValuesArray(out count); | |||
for (int i = 0; i < count; i++) | |||
RemoveEntityViewFromEngines(entityViewEnginesDB, ref values[i], ref profiler); | |||
} | |||
public ITypeSafeDictionary Create() | |||
static void RemoveEntityViewFromEngines( | |||
Dictionary<Type, FasterList<IEngine>> entityViewEnginesDB, ref TValue entity, | |||
ExclusiveGroup.ExclusiveGroupStruct? previousGroup, | |||
ref PlatformProfiler profiler) | |||
{ | |||
return new TypeSafeDictionary<TValue>(); | |||
} | |||
public bool ExecuteOnEntityView<W>(int entityGidEntityId, ref W value, EntityAction<TValue, W> action) | |||
{ | |||
uint findIndex; | |||
if (FindIndex(entityGidEntityId, out findIndex)) | |||
{ | |||
action(ref _values[findIndex], ref value); | |||
if (!entityViewEnginesDB.TryGetValue(_type, out var entityViewsEngines)) return; | |||
return true; | |||
if (previousGroup == null) | |||
{ | |||
for (var i = 0; i < entityViewsEngines.Count; i++) | |||
try | |||
{ | |||
using (profiler.Sample(entityViewsEngines[i], _typeName)) | |||
(entityViewsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity); | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new ECSException( | |||
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e); | |||
} | |||
} | |||
return false; | |||
} | |||
public bool ExecuteOnEntityView(int entityGidEntityId, EntityAction<TValue> action) | |||
{ | |||
uint findIndex; | |||
if (FindIndex(entityGidEntityId, out findIndex)) | |||
else | |||
{ | |||
action(ref _values[findIndex]); | |||
return true; | |||
for (var i = 0; i < entityViewsEngines.Count; i++) | |||
try | |||
{ | |||
using (profiler.Sample(entityViewsEngines[i], _typeName)) | |||
(entityViewsEngines[i] as IReactOnSwap<TValue>).MovedFrom(ref entity, previousGroup.Value); | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new ECSException( | |||
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e); | |||
} | |||
} | |||
return false; | |||
} | |||
public uint FindElementIndex(int entityGidEntityId) | |||
{ | |||
uint findIndex; | |||
if (FindIndex(entityGidEntityId, out findIndex) == false) | |||
throw new Exception("Entity not found in this group"); | |||
return findIndex; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal ref TValue FindElement(uint entityGidEntityId) | |||
{ | |||
#if DEBUG && !PROFILER | |||
if (FindIndex(entityGidEntityId, out var findIndex) == false) | |||
throw new Exception("Entity not found in this group ".FastConcat(typeof(TValue).ToString())); | |||
#else | |||
FindIndex(entityGidEntityId, out var findIndex); | |||
#endif | |||
return ref _values[findIndex]; | |||
} | |||
public bool TryFindElementIndex(int entityGidEntityId, out uint index) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal bool TryFindElementIndex(uint entityGidEntityId, out uint index) | |||
{ | |||
return FindIndex(entityGidEntityId, out index); | |||
} | |||
static readonly Type _type = typeof(TValue); | |||
static readonly string _typeName = _type.Name; | |||
} | |||
} |
@@ -1,20 +1,17 @@ | |||
using System.Collections.Generic; | |||
using System; | |||
namespace Svelto.ECS | |||
{ | |||
public class DispatchOnChange<T> : DispatchOnSet<T> where T:struct | |||
public class DispatchOnChange<T> : DispatchOnSet<T> where T:struct, IEquatable<T> | |||
{ | |||
public DispatchOnChange(int senderID) : base(senderID) | |||
public DispatchOnChange(EGID senderID) : base(senderID) | |||
{ } | |||
public DispatchOnChange() | |||
{} | |||
public new T value | |||
{ | |||
set | |||
{ | |||
if (EqualityComparer<T>.Default.Equals(value, _value) == false) | |||
if (value.Equals(_value) == false) | |||
base.value = value; | |||
} | |||
@@ -5,16 +5,6 @@ namespace Svelto.ECS | |||
{ | |||
public class DispatchOnSet<T> where T:struct | |||
{ | |||
static ExclusiveGroup OBSOLETE_GROUP = new ExclusiveGroup(); | |||
public DispatchOnSet(int senderID) | |||
{ | |||
Console.LogWarningDebug("This method is obsolete and shouldn't be used anymore"); | |||
_senderID = new EGID(senderID, OBSOLETE_GROUP); | |||
_subscribers = new WeakEvent<EGID, T>(); | |||
} | |||
public DispatchOnSet(EGID senderID) | |||
{ | |||
_subscribers = new WeakEvent<EGID, T>(); | |||
@@ -22,11 +12,6 @@ namespace Svelto.ECS | |||
_senderID = senderID; | |||
} | |||
public DispatchOnSet() | |||
{ | |||
_subscribers = new WeakEvent<EGID, T>(); | |||
} | |||
public T value | |||
{ | |||
set | |||
@@ -36,15 +21,12 @@ namespace Svelto.ECS | |||
_subscribers.Invoke(_senderID, value); | |||
} | |||
get | |||
{ | |||
return _value; | |||
} | |||
get => _value; | |||
} | |||
public void NotifyOnValueSet(Action<EGID, T> action) | |||
{ | |||
_subscribers += action; | |||
_subscribers += action; | |||
} | |||
public void StopNotify(Action<EGID, T> action) | |||
@@ -53,8 +35,32 @@ namespace Svelto.ECS | |||
} | |||
protected T _value; | |||
readonly EGID _senderID; | |||
internal EGID _senderID; | |||
WeakEvent<EGID, T> _subscribers; | |||
} | |||
public static class DispatchExtensions | |||
{ | |||
public static DispatchOnSet<T> Setup<T>(DispatchOnSet<T> dispatcher, EGID entity) where T : struct | |||
{ | |||
if (dispatcher == null) | |||
dispatcher = new DispatchOnSet<T>(entity); | |||
else | |||
dispatcher._senderID = entity; | |||
return dispatcher; | |||
} | |||
public static DispatchOnChange<T> Setup<T>(DispatchOnChange<T> dispatcher, EGID entity) | |||
where T : struct, IEquatable<T> | |||
{ | |||
if (dispatcher == null) | |||
dispatcher = new DispatchOnChange<T>(entity); | |||
else | |||
dispatcher._senderID = entity; | |||
return dispatcher; | |||
} | |||
} | |||
} |
@@ -17,9 +17,9 @@ namespace Svelto.ECS | |||
Array.Copy(defaultEntities, 0, entitiesToBuild, 0, length); | |||
Array.Copy(extraEntities, 0, entitiesToBuild, length, extraEntities.Length); | |||
var _builder = new EntityBuilder<EntityInfoView> | |||
var _builder = new EntityBuilder<EntityStructInfoView> | |||
{ | |||
_initializer = new EntityInfoView | |||
_initializer = new EntityStructInfoView | |||
{ | |||
entitiesToBuild = entitiesToBuild, | |||
type = typeof(TType) | |||
@@ -1,3 +1,4 @@ | |||
#if !REAL_ID | |||
using System; | |||
using System.Collections.Generic; | |||
#pragma warning disable 660,661 | |||
@@ -6,17 +7,11 @@ namespace Svelto.ECS | |||
{ | |||
public struct EGID:IEquatable<EGID>,IEqualityComparer<EGID>,IComparable<EGID> | |||
{ | |||
readonly long _GID; | |||
public int entityID | |||
{ | |||
get { return (int) (_GID & 0xFFFFFFFF); } | |||
} | |||
public ExclusiveGroup.ExclusiveGroupStruct groupID | |||
{ | |||
get { return new ExclusiveGroup.ExclusiveGroupStruct((int) (_GID >> 32)); } | |||
} | |||
readonly ulong _GID; | |||
public uint entityID => (uint) (_GID & 0xFFFFFFFF); | |||
public ExclusiveGroup.ExclusiveGroupStruct groupID => new ExclusiveGroup.ExclusiveGroupStruct((uint) (_GID >> 32)); | |||
public static bool operator ==(EGID obj1, EGID obj2) | |||
{ | |||
@@ -28,26 +23,24 @@ namespace Svelto.ECS | |||
return obj1._GID != obj2._GID; | |||
} | |||
public EGID(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) : this() | |||
public EGID(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) : this() | |||
{ | |||
_GID = MAKE_GLOBAL_ID(entityID, groupID); | |||
} | |||
static long MAKE_GLOBAL_ID(int entityId, int groupId) | |||
static ulong MAKE_GLOBAL_ID(uint entityId, uint groupId) | |||
{ | |||
return (long)groupId << 32 | ((long)(uint)entityId & 0xFFFFFFFF); | |||
return (ulong)groupId << 32 | ((ulong)entityId & 0xFFFFFFFF); | |||
} | |||
public static explicit operator int(EGID id) | |||
public static explicit operator uint(EGID id) | |||
{ | |||
return id.entityID; | |||
} | |||
public static explicit operator long(EGID id) | |||
{ | |||
return id._GID; | |||
} | |||
//in the way it's used, ulong must be always the same for each id/group | |||
public static explicit operator ulong(EGID id) { return id._GID; } | |||
public bool Equals(EGID other) | |||
{ | |||
return _GID == other._GID; | |||
@@ -68,9 +61,142 @@ namespace Svelto.ECS | |||
return _GID.CompareTo(other._GID); | |||
} | |||
internal EGID(int entityID, int groupID) : this() | |||
internal EGID(uint entityID, uint groupID) : this() | |||
{ | |||
_GID = MAKE_GLOBAL_ID(entityID, groupID); | |||
} | |||
} | |||
} | |||
} | |||
#else | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
#pragma warning disable 660,661 | |||
namespace Svelto.ECS | |||
{ | |||
public struct EGID:IEquatable<EGID>,IEqualityComparer<EGID>,IComparable<EGID> | |||
{ | |||
readonly ulong _GID; | |||
const int idbits = 22; //one bit is reserved | |||
const int groupbits = 20; | |||
const int realidbits = 21; | |||
public EGID(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) : this() | |||
{ | |||
DBC.ECS.Check.Require(entityID < bit21, "the entityID value is outside the range, max value: (2^22)-1"); | |||
DBC.ECS.Check.Require(groupID < bit20, "the groupID value is outside the range"); | |||
_GID = MAKE_GLOBAL_ID(entityID, groupID, 0, 1); | |||
} | |||
const uint bit21 = 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001_1111_1111_1111_1111_1111; | |||
const uint bit22 = 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0011_1111_1111_1111_1111_1111; | |||
const uint bit20 = 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_1111_1111_1111_1111_1111; | |||
public uint entityID | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get { return (uint) (_GID & bit22); } | |||
} | |||
public ExclusiveGroup.ExclusiveGroupStruct groupID => | |||
new ExclusiveGroup.ExclusiveGroupStruct((uint) ((_GID >> idbits) & bit20)); | |||
// 1 21 20 1 21 | |||
// | | realid | groupid |R| entityID | | |||
static ulong MAKE_GLOBAL_ID(uint entityId, uint groupId, uint realId, byte hasID) | |||
{ | |||
var makeGlobalId = (((ulong)realId & bit21) << (idbits+groupbits)) | (((ulong)groupId & bit20) << idbits) | ((ulong)entityId & bit22); | |||
return makeGlobalId | (ulong) (hasID << idbits + groupbits + realidbits); | |||
} | |||
public static explicit operator uint(EGID id) | |||
{ | |||
return id.entityID; | |||
} | |||
public static bool operator ==(EGID obj1, EGID obj2) | |||
{ | |||
throw new NotSupportedException(); | |||
} | |||
public static bool operator !=(EGID obj1, EGID obj2) | |||
{ | |||
throw new NotSupportedException(); | |||
} | |||
public bool Equals(EGID other) | |||
{ | |||
throw new NotSupportedException(); | |||
} | |||
public bool Equals(EGID x, EGID y) | |||
{ | |||
throw new NotSupportedException(); | |||
} | |||
public int CompareTo(EGID other) | |||
{ | |||
throw new NotSupportedException(); | |||
} | |||
//in the way it's used, ulong must be always the same for each id/group | |||
public static explicit operator ulong(EGID id) | |||
{ | |||
throw new NotSupportedException(); | |||
} | |||
public int GetHashCode(EGID egid) | |||
{ | |||
throw new NotSupportedException(); | |||
} | |||
internal EGID(ulong GID) : this() | |||
{ | |||
_GID = GID; | |||
} | |||
internal EGID(uint entityID, uint groupID) : this() | |||
{ | |||
_GID = MAKE_GLOBAL_ID(entityID, groupID, 0, 1); | |||
} | |||
internal static EGID UPDATE_REAL_ID_AND_GROUP(EGID egid, uint toGroupID, uint realID) | |||
{ | |||
if (egid.hasID == 0) | |||
return new EGID(MAKE_GLOBAL_ID(SAFE_ID(realID), toGroupID, realID, 0)); | |||
return new EGID(MAKE_GLOBAL_ID(egid.entityID, toGroupID, realID, 1)); | |||
} | |||
internal static EGID UPDATE_REAL_ID(EGID egid, uint realID) | |||
{ | |||
if (egid.hasID == 0) | |||
return new EGID(MAKE_GLOBAL_ID(SAFE_ID(realID), egid.groupID, realID, 0)); | |||
return new EGID(MAKE_GLOBAL_ID(egid.entityID, egid.groupID, realID, 1)); | |||
} | |||
internal static EGID CREATE_WITHOUT_ID(uint toGroupID, uint realID) | |||
{ | |||
var _GID = MAKE_GLOBAL_ID(SAFE_ID(realID), toGroupID, realID, 0); | |||
return new EGID(_GID); | |||
} | |||
public byte hasID { get { return (byte) (_GID >> idbits + groupbits + realidbits); } } | |||
internal uint realID | |||
{ | |||
get { return ((uint)(_GID >> idbits + groupbits)) & bit21; } | |||
} | |||
static uint SAFE_ID(uint u) { return u | (bit21 + 1); } | |||
} | |||
} | |||
#endif |
@@ -1,16 +1,16 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public struct EGIDMapper<T> where T : IEntityStruct | |||
public struct EGIDMapper<T> where T : struct, IEntityStruct | |||
{ | |||
internal TypeSafeDictionary<T> map; | |||
public ref T entity(EGID id) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T Entity(EGID id) | |||
{ | |||
int count; | |||
var index = map.FindElementIndex(id.entityID); | |||
return ref map.GetValuesArray(out count)[index]; | |||
return ref map.FindElement(id.entityID); | |||
} | |||
} | |||
} |
@@ -1,46 +1,60 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures.Experimental; | |||
using Svelto.ECS.Internal; | |||
using Svelto.DataStructures.Experimental; | |||
using EntitiesDB = | |||
Svelto.DataStructures.Experimental.FasterDictionary<uint, System.Collections.Generic.Dictionary<System.Type, | |||
Svelto.ECS.Internal.ITypeSafeDictionary>>; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
class DoubleBufferedEntitiesToAdd<T> where T : FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>>, new() | |||
internal class DoubleBufferedEntitiesToAdd | |||
{ | |||
readonly T _entityViewsToAddBufferA = new T(); | |||
readonly T _entityViewsToAddBufferB = new T(); | |||
internal DoubleBufferedEntitiesToAdd() | |||
internal void Swap() | |||
{ | |||
other = _entityViewsToAddBufferA; | |||
current = _entityViewsToAddBufferB; | |||
Swap(ref current, ref other); | |||
Swap(ref currentEntitiesCreatedPerGroup, ref otherEntitiesCreatedPerGroup); | |||
} | |||
internal T other; | |||
internal T current; | |||
internal void Swap() | |||
void Swap<T>(ref T item1, ref T item2) | |||
{ | |||
var toSwap = other; | |||
other = current; | |||
current = toSwap; | |||
T toSwap = item2; item2 = item1; item1 = toSwap; | |||
} | |||
public void ClearOther() | |||
{ | |||
foreach (var item in other) | |||
//do not clear the groups created so far, they will be reused | |||
foreach (var groups in other) | |||
{ | |||
foreach (var subitem in item.Value) | |||
//do not remove the dictionaries of entities per type created so far, they will be reused | |||
foreach (var entitiesPerType in groups.Value) | |||
{ | |||
subitem.Value.Clear(); | |||
//clear the dictionary of entities create do far (it won't allocate though) | |||
entitiesPerType.Value.Clear(); | |||
} | |||
item.Value.Clear(); | |||
} | |||
otherEntitiesCreatedPerGroup.Clear(); | |||
} | |||
internal FasterDictionary<uint, uint> currentEntitiesCreatedPerGroup; | |||
internal FasterDictionary<uint, uint> otherEntitiesCreatedPerGroup; | |||
internal EntitiesDB current; | |||
internal EntitiesDB other; | |||
readonly EntitiesDB _entityViewsToAddBufferA = new EntitiesDB(); | |||
readonly EntitiesDB _entityViewsToAddBufferB = new EntitiesDB(); | |||
readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupA = new FasterDictionary<uint, uint>(); | |||
readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupB = new FasterDictionary<uint, uint>(); | |||
public DoubleBufferedEntitiesToAdd() | |||
{ | |||
currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA; | |||
otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB; | |||
other.Clear(); | |||
current = _entityViewsToAddBufferA; | |||
other = _entityViewsToAddBufferB; | |||
} | |||
} | |||
} |
@@ -20,22 +20,20 @@ namespace Svelto.ECS | |||
/// </summary> | |||
public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler) | |||
{ | |||
#if DEBUG && !PROFILER | |||
_entitiesOperationsDebug = new FasterDictionary<long, EntitySubmitOperationType>(); | |||
#endif | |||
_entitiesOperations = new FasterList<EntitySubmitOperation>(); | |||
_entityEngines = new Dictionary<Type, FasterList<IHandleEntityViewEngineAbstracted>>(); | |||
_entitiesOperations = new FasterDictionary<ulong, EntitySubmitOperation>(); | |||
_reactiveEnginesAddRemove = new Dictionary<Type, FasterList<IEngine>>(); | |||
_reactiveEnginesSwap = new Dictionary<Type, FasterList<IEngine>>(); | |||
_enginesSet = new HashSet<IEngine>(); | |||
_disposableEngines = new FasterList<IDisposable>(); | |||
_transientEntitiesOperations = new FasterList<EntitySubmitOperation>(); | |||
_groupEntityDB = new FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>>(); | |||
_groupsPerEntity = new Dictionary<Type, FasterDictionary<int, ITypeSafeDictionary>>(); | |||
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd<FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>>>(); | |||
_entitiesDB = new EntitiesDB(_groupEntityDB, _groupsPerEntity); | |||
_entitiesStream = new EntitiesStream(_entitiesDB); | |||
_groupEntityDB = new FasterDictionary<uint, Dictionary<Type, ITypeSafeDictionary>>(); | |||
_groupsPerEntity = new Dictionary<Type, FasterDictionary<uint, ITypeSafeDictionary>>(); | |||
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd(); | |||
_entitiesStream = new EntitiesStream(); | |||
_entitiesDB = new EntitiesDB(_groupEntityDB, _groupsPerEntity, _entitiesStream); | |||
_scheduler = entityViewScheduler; | |||
_scheduler.onTick = new WeakAction(SubmitEntityViews); | |||
} | |||
@@ -48,18 +46,18 @@ namespace Svelto.ECS | |||
try | |||
{ | |||
var viewEngine = engine as IHandleEntityViewEngineAbstracted; | |||
if (viewEngine != null) | |||
CheckEntityViewsEngine(viewEngine); | |||
if (engine is IReactOnAddAndRemove viewEngine) | |||
CheckEntityViewsEngine(viewEngine, _reactiveEnginesAddRemove); | |||
if (engine is IReactOnSwap viewEngineSwap) | |||
CheckEntityViewsEngine(viewEngineSwap, _reactiveEnginesSwap); | |||
_enginesSet.Add(engine); | |||
if (engine is IDisposable) | |||
_disposableEngines.Add(engine as IDisposable); | |||
var queryableEntityViewEngine = engine as IQueryingEntitiesEngine; | |||
if (queryableEntityViewEngine != null) | |||
if (engine is IQueryingEntitiesEngine queryableEntityViewEngine) | |||
{ | |||
queryableEntityViewEngine.entitiesDB = _entitiesDB; | |||
queryableEntityViewEngine.Ready(); | |||
@@ -75,25 +73,19 @@ namespace Svelto.ECS | |||
} | |||
} | |||
void CheckEntityViewsEngine(IEngine engine) | |||
void CheckEntityViewsEngine(IEngine engine, Dictionary<Type, FasterList<IEngine>> engines) | |||
{ | |||
var baseType = engine.GetType().GetBaseType(); | |||
var interfaces = engine.GetType().GetInterfaces(); | |||
while (baseType != _objectType) | |||
foreach (var interf in interfaces) | |||
{ | |||
if (baseType.IsGenericTypeEx()) | |||
if (interf.IsGenericTypeEx() && typeof(IReactEngine).IsAssignableFrom(interf)) | |||
{ | |||
var genericArguments = baseType.GetGenericArgumentsEx(); | |||
AddEngine(engine as IHandleEntityViewEngineAbstracted, genericArguments, _entityEngines); | |||
var genericArguments = interf.GetGenericArgumentsEx(); | |||
return; | |||
AddEngine(engine, genericArguments, engines); | |||
} | |||
baseType = baseType.GetBaseType(); | |||
} | |||
throw new ArgumentException("Not Supported Engine " + engine); | |||
} | |||
static void AddEngine<T>(T engine, Type[] entityViewTypes, | |||
@@ -109,8 +101,7 @@ namespace Svelto.ECS | |||
static void AddEngine<T>(T engine, Dictionary<Type, FasterList<T>> engines, Type type) where T : IEngine | |||
{ | |||
FasterList<T> list; | |||
if (engines.TryGetValue(type, out list) == false) | |||
if (engines.TryGetValue(type, out var list) == false) | |||
{ | |||
list = new FasterList<T>(); | |||
@@ -120,9 +111,10 @@ namespace Svelto.ECS | |||
list.Add(engine); | |||
} | |||
readonly Dictionary<Type, FasterList<IHandleEntityViewEngineAbstracted>> _entityEngines; | |||
readonly HashSet<IEngine> _enginesSet; | |||
readonly FasterList<IDisposable> _disposableEngines; | |||
readonly Dictionary<Type, FasterList<IEngine>> _reactiveEnginesAddRemove; | |||
readonly Dictionary<Type, FasterList<IEngine>> _reactiveEnginesSwap; | |||
readonly HashSet<IEngine> _enginesSet; | |||
readonly FasterList<IDisposable> _disposableEngines; | |||
//one datastructure rule them all: | |||
//split by group | |||
@@ -130,12 +122,17 @@ namespace Svelto.ECS | |||
//to the FasterDictionary capabilities OR it's possible to get a specific entityView indexed by | |||
//ID. This ID doesn't need to be the EGID, it can be just the entityID | |||
//for each group id, save a dictionary indexed by entity type of entities indexed by id | |||
readonly FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>> _groupEntityDB; | |||
readonly EntitiesDB _entitiesDB; | |||
//ITypeSafeDictionary = Key = entityID, Value = EntityStruct | |||
readonly FasterDictionary<uint, Dictionary<Type, ITypeSafeDictionary>> _groupEntityDB; | |||
//for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are | |||
//found indexed by group id | |||
readonly Dictionary<Type, FasterDictionary<int, ITypeSafeDictionary>> _groupsPerEntity; //yes I am being sarcastic | |||
//found indexed by group id | |||
//EntityViewType //groupID //entityID, EntityStruct | |||
readonly Dictionary<Type, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity; | |||
readonly EntitiesStream _entitiesStream; | |||
readonly EntitiesDB _entitiesDB; | |||
static readonly Type _objectType = typeof(object); | |||
static readonly Type OBJECT_TYPE = typeof(object); | |||
static readonly Type ENTITY_INFO_VIEW_TYPE = typeof(EntityStructInfoView); | |||
} | |||
} |
@@ -1,12 +1,13 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures.Experimental; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot: IDisposable | |||
public partial class EnginesRoot : IDisposable | |||
{ | |||
/// <summary> | |||
/// Dispose an EngineRoot once not used anymore, so that all the | |||
@@ -23,7 +24,7 @@ namespace Svelto.ECS | |||
{ | |||
try | |||
{ | |||
entityList.Value.RemoveEntitiesFromEngines(_entityEngines, ref profiler); | |||
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, ref profiler); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -48,17 +49,17 @@ namespace Svelto.ECS | |||
~EnginesRoot() | |||
{ | |||
Console.LogWarning("Engines Root has been garbage collected, don't forget to call Dispose()!"); | |||
Dispose(); | |||
} | |||
///-------------------------------------------- | |||
/// | |||
public IEntitiesStream GenerateEntityStream() | |||
public IEntityStreamConsumerFactory GenerateConsumerFactory() | |||
{ | |||
return new GenericEntitiesStream(new DataStructures.WeakReference<EnginesRoot>(this)); | |||
return new GenericentityStreamConsumerFactory(new DataStructures.WeakReference<EnginesRoot>(this)); | |||
} | |||
public IEntityFactory GenerateEntityFactory() | |||
{ | |||
return new GenericEntityFactory(new DataStructures.WeakReference<EnginesRoot>(this)); | |||
@@ -70,155 +71,171 @@ namespace Svelto.ECS | |||
} | |||
///-------------------------------------------- | |||
EntityStructInitializer BuildEntity<T>(EGID entityID, object[] implementors) | |||
where T : IEntityDescriptor, new() | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
EntityStructInitializer BuildEntity<T>(EGID entityID, object[] implementors) where T : IEntityDescriptor, new() | |||
{ | |||
return BuildEntity(entityID, EntityDescriptorTemplate<T>.descriptor, implementors); | |||
} | |||
EntityStructInitializer BuildEntity<T>(EGID entityID, | |||
T entityDescriptor, | |||
object[] implementors) where T:IEntityDescriptor | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
EntityStructInitializer BuildEntity<T>(EGID entityID, T entityDescriptor, object[] implementors) | |||
where T : IEntityDescriptor | |||
{ | |||
var descriptorEntitiesToBuild = entityDescriptor.entitiesToBuild; | |||
CheckAddEntityID(entityID, entityDescriptor); | |||
var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd.current, | |||
var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, | |||
descriptorEntitiesToBuild, implementors); | |||
return new EntityStructInitializer(entityID, dic); | |||
} | |||
///-------------------------------------------- | |||
void Preallocate<T>(int groupID, int size) where T : IEntityDescriptor, new() | |||
#if REAL_ID | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
EntityStructInitializer BuildEntity<T>(ExclusiveGroup.ExclusiveGroupStruct groupID, object[] implementors) where T : IEntityDescriptor, new() | |||
{ | |||
//temporary egid, will change during the real submission, needed for the initialization look up | |||
var egid = EGID.CREATE_WITHOUT_ID(groupID, _groupedEntityToAdd.entitiesBuiltThisSubmission++); | |||
var dic = EntityFactory.BuildGroupedEntities(egid, _groupedEntityToAdd.current, | |||
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, implementors); | |||
return new EntityStructInitializer(egid, dic.groups); | |||
} | |||
#endif | |||
///-------------------------------------------- | |||
void Preallocate<T>(uint groupID, uint size) where T : IEntityDescriptor, new() | |||
{ | |||
var entityViewsToBuild = EntityDescriptorTemplate<T>.descriptor.entitiesToBuild; | |||
var count = entityViewsToBuild.Length; | |||
var numberOfEntityViews = entityViewsToBuild.Length; | |||
//reserve space in the database | |||
Dictionary<Type, ITypeSafeDictionary> group; | |||
if (_groupEntityDB.TryGetValue(groupID, out group) == false) | |||
if (_groupEntityDB.TryGetValue(groupID, out var @group) == false) | |||
group = _groupEntityDB[groupID] = new Dictionary<Type, ITypeSafeDictionary>(); | |||
//reserve space in building buffer | |||
Dictionary<Type, ITypeSafeDictionary> groupBuffer; | |||
if (_groupedEntityToAdd.current.TryGetValue(groupID, out groupBuffer) == false) | |||
groupBuffer = _groupedEntityToAdd.current[groupID] = new Dictionary<Type, ITypeSafeDictionary>(); | |||
for (var index = 0; index < count; index++) | |||
for (var index = 0; index < numberOfEntityViews; index++) | |||
{ | |||
var entityViewBuilder = entityViewsToBuild[index]; | |||
var entityViewType = entityViewBuilder.GetEntityType(); | |||
var entityViewType = entityViewBuilder.GetEntityType(); | |||
ITypeSafeDictionary dbList; | |||
if (group.TryGetValue(entityViewType, out dbList) == false) | |||
if (group.TryGetValue(entityViewType, out var dbList) == false) | |||
group[entityViewType] = entityViewBuilder.Preallocate(ref dbList, size); | |||
else | |||
dbList.AddCapacity(size); | |||
if (groupBuffer.TryGetValue(entityViewType, out dbList) == false) | |||
groupBuffer[entityViewType] = entityViewBuilder.Preallocate(ref dbList, size); | |||
else | |||
dbList.AddCapacity(size); | |||
dbList.SetCapacity(size); | |||
if (_groupsPerEntity.TryGetValue(entityViewType, out var groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[entityViewType] = new FasterDictionary<uint, ITypeSafeDictionary>(); | |||
groupedGroup[groupID] = dbList; | |||
} | |||
} | |||
///-------------------------------------------- | |||
/// | |||
void MoveEntity(IEntityBuilder[] entityBuilders, EGID entityGID, Type originalDescriptorType, EGID toEntityGID, | |||
Dictionary<Type, ITypeSafeDictionary> toGroup = null) | |||
void MoveEntity(IEntityBuilder[] entityBuilders, EGID fromEntityGID, Type originalDescriptorType, EGID? toEntityGID) | |||
{ | |||
var profiler = new PlatformProfiler(); | |||
using (profiler.StartNewSession("Move Entity")) | |||
{ | |||
//for each entity view generated by the entity descriptor | |||
Dictionary<Type, ITypeSafeDictionary> fromGroup; | |||
if (_groupEntityDB.TryGetValue(entityGID.groupID, out fromGroup) == false) | |||
throw new ECSException("from group not found eid: " | |||
.FastConcat(entityGID.entityID).FastConcat(" group: ") | |||
.FastConcat(entityGID.groupID)); | |||
ITypeSafeDictionary entityInfoViewDic; | |||
EntityInfoView entityInfoView = default; | |||
if (_groupEntityDB.TryGetValue(fromEntityGID.groupID, out var fromGroup) == false) | |||
throw new ECSException("from group not found eid: ".FastConcat(fromEntityGID.entityID) | |||
.FastConcat(" group: ").FastConcat(fromEntityGID.groupID)); | |||
//Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor! | |||
bool correctEntityDescriptorFound = true; | |||
if (fromGroup.TryGetValue(_entityInfoView, out entityInfoViewDic) | |||
&& (entityInfoViewDic as TypeSafeDictionary<EntityInfoView>).TryGetValue | |||
(entityGID.entityID, out entityInfoView) && | |||
(correctEntityDescriptorFound = entityInfoView.type == originalDescriptorType)) | |||
EntityStructInfoView entityInfoView = default; | |||
if (fromGroup.TryGetValue(ENTITY_INFO_VIEW_TYPE, out var entityInfoViewDic) && | |||
(entityInfoViewDic as TypeSafeDictionary<EntityStructInfoView>).TryGetValue( | |||
fromEntityGID.entityID, out entityInfoView) && (correctEntityDescriptorFound = | |||
entityInfoView.type == originalDescriptorType)) | |||
{ | |||
var entitiesToMove = entityInfoView.entitiesToBuild; | |||
Dictionary<Type, ITypeSafeDictionary> toGroup = null; | |||
if (toEntityGID != null) | |||
{ | |||
var toGroupID = toEntityGID.Value.groupID; | |||
if (_groupEntityDB.TryGetValue(toGroupID, out toGroup) == false) | |||
toGroup = _groupEntityDB[toGroupID] = new Dictionary<Type, ITypeSafeDictionary>(); | |||
} | |||
for (int i = 0; i < entitiesToMove.Length; i++) | |||
MoveEntityView(entityGID, toEntityGID, toGroup, fromGroup, entitiesToMove[i].GetEntityType(), profiler); | |||
MoveEntityView(fromEntityGID, toEntityGID, toGroup, ref fromGroup, | |||
entitiesToMove[i].GetEntityType(), profiler); | |||
} | |||
//otherwise it's a normal static entity descriptor | |||
else | |||
{ | |||
#if DEBUG && !PROFILER | |||
if (correctEntityDescriptorFound == false) | |||
#if RELAXED_ECS | |||
Console.LogError(INVALID_DYNAMIC_DESCRIPTOR_ERROR | |||
.FastConcat(" ID ").FastConcat(entityGID.entityID) | |||
.FastConcat(" group ID ").FastConcat(entityGID.groupID).FastConcat( | |||
" descriptor found: ", entityInfoView.type.Name, | |||
" descriptor Excepted ", originalDescriptorType.Name)); | |||
#else | |||
throw new ECSException(INVALID_DYNAMIC_DESCRIPTOR_ERROR.FastConcat(" ID ").FastConcat(entityGID.entityID) | |||
.FastConcat(" group ID ").FastConcat(entityGID.groupID).FastConcat( | |||
throw new ECSException(INVALID_DYNAMIC_DESCRIPTOR_ERROR.FastConcat(" ID ").FastConcat(fromEntityGID.entityID) | |||
.FastConcat(" group ID ").FastConcat(fromEntityGID.groupID).FastConcat( | |||
" descriptor found: ", entityInfoView.type.Name, " descriptor Excepted ", | |||
originalDescriptorType.Name)); | |||
#endif | |||
#endif | |||
Dictionary<Type, ITypeSafeDictionary> toGroup = null; | |||
if (toEntityGID != null) | |||
{ | |||
var toGroupID = toEntityGID.Value.groupID; | |||
if (_groupEntityDB.TryGetValue(toGroupID, out toGroup) == false) | |||
toGroup = _groupEntityDB[toGroupID] = new Dictionary<Type, ITypeSafeDictionary>(); | |||
} | |||
for (var i = 0; i < entityBuilders.Length; i++) | |||
MoveEntityView(entityGID, toEntityGID, toGroup, fromGroup, entityBuilders[i].GetEntityType(), profiler); | |||
MoveEntityView(fromEntityGID, toEntityGID, toGroup, ref fromGroup, | |||
entityBuilders[i].GetEntityType(), profiler); | |||
} | |||
} | |||
} | |||
void MoveEntityView(EGID entityGID, EGID toEntityGID, Dictionary<Type, ITypeSafeDictionary> toGroup, | |||
Dictionary<Type, ITypeSafeDictionary> fromGroup, Type entityType, PlatformProfiler profiler) | |||
void MoveEntityView(EGID entityGID, EGID? toEntityGID, Dictionary<Type, ITypeSafeDictionary> toGroup, | |||
ref Dictionary<Type, ITypeSafeDictionary> fromGroup, Type entityViewType, PlatformProfiler profiler) | |||
{ | |||
ITypeSafeDictionary fromTypeSafeDictionary; | |||
if (fromGroup.TryGetValue(entityType, out fromTypeSafeDictionary) == false) | |||
if (fromGroup.TryGetValue(entityViewType, out var fromTypeSafeDictionary) == false) | |||
{ | |||
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID).FastConcat(" group: ").FastConcat(entityGID.groupID)); | |||
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID) | |||
.FastConcat(" group: ").FastConcat(entityGID.groupID)); | |||
} | |||
ITypeSafeDictionary dictionaryOfEntities = null; | |||
ITypeSafeDictionary toEntitiesDictionary = null; | |||
//in case we want to move to a new group, otherwise is just a remove | |||
if (toGroup != null) | |||
if (toEntityGID != null) | |||
{ | |||
if (toGroup.TryGetValue(entityType, out dictionaryOfEntities) == false) | |||
if (toGroup.TryGetValue(entityViewType, out toEntitiesDictionary) == false) | |||
{ | |||
dictionaryOfEntities = fromTypeSafeDictionary.Create(); | |||
toGroup.Add(entityType, dictionaryOfEntities); | |||
toEntitiesDictionary = fromTypeSafeDictionary.Create(); | |||
toGroup.Add(entityViewType, toEntitiesDictionary); | |||
} | |||
FasterDictionary<int, ITypeSafeDictionary> groupedGroup; | |||
if (_groupsPerEntity.TryGetValue(entityType, out groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[entityType] = new FasterDictionary<int, ITypeSafeDictionary>(); | |||
groupedGroup[toEntityGID.groupID] = dictionaryOfEntities; | |||
if (_groupsPerEntity.TryGetValue(entityViewType, out var groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[entityViewType] = new FasterDictionary<uint, ITypeSafeDictionary>(); | |||
groupedGroup[toEntityGID.Value.groupID] = toEntitiesDictionary; | |||
} | |||
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) | |||
{ | |||
throw new EntityNotFoundException(entityGID.entityID, entityGID.groupID, entityType); | |||
throw new EntityNotFoundException(entityGID, entityViewType); | |||
} | |||
fromTypeSafeDictionary.MoveEntityFromDictionaryAndEngines(entityGID, toEntityGID, dictionaryOfEntities, | |||
_entityEngines, ref profiler); | |||
fromTypeSafeDictionary.MoveEntityFromDictionaryAndEngines(entityGID, toEntityGID, | |||
toEntitiesDictionary, | |||
toEntityGID == null ? _reactiveEnginesAddRemove : | |||
_reactiveEnginesSwap, | |||
ref profiler); | |||
if (fromTypeSafeDictionary.Count == 0) //clean up | |||
{ | |||
_groupsPerEntity[entityType].Remove(entityGID.groupID); | |||
_groupsPerEntity[entityViewType].Remove(entityGID.groupID); | |||
//I don't remove the group if empty on purpose, in case it needs to be reused however I trim it to save | |||
//memory | |||
@@ -226,24 +243,25 @@ namespace Svelto.ECS | |||
} | |||
} | |||
void RemoveGroupAndEntitiesFromDB(int groupID, Type entityDescriptor) | |||
void RemoveGroupAndEntitiesFromDB(uint groupID, Type entityDescriptor) | |||
{ | |||
/* var profiler = new PlatformProfiler(); | |||
using (profiler.StartNewSession("Remove Group Of Entities")) | |||
{ | |||
FasterDictionary<int, ITypeSafeDictionary> @group; | |||
if (_groupsPerEntity.TryGetValue(entityDescriptor, out group)) | |||
{ | |||
if (group.TryGetValue()) | |||
foreach (var entity in group) | |||
{ | |||
MoveEntity(entity.); | |||
} | |||
} | |||
}*/ | |||
throw new NotImplementedException(); | |||
/* var profiler = new PlatformProfiler(); | |||
using (profiler.StartNewSession("Remove Group Of Entities")) | |||
{ | |||
FasterDictionary<int, ITypeSafeDictionary> @group; | |||
if (_groupsPerEntity.TryGetValue(entityDescriptor, out group)) | |||
{ | |||
if (group.TryGetValue()) | |||
foreach (var entity in group) | |||
{ | |||
MoveEntity(entity.); | |||
} | |||
} | |||
}*/ | |||
} | |||
void RemoveGroupAndEntitiesFromDB(int groupID) | |||
void RemoveGroupAndEntitiesFromDB(uint groupID) | |||
{ | |||
var profiler = new PlatformProfiler(); | |||
using (profiler.StartNewSession("Remove Group")) | |||
@@ -252,7 +270,7 @@ namespace Svelto.ECS | |||
foreach (var dictionaryOfEntities in dictionariesOfEntities) | |||
{ | |||
var platformProfiler = profiler; | |||
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_entityEngines, ref platformProfiler); | |||
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, ref platformProfiler); | |||
var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key]; | |||
groupedGroupOfEntities.Remove(groupID); | |||
} | |||
@@ -264,25 +282,23 @@ namespace Svelto.ECS | |||
} | |||
///-------------------------------------------- | |||
void SwapEntityGroup(IEntityBuilder[] builders, Type originalEntityDescriptor, EGID fromEntityID, EGID toEntityID) | |||
void SwapEntityGroup(IEntityBuilder[] builders, Type originalEntityDescriptor, EGID fromEntityID, | |||
EGID toEntityID) | |||
{ | |||
DBC.ECS.Check.Require(fromEntityID != toEntityID, "the entity destionation EGID is equal to the source EGID"); | |||
Dictionary<Type, ITypeSafeDictionary> toGroup; | |||
DBC.ECS.Check.Require(fromEntityID.groupID != toEntityID.groupID, | |||
"entity group is the same of the destination group"); | |||
if (_groupEntityDB.TryGetValue(toEntityID.groupID, out toGroup) == false) | |||
toGroup = _groupEntityDB[toEntityID.groupID] = new Dictionary<Type, ITypeSafeDictionary>(); | |||
MoveEntity(builders, fromEntityID, originalEntityDescriptor, toEntityID); | |||
} | |||
MoveEntity(builders, fromEntityID, originalEntityDescriptor, toEntityID, toGroup); | |||
internal Consumer<T> GenerateConsumer<T>(string name, int capacity) where T : unmanaged, IEntityStruct | |||
{ | |||
return _entitiesStream.GenerateConsumer<T>(name, capacity); | |||
} | |||
readonly EntitiesStream _entitiesStream; | |||
readonly Type _entityInfoView = typeof(EntityInfoView); | |||
const string INVALID_DYNAMIC_DESCRIPTOR_ERROR = "Found an entity requesting an invalid dynamic descriptor, this " + | |||
"can happen only if you are building different entities with the " + | |||
"same ID in the same group! The operation will continue using" + | |||
"the base descriptor only "; | |||
const string INVALID_DYNAMIC_DESCRIPTOR_ERROR = | |||
"Found an entity requesting an invalid dynamic descriptor, this " + | |||
"can happen only if you are building different entities with the " + | |||
"same ID in the same group! The operation will continue using" + "the base descriptor only "; | |||
} | |||
} |
@@ -1,25 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
class GenericEntitiesStream : IEntitiesStream | |||
{ | |||
public GenericEntitiesStream(DataStructures.WeakReference<EnginesRoot> weakReference) | |||
{ | |||
_weakEngine = weakReference; | |||
} | |||
public Consumer<T> GenerateConsumer<T>(int capacity) where T : unmanaged, IEntityStruct | |||
{ | |||
return _weakEngine.Target._entitiesStream.GenerateConsumer<T>(capacity); | |||
} | |||
public void PublishEntity<T>(EGID id) where T : unmanaged, IEntityStruct | |||
{ | |||
_weakEngine.Target._entitiesStream.PublishEntity<T>(id); | |||
} | |||
readonly DataStructures.WeakReference<EnginesRoot> _weakEngine; | |||
} | |||
} | |||
} |
@@ -9,9 +9,9 @@ | |||
_weakEngine = weakReference; | |||
} | |||
public EntityStructInitializer BuildEntity<T>(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, object[] implementors) where T : IEntityDescriptor, new() | |||
public EntityStructInitializer BuildEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, object[] implementors) where T : IEntityDescriptor, new() | |||
{ | |||
return _weakEngine.Target.BuildEntity<T>(new EGID(entityID, (int)groupStructId), implementors); | |||
return _weakEngine.Target.BuildEntity<T>(new EGID(entityID, groupStructId), implementors); | |||
} | |||
public EntityStructInitializer BuildEntity<T>(EGID egid, object[] implementors) where T : IEntityDescriptor, new() | |||
@@ -19,17 +19,24 @@ | |||
return _weakEngine.Target.BuildEntity<T>(egid, implementors); | |||
} | |||
#if REAL_ID | |||
public EntityStructInitializer BuildEntity<T>(ExclusiveGroup.ExclusiveGroupStruct groupID, object[] implementors = null) where T : IEntityDescriptor, new() | |||
{ | |||
return _weakEngine.Target.BuildEntity<T>(groupID, implementors); | |||
} | |||
#endif | |||
public EntityStructInitializer BuildEntity<T>(EGID egid, T entityDescriptor, object[] implementors) where T:IEntityDescriptor | |||
{ | |||
return _weakEngine.Target.BuildEntity(egid, entityDescriptor, implementors); | |||
} | |||
public EntityStructInitializer BuildEntity<T>(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, object[] implementors) where T:IEntityDescriptor | |||
public EntityStructInitializer BuildEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, object[] implementors) where T:IEntityDescriptor | |||
{ | |||
return _weakEngine.Target.BuildEntity(new EGID(entityID, (int)groupStructId), descriptorEntity, implementors); | |||
return _weakEngine.Target.BuildEntity(new EGID(entityID, groupStructId), descriptorEntity, implementors); | |||
} | |||
public void PreallocateEntitySpace<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, int size) where T : IEntityDescriptor, new() | |||
public void PreallocateEntitySpace<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) where T : IEntityDescriptor, new() | |||
{ | |||
_weakEngine.Target.Preallocate<T>(groupStructId, size); | |||
} | |||
@@ -1,11 +1,12 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
class GenericEntityFunctions : IEntityFunctions | |||
sealed class GenericEntityFunctions : IEntityFunctions | |||
{ | |||
readonly DataStructures.WeakReference<EnginesRoot> _weakReference; | |||
@@ -14,90 +15,98 @@ namespace Svelto.ECS | |||
_weakReference = weakReference; | |||
} | |||
public void RemoveEntity<T>(int entityID, int groupID) where T : IEntityDescriptor, new() | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveEntity<T>(uint entityID, uint groupID) where T : IEntityDescriptor, new() | |||
{ | |||
RemoveEntity<T>(new EGID(entityID, groupID)); | |||
} | |||
public void RemoveEntity<T>(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : | |||
IEntityDescriptor, new() | |||
{ | |||
RemoveEntity<T>(new EGID(entityID, groupID)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveEntity<T>(EGID entityEGID) where T : IEntityDescriptor, new() | |||
{ | |||
_weakReference.Target.CheckRemoveEntityID(entityEGID, EntityDescriptorTemplate<T>.descriptor); | |||
_weakReference.Target.QueueEntitySubmitOperation<T>( | |||
new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID.entityID, entityEGID.entityID, | |||
entityEGID.groupID, | |||
-1, EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, typeof(T))); | |||
new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID, | |||
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, typeof(T))); | |||
} | |||
public void RemoveEntities<T>(int groupID) where T : IEntityDescriptor, new() | |||
public void RemoveEntities<T>(uint groupID) where T : IEntityDescriptor, new() | |||
{ | |||
throw new NotImplementedException(); | |||
//_weakReference.Target.QueueEntitySubmitOperation( | |||
// new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, -1, -1, groupID, -1, null, typeof(T))); | |||
} | |||
public void RemoveEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupID) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
throw new NotImplementedException(); | |||
//_weakReference.Target.QueueEntitySubmitOperation( | |||
// new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, -1, -1, groupID, -1, null, typeof(T))); | |||
} | |||
public void RemoveGroupAndEntities(int groupID) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveGroupAndEntities(uint groupID) | |||
{ | |||
_weakReference.Target.QueueEntitySubmitOperation( | |||
new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, -1, -1, groupID, -1, null, null)); | |||
new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(), new EGID(0, groupID))); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) | |||
{ | |||
RemoveGroupAndEntities((int)groupID); | |||
RemoveGroupAndEntities((uint)groupID); | |||
} | |||
public void SwapEntityGroup<T>(int entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, | |||
ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new() | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, | |||
ExclusiveGroup.ExclusiveGroupStruct toGroupID) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
SwapEntityGroup<T>(new EGID(entityID, fromGroupID), toGroupID); | |||
} | |||
public void SwapEntityGroup<T>(EGID id, ExclusiveGroup.ExclusiveGroupStruct toGroupID) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
SwapEntityGroup<T>(id, new EGID(id.entityID, toGroupID)); | |||
SwapEntityGroup<T>(fromID, new EGID(fromID.entityID, (uint)toGroupID)); | |||
} | |||
public void SwapEntityGroup<T>(EGID id, ExclusiveGroup.ExclusiveGroupStruct toGroupID | |||
, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new() | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID | |||
, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
if (id.groupID != mustBeFromGroup) | |||
if (fromID.groupID != mustBeFromGroup) | |||
throw new ECSException("Entity is not coming from the expected group"); | |||
SwapEntityGroup<T>(id, toGroupID); | |||
SwapEntityGroup<T>(fromID, toGroupID); | |||
} | |||
public void SwapEntityGroup<T>(EGID id, EGID toID) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(EGID fromID, EGID toID) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
_weakReference.Target.QueueEntitySubmitOperation<T>( | |||
new EntitySubmitOperation(EntitySubmitOperationType.Swap, | |||
id.entityID, toID.entityID, id.groupID, toID.groupID, | |||
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, typeof(T))); | |||
fromID, toID, EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, typeof(T))); | |||
} | |||
public void SwapEntityGroup<T>(EGID id, EGID toID | |||
, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new() | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(EGID fromID, EGID toID | |||
, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
if (id.groupID != mustBeFromGroup) | |||
if (fromID.groupID != mustBeFromGroup) | |||
throw new ECSException("Entity is not coming from the expected group"); | |||
SwapEntityGroup<T>(id, toID); | |||
SwapEntityGroup<T>(fromID, toID); | |||
} | |||
} | |||
@@ -106,30 +115,28 @@ namespace Svelto.ECS | |||
#if DEBUG && !PROFILER | |||
entitySubmitOperation.trace = Environment.StackTrace; | |||
#endif | |||
_entitiesOperations.AddRef(ref entitySubmitOperation); | |||
_entitiesOperations.Add((ulong)entitySubmitOperation.fromID, ref entitySubmitOperation); | |||
} | |||
void QueueEntitySubmitOperation<T>(EntitySubmitOperation entitySubmitOperation) where T:IEntityDescriptor | |||
{ | |||
#if DEBUG && !PROFILER | |||
#if DEBUG && !PROFILER | |||
entitySubmitOperation.trace = Environment.StackTrace; | |||
var egid = new EGID(entitySubmitOperation.ID, entitySubmitOperation.fromGroupID); | |||
if (_entitiesOperationsDebug.ContainsKey((long)egid) == true) | |||
Console.LogError("Only one entity operation per submission is allowed. id: " | |||
.FastConcat(entitySubmitOperation.ID) | |||
.FastConcat(" groupid: ") | |||
.FastConcat(entitySubmitOperation.fromGroupID) | |||
.FastConcat(" entityType: ") | |||
.FastConcat(typeof(T).Name) | |||
.FastConcat(" submission type ", entitySubmitOperation.type.ToString(), | |||
" previous type: ", _entitiesOperationsDebug[(long)egid].ToString())); | |||
if (_entitiesOperations.TryGetValue((ulong) entitySubmitOperation.fromID, out var entitySubmitedOperation) == true) | |||
{ | |||
if (entitySubmitedOperation != entitySubmitOperation) | |||
Console.LogError("Only one entity operation per submission is allowed".FastConcat(" entityViewType: ") | |||
.FastConcat(typeof(T).Name) | |||
.FastConcat(" submission type ", entitySubmitOperation.type.ToString(), | |||
" from ID: ", entitySubmitOperation.fromID.entityID.ToString()) | |||
.FastConcat( " previous operation type: ", | |||
_entitiesOperations[(ulong) entitySubmitOperation.fromID].type | |||
.ToString())); | |||
} | |||
else | |||
_entitiesOperationsDebug[(long)egid] = entitySubmitOperation.type; | |||
#endif | |||
_entitiesOperations.AddRef(ref entitySubmitOperation); | |||
_entitiesOperations.Set((ulong)entitySubmitOperation.fromID, ref entitySubmitOperation); | |||
} | |||
#if DEBUG && !PROFILER | |||
readonly Svelto.DataStructures.Experimental.FasterDictionary<long, EntitySubmitOperationType> _entitiesOperationsDebug; | |||
#endif | |||
} | |||
} |
@@ -10,6 +10,8 @@ namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
readonly FasterList<EntitySubmitOperation> _transientEntitiesOperations; | |||
void SubmitEntityViews() | |||
{ | |||
var profiler = new PlatformProfiler(); | |||
@@ -19,11 +21,9 @@ namespace Svelto.ECS | |||
{ | |||
using (profiler.Sample("Remove and Swap operations")) | |||
{ | |||
#if DEBUG && !PROFILER | |||
_entitiesOperationsDebug.Clear(); | |||
#endif | |||
_transientEntitiesOperations.FastClear(); | |||
_transientEntitiesOperations.AddRange(_entitiesOperations); | |||
var entitySubmitOperations = _entitiesOperations.GetValuesArray(out var count); | |||
_transientEntitiesOperations.AddRange(entitySubmitOperations, count); | |||
_entitiesOperations.FastClear(); | |||
var entitiesOperations = _transientEntitiesOperations.ToArrayFast(); | |||
@@ -36,22 +36,19 @@ namespace Svelto.ECS | |||
case EntitySubmitOperationType.Swap: | |||
SwapEntityGroup(entitiesOperations[i].builders, | |||
entitiesOperations[i].entityDescriptor, | |||
new EGID(entitiesOperations[i].ID, | |||
entitiesOperations[i].fromGroupID), | |||
new EGID(entitiesOperations[i].toID, | |||
entitiesOperations[i].toGroupID)); | |||
entitiesOperations[i].fromID, | |||
entitiesOperations[i].toID); | |||
break; | |||
case EntitySubmitOperationType.Remove: | |||
MoveEntity(entitiesOperations[i].builders, | |||
new EGID(entitiesOperations[i].ID, | |||
entitiesOperations[i].fromGroupID), | |||
entitiesOperations[i].entityDescriptor, new EGID()); | |||
entitiesOperations[i].fromID, | |||
entitiesOperations[i].entityDescriptor, null); | |||
break; | |||
case EntitySubmitOperationType.RemoveGroup: | |||
if (entitiesOperations[i].entityDescriptor == null) | |||
RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromGroupID); | |||
RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromID.groupID); | |||
else | |||
RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromGroupID, | |||
RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromID.groupID, | |||
entitiesOperations[i].entityDescriptor); | |||
break; | |||
@@ -59,37 +56,27 @@ namespace Svelto.ECS | |||
} | |||
catch (Exception e) | |||
{ | |||
#if DEBUG && !PROFILER | |||
var str = "Crash while executing Entity Operation" | |||
.FastConcat(entitiesOperations[i].type.ToString()).FastConcat(" id: ") | |||
.FastConcat(entitiesOperations[i].ID).FastConcat(" to id: ") | |||
.FastConcat(entitiesOperations[i].toID).FastConcat(" from groupid: ") | |||
.FastConcat(entitiesOperations[i].fromGroupID).FastConcat(" to groupid: ") | |||
.FastConcat(entitiesOperations[i].toGroupID); | |||
#if RELAXED_ECS | |||
Console.LogException(str.FastConcat(" ", entitiesOperations[i].trace), e); | |||
.FastConcat(entitiesOperations[i].type.ToString()); | |||
#if RELAXED_ECS && !PROFILER | |||
Console.LogException(str.FastConcat(" " | |||
#if DEBUG && !PROFILER | |||
, entitiesOperations[i].trace | |||
#endif | |||
), e); | |||
#else | |||
throw new ECSException(str.FastConcat(" ").FastConcat(entitiesOperations[i].trace), e); | |||
throw new ECSException(str.FastConcat(" ") | |||
#if DEBUG && !PROFILER | |||
.FastConcat(entitiesOperations[i].trace) | |||
#endif | |||
#else | |||
var str = "Entity Operation is ".FastConcat(entitiesOperations[i].type.ToString()) | |||
.FastConcat(" id: ") | |||
.FastConcat(entitiesOperations[i].ID) | |||
.FastConcat(" to id: ") | |||
.FastConcat(entitiesOperations[i].toID) | |||
.FastConcat(" from groupid: ") | |||
.FastConcat(entitiesOperations[i].fromGroupID) | |||
.FastConcat(" to groupid: ") | |||
.FastConcat(entitiesOperations[i].toGroupID); | |||
Console.LogException(str, e); | |||
, e); | |||
#endif | |||
} | |||
} | |||
} | |||
} | |||
if (_groupedEntityToAdd.current.Count > 0) | |||
if (_groupedEntityToAdd.currentEntitiesCreatedPerGroup.Count > 0) | |||
{ | |||
using (profiler.Sample("Add operations")) | |||
{ | |||
@@ -98,18 +85,12 @@ namespace Svelto.ECS | |||
try | |||
{ | |||
//Note: if N entity of the same type are added on the same frame the Add callback is called N | |||
//times on the same frame. if the Add callback builds a new entity, that entity will not | |||
//be available in the database until the N callbacks are done solving it could be complicated as | |||
//callback and database update must be interleaved. | |||
AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd.other, profiler); | |||
//Note: if N entity of the same type are added on the same frame the Add callback is called | |||
//N times on the same frame. if the Add callback builds a new entity, that entity will not | |||
//be available in the database until the N callbacks are done. Solving this could be | |||
//complicated as callback and database update must be interleaved. | |||
AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd, profiler); | |||
} | |||
#if !DEBUG | |||
catch (Exception e) | |||
{ | |||
Console.LogException(e); | |||
} | |||
#endif | |||
finally | |||
{ | |||
//other can be cleared now, but let's avoid deleting the dictionary every time | |||
@@ -120,57 +101,63 @@ namespace Svelto.ECS | |||
} | |||
} | |||
void AddEntityViewsToTheDBAndSuitableEngines( | |||
FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>> groupsOfEntitiesToSubmit, | |||
PlatformProfiler profiler) | |||
void AddEntityViewsToTheDBAndSuitableEngines(DoubleBufferedEntitiesToAdd dbgroupsOfEntitiesToSubmit, | |||
PlatformProfiler profiler) | |||
{ | |||
//each group is indexed by entity view type. for each type there is a dictionary indexed by entityID | |||
var groupsOfEntitiesToSubmit = dbgroupsOfEntitiesToSubmit.other; | |||
foreach (var groupOfEntitiesToSubmit in groupsOfEntitiesToSubmit) | |||
{ | |||
Dictionary<Type, ITypeSafeDictionary> groupDB; | |||
int groupID = groupOfEntitiesToSubmit.Key; | |||
{ | |||
var groupID = groupOfEntitiesToSubmit.Key; | |||
if (dbgroupsOfEntitiesToSubmit.otherEntitiesCreatedPerGroup.ContainsKey(groupID) == false) continue; | |||
//if the group doesn't exist in the current DB let's create it first | |||
if (_groupEntityDB.TryGetValue(groupID, out groupDB) == false) | |||
if (_groupEntityDB.TryGetValue(groupID, out var groupDB) == false) | |||
groupDB = _groupEntityDB[groupID] = new Dictionary<Type, ITypeSafeDictionary>(); | |||
//add the entityViews in the group | |||
foreach (var entityViewTypeSafeDictionary in groupOfEntitiesToSubmit.Value) | |||
foreach (var entityViewsToSubmit in groupOfEntitiesToSubmit.Value) | |||
{ | |||
ITypeSafeDictionary dbDic; | |||
FasterDictionary<int, ITypeSafeDictionary> groupedGroup = null; | |||
if (groupDB.TryGetValue(entityViewTypeSafeDictionary.Key, out dbDic) == false) | |||
dbDic = groupDB[entityViewTypeSafeDictionary.Key] = entityViewTypeSafeDictionary.Value.Create(); | |||
if (_groupsPerEntity.TryGetValue(entityViewTypeSafeDictionary.Key, out groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[entityViewTypeSafeDictionary.Key] = | |||
new FasterDictionary<int, ITypeSafeDictionary>(); | |||
var type = entityViewsToSubmit.Key; | |||
var typeSafeDictionary = entityViewsToSubmit.Value; | |||
if (groupDB.TryGetValue(type, out var dbDic) == false) | |||
dbDic = groupDB[type] = typeSafeDictionary.Create(); | |||
//Fill the DB with the entity views generate this frame. | |||
dbDic.FillWithIndexedEntities(entityViewTypeSafeDictionary.Value); | |||
dbDic.AddEntitiesFromDictionary(typeSafeDictionary, groupID); | |||
if (_groupsPerEntity.TryGetValue(type, out var groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[type] = new FasterDictionary<uint, ITypeSafeDictionary>(); | |||
groupedGroup[groupID] = dbDic; | |||
} | |||
} | |||
//then submit everything in the engines, so that the DB is up to date | |||
//with all the entity views and struct created by the entity built | |||
foreach (var groupToSubmit in groupsOfEntitiesToSubmit) | |||
//then submit everything in the engines, so that the DB is up to date with all the entity views and struct | |||
//created by the entity built | |||
using (profiler.Sample("Add entities to engines")) | |||
{ | |||
foreach (var entityViewsPerType in groupToSubmit.Value) | |||
foreach (var groupToSubmit in groupsOfEntitiesToSubmit) | |||
{ | |||
using (profiler.Sample("Add entities to engines")) | |||
var groupID = groupToSubmit.Key; | |||
var groupDB = _groupEntityDB[groupID]; | |||
foreach (var entityViewsPerType in groupToSubmit.Value) | |||
{ | |||
entityViewsPerType.Value.AddEntitiesToEngines(_entityEngines, ref profiler); | |||
var realDic = groupDB[entityViewsPerType.Key]; | |||
entityViewsPerType.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, realDic, ref profiler); | |||
} | |||
} | |||
} | |||
} | |||
readonly DoubleBufferedEntitiesToAdd<FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>>> | |||
_groupedEntityToAdd; | |||
readonly IEntitySubmissionScheduler _scheduler; | |||
readonly FasterList<EntitySubmitOperation> _transientEntitiesOperations; | |||
readonly FasterList<EntitySubmitOperation> _entitiesOperations; | |||
readonly DoubleBufferedEntitiesToAdd _groupedEntityToAdd; | |||
readonly IEntitySubmissionScheduler _scheduler; | |||
readonly FasterDictionary<ulong, EntitySubmitOperation> _entitiesOperations; | |||
//temp | |||
} | |||
} |
@@ -4,7 +4,7 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Experimental; | |||
@@ -12,289 +12,235 @@ namespace Svelto.ECS.Internal | |||
{ | |||
partial class EntitiesDB : IEntitiesDB | |||
{ | |||
internal EntitiesDB(FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>> groupEntityViewsDB, | |||
Dictionary<Type, FasterDictionary<int, ITypeSafeDictionary>> groupedGroups) | |||
internal EntitiesDB(FasterDictionary<uint, Dictionary<Type, ITypeSafeDictionary>> groupEntityViewsDB, | |||
Dictionary<Type, FasterDictionary<uint, ITypeSafeDictionary>> groupsPerEntity, EntitiesStream entityStream) | |||
{ | |||
_groupEntityViewsDB = groupEntityViewsDB; | |||
_groupedGroups = groupedGroups; | |||
_groupsPerEntity = groupsPerEntity; | |||
_entityStream = entityStream; | |||
} | |||
public ReadOnlyCollectionStruct<T> QueryEntityViews<T>(int group) where T:class, IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T QueryUniqueEntity<T>(ExclusiveGroup.ExclusiveGroupStruct @group) where T : struct, IEntityStruct | |||
{ | |||
if (QueryEntitySafeDictionary(group, out TypeSafeDictionary<T> typeSafeDictionary) == false) | |||
return new ReadOnlyCollectionStruct<T>(RetrieveEmptyEntityViewArray<T>(), 0); | |||
var entities = QueryEntities<T>(@group, out var count); | |||
return typeSafeDictionary.Values; | |||
} | |||
public ReadOnlyCollectionStruct<T> QueryEntityViews<T>(ExclusiveGroup.ExclusiveGroupStruct group) where T : class, IEntityStruct | |||
{ | |||
return QueryEntityViews<T>((int) group); | |||
} | |||
public T QueryEntityView<T>(int id, ExclusiveGroup.ExclusiveGroupStruct group) where T : class, IEntityStruct | |||
{ | |||
return QueryEntityView<T>(new EGID(id, (int) group)); | |||
} | |||
public ref T QueryUniqueEntity<T>(int @group) where T : IEntityStruct | |||
{ | |||
var entities = QueryEntities<T>(group, out var count); | |||
if (count != 1) throw new ECSException("Unique entities must be unique!".FastConcat(typeof(T).ToString())); | |||
if (count != 1) throw new ECSException("Unique entities must be unique! ".FastConcat(typeof(T).ToString())); | |||
return ref entities[0]; | |||
} | |||
public ref T QueryUniqueEntity<T>(ExclusiveGroup.ExclusiveGroupStruct @group) where T : IEntityStruct | |||
{ | |||
return ref QueryUniqueEntity<T>((int) @group); | |||
} | |||
public ref T QueryEntity<T>(EGID entityGID) where T : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T QueryEntity<T>(EGID entityGID) where T : struct, IEntityStruct | |||
{ | |||
T[] array; | |||
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out var index)) != null) | |||
return ref array[index]; | |||
throw new EntityNotFoundException(entityGID.entityID, entityGID.groupID, typeof(T)); | |||
} | |||
public ref T QueryEntity<T>(int id, ExclusiveGroup.ExclusiveGroupStruct group) where T : IEntityStruct | |||
{ | |||
return ref QueryEntity<T>(new EGID(id, group)); | |||
throw new EntityNotFoundException(entityGID, typeof(T)); | |||
} | |||
public ref T QueryEntity<T>(int id, int group) where T : IEntityStruct | |||
public ref T QueryEntity<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct @group) where T : struct, IEntityStruct | |||
{ | |||
return ref QueryEntity<T>(new EGID(id, group)); | |||
return ref QueryEntity<T>(new EGID(id, @group)); | |||
} | |||
public T[] QueryEntities<T>(int group, out int count) where T : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public T[] QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) where T : struct, IEntityStruct | |||
{ | |||
uint @group = groupStruct; | |||
count = 0; | |||
if (QueryEntitySafeDictionary(group, out TypeSafeDictionary<T> typeSafeDictionary) == false) | |||
if (QueryEntitySafeDictionary(@group, out TypeSafeDictionary<T> typeSafeDictionary) == false) | |||
return RetrieveEmptyEntityViewArray<T>(); | |||
return typeSafeDictionary.GetValuesArray(out count); | |||
} | |||
public T[] QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out int count) where T : IEntityStruct | |||
public EntityCollection<T> QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) | |||
where T : struct, IEntityStruct | |||
{ | |||
return new EntityCollection<T>(QueryEntities<T>(groupStruct, out var count), count); | |||
} | |||
public EntityCollections<T> QueryEntities<T>(ExclusiveGroup[] groups) where T : struct, IEntityStruct | |||
{ | |||
return QueryEntities<T>((int) groupStruct, out count); | |||
return new EntityCollections<T>(this, groups); | |||
} | |||
public (T1[], T2[]) QueryEntities<T1, T2>(int @group, out int count) where T1 : IEntityStruct where T2 : IEntityStruct | |||
public EntityCollections<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup[] groups) | |||
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct | |||
{ | |||
var T1entities = QueryEntities<T1>(group, out var countCheck); | |||
var T2entities = QueryEntities<T2>(group, out count); | |||
if (count != countCheck) | |||
return new EntityCollections<T1, T2>(this, groups); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public (T1[], T2[]) QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) | |||
where T1 : struct, IEntityStruct | |||
where T2 : struct, IEntityStruct | |||
{ | |||
var T1entities = QueryEntities<T1>(@groupStruct, out var countCheck); | |||
var T2entities = QueryEntities<T2>(@groupStruct, out count); | |||
if (count != countCheck) | |||
throw new ECSException("Entity views count do not match in group. Entity 1: ". | |||
FastConcat(typeof(T1).ToString()).FastConcat( | |||
"Entity 2: ".FastConcat(typeof(T2).ToString()))); | |||
FastConcat(typeof(T1).ToString()).FastConcat( | |||
"Entity 2: ".FastConcat(typeof(T2).ToString()))); | |||
return (T1entities, T2entities); | |||
} | |||
public (T1[], T2[]) QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out int count) where T1 : IEntityStruct where T2 : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public (T1[], T2[], T3[]) QueryEntities | |||
<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) | |||
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct | |||
{ | |||
return QueryEntities<T1, T2>((int) groupStruct, out count); | |||
var T1entities = QueryEntities<T1>(@groupStruct, out var countCheck1); | |||
var T2entities = QueryEntities<T2>(@groupStruct, out var countCheck2); | |||
var T3entities = QueryEntities<T3>(@groupStruct, out count); | |||
if (count != countCheck1 || count != countCheck2) | |||
throw new ECSException("Entity views count do not match in group. Entity 1: ". | |||
FastConcat(typeof(T1).ToString()). | |||
FastConcat(" Entity 2: ".FastConcat(typeof(T2).ToString()). | |||
FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())))); | |||
return (T1entities, T2entities, T3entities); | |||
} | |||
public EGIDMapper<T> QueryMappedEntities<T>(int groupID) where T : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId) where T : struct, IEntityStruct | |||
{ | |||
TypeSafeDictionary<T> typeSafeDictionary; | |||
if (QueryEntitySafeDictionary(groupID, out typeSafeDictionary) == false) | |||
throw new EntityGroupNotFoundException(groupID, typeof(T)); | |||
uint groupId = groupStructId; | |||
if (QueryEntitySafeDictionary(groupId, out TypeSafeDictionary<T> typeSafeDictionary) == false) | |||
throw new EntityGroupNotFoundException(groupId, typeof(T)); | |||
EGIDMapper<T> mapper; | |||
mapper.map = typeSafeDictionary; | |||
int count; | |||
typeSafeDictionary.GetValuesArray(out count); | |||
typeSafeDictionary.GetValuesArray(out _); | |||
return mapper; | |||
} | |||
public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId) where T : IEntityStruct | |||
{ | |||
return QueryMappedEntities<T>((int) groupStructId); | |||
} | |||
public T[] QueryEntitiesAndIndex<T>(EGID entityGID, out uint index) where T : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public T[] QueryEntitiesAndIndex<T>(EGID entityGID, out uint index) where T : struct, IEntityStruct | |||
{ | |||
T[] array; | |||
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out index)) != null) | |||
return array; | |||
throw new EntityNotFoundException(entityGID.entityID, entityGID.groupID, typeof(T)); | |||
throw new EntityNotFoundException(entityGID, typeof(T)); | |||
} | |||
public bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array) where T : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array) where T : struct, IEntityStruct | |||
{ | |||
if ((array = QueryEntitiesAndIndexInternal<T>(entityGid, out index)) != null) | |||
return true; | |||
return false; | |||
} | |||
public T[] QueryEntitiesAndIndex<T>(int id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) where T : IEntityStruct | |||
{ | |||
return QueryEntitiesAndIndex<T>(new EGID(id, group), out index); | |||
} | |||
public bool TryQueryEntitiesAndIndex<T>(int id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) where T : IEntityStruct | |||
{ | |||
return TryQueryEntitiesAndIndex(new EGID(id, group), out index, out array); | |||
return false; | |||
} | |||
public T[] QueryEntitiesAndIndex<T>(int id, int group, out uint index) where T : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public T[] QueryEntitiesAndIndex<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) where T : struct, IEntityStruct | |||
{ | |||
return QueryEntitiesAndIndex<T>(new EGID(id, group), out index); | |||
} | |||
public bool TryQueryEntitiesAndIndex<T>(int id, int group, out uint index, out T[] array) where T : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryQueryEntitiesAndIndex<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) where T : struct, IEntityStruct | |||
{ | |||
return TryQueryEntitiesAndIndex(new EGID(id, group), out index, out array); | |||
} | |||
public T QueryEntityView<T>(EGID entityGID) where T : class, IEntityStruct | |||
{ | |||
T entityView; | |||
if (TryQueryEntityViewInGroupInternal(entityGID, out entityView) == false) | |||
throw new EntityNotFoundException(entityGID.entityID, entityGID.groupID, typeof(T)); | |||
return entityView; | |||
} | |||
public bool Exists<T>(EGID entityGID) where T : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Exists<T>(EGID entityGID) where T : struct, IEntityStruct | |||
{ | |||
TypeSafeDictionary<T> casted; | |||
if (QueryEntitySafeDictionary(entityGID.groupID, out casted) == false) return false; | |||
if (QueryEntitySafeDictionary(entityGID.groupID, out TypeSafeDictionary<T> casted) == false) return false; | |||
return casted != null && casted.ContainsKey(entityGID.entityID); | |||
} | |||
public bool Exists<T>(int id, int groupid) where T : IEntityStruct | |||
{ | |||
return Exists<T>(new EGID(id, groupid)); | |||
} | |||
//search for the group | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid) | |||
{ | |||
return _groupEntityViewsDB.ContainsKey(gid); | |||
} | |||
public bool HasAny<T>(int group) where T : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool HasAny<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct | |||
{ | |||
int count; | |||
QueryEntities<T>(group, out count); | |||
QueryEntities<T>(groupStruct, out var count); | |||
return count > 0; | |||
} | |||
public bool HasAny<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : IEntityStruct | |||
{ | |||
return HasAny<T>((int) groupStruct); | |||
} | |||
public int Count<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : IEntityStruct | |||
{ | |||
int count; | |||
QueryEntities<T>(groupStruct, out count); | |||
return count; | |||
} | |||
public int Count<T>(int groupStruct) where T : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public uint Count<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct | |||
{ | |||
int count; | |||
QueryEntities<T>(groupStruct, out count); | |||
QueryEntities<T>(groupStruct, out var count); | |||
return count; | |||
} | |||
public bool TryQueryEntityView<T>(EGID entityegid, out T entityView) where T : class, IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityStruct | |||
{ | |||
return TryQueryEntityViewInGroupInternal(entityegid, out entityView); | |||
_entityStream.PublishEntity(ref QueryEntity<T>(egid)); | |||
} | |||
public bool TryQueryEntityView<T>(int id, ExclusiveGroup.ExclusiveGroupStruct group, out T entityView) where T : class, IEntityStruct | |||
{ | |||
return TryQueryEntityViewInGroupInternal(new EGID(id, (int) group), out entityView); | |||
} | |||
bool TryQueryEntityViewInGroupInternal<T>(EGID entityGID, out T entityView) where T:class, IEntityStruct | |||
{ | |||
entityView = null; | |||
TypeSafeDictionary<T> safeDictionary; | |||
if (QueryEntitySafeDictionary(entityGID.groupID, out safeDictionary) == false) return false; | |||
return safeDictionary.TryGetValue(entityGID.entityID, out entityView); | |||
} | |||
T[] QueryEntitiesAndIndexInternal<T>(EGID entityGID, out uint index) where T : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
T[] QueryEntitiesAndIndexInternal<T>(EGID entityGID, out uint index) where T : struct, IEntityStruct | |||
{ | |||
TypeSafeDictionary<T> safeDictionary; | |||
index = 0; | |||
if (QueryEntitySafeDictionary(entityGID.groupID, out safeDictionary) == false) | |||
if (QueryEntitySafeDictionary(entityGID.groupID, out TypeSafeDictionary<T> safeDictionary) == false) | |||
return null; | |||
if (safeDictionary.TryFindElementIndex(entityGID.entityID, out index) == false) | |||
return null; | |||
int count; | |||
return safeDictionary.GetValuesArray(out count); | |||
return safeDictionary.GetValuesArray(out _); | |||
} | |||
bool QueryEntitySafeDictionary<T>(int group, out TypeSafeDictionary<T> typeSafeDictionary) where T : IEntityStruct | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
bool QueryEntitySafeDictionary<T>(uint group, out TypeSafeDictionary<T> typeSafeDictionary) where T : struct, IEntityStruct | |||
{ | |||
Dictionary<Type, ITypeSafeDictionary> entitiesInGroupPerType; | |||
typeSafeDictionary = null; | |||
//search for the group | |||
if (_groupEntityViewsDB.TryGetValue(group, out entitiesInGroupPerType) == false) | |||
//search for the group | |||
if (_groupEntityViewsDB.TryGetValue(group, out var entitiesInGroupPerType) == false) | |||
return false; | |||
//search for the indexed entities in the group | |||
ITypeSafeDictionary safeDictionary; | |||
if (entitiesInGroupPerType.TryGetValue(typeof(T), out safeDictionary) == false) | |||
if (entitiesInGroupPerType.TryGetValue(typeof(T), out var safeDictionary) == false) | |||
return false; | |||
//return the indexes entities if they exist | |||
typeSafeDictionary = (safeDictionary as TypeSafeDictionary<T>); | |||
return true; | |||
} | |||
[Conditional("ENABLE_DEBUG_FUNC")] | |||
static void SafetyChecks<T>(TypeSafeDictionary<T> typeSafeDictionary, int count) where T : IEntityStruct | |||
{ | |||
if (typeSafeDictionary.Count != count) | |||
throw new ECSException("Entities cannot be swapped or removed during an iteration"); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
static ReadOnlyCollectionStruct<T> RetrieveEmptyEntityViewList<T>() | |||
{ | |||
var arrayFast = FasterList<T>.DefaultList.ToArrayFast(); | |||
return new ReadOnlyCollectionStruct<T>(arrayFast, 0); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
static T[] RetrieveEmptyEntityViewArray<T>() | |||
{ | |||
return FasterList<T>.DefaultList.ToArrayFast(); | |||
} | |||
//grouped set of entity views, this is the standard way to handle entity views entity views are grouped per | |||
//group, then indexable per type, then indexable per EGID. however the TypeSafeDictionary can return an array of | |||
//values directly, that can be iterated over, so that is possible to iterate over all the entity views of | |||
//values directly, that can be iterated over, so that is possible to iterate over all the entity views of | |||
//a specific type inside a specific group. | |||
readonly FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>> _groupEntityViewsDB; | |||
readonly FasterDictionary<uint, Dictionary<Type, ITypeSafeDictionary>> _groupEntityViewsDB; | |||
//needed to be able to iterate over all the entities of the same type regardless the group | |||
//may change in future | |||
readonly Dictionary<Type, FasterDictionary<int, ITypeSafeDictionary>> _groupedGroups; | |||
readonly Dictionary<Type, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity; | |||
readonly EntitiesStream _entityStream; | |||
} | |||
} |
@@ -7,41 +7,16 @@ using System.Reflection; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EntityBuilder<T> | |||
public static class EntityBuilderUtilities | |||
{ | |||
#if DISABLE_CHECKS | |||
[Conditional("_CHECKS_DISABLED")] | |||
#endif | |||
static void CheckFields(Type type, bool needsReflection, bool isRoot) | |||
public static void CheckFields(Type type, bool needsReflection) | |||
{ | |||
if (ENTITY_VIEW_TYPE == ENTITYINFOVIEW_TYPE || type == EGIDType || type == ECLUSIVEGROUPSTRUCTTYPE) | |||
if (type == ENTITY_STRUCT_INFO_VIEW || type == EGIDType || type == ECLUSIVEGROUPSTRUCTTYPE) | |||
return; | |||
{ | |||
var methods = type.GetMethods(BindingFlags.Public | | |||
BindingFlags.Instance | BindingFlags.DeclaredOnly); | |||
var properties = type.GetProperties(BindingFlags.Public | | |||
BindingFlags.Instance | BindingFlags.DeclaredOnly); | |||
if (isRoot) | |||
{ | |||
if (properties.Length > 1) | |||
ProcessError("Entity views cannot have public methods or properties.", type); | |||
if (methods.Length > properties.Length + 1) | |||
ProcessError("Entity views cannot have public methods or properties.", type); | |||
} | |||
else | |||
{ | |||
if (properties.Length > 0) | |||
ProcessError("Entity components fields cannot have public methods or properties.", type); | |||
if (methods.Length > 0) | |||
ProcessError("Entity components fields cannot have public methods or properties.", type); | |||
} | |||
} | |||
if (needsReflection == false) | |||
{ | |||
if (type.IsClass) | |||
@@ -53,6 +28,8 @@ namespace Svelto.ECS | |||
{ | |||
var field = fields[i]; | |||
var fieldFieldType = field.FieldType; | |||
if (fieldFieldType == STRINGTYPE) continue; | |||
SubCheckFields(fieldFieldType); | |||
} | |||
@@ -97,7 +74,7 @@ namespace Svelto.ECS | |||
{ | |||
if (fieldFieldType.IsValueType == true && !fieldFieldType.IsEnum && fieldFieldType.IsPrimitive == false) | |||
{ | |||
CheckFields(fieldFieldType, false, false); | |||
CheckFields(fieldFieldType, false); | |||
} | |||
return; | |||
@@ -110,6 +87,7 @@ namespace Svelto.ECS | |||
static void ProcessError(string message, Type type) | |||
{ | |||
#if !RELAXED_ECS | |||
Type ENTITY_VIEW_TYPE = typeof(Type); | |||
throw new EntityStructException(message, ENTITY_VIEW_TYPE, type); | |||
#endif | |||
} | |||
@@ -119,6 +97,8 @@ namespace Svelto.ECS | |||
static readonly Type DISPATCHONSETTYPE = typeof(DispatchOnSet<>); | |||
static readonly Type DISPATCHONCHANGETYPE = typeof(DispatchOnChange<>); | |||
static readonly Type STRINGTYPE = typeof(String); | |||
static readonly Type ENTITY_VIEW_TYPE = typeof(Type); | |||
static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityStructInfoView); | |||
} | |||
public class EntityStructException : Exception | |||
@@ -7,15 +7,15 @@ using Svelto.Utilities; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EntityBuilder<T> : IEntityBuilder where T : IEntityStruct, new() | |||
public class EntityBuilder<T> : IEntityBuilder where T : struct, IEntityStruct | |||
{ | |||
public EntityBuilder() | |||
{ | |||
_initializer = DEFAULT_IT; | |||
CheckFields(ENTITY_VIEW_TYPE, NEEDS_REFLECTION, true); | |||
EntityBuilderUtilities.CheckFields(ENTITY_VIEW_TYPE, NEEDS_REFLECTION); | |||
if (NEEDS_REFLECTION == true) | |||
if (NEEDS_REFLECTION) | |||
EntityView<T>.InitCache(); | |||
} | |||
@@ -26,69 +26,63 @@ namespace Svelto.ECS | |||
var castedDic = dictionary as TypeSafeDictionary<T>; | |||
if (NEEDS_REFLECTION == true) | |||
if (NEEDS_REFLECTION) | |||
{ | |||
DBC.ECS.Check.Require(implementors != null, "Implementors not found while building an EntityView"); | |||
DBC.ECS.Check.Require(castedDic.ContainsKey(entityID.entityID) == false, | |||
"building an entity with already used entity id! id: ".FastConcat((long)entityID).FastConcat(" ", ENTITY_VIEW_NAME)); | |||
"building an entity with already used entity id! id: ".FastConcat((ulong) entityID) | |||
.FastConcat(" ", ENTITY_VIEW_NAME)); | |||
T entityView; | |||
EntityView<T>.BuildEntityView(entityID, out entityView); | |||
EntityView<T>.BuildEntityView(out var entityView); | |||
this.FillEntityView(ref entityView, entityViewBlazingFastReflection, implementors, implementorsByType, | |||
this.FillEntityView(ref entityView, entityViewBlazingFastReflection, implementors, implementorsByType, | |||
cachedTypes); | |||
castedDic.Add(entityID.entityID, ref entityView); | |||
} | |||
else | |||
{ | |||
_initializer.ID = entityID; | |||
castedDic.Add(entityID.entityID, _initializer); | |||
} | |||
} | |||
ITypeSafeDictionary IEntityBuilder.Preallocate(ref ITypeSafeDictionary dictionary, int size) | |||
ITypeSafeDictionary IEntityBuilder.Preallocate(ref ITypeSafeDictionary dictionary, uint size) | |||
{ | |||
return Preallocate(ref dictionary, size); | |||
} | |||
public static ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, int size) | |||
static ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size) | |||
{ | |||
if (dictionary == null) | |||
dictionary = new TypeSafeDictionary<T>(size); | |||
else | |||
dictionary.AddCapacity(size); | |||
dictionary.SetCapacity(size); | |||
return dictionary; | |||
} | |||
public Type GetEntityType() | |||
{ | |||
return ENTITY_VIEW_TYPE; | |||
} | |||
public Type GetEntityType() { return ENTITY_VIEW_TYPE; } | |||
#if DEBUG && !PROFILER | |||
readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType = new Dictionary<Type, ECSTuple<object, int>>(); | |||
readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType = | |||
new Dictionary<Type, ECSTuple<object, int>>(); | |||
#else | |||
readonly Dictionary<Type, object> implementorsByType = new Dictionary<Type, object>(); | |||
#endif | |||
//this is used to avoid newing a dictionary every time, but it's used locally only and it's cleared for each use | |||
readonly Dictionary<Type, Type[]> cachedTypes = new Dictionary<Type, Type[]>(); | |||
static FasterList<KeyValuePair<Type, ActionCast<T>>> entityViewBlazingFastReflection | |||
{ | |||
get { return EntityView<T>.cachedFields; } | |||
} | |||
static readonly Type ENTITY_VIEW_TYPE = typeof(T); | |||
static readonly T DEFAULT_IT = default(T); | |||
static readonly Type ENTITYINFOVIEW_TYPE = typeof(EntityInfoView); | |||
static readonly bool NEEDS_REFLECTION = typeof(IEntityViewStruct).IsAssignableFrom(typeof(T)); | |||
static readonly string ENTITY_VIEW_NAME = ENTITY_VIEW_TYPE.ToString(); | |||
static FasterList<KeyValuePair<Type, ActionCast<T>>> entityViewBlazingFastReflection => | |||
EntityView<T>.cachedFields; | |||
internal static readonly Type ENTITY_VIEW_TYPE = typeof(T); | |||
static readonly T DEFAULT_IT = default; | |||
static readonly Type ENTITYINFOVIEW_TYPE = typeof(EntityStructInfoView); | |||
static readonly bool NEEDS_REFLECTION = typeof(IEntityViewStruct).IsAssignableFrom(typeof(T)); | |||
static readonly string ENTITY_VIEW_NAME = ENTITY_VIEW_TYPE.ToString(); | |||
internal static readonly bool HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_VIEW_TYPE); | |||
internal T _initializer; | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
diff a/Assets/Svelto/Svelto.ECS/EntityBuilder.cs b/Assets/Svelto/Svelto.ECS/EntityBuilder.cs (rejected hunks) | |||
@@ -1,5 +1,6 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
+using DBC.ECS; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Hybrid; | |||
using Svelto.ECS.Internal; |
@@ -0,0 +1,195 @@ | |||
using System; | |||
using System.Collections; | |||
using System.Collections.Generic; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public struct EntityCollection<T> | |||
{ | |||
public EntityCollection(T[] array, uint count) | |||
{ | |||
_array = array; | |||
_count = count; | |||
} | |||
public EntityIterator<T> GetEnumerator() { return new EntityIterator<T>(_array, _count); } | |||
readonly T[] _array; | |||
readonly uint _count; | |||
} | |||
public struct EntityCollections<T> where T : struct, IEntityStruct | |||
{ | |||
public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
public EntityGroupsIterator<T> GetEnumerator() { return new EntityGroupsIterator<T>(_db, _groups); } | |||
readonly IEntitiesDB _db; | |||
readonly ExclusiveGroup[] _groups; | |||
} | |||
public struct EntityCollections<T1, T2> where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct | |||
{ | |||
public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
public EntityGroupsIterator<T1, T2> GetEnumerator() { return new EntityGroupsIterator<T1, T2>(_db, _groups); } | |||
readonly IEntitiesDB _db; | |||
readonly ExclusiveGroup[] _groups; | |||
} | |||
public struct EntityGroupsIterator<T> : IEnumerator<T> where T : struct, IEntityStruct | |||
{ | |||
public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
_indexGroup = -1; | |||
_index = -1; | |||
} | |||
public bool MoveNext() | |||
{ | |||
while (_index + 1 >= _count && ++_indexGroup < _groups.Length) | |||
{ | |||
_index = -1; | |||
_array = _db.QueryEntities<T>(_groups[_indexGroup], out _count); | |||
} | |||
if (++_index < _count) | |||
{ | |||
return true; | |||
} | |||
return false; | |||
} | |||
public void Reset() | |||
{ | |||
_index = -1; | |||
_indexGroup = -1; | |||
_array = _db.QueryEntities<T>(_groups[0], out _count); | |||
} | |||
public ref T Current => ref _array[_index]; | |||
T IEnumerator<T>. Current => throw new NotImplementedException(); | |||
object IEnumerator.Current => throw new NotImplementedException(); | |||
public void Dispose() { } | |||
readonly IEntitiesDB _db; | |||
readonly ExclusiveGroup[] _groups; | |||
T[] _array; | |||
uint _count; | |||
int _index; | |||
int _indexGroup; | |||
} | |||
public struct ValueRef<T1, T2> | |||
{ | |||
readonly T1[] array1; | |||
readonly T2[] array2; | |||
readonly uint index; | |||
public ValueRef(T1[] entity1, T2[] entity2, uint i):this() | |||
{ | |||
array1 = entity1; | |||
array2 = entity2; | |||
index = i; | |||
} | |||
public ref T1 Item1 => ref array1[index]; | |||
public ref T2 Item2 => ref array2[index]; | |||
} | |||
public struct EntityGroupsIterator<T1, T2> : IEnumerator<ValueRef<T1, T2>> where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct | |||
{ | |||
public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
_indexGroup = -1; | |||
_index = -1; | |||
} | |||
public bool MoveNext() | |||
{ | |||
while (_index + 1 >= _count && ++_indexGroup < _groups.Length) | |||
{ | |||
_index = -1; | |||
_value = new ValueRef<T1, T2>(_db.QueryEntities<T1>(_groups[_indexGroup], out _count), | |||
_db.QueryEntities<T2>(_groups[_indexGroup], out var count1), (uint) _index + 1); | |||
#if DEBUG && !PROFILER | |||
if (_count != count1) | |||
throw new ECSException("number of entities in group doesn't match"); | |||
#endif | |||
} | |||
if (++_index < _count) | |||
{ | |||
return true; | |||
} | |||
return false; | |||
} | |||
public void Reset() | |||
{ | |||
_index = -1; | |||
_indexGroup = -1; | |||
_value = new ValueRef<T1, T2>(_db.QueryEntities<T1>(_groups[_indexGroup], out _count), | |||
_db.QueryEntities<T2>(_groups[_indexGroup], out var count1), 0); | |||
#if DEBUG && !PROFILER | |||
if (_count != count1) | |||
throw new ECSException("number of entities in group doesn't match"); | |||
#endif | |||
} | |||
public ValueRef<T1, T2> Current => _value; | |||
ValueRef<T1, T2> IEnumerator<ValueRef<T1, T2>>. Current => throw new NotImplementedException(); | |||
object IEnumerator.Current => throw new NotImplementedException(); | |||
public void Dispose() { } | |||
readonly IEntitiesDB _db; | |||
readonly ExclusiveGroup[] _groups; | |||
uint _count; | |||
int _index; | |||
int _indexGroup; | |||
ValueRef<T1, T2> _value; | |||
} | |||
public struct EntityIterator<T> : IEnumerator<T> | |||
{ | |||
public EntityIterator(T[] array, uint count) : this() | |||
{ | |||
_array = array; | |||
_count = count; | |||
_index = -1; | |||
} | |||
public bool MoveNext() { return ++_index < _count; } | |||
public void Reset() { _index = -1; } | |||
public ref T Current => ref _array[_index]; | |||
T IEnumerator<T>. Current => throw new NotImplementedException(); | |||
object IEnumerator.Current => throw new NotImplementedException(); | |||
public void Dispose() { } | |||
readonly T[] _array; | |||
readonly uint _count; | |||
int _index; | |||
} | |||
} |
@@ -1,42 +1,42 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures.Experimental; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
static class EntityFactory | |||
{ | |||
internal static Dictionary<Type, ITypeSafeDictionary> | |||
BuildGroupedEntities(EGID egid, | |||
FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>> groupEntityViewsByType, | |||
IEntityBuilder[] entitiesToBuild, | |||
object[] implementors) | |||
internal static Dictionary<Type, ITypeSafeDictionary> BuildGroupedEntities(EGID egid, | |||
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd, IEntityBuilder[] entitiesToBuild, | |||
object[] implementors) | |||
{ | |||
var @group = FetchEntityGroup(egid.groupID, groupEntityViewsByType); | |||
var @group = FetchEntityGroup(egid.groupID, groupEntitiesToAdd); | |||
BuildEntitiesAndAddToGroup(egid, group, entitiesToBuild, implementors); | |||
return group; | |||
} | |||
static Dictionary<Type, ITypeSafeDictionary> FetchEntityGroup(int groupID, | |||
FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>> groupEntityViewsByType) | |||
static Dictionary<Type, ITypeSafeDictionary> FetchEntityGroup(uint groupID, | |||
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityViewsByType) | |||
{ | |||
Dictionary<Type, ITypeSafeDictionary> group; | |||
if (groupEntityViewsByType.TryGetValue(groupID, out @group) == false) | |||
if (groupEntityViewsByType.current.TryGetValue(groupID, out Dictionary<Type, ITypeSafeDictionary> @group) == | |||
false) | |||
{ | |||
@group = new Dictionary<Type, ITypeSafeDictionary>(); | |||
groupEntityViewsByType.Add(groupID, @group); | |||
groupEntityViewsByType.current.Add(groupID, @group); | |||
} | |||
groupEntityViewsByType.currentEntitiesCreatedPerGroup.TryGetValue(groupID, out var value); | |||
groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = value+1; | |||
return @group; | |||
} | |||
static void BuildEntitiesAndAddToGroup(EGID entityID, | |||
Dictionary<Type, ITypeSafeDictionary> @group, | |||
IEntityBuilder[] entitiesToBuild, | |||
object[] implementors) | |||
static void BuildEntitiesAndAddToGroup(EGID entityID, | |||
Dictionary<Type, ITypeSafeDictionary> @group, | |||
IEntityBuilder[] entitiesToBuild, | |||
object[] implementors) | |||
{ | |||
var count = entitiesToBuild.Length; | |||
#if DEBUG && !PROFILER | |||
@@ -44,35 +44,32 @@ namespace Svelto.ECS.Internal | |||
for (var index = 0; index < count; ++index) | |||
{ | |||
var entityType = entitiesToBuild[index].GetEntityType(); | |||
if (types.Contains(entityType)) | |||
var entityViewType = entitiesToBuild[index].GetEntityType(); | |||
if (types.Contains(entityViewType)) | |||
{ | |||
throw new ECSException("EntityBuilders must be unique inside an EntityDescriptor"); | |||
} | |||
types.Add(entityType); | |||
types.Add(entityViewType); | |||
} | |||
#endif | |||
#endif | |||
for (var index = 0; index < count; ++index) | |||
{ | |||
var entityViewBuilder = entitiesToBuild[index]; | |||
var entityViewType = entityViewBuilder.GetEntityType(); | |||
var entityViewType = entityViewBuilder.GetEntityType(); | |||
BuildEntity(entityID, @group, entityViewType, entityViewBuilder, implementors); | |||
} | |||
} | |||
static void BuildEntity(EGID entityID, Dictionary<Type, ITypeSafeDictionary> @group, | |||
Type entityViewType, IEntityBuilder entityBuilder, object[] implementors) | |||
static void BuildEntity(EGID entityID, Dictionary<Type, ITypeSafeDictionary> @group, Type entityViewType, | |||
IEntityBuilder entityBuilder, object[] implementors) | |||
{ | |||
ITypeSafeDictionary safeDictionary; | |||
var entityViewsPoolWillBeCreated = @group.TryGetValue(entityViewType, out safeDictionary) == false; | |||
var entityViewsPoolWillBeCreated = @group.TryGetValue(entityViewType, out var safeDictionary) == false; | |||
//passing the undefined entityViewsByType inside the entityViewBuilder will allow | |||
//it to be created with the correct type and casted back to the undefined list. | |||
//that's how the list will be eventually of the target type. | |||
//passing the undefined entityViewsByType inside the entityViewBuilder will allow it to be created with the | |||
//correct type and casted back to the undefined list. that's how the list will be eventually of the target | |||
//type. | |||
entityBuilder.BuildEntityAndAddToList(ref safeDictionary, entityID, implementors); | |||
if (entityViewsPoolWillBeCreated) | |||
@@ -4,7 +4,7 @@ namespace Svelto.ECS.Internal | |||
{ | |||
class EntityGroupNotFoundException : Exception | |||
{ | |||
public EntityGroupNotFoundException(int groupId, Type type) | |||
public EntityGroupNotFoundException(uint groupId, Type type) | |||
: base("entity group not found ".FastConcat(type.ToString())) | |||
{ | |||
} | |||
@@ -0,0 +1,11 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public struct EntityHierarchyStruct: IEntityStruct, INeedEGID | |||
{ | |||
public readonly ExclusiveGroup.ExclusiveGroupStruct parentGroup; | |||
public EntityHierarchyStruct(ExclusiveGroup @group): this() { parentGroup = group; } | |||
public EGID ID { get; set; } | |||
} | |||
} |
@@ -2,7 +2,7 @@ using System; | |||
namespace Svelto.ECS | |||
{ | |||
public struct EntityInfoView : IEntityStruct | |||
public struct EntityStructInfoView: IEntityStruct, INeedEGID | |||
{ | |||
public EGID ID { get; set; } | |||
public Type type { get; set; } | |||
@@ -4,7 +4,7 @@ namespace Svelto.ECS | |||
{ | |||
public class EntityNotFoundException : Exception | |||
{ | |||
public EntityNotFoundException(int entityGidEntityId, int entityGidGroupId, Type type) | |||
public EntityNotFoundException(EGID entityGidEntityId, Type type) | |||
: base("entity not found ".FastConcat(type.ToString())) | |||
{} | |||
} |
@@ -1,16 +1,10 @@ | |||
using System; | |||
using System.Collections.Concurrent; | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IEntitiesStream | |||
{ | |||
Consumer<T> GenerateConsumer<T>(int capacity) where T : unmanaged, IEntityStruct; | |||
void PublishEntity<T>(EGID id) where T : unmanaged, IEntityStruct; | |||
} | |||
/// <summary> | |||
/// Do not use this class in place of a normal polling. | |||
/// I eventually realised than in ECS no form of communication other than polling entity components can exist. | |||
@@ -22,31 +16,25 @@ namespace Svelto.ECS | |||
/// one only | |||
/// - you want to communicate between EnginesRoots | |||
/// </summary> | |||
class EntitiesStream : IEntitiesStream | |||
class EntitiesStream | |||
{ | |||
public EntitiesStream(IEntitiesDB entitiesDb) | |||
{ | |||
_entitiesDB = entitiesDb; | |||
} | |||
public Consumer<T> GenerateConsumer<T>(int capacity) where T : unmanaged, IEntityStruct | |||
internal Consumer<T> GenerateConsumer<T>(string name, int capacity) where T : unmanaged, IEntityStruct | |||
{ | |||
if (_streams.ContainsKey(typeof(T)) == false) _streams[typeof(T)] = new EntityStream<T>(); | |||
return (_streams[typeof(T)] as EntityStream<T>).GenerateConsumer(capacity); | |||
return (_streams[typeof(T)] as EntityStream<T>).GenerateConsumer(name, capacity); | |||
} | |||
public void PublishEntity<T>(EGID id) where T : unmanaged, IEntityStruct | |||
internal void PublishEntity<T>(ref T entity) where T : unmanaged, IEntityStruct | |||
{ | |||
if (_streams.TryGetValue(typeof(T), out var typeSafeStream)) | |||
(typeSafeStream as EntityStream<T>).PublishEntity(ref _entitiesDB.QueryEntity<T>(id)); | |||
(typeSafeStream as EntityStream<T>).PublishEntity(ref entity); | |||
else | |||
Console.LogWarning("No Consumers are waiting for this entity to change " | |||
.FastConcat(typeof(T).ToString())); | |||
} | |||
readonly Dictionary<Type, ITypeSafeStream> _streams = new Dictionary<Type, ITypeSafeStream>(); | |||
readonly IEntitiesDB _entitiesDB; | |||
readonly ConcurrentDictionary<Type, ITypeSafeStream> _streams = new ConcurrentDictionary<Type, ITypeSafeStream>(); | |||
} | |||
interface ITypeSafeStream | |||
@@ -60,46 +48,61 @@ namespace Svelto.ECS | |||
_buffers[i].Enqueue(ref entity); | |||
} | |||
public Consumer<T> GenerateConsumer(int capacity) | |||
public Consumer<T> GenerateConsumer(string name, int capacity) | |||
{ | |||
var consumer = new Consumer<T>(capacity); | |||
var consumer = new Consumer<T>(name, capacity, this); | |||
_buffers.Add(consumer); | |||
return consumer; | |||
} | |||
public void RemoveConsumer(Consumer<T> consumer) | |||
{ | |||
_buffers.UnorderedRemove(consumer); | |||
} | |||
readonly FasterList<Consumer<T>> _buffers = new FasterList<Consumer<T>>(); | |||
readonly FasterListThreadSafe<Consumer<T>> _buffers = new FasterListThreadSafe<Consumer<T>>(); | |||
} | |||
public class Consumer<T> where T:unmanaged, IEntityStruct | |||
public struct Consumer<T>: IDisposable where T:unmanaged, IEntityStruct | |||
{ | |||
public Consumer(int capacity) | |||
internal Consumer(string name, int capacity, EntityStream<T> stream) | |||
{ | |||
_ringBuffer = new RingBuffer<T>(capacity); | |||
_capacity = capacity; | |||
_name = name; | |||
_stream = stream; | |||
} | |||
public void Enqueue(ref T entity) | |||
internal void Enqueue(ref T entity) | |||
{ | |||
if (_ringBuffer.Count >= _capacity) | |||
throw new Exception("EntityStream capacity has been saturated"); | |||
_ringBuffer.Enqueue(ref entity); | |||
_ringBuffer.Enqueue(ref entity, _name); | |||
} | |||
/// <summary> | |||
/// this can be better, I probably would need to get the group regardless if it supports EGID or not | |||
/// </summary> | |||
/// <param name="group"></param> | |||
/// <param name="entity"></param> | |||
/// <returns></returns> | |||
public bool TryDequeue(ExclusiveGroup group, out T entity) | |||
{ | |||
if (_ringBuffer.TryDequeue(out entity) == true) | |||
return entity.ID.groupID == @group; | |||
if (_ringBuffer.TryDequeue(out entity, _name) == true) | |||
{ | |||
if (EntityBuilder<T>.HAS_EGID) | |||
return (entity as INeedEGID).ID.groupID == @group; | |||
return true; | |||
} | |||
return false; | |||
} | |||
public bool TryDequeue(out T entity) { return _ringBuffer.TryDequeue(out entity); } | |||
public bool TryDequeue(out T entity) { return _ringBuffer.TryDequeue(out entity, _name); } | |||
public void Flush() { _ringBuffer.Reset(); } | |||
public void Dispose() { _stream.RemoveConsumer(this); } | |||
readonly RingBuffer<T> _ringBuffer; | |||
readonly int _capacity; | |||
readonly RingBuffer<T> _ringBuffer; | |||
readonly EntityStream<T> _stream; | |||
readonly string _name; | |||
} | |||
} |
@@ -6,26 +6,31 @@ namespace Svelto.ECS | |||
{ | |||
public struct EntityStructInitializer | |||
{ | |||
public EntityStructInitializer(EGID id, Dictionary<Type, ITypeSafeDictionary> current) | |||
public EntityStructInitializer(EGID id, Dictionary<Type, ITypeSafeDictionary> @group) | |||
{ | |||
_current = current; | |||
_id = id; | |||
_group = @group; | |||
ID = id; | |||
} | |||
public void Init<T>(T initializer) where T: struct, IEntityStruct | |||
{ | |||
DBC.ECS.Check.Require(_current.ContainsKey(typeof(T) )== true, | |||
"Entity to initialise not generated by the EntiyDescriptor"); | |||
var typeSafeDictionary = (TypeSafeDictionary<T>) _current[typeof(T)]; | |||
if (_group.TryGetValue(EntityBuilder<T>.ENTITY_VIEW_TYPE, out var typeSafeDictionary) == true) | |||
{ | |||
var dictionary = typeSafeDictionary as TypeSafeDictionary<T>; | |||
initializer.ID = _id; | |||
if (EntityBuilder<T>.HAS_EGID) | |||
{ | |||
var needEgid = ((INeedEGID) initializer); | |||
needEgid.ID = ID; | |||
initializer = (T) needEgid; | |||
} | |||
int count; | |||
typeSafeDictionary.GetValuesArray(out count)[typeSafeDictionary.FindElementIndex(_id.entityID)] = initializer; | |||
if (dictionary.TryFindElementIndex(ID.entityID, out var findElementIndex)) | |||
dictionary.GetValuesArray(out _)[findElementIndex] = initializer; | |||
} | |||
} | |||
readonly Dictionary<Type, ITypeSafeDictionary> _current; | |||
readonly EGID _id; | |||
readonly EGID ID; | |||
readonly Dictionary<Type, ITypeSafeDictionary> _group; | |||
} | |||
} |
@@ -2,41 +2,55 @@ | |||
namespace Svelto.ECS | |||
{ | |||
#pragma warning disable 660,661 | |||
struct EntitySubmitOperation | |||
#pragma warning restore 660,661 | |||
: IEquatable<EntitySubmitOperation> | |||
{ | |||
public readonly EntitySubmitOperationType type; | |||
public readonly IEntityBuilder[] builders; | |||
public readonly int ID; | |||
public readonly int toID; | |||
public readonly int toGroupID; | |||
public readonly int fromGroupID; | |||
public readonly EGID fromID; | |||
public readonly EGID toID; | |||
public readonly Type entityDescriptor; | |||
#if DEBUG && !PROFILER | |||
public string trace; | |||
#endif | |||
public EntitySubmitOperation( | |||
EntitySubmitOperationType operation, int entityId, int toId, int fromGroupId, int toGroupId, | |||
IEntityBuilder[] builders, Type entityDescriptor) | |||
public EntitySubmitOperation(EntitySubmitOperationType operation, EGID from, EGID to, | |||
IEntityBuilder[] builders = null, | |||
Type entityDescriptor = null) | |||
{ | |||
type = operation; | |||
type = operation; | |||
this.builders = builders; | |||
ID = entityId; | |||
toID = toId; | |||
fromID = from; | |||
toID = to; | |||
toGroupID = toGroupId; | |||
fromGroupID = fromGroupId; | |||
this.entityDescriptor = entityDescriptor; | |||
#if DEBUG && !PROFILER | |||
trace = string.Empty; | |||
#endif | |||
} | |||
public static bool operator ==(EntitySubmitOperation obj1, EntitySubmitOperation obj2) | |||
{ | |||
return obj1.Equals(obj2); | |||
} | |||
public static bool operator !=(EntitySubmitOperation obj1, EntitySubmitOperation obj2) | |||
{ | |||
return obj1.Equals(obj2) == false; | |||
} | |||
public bool Equals(EntitySubmitOperation other) | |||
{ | |||
return type == other.type && fromID == other.fromID && toID == other.toID; | |||
} | |||
} | |||
enum EntitySubmitOperationType | |||
{ | |||
Swap, | |||
Remove, | |||
RemoveGroup, | |||
RemoveGroup | |||
} | |||
} |
@@ -6,7 +6,7 @@ using Svelto.Utilities; | |||
namespace Svelto.ECS | |||
{ | |||
static class EntityView<T> where T: IEntityStruct, new() | |||
static class EntityView<T> where T: struct, IEntityStruct | |||
{ | |||
internal static readonly FasterList<KeyValuePair<Type, ActionCast<T>>> cachedFields; | |||
@@ -32,9 +32,9 @@ namespace Svelto.ECS | |||
internal static void InitCache() | |||
{} | |||
internal static void BuildEntityView(EGID ID, out T entityView) | |||
internal static void BuildEntityView(out T entityView) | |||
{ | |||
entityView = new T { ID = ID }; | |||
entityView = new T {}; | |||
} | |||
} | |||
} |
@@ -38,12 +38,10 @@ namespace Svelto.ECS | |||
, Dictionary<Type, Type[]> cachedTypes | |||
) | |||
{ | |||
int count; | |||
//efficient way to collect the fields of every EntityViewType | |||
var setters = | |||
FasterList<KeyValuePair<Type, ActionCast<T>>> | |||
.NoVirt.ToArrayFast(entityViewBlazingFastReflection, out count); | |||
.NoVirt.ToArrayFast(entityViewBlazingFastReflection, out var count); | |||
for (var index = 0; index < implementors.Length; index++) | |||
{ | |||
@@ -53,17 +51,14 @@ namespace Svelto.ECS | |||
{ | |||
var type = implementor.GetType(); | |||
Type[] interfaces; | |||
if (cachedTypes.TryGetValue(type, out interfaces) == false) | |||
if (cachedTypes.TryGetValue(type, out var interfaces) == false) | |||
interfaces = cachedTypes[type] = type.GetInterfacesEx(); | |||
for (var iindex = 0; iindex < interfaces.Length; iindex++) | |||
{ | |||
var componentType = interfaces[iindex]; | |||
#if DEBUG && !PROFILER | |||
ECSTuple<object, int> implementorData; | |||
if (implementorsByType.TryGetValue(componentType, out implementorData)) | |||
if (implementorsByType.TryGetValue(componentType, out var implementorData)) | |||
{ | |||
implementorData.numberOfImplementations++; | |||
implementorsByType[componentType] = implementorData; | |||
@@ -1,4 +1,4 @@ | |||
using System; | |||
using System; | |||
using System.Collections.Generic; | |||
#pragma warning disable 660,661 | |||
@@ -41,12 +41,12 @@ namespace Svelto.ECS | |||
return group._group; | |||
} | |||
public static explicit operator int(ExclusiveGroup group) | |||
public static explicit operator uint(ExclusiveGroup group) | |||
{ | |||
return group._group; | |||
} | |||
public static ExclusiveGroupStruct operator+(ExclusiveGroup a, int b) | |||
public static ExclusiveGroupStruct operator+(ExclusiveGroup a, uint b) | |||
{ | |||
return a._group + b; | |||
} | |||
@@ -91,7 +91,7 @@ namespace Svelto.ECS | |||
{ | |||
ExclusiveGroupStruct groupStruct; | |||
groupStruct._id = (int) _globalId; | |||
groupStruct._id = _globalId; | |||
DBC.ECS.Check.Require(_globalId + 1 < ushort.MaxValue, "too many exclusive groups created"); | |||
_globalId++; | |||
@@ -103,22 +103,22 @@ namespace Svelto.ECS | |||
/// </summary> | |||
internal ExclusiveGroupStruct(ushort range) | |||
{ | |||
_id = (int) _globalId; | |||
_id = _globalId; | |||
DBC.ECS.Check.Require(_globalId + range < ushort.MaxValue, "too many exclusive groups created"); | |||
_globalId += range; | |||
} | |||
internal ExclusiveGroupStruct(int groupID) | |||
internal ExclusiveGroupStruct(uint groupID) | |||
{ | |||
_id = groupID; | |||
} | |||
public static implicit operator int(ExclusiveGroupStruct groupStruct) | |||
public static implicit operator uint(ExclusiveGroupStruct groupStruct) | |||
{ | |||
return groupStruct._id; | |||
} | |||
public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, int b) | |||
public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, uint b) | |||
{ | |||
var group = new ExclusiveGroupStruct(); | |||
@@ -127,7 +127,7 @@ namespace Svelto.ECS | |||
return group; | |||
} | |||
int _id; | |||
uint _id; | |||
static uint _globalId; | |||
} | |||
@@ -1,161 +1,44 @@ | |||
using System; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
partial class EntitiesDB | |||
{ | |||
public void ExecuteOnEntity<T>(int id, | |||
ExclusiveGroup.ExclusiveGroupStruct groupid, | |||
EntityAction<T> action) where T : IEntityStruct | |||
{ | |||
ExecuteOnEntity(id, (int)groupid, action); | |||
} | |||
public void ExecuteOnEntity<T, W>(EGID entityGID, ref W value, EntityAction<T, W> action) where T: IEntityStruct | |||
{ | |||
TypeSafeDictionary<T> casted; | |||
if (QueryEntitySafeDictionary(entityGID.groupID, out casted)) | |||
{ | |||
if (casted != null) | |||
if (casted.ExecuteOnEntityView(entityGID.entityID, ref value, action)) | |||
return; | |||
} | |||
throw new EntityNotFoundException(entityGID.entityID, entityGID.groupID, typeof(T)); | |||
} | |||
public void ExecuteOnEntity<T>(EGID entityGID, EntityAction<T> action) where T : IEntityStruct | |||
{ | |||
TypeSafeDictionary<T> casted; | |||
if (QueryEntitySafeDictionary(entityGID.groupID, out casted)) | |||
{ | |||
if (casted != null) | |||
if (casted.ExecuteOnEntityView(entityGID.entityID, action)) | |||
return; | |||
} | |||
throw new EntityNotFoundException(entityGID.entityID, entityGID.groupID, typeof(T)); | |||
} | |||
public void ExecuteOnEntity<T>(int id, int groupid, EntityAction<T> action) where T : IEntityStruct | |||
{ | |||
ExecuteOnEntity(new EGID(id, groupid), action); | |||
} | |||
public void ExecuteOnEntity<T, W>(int id, int groupid, ref W value, EntityAction<T, W> action) | |||
where T : IEntityStruct | |||
{ | |||
ExecuteOnEntity(new EGID(id, groupid), ref value, action); | |||
} | |||
public void ExecuteOnEntity<T, W>(int id, | |||
ExclusiveGroup.ExclusiveGroupStruct groupid, | |||
ref W value, | |||
EntityAction<T, W> action) where T : IEntityStruct | |||
{ | |||
ExecuteOnEntity(id, (int)groupid, ref value, action); | |||
} | |||
//---------------------------------------------------------------------------------------------------------- | |||
public void ExecuteOnEntities<T>(int groupID, EntitiesAction<T> action) where T : IEntityStruct | |||
{ | |||
if (QueryEntitySafeDictionary(groupID, out TypeSafeDictionary<T> typeSafeDictionary) == false) return; | |||
var entities = typeSafeDictionary.GetValuesArray(out var count); | |||
for (var i = 0; i < count; i++) | |||
action(ref entities[i], new EntityActionData(this, i)); | |||
SafetyChecks(typeSafeDictionary, count); | |||
} | |||
public void ExecuteOnEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, | |||
EntitiesAction<T> action) where T : IEntityStruct | |||
{ | |||
ExecuteOnEntities((int)groupStructId, action); | |||
} | |||
public void ExecuteOnEntities<T, W>(int groupID, ref W value, EntitiesAction<T, W> action) where T:IEntityStruct | |||
{ | |||
if (QueryEntitySafeDictionary(groupID, out TypeSafeDictionary<T> typeSafeDictionary) == false) return; | |||
var entities = typeSafeDictionary.GetValuesArray(out var count); | |||
for (var i = 0; i < count; i++) | |||
action(ref entities[i], ref value, new EntityActionData(this, i)); | |||
SafetyChecks(typeSafeDictionary, count); | |||
} | |||
public void ExecuteOnEntities<T, W>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, | |||
ref W value, EntitiesAction<T, W> action) where T : IEntityStruct | |||
{ | |||
ExecuteOnEntities((int)groupStructId, ref value, action); | |||
} | |||
//----------------------------------------------------------------------------------------------------------- | |||
public void ExecuteOnAllEntities<T>(AllEntitiesAction<T> action) where T : IEntityStruct | |||
public void ExecuteOnAllEntities<T>(Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB> action) | |||
where T : struct, IEntityStruct | |||
{ | |||
var type = typeof(T); | |||
if (_groupedGroups.TryGetValue(type, out var dic)) | |||
if (_groupsPerEntity.TryGetValue(type, out var dic)) | |||
{ | |||
var typeSafeDictionaries = dic.GetValuesArray(out var count); | |||
for (int j = 0; j < count; j++) | |||
foreach (var pair in dic) | |||
{ | |||
var typeSafeDictionary = typeSafeDictionaries[j]; | |||
var casted = typeSafeDictionary as TypeSafeDictionary<T>; | |||
var entities = | |||
(pair.Value as TypeSafeDictionary<T>).GetValuesArray(out var innerCount); | |||
var entities = casted.GetValuesArray(out var innerCount); | |||
for (int i = 0; i < innerCount; i++) | |||
action(ref entities[i], this); | |||
SafetyChecks(casted, innerCount); | |||
if (innerCount > 0) | |||
action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this); | |||
} | |||
} | |||
} | |||
public void ExecuteOnAllEntities<T, W>(ref W value, AllEntitiesAction<T, W> action) where T : IEntityStruct | |||
public void ExecuteOnAllEntities | |||
<T, W>(ref W value, Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB, W> action) | |||
where T : struct, IEntityStruct | |||
{ | |||
var type = typeof(T); | |||
if (_groupedGroups.TryGetValue(type, out var dic)) | |||
if (_groupsPerEntity.TryGetValue(type, out var dic)) | |||
{ | |||
var typeSafeDictionaries = dic.GetValuesArray(out var count); | |||
for (int j = 0; j < count; j++) | |||
foreach (var pair in dic) | |||
{ | |||
var typeSafeDictionary = typeSafeDictionaries[j]; | |||
var casted = typeSafeDictionary as TypeSafeDictionary<T>; | |||
var entities = casted.GetValuesArray(out var innerCount); | |||
var entities = | |||
(pair.Value as TypeSafeDictionary<T>).GetValuesArray(out var innerCount); | |||
for (int i = 0; i < innerCount; i++) | |||
action(ref entities[i], ref value, this); | |||
SafetyChecks(casted, innerCount); | |||
if (innerCount > 0) | |||
action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this, value); | |||
} | |||
} | |||
} | |||
public void ExecuteOnAllEntities<T>(ExclusiveGroup[] groups, EntitiesAction<T> action) where T : IEntityStruct | |||
{ | |||
foreach (var group in groups) | |||
{ | |||
ExecuteOnEntities(group, action); | |||
} | |||
} | |||
public void ExecuteOnAllEntities<T, W>(ExclusiveGroup[] groups, | |||
ref W value, | |||
EntitiesAction<T, W> action) where T : IEntityStruct | |||
{ | |||
foreach (var group in groups) | |||
{ | |||
ExecuteOnEntities(group, ref value, action); | |||
} | |||
} | |||
} | |||
} |
@@ -3,7 +3,7 @@ using UnityEngine; | |||
namespace Svelto.ECS.Unity | |||
{ | |||
public class GenericEntityDescriptorHolder<T>: | |||
public abstract class GenericEntityDescriptorHolder<T>: | |||
MonoBehaviour , IEntityDescriptorHolder | |||
where T: IEntityDescriptor, new() | |||
{ | |||
@@ -13,11 +13,12 @@ namespace Svelto.ECS.Unity | |||
} | |||
public string groupName => _groupName; | |||
public ushort id => _id; | |||
[SerializeField] | |||
#pragma warning disable 649 | |||
string _groupName; | |||
#pragma warning restore 649 | |||
#pragma warning disable 649 | |||
[SerializeField] string _groupName; | |||
[SerializeField] ushort _id = 0; | |||
#pragma warning restore 649 | |||
} | |||
} | |||
#endif |
@@ -1,38 +0,0 @@ | |||
#if UNITY_5 || UNITY_5_3_OR_NEWER | |||
using UnityEngine; | |||
namespace Svelto.ECS.Unity | |||
{ | |||
public static class SveltoEntityFactoryForUnity | |||
{ | |||
public static T Create<T>(EGID ID, Transform contextHolder, | |||
IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holder = contextHolder.GetComponentInChildren<T>(true); | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
return holder; | |||
} | |||
public static void CreateAll<T>(ExclusiveGroup group, Transform contextHolder, | |||
IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holders = contextHolder.GetComponentsInChildren<T>(true); | |||
foreach (var holder in holders) | |||
{ | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
ExclusiveGroup.ExclusiveGroupStruct realGroup = group; | |||
if (string.IsNullOrEmpty( holder.groupName) == false) | |||
realGroup = ExclusiveGroup.Search(holder.groupName); | |||
factory.BuildEntity(holder.GetInstanceID(), realGroup, holder.GetDescriptor(), implementors); | |||
} | |||
} | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,72 @@ | |||
#if UNITY_5 || UNITY_5_3_OR_NEWER | |||
using UnityEngine; | |||
namespace Svelto.ECS.Unity | |||
{ | |||
public static class SveltoGUIHelper | |||
{ | |||
public static T CreateFromPrefab<T>(uint startIndex, Transform contextHolder, IEntityFactory factory, ExclusiveGroup group) where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holder = Create<T>(new EGID(startIndex++, group), contextHolder, factory); | |||
var childs = contextHolder.GetComponentsInChildren<IEntityDescriptorHolder>(true); | |||
foreach (var child in childs) | |||
{ | |||
if (child.GetType() != typeof(T)) | |||
{ | |||
var childImplementors = (child as MonoBehaviour).GetComponents<IImplementor>(); | |||
InternalBuildAll(startIndex, child, factory, group, childImplementors); | |||
} | |||
} | |||
return holder; | |||
} | |||
public static T Create<T>(EGID ID, Transform contextHolder, | |||
IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holder = contextHolder.GetComponentInChildren<T>(true); | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
return holder; | |||
} | |||
public static uint CreateAll<T>(uint startIndex, ExclusiveGroup group, Transform contextHolder, | |||
IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holders = contextHolder.GetComponentsInChildren<T>(true); | |||
foreach (var holder in holders) | |||
{ | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
InternalBuildAll(startIndex, holder, factory, group, implementors); | |||
} | |||
return startIndex; | |||
} | |||
static void InternalBuildAll(uint startIndex, IEntityDescriptorHolder descriptorHolder, IEntityFactory factory, ExclusiveGroup group, IImplementor[] implementors) | |||
{ | |||
ExclusiveGroup.ExclusiveGroupStruct realGroup = group; | |||
if (string.IsNullOrEmpty(descriptorHolder.groupName) == false) | |||
realGroup = ExclusiveGroup.Search(descriptorHolder.groupName); | |||
EGID egid; | |||
var holderId = descriptorHolder.id; | |||
if (holderId == 0) | |||
egid = new EGID(startIndex++, realGroup); | |||
else | |||
egid = new EGID(holderId, realGroup); | |||
var init = factory.BuildEntity(egid, descriptorHolder.GetDescriptor(), implementors); | |||
init.Init(new EntityHierarchyStruct(group)); | |||
} | |||
} | |||
} | |||
#endif |
@@ -30,13 +30,15 @@ namespace Svelto.ECS.Schedulers.Unity | |||
public WeakAction onTick; | |||
} | |||
public UnityEntitySubmissionScheduler(string name = "ECSScheduler") { _name = name; } | |||
public WeakAction onTick | |||
{ | |||
set | |||
{ | |||
if (_scheduler == null) | |||
{ | |||
GameObject go = new GameObject("ECSScheduler"); | |||
GameObject go = new GameObject(_name); | |||
_scheduler = go.AddComponent<Scheduler>(); | |||
} | |||
@@ -45,6 +47,7 @@ namespace Svelto.ECS.Schedulers.Unity | |||
} | |||
Scheduler _scheduler; | |||
string _name; | |||
} | |||
} | |||
#endif |
@@ -1,40 +1,31 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public abstract class GenericEntityDescriptor<T>:IEntityDescriptor where T : IEntityStruct, new() | |||
public abstract class GenericEntityDescriptor<T> : IEntityDescriptor where T : struct, IEntityStruct | |||
{ | |||
static GenericEntityDescriptor() | |||
{ | |||
_entityBuilders = new IEntityBuilder[] { new EntityBuilder<T>() }; | |||
} | |||
public IEntityBuilder[] entitiesToBuild | |||
{ | |||
get { return _entityBuilders; } | |||
} | |||
static readonly IEntityBuilder[] _entityBuilders; | |||
static GenericEntityDescriptor() { _entityBuilders = new IEntityBuilder[] {new EntityBuilder<T>()}; } | |||
public IEntityBuilder[] entitiesToBuild => _entityBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U> : IEntityDescriptor where T : IEntityStruct, new() | |||
where U : IEntityStruct, new() | |||
public abstract class GenericEntityDescriptor<T, U> : IEntityDescriptor | |||
where T : struct, IEntityStruct where U : struct, IEntityStruct | |||
{ | |||
static readonly IEntityBuilder[] _entityBuilders; | |||
static GenericEntityDescriptor() | |||
{ | |||
_entityBuilders = new IEntityBuilder[] {new EntityBuilder<T>(), new EntityBuilder<U>()}; | |||
} | |||
public IEntityBuilder[] entitiesToBuild | |||
{ | |||
get { return _entityBuilders; } | |||
} | |||
static readonly IEntityBuilder[] _entityBuilders; | |||
public IEntityBuilder[] entitiesToBuild => _entityBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U, V> : IEntityDescriptor where T : IEntityStruct, new() | |||
where U : IEntityStruct, new() | |||
where V : IEntityStruct, new() | |||
public abstract class GenericEntityDescriptor<T, U, V> : IEntityDescriptor | |||
where T : struct, IEntityStruct where U : struct, IEntityStruct where V : struct, IEntityStruct | |||
{ | |||
static readonly IEntityBuilder[] _entityBuilders; | |||
static GenericEntityDescriptor() | |||
{ | |||
_entityBuilders = new IEntityBuilder[] | |||
@@ -45,19 +36,15 @@ | |||
}; | |||
} | |||
public IEntityBuilder[] entitiesToBuild | |||
{ | |||
get { return _entityBuilders; } | |||
} | |||
static readonly IEntityBuilder[] _entityBuilders; | |||
public IEntityBuilder[] entitiesToBuild => _entityBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U, V, W> : IEntityDescriptor where T : IEntityStruct, new() | |||
where U : IEntityStruct, new() | |||
where V : IEntityStruct, new() | |||
where W : IEntityStruct, new() | |||
public abstract class GenericEntityDescriptor<T, U, V, W> : IEntityDescriptor | |||
where T : struct, IEntityStruct where U : struct, IEntityStruct where V : struct, IEntityStruct | |||
where W : struct, IEntityStruct | |||
{ | |||
static readonly IEntityBuilder[] _entityBuilders; | |||
static GenericEntityDescriptor() | |||
{ | |||
_entityBuilders = new IEntityBuilder[] | |||
@@ -69,20 +56,15 @@ | |||
}; | |||
} | |||
public IEntityBuilder[] entitiesToBuild | |||
{ | |||
get { return _entityBuilders; } | |||
} | |||
static readonly IEntityBuilder[] _entityBuilders; | |||
public IEntityBuilder[] entitiesToBuild => _entityBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U, V, W, X> : IEntityDescriptor where T : IEntityStruct, new() | |||
where U : IEntityStruct, new() | |||
where V : IEntityStruct, new() | |||
where W : IEntityStruct, new() | |||
where X : IEntityStruct, new() | |||
public abstract class GenericEntityDescriptor<T, U, V, W, X> : IEntityDescriptor | |||
where T : struct, IEntityStruct where U : struct, IEntityStruct where V : struct, IEntityStruct | |||
where W : struct, IEntityStruct where X : struct, IEntityStruct | |||
{ | |||
static readonly IEntityBuilder[] _entityBuilders; | |||
static GenericEntityDescriptor() | |||
{ | |||
_entityBuilders = new IEntityBuilder[] | |||
@@ -95,21 +77,15 @@ | |||
}; | |||
} | |||
public IEntityBuilder[] entitiesToBuild | |||
{ | |||
get { return _entityBuilders; } | |||
} | |||
static readonly IEntityBuilder[] _entityBuilders; | |||
public IEntityBuilder[] entitiesToBuild => _entityBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U, V, W, X, Y> : IEntityDescriptor where T : IEntityStruct, new() | |||
where U : IEntityStruct, new() | |||
where V : IEntityStruct, new() | |||
where W : IEntityStruct, new() | |||
where X : IEntityStruct, new() | |||
where Y : IEntityStruct, new() | |||
public abstract class GenericEntityDescriptor<T, U, V, W, X, Y> : IEntityDescriptor | |||
where T : struct, IEntityStruct where U : struct, IEntityStruct where V : struct, IEntityStruct | |||
where W : struct, IEntityStruct where X : struct, IEntityStruct where Y : struct, IEntityStruct | |||
{ | |||
static readonly IEntityBuilder[] _entityBuilders; | |||
static GenericEntityDescriptor() | |||
{ | |||
_entityBuilders = new IEntityBuilder[] | |||
@@ -123,11 +99,6 @@ | |||
}; | |||
} | |||
public IEntityBuilder[] entitiesToBuild | |||
{ | |||
get { return _entityBuilders; } | |||
} | |||
static readonly IEntityBuilder[] _entityBuilders; | |||
public IEntityBuilder[] entitiesToBuild => _entityBuilders; | |||
} | |||
} | |||
} |
@@ -0,0 +1,22 @@ | |||
namespace Svelto.ECS | |||
{ | |||
class GenericentityStreamConsumerFactory : IEntityStreamConsumerFactory | |||
{ | |||
public GenericentityStreamConsumerFactory(DataStructures.WeakReference<EnginesRoot> weakReference) | |||
{ | |||
_enginesRoot = weakReference; | |||
} | |||
public Consumer<T> GenerateConsumer<T>(string name, int capacity) where T : unmanaged, IEntityStruct | |||
{ | |||
return _enginesRoot.Target.GenerateConsumer<T>(name, capacity); | |||
} | |||
readonly DataStructures.WeakReference<EnginesRoot> _enginesRoot; | |||
} | |||
public interface IEntityStreamConsumerFactory | |||
{ | |||
Consumer<T> GenerateConsumer<T>(string name, int capacity) where T : unmanaged, IEntityStruct; | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
namespace Svelto.ECS.Hybrid | |||
{ | |||
public interface IEntityViewStruct:IEntityStruct, INeedEGID | |||
{} | |||
} | |||
@@ -1,24 +1,13 @@ | |||
namespace Svelto.ECS.Internal | |||
{ | |||
public interface IHandleEntityViewEngineAbstracted : IEngine | |||
public interface IReactEngine: IEngine | |||
{} | |||
public interface IHandleEntityStructEngine<T> : IHandleEntityViewEngineAbstracted | |||
{ | |||
void AddInternal(ref T entityView); | |||
void RemoveInternal(ref T entityView); | |||
} | |||
public class EngineInfo | |||
{ | |||
#if ENABLE_PLATFORM_PROFILER | |||
protected EngineInfo() | |||
{ | |||
name = GetType().FullName; | |||
} | |||
#endif | |||
internal readonly string name = string.Empty; | |||
} | |||
public interface IReactOnAddAndRemove : IReactEngine | |||
{} | |||
public interface IReactOnSwap : IReactEngine | |||
{} | |||
} | |||
namespace Svelto.ECS | |||
@@ -1,26 +1,57 @@ | |||
using System; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IEntitiesDB: IObsoleteInterfaceDb | |||
public interface IEntitiesDB | |||
{ | |||
/// <summary> | |||
/// ECS is meant to work on a set of Entities. Working on a single entity is sometime necessary, but using | |||
/// the following functions inside a loop would be a mistake as performance can be significantly impacted | |||
/// return the buffer and the index of the entity inside the buffer using the input EGID | |||
/// return the buffer and the index of the entity inside the buffer using the input EGID | |||
/// </summary> | |||
/// <param name="entityGid"></param> | |||
/// <param name="index"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array) where T : struct, IEntityStruct; | |||
bool TryQueryEntitiesAndIndex | |||
<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) | |||
where T : struct, IEntityStruct; | |||
/// <summary> | |||
/// ECS is meant to work on a set of Entities. Working on a single entity is sometime necessary, but using | |||
/// the following functions inside a loop would be a mistake as performance can be significantly impacted | |||
/// return the buffer and the index of the entity inside the buffer using the input EGID | |||
/// </summary> | |||
/// <param name="entityGid"></param> | |||
/// <param name="index"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
bool TryQueryEntitiesAndIndex<T>(int id, int group, out uint index, out T[] array) where T : IEntityStruct; | |||
bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array) where T : IEntityStruct; | |||
bool TryQueryEntitiesAndIndex<T>(int id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) where T : IEntityStruct; | |||
ref T QueryUniqueEntity<T>(ExclusiveGroup.ExclusiveGroupStruct group) where T : IEntityStruct; | |||
ref T QueryUniqueEntity<T>(int group) where T : IEntityStruct; | |||
ref T QueryEntity<T>(EGID entityGid) where T : IEntityStruct; | |||
ref T QueryEntity<T>(int id, ExclusiveGroup.ExclusiveGroupStruct group) where T : IEntityStruct; | |||
ref T QueryEntity<T>(int id, int group) where T : IEntityStruct; | |||
T[] QueryEntitiesAndIndex<T>(EGID entityGid, out uint index) where T : struct, IEntityStruct; | |||
T[] QueryEntitiesAndIndex<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) | |||
where T : struct, IEntityStruct; | |||
/// <summary> | |||
/// QueryUniqueEntity is a contract method that explicitly declare the intention to have just on entity in a | |||
/// specific group, usually used for GUI elements | |||
/// </summary> | |||
/// <param name="group"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
ref T QueryUniqueEntity<T>(ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct; | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
/// <param name="entityGid"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
ref T QueryEntity<T>(EGID entityGid) where T : struct, IEntityStruct; | |||
ref T QueryEntity<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct; | |||
/// <summary> | |||
/// Fast and raw (therefore not safe) return of entities buffer | |||
/// Modifying a buffer would compromise the integrity of the whole DB | |||
@@ -29,13 +60,22 @@ namespace Svelto.ECS | |||
/// <param name="count"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
T[] QueryEntities<T>(int group, out int count) where T : IEntityStruct; | |||
T[] QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) | |||
where T : struct, IEntityStruct; | |||
(T1[], T2[]) QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) | |||
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; | |||
(T1[], T2[], T3[]) QueryEntities<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) | |||
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct; | |||
T[] QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out int count) where T : IEntityStruct; | |||
(T1[], T2[]) QueryEntities<T1, T2>(int group, out int count) where T1 : IEntityStruct where T2 : IEntityStruct; | |||
EntityCollection<T> QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) | |||
where T : struct, IEntityStruct; | |||
EntityCollections<T> QueryEntities<T>(ExclusiveGroup[] groups) where T : struct, IEntityStruct; | |||
EntityCollections<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup[] groups) | |||
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; | |||
(T1[], T2[]) QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out int count) | |||
where T1 : IEntityStruct where T2 : IEntityStruct; | |||
/// <summary> | |||
/// this version returns a mapped version of the entity array so that is possible to find the | |||
/// index of the entity inside the returned buffer through it's EGID | |||
@@ -45,79 +85,54 @@ namespace Svelto.ECS | |||
/// <param name="mapper"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
EGIDMapper<T> QueryMappedEntities<T>(int groupID) where T : IEntityStruct; | |||
EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId) where T : IEntityStruct; | |||
/// <summary> | |||
/// Execute an action on entities. Be sure that the action is not capturing variables | |||
/// otherwise you will allocate memory which will have a great impact on the execution performance. | |||
/// ExecuteOnEntities can be used to iterate safely over entities, several checks are in place | |||
/// to be sure that everything will be done correctly. | |||
/// Cache friendliness is guaranteed if only Entity Structs are used, but | |||
/// </summary> | |||
/// <param name="egid"></param> | |||
/// <param name="action"></param> | |||
/// <typeparam name="T"></typeparam> | |||
void ExecuteOnEntities<T>(int groupID, EntitiesAction<T> action) where T : IEntityStruct; | |||
void ExecuteOnEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, EntitiesAction<T> action) where T : IEntityStruct; | |||
void ExecuteOnEntities<T, W>(int groupID, ref W value, EntitiesAction<T, W> action) where T : IEntityStruct; | |||
void ExecuteOnEntities<T, W>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, ref W value, EntitiesAction<T, W> action) where T : IEntityStruct; | |||
EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId) | |||
where T : struct, IEntityStruct; | |||
/// <summary> | |||
/// Execute an action on ALL the entities regardless the group. This function doesn't guarantee cache | |||
/// friendliness even if just EntityStructs are used. | |||
/// Safety checks are in place | |||
/// friendliness even if just EntityStructs are used. | |||
/// Safety checks are in place | |||
/// </summary> | |||
/// <param name="damageableGroups"></param> | |||
/// <param name="action"></param> | |||
/// <typeparam name="T"></typeparam> | |||
void ExecuteOnAllEntities<T>(AllEntitiesAction<T> action) where T : IEntityStruct; | |||
void ExecuteOnAllEntities<T, W>(ref W value, AllEntitiesAction<T, W> action) where T : IEntityStruct; | |||
void ExecuteOnAllEntities<T>(ExclusiveGroup[] groups, EntitiesAction<T> action) where T : IEntityStruct; | |||
void ExecuteOnAllEntities<T, W>(ExclusiveGroup[] groups, ref W value, EntitiesAction<T, W> action) where T : IEntityStruct; | |||
void ExecuteOnAllEntities<T>(Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB> action) | |||
where T : struct, IEntityStruct; | |||
void ExecuteOnAllEntities<T, W>(ref W value, | |||
Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB, W> action) | |||
where T : struct, IEntityStruct; | |||
/// <summary> | |||
/// ECS is meant to work on a set of Entities. Working on a single entity is sometime necessary, but using | |||
/// the following functions inside a loop would be a mistake as performance can be significantly impacted | |||
/// Execute an action on a specific Entity. Be sure that the action is not capturing variables | |||
/// otherwise you will allocate memory which will have a great impact on the execution performance | |||
/// | |||
/// </summary> | |||
/// <param name="egid"></param> | |||
/// <param name="action"></param> | |||
/// <typeparam name="T"></typeparam> | |||
void ExecuteOnEntity<T>(EGID egid, EntityAction<T> action) where T : IEntityStruct; | |||
void ExecuteOnEntity<T>(int id, int groupid, EntityAction<T> action) where T : IEntityStruct; | |||
void ExecuteOnEntity<T>(int id, ExclusiveGroup.ExclusiveGroupStruct groupid, EntityAction<T> action) where T : IEntityStruct; | |||
void ExecuteOnEntity<T, W>(EGID egid, ref W value, EntityAction<T, W> action) where T : IEntityStruct; | |||
void ExecuteOnEntity<T, W>(int id, int groupid, ref W value, EntityAction<T, W> action) where T : IEntityStruct; | |||
void ExecuteOnEntity<T, W>(int id, ExclusiveGroup.ExclusiveGroupStruct groupid, ref W value, EntityAction<T, W> action) where T : IEntityStruct; | |||
bool Exists<T>(EGID egid) where T : IEntityStruct; | |||
bool Exists<T>(int id, int groupid) where T : IEntityStruct; | |||
bool Exists (ExclusiveGroup.ExclusiveGroupStruct gid); | |||
bool HasAny<T>(int group) where T:IEntityStruct; | |||
bool HasAny<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T:IEntityStruct; | |||
int Count<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T:IEntityStruct; | |||
int Count<T>(int groupStruct) where T:IEntityStruct; | |||
} | |||
/// <returns></returns> | |||
bool Exists<T>(EGID egid) where T : struct, IEntityStruct; | |||
bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid); | |||
public delegate void EntityAction<T, W>(ref T target, ref W value); | |||
public delegate void EntityAction<T>(ref T target); | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
/// <param name="group"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
bool HasAny<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; | |||
public delegate void AllEntitiesAction<T, W>(ref T target, ref W value, IEntitiesDB entitiesDb); | |||
public delegate void AllEntitiesAction<T>(ref T target, IEntitiesDB entitiesDb); | |||
public delegate void EntitiesAction<T, W>(ref T target, ref W value, EntityActionData extraParams); | |||
public delegate void EntitiesAction<T>(ref T target, EntityActionData extraParams); | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
/// <param name="groupStruct"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
uint Count<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; | |||
public struct EntityActionData | |||
{ | |||
public readonly IEntitiesDB entitiesDB; | |||
public readonly int entityIndex; | |||
public EntityActionData(IEntitiesDB entitiesDb, int index) | |||
{ | |||
this.entitiesDB = entitiesDb; | |||
entityIndex = index; | |||
} | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
/// <param name="egid"></param> | |||
/// <typeparam name="T"></typeparam> | |||
void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityStruct; | |||
} | |||
} |
@@ -6,7 +6,7 @@ namespace Svelto.ECS | |||
public interface IEntityBuilder | |||
{ | |||
void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID entityID, object[] implementors); | |||
ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, int size); | |||
ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size); | |||
Type GetEntityType(); | |||
} |
@@ -3,7 +3,8 @@ namespace Svelto.ECS | |||
public interface IEntityDescriptorHolder | |||
{ | |||
IEntityDescriptor GetDescriptor(); | |||
string groupName { get; } | |||
ushort id { get; } | |||
} | |||
} |
@@ -16,13 +16,14 @@ namespace Svelto.ECS | |||
/// </summary> | |||
public interface IEntityFactory | |||
{ | |||
/// <summary> | |||
///where performance is critical, you may wish to pre allocate the space needed | |||
///to store the entities | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
/// <param name="size"></param> | |||
void PreallocateEntitySpace<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, int size) | |||
/// <summary> | |||
/// where performance is critical, you may wish to pre allocate the space needed | |||
/// to store the entities | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
/// <param name="groupStructId"></param> | |||
/// <param name="size"></param> | |||
void PreallocateEntitySpace<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) | |||
where T : IEntityDescriptor, new(); | |||
/// <summary> | |||
@@ -39,11 +40,25 @@ namespace Svelto.ECS | |||
/// <param name="groupStructId"></param> | |||
/// <param name="ed"></param> | |||
/// <param name="implementors"></param> | |||
EntityStructInitializer BuildEntity<T>(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, | |||
EntityStructInitializer BuildEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, | |||
object[] implementors = null) | |||
where T : IEntityDescriptor, new(); | |||
EntityStructInitializer BuildEntity<T>(EGID egid, object[] implementors = null) | |||
where T:IEntityDescriptor, new(); | |||
#if REAL_ID | |||
/// <summary> | |||
/// BuildEntity version without specifying the entity ID. The complete EGID will be found inside | |||
/// the EntityStructInitializer and/or the single entity components | |||
/// </summary> | |||
/// <param name="groupID"></param> | |||
/// <param name="implementors"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
EntityStructInitializer BuildEntity<T>(ExclusiveGroup.ExclusiveGroupStruct groupID, object[] implementors = null) | |||
where T : IEntityDescriptor, new(); | |||
#endif | |||
/// <summary> | |||
/// When the type of the entity is not known (this is a special case!) an EntityDescriptorInfo | |||
/// can be built in place of the generic parameter T. | |||
@@ -52,7 +67,7 @@ namespace Svelto.ECS | |||
/// <param name="entityDescriptor"></param> | |||
/// <param name="implementors"></param> | |||
/// | |||
EntityStructInitializer BuildEntity<T>(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, | |||
EntityStructInitializer BuildEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, | |||
T descriptorEntity, | |||
object[] implementors = null) | |||
where T : IEntityDescriptor; | |||
@@ -5,21 +5,21 @@ namespace Svelto.ECS | |||
//being entity ID globally not unique, the group must be specified when | |||
//an entity is removed. Not specifying the group will attempt to remove | |||
//the entity from the special standard group. | |||
void RemoveEntity<T>(int entityID, int groupID) where T : IEntityDescriptor, new(); | |||
void RemoveEntity<T>(int entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); | |||
void RemoveEntity<T>(uint entityID, uint groupID) where T : IEntityDescriptor, new(); | |||
void RemoveEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); | |||
void RemoveEntity<T>(EGID entityegid) where T : IEntityDescriptor, new(); | |||
void RemoveEntities<T>(int groupID) where T : IEntityDescriptor, new(); | |||
void RemoveEntities<T>(uint groupID) where T : IEntityDescriptor, new(); | |||
void RemoveEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); | |||
void RemoveGroupAndEntities(int groupID); | |||
void RemoveGroupAndEntities(uint groupID); | |||
void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID); | |||
void SwapEntityGroup<T>(int entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID id, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID id, ExclusiveGroup.ExclusiveGroupStruct toGroupID, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID id, EGID toId) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID id, EGID toId, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID fromID, EGID toId) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID fromID, EGID toId, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new(); | |||
} | |||
} |
@@ -2,6 +2,9 @@ namespace Svelto.ECS | |||
{ | |||
///<summary>EntityStruct MUST implement IEntiyStruct</summary> | |||
public interface IEntityStruct | |||
{} | |||
public interface INeedEGID | |||
{ | |||
EGID ID { get; set; } | |||
} |
@@ -1,7 +0,0 @@ | |||
namespace Svelto.ECS.Hybrid | |||
{ | |||
///<summary>EntityViewStructs MUST implement IEntityViewStruct</summary> | |||
public interface IEntityViewStruct:IEntityStruct | |||
{} | |||
} | |||
@@ -1,51 +0,0 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IObsoleteInterfaceDb | |||
{ | |||
/// <summary> | |||
/// All the EntityView related methods are left for back compatibility, but | |||
/// shouldn't be used anymore. Always pick EntityViewStruct or EntityStruct | |||
/// over EntityView | |||
/// </summary> | |||
[Obsolete] | |||
ReadOnlyCollectionStruct<T> QueryEntityViews<T>(int group) where T : class, IEntityStruct; | |||
[Obsolete] | |||
ReadOnlyCollectionStruct<T> QueryEntityViews<T>(ExclusiveGroup.ExclusiveGroupStruct group) where T : class, IEntityStruct; | |||
/// <summary> | |||
/// All the EntityView related methods are left for back compatibility, but | |||
/// shouldn't be used anymore. Always pick EntityViewStruct or EntityStruct | |||
/// over EntityView | |||
/// </summary> | |||
[Obsolete] | |||
bool TryQueryEntityView<T>(EGID egid, out T entityView) where T : class, IEntityStruct; | |||
[Obsolete] | |||
bool TryQueryEntityView<T>(int id, ExclusiveGroup.ExclusiveGroupStruct group, out T entityView) where T : class, IEntityStruct; | |||
/// <summary> | |||
/// All the EntityView related methods are left for back compatibility, but | |||
/// shouldn't be used anymore. Always pick EntityViewStruct or EntityStruct | |||
/// over EntityView | |||
/// </summary> | |||
[Obsolete] | |||
T QueryEntityView<T>(EGID egid) where T : class, IEntityStruct; | |||
[Obsolete] | |||
T QueryEntityView<T>(int id, ExclusiveGroup.ExclusiveGroupStruct group) where T : class, IEntityStruct; | |||
/// <summary> | |||
/// ECS is meant to work on a set of Entities. Working on a single entity is sometime necessary, but using | |||
/// the following functions inside a loop would be a mistake as performance can be significantly impacted | |||
/// return the buffer and the index of the entity inside the buffer using the input EGID | |||
/// </summary> | |||
/// <param name="entityGid"></param> | |||
/// <param name="index"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
[Obsolete] | |||
T[] QueryEntitiesAndIndex<T>(EGID entityGid, out uint index) where T : IEntityStruct; | |||
[Obsolete] | |||
T[] QueryEntitiesAndIndex<T>(int id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) where T : IEntityStruct; | |||
[Obsolete] | |||
T[] QueryEntitiesAndIndex<T>(int id, int group, out uint index) where T : IEntityStruct; | |||
} | |||
} |
@@ -0,0 +1,10 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IReactOnAddAndRemove<T> : IReactOnAddAndRemove where T : IEntityStruct | |||
{ | |||
void Add(ref T entityView); | |||
void Remove(ref T entityView); | |||
} | |||
} |
@@ -0,0 +1,10 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IReactOnSwap<T> : IReactOnSwap where T : IEntityStruct | |||
{ | |||
void MovedTo(ref T entityView, ExclusiveGroup.ExclusiveGroupStruct previousGroup); | |||
void MovedFrom(ref T entityView, ExclusiveGroup.ExclusiveGroupStruct previousGroupValue); | |||
} | |||
} |
@@ -1,44 +0,0 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public abstract class MultiEntitiesEngine<T, U> : SingleEntityEngine<T>, IHandleEntityStructEngine<U> | |||
where U : IEntityStruct where T : IEntityStruct | |||
{ | |||
public void AddInternal(ref U entityView) | |||
{ Add(ref entityView); } | |||
public void RemoveInternal(ref U entityView) | |||
{ Remove(ref entityView); } | |||
protected abstract void Add(ref U entityView); | |||
protected abstract void Remove(ref U entityView); | |||
} | |||
public abstract class MultiEntitiesEngine<T, U, V> : MultiEntitiesEngine<T, U>, IHandleEntityStructEngine<V> | |||
where V : IEntityStruct where U : IEntityStruct where T : IEntityStruct | |||
{ | |||
public void AddInternal(ref V entityView) | |||
{ Add(ref entityView); } | |||
public void RemoveInternal(ref V entityView) | |||
{ Remove(ref entityView); } | |||
protected abstract void Add(ref V entityView); | |||
protected abstract void Remove(ref V entityView); | |||
} | |||
/// <summary> | |||
/// Please do not add more MultiEntityViewsEngine if you use more than 4 nodes, your engine has | |||
/// already too many responsibilities. | |||
/// </summary> | |||
public abstract class MultiEntitiesEngine<T, U, V, W> : MultiEntitiesEngine<T, U, V>, IHandleEntityStructEngine<W> | |||
where W : IEntityStruct where V : IEntityStruct where U : IEntityStruct where T : IEntityStruct | |||
{ | |||
public void AddInternal(ref W entityView) | |||
{ Add(ref entityView); } | |||
public void RemoveInternal(ref W entityView) | |||
{ Remove(ref entityView); } | |||
protected abstract void Add(ref W entityView); | |||
protected abstract void Remove(ref W entityView); | |||
} | |||
} |
@@ -1,45 +0,0 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public abstract class MultiEntityViewsEngine<T, U> : SingleEntityViewEngine<T>, IHandleEntityStructEngine<U> | |||
where U : class, IEntityStruct where T : class, IEntityStruct | |||
{ | |||
public void AddInternal(ref U entityView) | |||
{ Add(entityView); } | |||
public void RemoveInternal(ref U entityView) | |||
{ Remove(entityView); } | |||
protected abstract void Add(U entityView); | |||
protected abstract void Remove(U entityView); | |||
} | |||
public abstract class MultiEntityViewsEngine<T, U, V> : MultiEntityViewsEngine<T, U>, IHandleEntityStructEngine<V> | |||
where V : class, IEntityStruct where U : class, IEntityStruct where T : class, IEntityStruct | |||
{ | |||
public void AddInternal(ref V entityView) | |||
{ Add(entityView); } | |||
public void RemoveInternal(ref V entityView) | |||
{ Remove(entityView); } | |||
protected abstract void Add(V entityView); | |||
protected abstract void Remove(V entityView); | |||
} | |||
/// <summary> | |||
/// Please do not add more MultiEntityViewsEngine | |||
/// if you use more than 4 nodes, your engine has | |||
/// already too many responsabilities. | |||
/// </summary> | |||
public abstract class MultiEntityViewsEngine<T, U, V, W> : MultiEntityViewsEngine<T, U, V>, IHandleEntityStructEngine<W> | |||
where W : class, IEntityStruct where V : class, IEntityStruct where U : class, IEntityStruct where T : class, IEntityStruct | |||
{ | |||
public void AddInternal(ref W entityView) | |||
{ Add(entityView); } | |||
public void RemoveInternal(ref W entityView) | |||
{ Remove(entityView); } | |||
protected abstract void Add(W entityView); | |||
protected abstract void Remove(W entityView); | |||
} | |||
} |
@@ -99,7 +99,7 @@ namespace Svelto.ECS | |||
/// </summary> | |||
public class Sequencer<S> where S: Sequencer<S>, new() | |||
{ | |||
protected void SetSequence(Steps steps) | |||
public void SetSequence(Steps steps) | |||
{ | |||
_steps = steps; | |||
} | |||
@@ -1,16 +0,0 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public abstract class SingleEntityEngine<T> : EngineInfo, IHandleEntityStructEngine<T> where T : IEntityStruct | |||
{ | |||
public void AddInternal(ref T entityView) | |||
{ Add(ref entityView); } | |||
public void RemoveInternal(ref T entityView) | |||
{ Remove(ref entityView); } | |||
protected abstract void Add(ref T entityView); | |||
protected abstract void Remove(ref T entityView); | |||
} | |||
} |
@@ -1,16 +0,0 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public abstract class SingleEntityViewEngine<T> : EngineInfo, IHandleEntityStructEngine<T> where T : class, IEntityStruct | |||
{ | |||
public void AddInternal(ref T entityView) | |||
{ Add(entityView); } | |||
public void RemoveInternal(ref T entityView) | |||
{ Remove(entityView); } | |||
protected abstract void Add(T entityView); | |||
protected abstract void Remove(T entityView); | |||
} | |||
} |
@@ -6,5 +6,10 @@ | |||
"optionalUnityReferences": [], | |||
"includePlatforms": [], | |||
"excludePlatforms": [], | |||
"allowUnsafeCode": false | |||
"allowUnsafeCode": false, | |||
"overrideReferences": false, | |||
"precompiledReferences": [], | |||
"autoReferenced": true, | |||
"defineConstraints": [], | |||
"versionDefines": [] | |||
} |
@@ -1,21 +1,21 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<AssemblyName>Svelto.ECS</AssemblyName> | |||
<LangVersion>6</LangVersion> | |||
<LangVersion>7.3</LangVersion> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> | |||
<PlatformTarget>AnyCPU</PlatformTarget> | |||
<UseSharedCompilation>false</UseSharedCompilation> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> | |||
<PlatformTarget>AnyCPU</PlatformTarget> | |||
<UseSharedCompilation>false</UseSharedCompilation> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="System.Reflection" Version="4.3.0" /> | |||
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" /> | |||
<PackageReference Include="System.Reflection.Emit.ILGeneration" Version="4.3.0" /> | |||
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" /> | |||
<PackageReference Include="System.Reflection.Extensions" Version="4.3.0" /> | |||
<PackageReference Include="System.Reflection.Primitives" Version="4.3.0" /> | |||
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.4.0" /> | |||
<ProjectReference Include="..\Svelto.Common\Svelto.Common.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Reference Include="System.Reflection.Emit.Lightweight"> | |||
<HintPath>C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.0.0\ref\netcoreapp2.0\System.Reflection.Emit.Lightweight.dll</HintPath> | |||
</Reference> | |||
<PackageReference Include="System.Memory" Version="4.5.2" /> | |||
</ItemGroup> | |||
</Project> |