@@ -1,8 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS | |||
{ | |||
public class AllowMultipleAttribute : Attribute | |||
{ | |||
} | |||
} |
@@ -1,81 +0,0 @@ | |||
#if DEBUG && !PROFILER | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
#else | |||
using System.Diagnostics; | |||
#endif | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
#if DEBUG && !PROFILER | |||
void CheckRemoveEntityID(EGID egid) | |||
{ | |||
// Console.LogError("<color=orange>removed</color>".FastConcat(egid.ToString())); | |||
if (_idCheckers.TryGetValue(egid.groupID, out var hash)) | |||
{ | |||
if (hash.Contains(egid.entityID) == false) | |||
throw new ECSException("Entity with not found ID is about to be removed: id: " | |||
.FastConcat(egid.entityID) | |||
.FastConcat(" groupid: ") | |||
.FastConcat(egid.groupID)); | |||
hash.Remove(egid.entityID); | |||
if (hash.Count == 0) | |||
_idCheckers.Remove(egid.groupID); | |||
} | |||
else | |||
{ | |||
throw new ECSException("Entity with not found ID is about to be removed: id: " | |||
.FastConcat(egid.entityID) | |||
.FastConcat(" groupid: ") | |||
.FastConcat(egid.groupID)); | |||
} | |||
} | |||
void CheckAddEntityID(EGID egid) | |||
{ | |||
// Console.LogError("<color=orange>added</color> ".FastConcat(egid.ToString())); | |||
if (_idCheckers.TryGetValue(egid.groupID, out var hash) == false) | |||
hash = _idCheckers[egid.groupID] = new HashSet<uint>(); | |||
else | |||
{ | |||
if (hash.Contains(egid.entityID)) | |||
throw new ECSException("Entity with used ID is about to be built: '" | |||
.FastConcat("' id: '") | |||
.FastConcat(egid.entityID) | |||
.FastConcat("' groupid: '") | |||
.FastConcat(egid.groupID) | |||
.FastConcat("'")); | |||
} | |||
hash.Add(egid.entityID); | |||
} | |||
void RemoveGroupID(ExclusiveGroup.ExclusiveGroupStruct groupID) | |||
{ | |||
_idCheckers.Remove(groupID); | |||
} | |||
readonly FasterDictionary<uint, HashSet<uint>> _idCheckers = new FasterDictionary<uint, HashSet<uint>>(); | |||
#else | |||
[Conditional("_CHECKS_DISABLED")] | |||
void CheckRemoveEntityID(EGID egid) | |||
{ | |||
} | |||
[Conditional("_CHECKS_DISABLED")] | |||
void CheckAddEntityID(EGID egid) | |||
{ | |||
} | |||
[Conditional("_CHECKS_DISABLED")] | |||
void RemoveGroupID(ExclusiveGroup.ExclusiveGroupStruct groupID) | |||
{ | |||
} | |||
#endif | |||
} | |||
} |
@@ -1,446 +0,0 @@ | |||
#if DISABLE_DBC || !DEBUG || PROFILER | |||
#define DISABLE_CHECKS | |||
using System.Diagnostics; | |||
#endif | |||
using System; | |||
namespace DBC.ECS | |||
{ | |||
/// <summary> | |||
/// Design By Contract Checks. | |||
/// | |||
/// Each method generates an exception or | |||
/// a trace assertion statement if the contract is broken. | |||
/// </summary> | |||
/// <remarks> | |||
/// This example shows how to call the Require method. | |||
/// Assume DBC_CHECK_PRECONDITION is defined. | |||
/// <code> | |||
/// public void Test(int x) | |||
/// { | |||
/// try | |||
/// { | |||
/// Check.Require(x > 1, "x must be > 1"); | |||
/// } | |||
/// catch (System.Exception ex) | |||
/// { | |||
/// Console.WriteLine(ex.ToString()); | |||
/// } | |||
/// } | |||
/// </code> | |||
/// If you wish to use trace assertion statements, intended for Debug scenarios, | |||
/// rather than exception handling then set | |||
/// | |||
/// <code>Check.UseAssertions = true</code> | |||
/// | |||
/// You can specify this in your application entry point and maybe make it | |||
/// dependent on conditional compilation flags or configuration file settings, e.g., | |||
/// <code> | |||
/// #if DBC_USE_ASSERTIONS | |||
/// Check.UseAssertions = true; | |||
/// #endif | |||
/// </code> | |||
/// You can direct output to a Trace listener. For example, you could insert | |||
/// <code> | |||
/// Trace.Listeners.Clear(); | |||
/// Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); | |||
/// </code> | |||
/// | |||
/// or direct output to a file or the Event Log. | |||
/// | |||
/// (Note: For ASP.NET clients use the Listeners collection | |||
/// of the Debug, not the Trace, object and, for a Release build, only exception-handling | |||
/// is possible.) | |||
/// </remarks> | |||
/// | |||
static class Check | |||
{ | |||
#region Interface | |||
/// <summary> | |||
/// Precondition check. | |||
/// </summary> | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Require(bool assertion, string message) | |||
{ | |||
if (UseExceptions) | |||
{ | |||
if (!assertion) | |||
throw new PreconditionException(message); | |||
} | |||
else | |||
{ | |||
Trace.Assert(assertion, "Precondition: " + message); | |||
} | |||
} | |||
/// <summary> | |||
/// Precondition check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Require(bool assertion, string message, Exception inner) | |||
{ | |||
if (UseExceptions) | |||
{ | |||
if (!assertion) | |||
throw new PreconditionException(message, inner); | |||
} | |||
else | |||
{ | |||
Trace.Assert(assertion, "Precondition: " + message); | |||
} | |||
} | |||
/// <summary> | |||
/// Precondition check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Require(bool assertion) | |||
{ | |||
if (UseExceptions) | |||
{ | |||
if (!assertion) | |||
throw new PreconditionException("Precondition failed."); | |||
} | |||
else | |||
{ | |||
Trace.Assert(assertion, "Precondition failed."); | |||
} | |||
} | |||
/// <summary> | |||
/// Postcondition check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Ensure(bool assertion, string message) | |||
{ | |||
if (UseExceptions) | |||
{ | |||
if (!assertion) | |||
throw new PostconditionException(message); | |||
} | |||
else | |||
{ | |||
Trace.Assert(assertion, "Postcondition: " + message); | |||
} | |||
} | |||
/// <summary> | |||
/// Postcondition check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Ensure(bool assertion, string message, Exception inner) | |||
{ | |||
if (UseExceptions) | |||
{ | |||
if (!assertion) | |||
throw new PostconditionException(message, inner); | |||
} | |||
else | |||
{ | |||
Trace.Assert(assertion, "Postcondition: " + message); | |||
} | |||
} | |||
/// <summary> | |||
/// Postcondition check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Ensure(bool assertion) | |||
{ | |||
if (UseExceptions) | |||
{ | |||
if (!assertion) | |||
throw new PostconditionException("Postcondition failed."); | |||
} | |||
else | |||
{ | |||
Trace.Assert(assertion, "Postcondition failed."); | |||
} | |||
} | |||
/// <summary> | |||
/// Invariant check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Invariant(bool assertion, string message) | |||
{ | |||
if (UseExceptions) | |||
{ | |||
if (!assertion) | |||
throw new InvariantException(message); | |||
} | |||
else | |||
{ | |||
Trace.Assert(assertion, "Invariant: " + message); | |||
} | |||
} | |||
/// <summary> | |||
/// Invariant check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Invariant(bool assertion, string message, Exception inner) | |||
{ | |||
if (UseExceptions) | |||
{ | |||
if (!assertion) | |||
throw new InvariantException(message, inner); | |||
} | |||
else | |||
{ | |||
Trace.Assert(assertion, "Invariant: " + message); | |||
} | |||
} | |||
/// <summary> | |||
/// Invariant check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Invariant(bool assertion) | |||
{ | |||
if (UseExceptions) | |||
{ | |||
if (!assertion) | |||
throw new InvariantException("Invariant failed."); | |||
} | |||
else | |||
{ | |||
Trace.Assert(assertion, "Invariant failed."); | |||
} | |||
} | |||
/// <summary> | |||
/// Assertion check. | |||
/// </summary> | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Assert(bool assertion, string message) | |||
{ | |||
if (UseExceptions) | |||
{ | |||
if (!assertion) | |||
throw new AssertionException(message); | |||
} | |||
else | |||
{ | |||
Trace.Assert(assertion, "Assertion: " + message); | |||
} | |||
} | |||
/// <summary> | |||
/// Assertion check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Assert(bool assertion, string message, Exception inner) | |||
{ | |||
if (UseExceptions) | |||
{ | |||
if (!assertion) | |||
throw new AssertionException(message, inner); | |||
} | |||
else | |||
{ | |||
Trace.Assert(assertion, "Assertion: " + message); | |||
} | |||
} | |||
/// <summary> | |||
/// Assertion check. | |||
/// </summary> | |||
/// | |||
#if DISABLE_CHECKS | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Assert(bool assertion) | |||
{ | |||
if (UseExceptions) | |||
{ | |||
if (!assertion) | |||
throw new AssertionException("Assertion failed."); | |||
} | |||
else | |||
{ | |||
Trace.Assert(assertion, "Assertion failed."); | |||
} | |||
} | |||
/// <summary> | |||
/// Set this if you wish to use Trace Assert statements | |||
/// instead of exception handling. | |||
/// (The Check class uses exception handling by default.) | |||
/// </summary> | |||
public static bool UseAssertions | |||
{ | |||
get | |||
{ | |||
return useAssertions; | |||
} | |||
set | |||
{ | |||
useAssertions = value; | |||
} | |||
} | |||
#endregion // Interface | |||
#region Implementation | |||
// No creation | |||
/// <summary> | |||
/// Is exception handling being used? | |||
/// </summary> | |||
static bool UseExceptions | |||
{ | |||
get | |||
{ | |||
return !useAssertions; | |||
} | |||
} | |||
// Are trace assertion statements being used? | |||
// Default is to use exception handling. | |||
static bool useAssertions; | |||
#endregion // Implementation | |||
} // End Check | |||
internal class Trace | |||
{ | |||
internal static void Assert(bool assertion, string v) | |||
{ | |||
#if NETFX_CORE | |||
System.Diagnostics.Contracts.Contract.Assert(assertion, v); | |||
#else | |||
System.Diagnostics.Trace.Assert(assertion, v); | |||
#endif | |||
} | |||
} | |||
#region Exceptions | |||
/// <summary> | |||
/// Exception raised when a contract is broken. | |||
/// Catch this exception type if you wish to differentiate between | |||
/// any DesignByContract exception and other runtime exceptions. | |||
/// | |||
/// </summary> | |||
public class DesignByContractException : Exception | |||
{ | |||
protected DesignByContractException() {} | |||
protected DesignByContractException(string message) : base(message) {} | |||
protected DesignByContractException(string message, Exception inner) : base(message, inner) {} | |||
} | |||
/// <summary> | |||
/// Exception raised when a precondition fails. | |||
/// </summary> | |||
public class PreconditionException : DesignByContractException | |||
{ | |||
/// <summary> | |||
/// Precondition Exception. | |||
/// </summary> | |||
public PreconditionException() {} | |||
/// <summary> | |||
/// Precondition Exception. | |||
/// </summary> | |||
public PreconditionException(string message) : base(message) {} | |||
/// <summary> | |||
/// Precondition Exception. | |||
/// </summary> | |||
public PreconditionException(string message, Exception inner) : base(message, inner) {} | |||
} | |||
/// <summary> | |||
/// Exception raised when a postcondition fails. | |||
/// </summary> | |||
public class PostconditionException : DesignByContractException | |||
{ | |||
/// <summary> | |||
/// Postcondition Exception. | |||
/// </summary> | |||
public PostconditionException() {} | |||
/// <summary> | |||
/// Postcondition Exception. | |||
/// </summary> | |||
public PostconditionException(string message) : base(message) {} | |||
/// <summary> | |||
/// Postcondition Exception. | |||
/// </summary> | |||
public PostconditionException(string message, Exception inner) : base(message, inner) {} | |||
} | |||
/// <summary> | |||
/// Exception raised when an invariant fails. | |||
/// </summary> | |||
public class InvariantException : DesignByContractException | |||
{ | |||
/// <summary> | |||
/// Invariant Exception. | |||
/// </summary> | |||
public InvariantException() {} | |||
/// <summary> | |||
/// Invariant Exception. | |||
/// </summary> | |||
public InvariantException(string message) : base(message) {} | |||
/// <summary> | |||
/// Invariant Exception. | |||
/// </summary> | |||
public InvariantException(string message, Exception inner) : base(message, inner) {} | |||
} | |||
/// <summary> | |||
/// Exception raised when an assertion fails. | |||
/// </summary> | |||
public class AssertionException : DesignByContractException | |||
{ | |||
/// <summary> | |||
/// Assertion Exception. | |||
/// </summary> | |||
public AssertionException() {} | |||
/// <summary> | |||
/// Assertion Exception. | |||
/// </summary> | |||
public AssertionException(string message) : base(message) {} | |||
/// <summary> | |||
/// Assertion Exception. | |||
/// </summary> | |||
public AssertionException(string message, Exception inner) : base(message, inner) {} | |||
} | |||
#endregion // Exception classes | |||
} // End Design By Contract |
@@ -1,46 +0,0 @@ | |||
using System; | |||
using System.Linq.Expressions; | |||
using System.Reflection; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
static class SetEGIDWithoutBoxing<T> where T : struct, IEntityStruct | |||
{ | |||
internal delegate void ActionCast(ref T target, EGID egid); | |||
public static readonly ActionCast SetIDWithoutBoxing = MakeSetter(); | |||
static ActionCast MakeSetter() | |||
{ | |||
if (EntityBuilder<T>.HAS_EGID) | |||
{ | |||
#if !ENABLE_IL2CPP | |||
Type myTypeA = typeof(T); | |||
PropertyInfo myFieldInfo = myTypeA.GetProperty("ID"); | |||
ParameterExpression targetExp = Expression.Parameter(typeof(T).MakeByRefType(), "target"); | |||
ParameterExpression valueExp = Expression.Parameter(typeof(EGID), "value"); | |||
MemberExpression fieldExp = Expression.Property(targetExp, myFieldInfo); | |||
BinaryExpression assignExp = Expression.Assign(fieldExp, valueExp); | |||
var setter = Expression.Lambda<ActionCast>(assignExp, targetExp, valueExp).Compile(); | |||
return setter; | |||
#else | |||
return (ref T target, EGID value) => | |||
{ | |||
var needEgid = (target as INeedEGID); | |||
needEgid.ID = value; | |||
target = (T) needEgid; | |||
}; | |||
#endif | |||
} | |||
return null; | |||
} | |||
public static void Warmup() | |||
{ | |||
} | |||
} | |||
} |
@@ -1,222 +0,0 @@ | |||
using System; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
public interface ITypeSafeDictionary | |||
{ | |||
int Count { get; } | |||
ITypeSafeDictionary Create(); | |||
void AddEntitiesToEngines( | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDb, | |||
ITypeSafeDictionary realDic, in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group); | |||
void RemoveEntitiesFromEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB, | |||
in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group); | |||
void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId); | |||
void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, in PlatformProfiler profiler); | |||
void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup); | |||
void RemoveEntityFromDictionary(EGID fromEntityGid, in PlatformProfiler profiler); | |||
void SetCapacity(uint size); | |||
void Trim(); | |||
void Clear(); | |||
void FastClear(); | |||
bool Has(uint entityIdEntityId); | |||
} | |||
class TypeSafeDictionary<TValue> : FasterDictionary<uint, TValue>, | |||
ITypeSafeDictionary where TValue : struct, IEntityStruct | |||
{ | |||
static readonly Type _type = typeof(TValue); | |||
static readonly string _typeName = _type.Name; | |||
static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type); | |||
public TypeSafeDictionary(uint size) : base(size) {} | |||
public TypeSafeDictionary() {} | |||
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId) | |||
{ | |||
var typeSafeDictionary = entitiesToSubmit as TypeSafeDictionary<TValue>; | |||
foreach (var tuple in typeSafeDictionary) | |||
{ | |||
try | |||
{ | |||
if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref tuple.Value, new EGID(tuple.Key, groupId)); | |||
Add(tuple.Key, tuple.Value); | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new TypeSafeDictionaryException( | |||
"trying to add an EntityView with the same ID more than once Entity: " | |||
.FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key), e); | |||
} | |||
} | |||
} | |||
public void AddEntitiesToEngines( | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB, | |||
ITypeSafeDictionary realDic, in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group) | |||
{ | |||
var typeSafeDictionary = realDic as TypeSafeDictionary<TValue>; | |||
//this can be optimized, should pass all the entities and not restart the process for each one | |||
foreach (var value in this) | |||
AddEntityViewToEngines(entityViewEnginesDB, ref typeSafeDictionary.GetValueByRef(value.Key), null, | |||
in profiler, new EGID(value.Key, group)); | |||
} | |||
public void RemoveEntitiesFromEngines( | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB, | |||
in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group) | |||
{ | |||
foreach (var value in this) | |||
RemoveEntityViewFromEngines(entityViewEnginesDB, ref GetValueByRef(value.Key), null, in profiler, | |||
new EGID(value.Key, group)); | |||
} | |||
public bool Has(uint entityIdEntityId) | |||
{ | |||
return ContainsKey(entityIdEntityId); | |||
} | |||
public void RemoveEntityFromDictionary(EGID fromEntityGid, in PlatformProfiler profiler) | |||
{ | |||
Remove(fromEntityGid.entityID); | |||
} | |||
public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup) | |||
{ | |||
var valueIndex = GetIndex(fromEntityGid.entityID); | |||
if (toGroup != null) | |||
{ | |||
var toGroupCasted = toGroup as TypeSafeDictionary<TValue>; | |||
ref var entity = ref valuesArray[valueIndex]; | |||
if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID); | |||
toGroupCasted.Add(fromEntityGid.entityID, entity); | |||
} | |||
} | |||
public void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, in PlatformProfiler profiler) | |||
{ | |||
var valueIndex = GetIndex(fromEntityGid.entityID); | |||
ref var entity = ref valuesArray[valueIndex]; | |||
if (toGroup != null) | |||
{ | |||
RemoveEntityViewFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler, | |||
fromEntityGid); | |||
var toGroupCasted = toGroup as TypeSafeDictionary<TValue>; | |||
var previousGroup = fromEntityGid.groupID; | |||
if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value); | |||
var index = toGroupCasted.GetIndex(toEntityID.Value.entityID); | |||
AddEntityViewToEngines(engines, ref toGroupCasted.valuesArray[index], previousGroup, | |||
in profiler, toEntityID.Value); | |||
} | |||
else | |||
RemoveEntityViewFromEngines(engines, ref entity, null, in profiler, fromEntityGid); | |||
} | |||
public ITypeSafeDictionary Create() | |||
{ | |||
return new TypeSafeDictionary<TValue>(); | |||
} | |||
void AddEntityViewToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB, | |||
ref TValue entity, ExclusiveGroup.ExclusiveGroupStruct? previousGroup, | |||
in PlatformProfiler profiler, EGID egid) | |||
{ | |||
//get all the engines linked to TValue | |||
if (!entityViewEnginesDB.TryGetValue(new RefWrapper<Type>(_type), out var entityViewsEngines)) return; | |||
if (previousGroup == null) | |||
{ | |||
for (var i = 0; i < entityViewsEngines.Count; i++) | |||
try | |||
{ | |||
using (profiler.Sample(entityViewsEngines[i], _typeName)) | |||
{ | |||
(entityViewsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity, egid); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new ECSException( | |||
"Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e); | |||
} | |||
} | |||
else | |||
{ | |||
for (var i = 0; i < entityViewsEngines.Count; i++) | |||
try | |||
{ | |||
using (profiler.Sample(entityViewsEngines[i], _typeName)) | |||
{ | |||
(entityViewsEngines[i] as IReactOnSwap<TValue>).MovedTo(ref entity, previousGroup.Value, | |||
egid); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new ECSException( | |||
"Code crashed inside MovedTo callback ".FastConcat(typeof(TValue).ToString()), e); | |||
} | |||
} | |||
} | |||
static void RemoveEntityViewFromEngines( | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> @group, ref TValue entity, | |||
ExclusiveGroup.ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid) | |||
{ | |||
if (!@group.TryGetValue(new RefWrapper<Type>(_type), out var entityViewsEngines)) return; | |||
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, egid); | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new ECSException( | |||
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e); | |||
} | |||
} | |||
#if SEEMS_UNNECESSARY | |||
else | |||
{ | |||
for (var i = 0; i < entityViewsEngines.Count; i++) | |||
try | |||
{ | |||
using (profiler.Sample(entityViewsEngines[i], _typeName)) | |||
(entityViewsEngines[i] as IReactOnSwap<TValue>).MovedFrom(ref entity, egid); | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new ECSException( | |||
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e); | |||
} | |||
} | |||
#endif | |||
} | |||
} | |||
} |
@@ -1,12 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS | |||
{ | |||
public class TypeSafeDictionaryException : Exception | |||
{ | |||
public TypeSafeDictionaryException(string message, Exception exception) : | |||
base(message, exception) | |||
{ | |||
} | |||
} | |||
} |
@@ -1,21 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS | |||
{ | |||
public class DispatchOnChange<T> : DispatchOnSet<T> where T:IEquatable<T> | |||
{ | |||
public DispatchOnChange(EGID senderID) : base(senderID) | |||
{ } | |||
public new T value | |||
{ | |||
set | |||
{ | |||
if (value.Equals(_value) == false) | |||
base.value = value; | |||
} | |||
get => _value; | |||
} | |||
} | |||
} |
@@ -1,42 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS | |||
{ | |||
public class DispatchOnSet<T> | |||
{ | |||
public DispatchOnSet(EGID senderID) | |||
{ | |||
_senderID = senderID; | |||
} | |||
public T value | |||
{ | |||
set | |||
{ | |||
_value = value; | |||
if (_paused == false) | |||
_subscribers(_senderID, value); | |||
} | |||
} | |||
public void NotifyOnValueSet(Action<EGID, T> action) | |||
{ | |||
_subscribers += action; | |||
} | |||
public void StopNotify(Action<EGID, T> action) | |||
{ | |||
_subscribers -= action; | |||
} | |||
public void PauseNotify() { _paused = true; } | |||
public void ResumeNotify() { _paused = false; } | |||
protected T _value; | |||
readonly EGID _senderID; | |||
Action<EGID, T> _subscribers; | |||
bool _paused; | |||
} | |||
} |
@@ -1,128 +0,0 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// DynamicEntityDescriptor can be used to add entity views to an existing EntityDescriptor that act as flags, | |||
/// at building time. | |||
/// This method allocates, so it shouldn't be abused | |||
/// </summary> | |||
/// <typeparam name="TType"></typeparam> | |||
public struct DynamicEntityDescriptor<TType> : IEntityDescriptor where TType : IEntityDescriptor, new() | |||
{ | |||
internal DynamicEntityDescriptor(bool isExtendible) : this() | |||
{ | |||
var defaultEntities = EntityDescriptorTemplate<TType>.descriptor.entitiesToBuild; | |||
var length = defaultEntities.Length; | |||
_entitiesToBuild = new IEntityBuilder[length + 1]; | |||
Array.Copy(defaultEntities, 0, _entitiesToBuild, 0, length); | |||
//assign it after otherwise the previous copy will overwrite the value in case the item | |||
//is already present | |||
_entitiesToBuild[length] = new EntityBuilder<EntityStructInfoView> | |||
( | |||
new EntityStructInfoView | |||
{ | |||
entitiesToBuild = _entitiesToBuild | |||
} | |||
); | |||
} | |||
public DynamicEntityDescriptor(IEntityBuilder[] extraEntityBuilders) : this() | |||
{ | |||
var extraEntitiesLength = extraEntityBuilders.Length; | |||
_entitiesToBuild = Construct(extraEntitiesLength, extraEntityBuilders, | |||
EntityDescriptorTemplate<TType>.descriptor.entitiesToBuild); | |||
} | |||
public DynamicEntityDescriptor(FasterList<IEntityBuilder> extraEntityBuilders) : this() | |||
{ | |||
var extraEntities = extraEntityBuilders.ToArrayFast(); | |||
var extraEntitiesLength = extraEntityBuilders.Count; | |||
_entitiesToBuild = Construct(extraEntitiesLength, extraEntities, | |||
EntityDescriptorTemplate<TType>.descriptor.entitiesToBuild); | |||
} | |||
public void ExtendWith<T>() where T : IEntityDescriptor, new() | |||
{ | |||
var newEntitiesToBuild = EntityDescriptorTemplate<T>.descriptor.entitiesToBuild; | |||
_entitiesToBuild = Construct(newEntitiesToBuild.Length, newEntitiesToBuild, _entitiesToBuild); | |||
} | |||
public void ExtendWith(IEntityBuilder[] extraEntities) | |||
{ | |||
_entitiesToBuild = Construct(extraEntities.Length, extraEntities, _entitiesToBuild); | |||
} | |||
static IEntityBuilder[] Construct(int extraEntitiesLength, IEntityBuilder[] extraEntities, | |||
IEntityBuilder[] startingEntities) | |||
{ | |||
IEntityBuilder[] localEntitiesToBuild; | |||
if (extraEntitiesLength == 0) | |||
{ | |||
localEntitiesToBuild = startingEntities; | |||
return localEntitiesToBuild; | |||
} | |||
var defaultEntities = startingEntities; | |||
var length = defaultEntities.Length; | |||
var index = SetupSpecialEntityStruct(defaultEntities, out localEntitiesToBuild, extraEntitiesLength); | |||
Array.Copy(extraEntities, 0, localEntitiesToBuild, length, extraEntitiesLength); | |||
//assign it after otherwise the previous copy will overwrite the value in case the item | |||
//is already present | |||
localEntitiesToBuild[index] = new EntityBuilder<EntityStructInfoView> | |||
( | |||
new EntityStructInfoView | |||
{ | |||
entitiesToBuild = localEntitiesToBuild | |||
} | |||
); | |||
return localEntitiesToBuild; | |||
} | |||
static int SetupSpecialEntityStruct(IEntityBuilder[] defaultEntities, out IEntityBuilder[] entitiesToBuild, | |||
int extraLenght) | |||
{ | |||
int length = defaultEntities.Length; | |||
int index = -1; | |||
for (var i = 0; i < length; i++) | |||
{ | |||
//the special entity already exists | |||
if (defaultEntities[i].GetEntityType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) | |||
{ | |||
index = i; | |||
break; | |||
} | |||
} | |||
if (index == -1) | |||
{ | |||
index = length + extraLenght; | |||
entitiesToBuild = new IEntityBuilder[index + 1]; | |||
} | |||
else | |||
entitiesToBuild = new IEntityBuilder[length + extraLenght]; | |||
Array.Copy(defaultEntities, 0, entitiesToBuild, 0, length); | |||
return index; | |||
} | |||
public IEntityBuilder[] entitiesToBuild => _entitiesToBuild; | |||
IEntityBuilder[] _entitiesToBuild; | |||
} | |||
} |
@@ -1,13 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS | |||
{ | |||
public class ECSException : Exception | |||
{ | |||
public ECSException(string message):base("<color=red>".FastConcat(message, "</color>")) | |||
{} | |||
public ECSException(string message, Exception innerE):base("<color=red>".FastConcat(message, "</color>"), innerE) | |||
{} | |||
} | |||
} |
@@ -1,47 +0,0 @@ | |||
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> | |||
{ | |||
static readonly FasterList<T> _resources = new FasterList<T>(); | |||
internal static ref T resources(uint id) | |||
{ | |||
return ref _resources[(int) id - 1]; | |||
} | |||
internal static uint ToECS(T resource) | |||
{ | |||
_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; | |||
} | |||
} | |||
public static class ResourceExtensions | |||
{ | |||
public static void Set<T>(ref this ECSResources<T> resource, T newText) | |||
{ | |||
if (resource.id != 0) | |||
ResourcesECSDB<T>.resources(resource.id) = newText; | |||
else | |||
resource.id = ResourcesECSDB<T>.ToECS(newText); | |||
} | |||
} | |||
} |
@@ -1,38 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS.Experimental | |||
{ | |||
[Serialization.DoNotSerialize] | |||
public struct ECSString:IEquatable<ECSString> | |||
{ | |||
uint id; | |||
public ECSString(string newText) | |||
{ | |||
id = ResourcesECSDB<string>.ToECS(newText); | |||
} | |||
public static implicit operator string(ECSString ecsString) | |||
{ | |||
return ResourcesECSDB<string>.FromECS(ecsString.id); | |||
} | |||
public void Set(string newText) | |||
{ | |||
if (id != 0) | |||
ResourcesECSDB<string>.resources(id) = newText; | |||
else | |||
id = ResourcesECSDB<string>.ToECS(newText); | |||
} | |||
public bool Equals(ECSString other) | |||
{ | |||
return other.id == id; | |||
} | |||
public override string ToString() | |||
{ | |||
return ResourcesECSDB<string>.FromECS(id); | |||
} | |||
} | |||
} |
@@ -1,77 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
#pragma warning disable 660,661 | |||
namespace Svelto.ECS | |||
{ | |||
//todo: add debug map | |||
[Serialization.DoNotSerialize] | |||
[Serializable] | |||
public struct EGID:IEquatable<EGID>,IEqualityComparer<EGID>,IComparable<EGID> | |||
{ | |||
public uint entityID => (uint) (_GID & 0xFFFFFFFF); | |||
public ExclusiveGroup.ExclusiveGroupStruct groupID => new ExclusiveGroup.ExclusiveGroupStruct((uint) (_GID >> 32)); | |||
public static bool operator ==(EGID obj1, EGID obj2) | |||
{ | |||
return obj1._GID == obj2._GID; | |||
} | |||
public static bool operator !=(EGID obj1, EGID obj2) | |||
{ | |||
return obj1._GID != obj2._GID; | |||
} | |||
public EGID(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) : this() | |||
{ | |||
_GID = MAKE_GLOBAL_ID(entityID, groupID); | |||
} | |||
static ulong MAKE_GLOBAL_ID(uint entityId, uint groupId) | |||
{ | |||
return (ulong)groupId << 32 | ((ulong)entityId & 0xFFFFFFFF); | |||
} | |||
public static explicit operator uint(EGID id) | |||
{ | |||
return id.entityID; | |||
} | |||
//in the way it's used, ulong must be always the same for each id/group | |||
public static explicit operator ulong(EGID id) { return id._GID; } | |||
public bool Equals(EGID other) | |||
{ | |||
return _GID == other._GID; | |||
} | |||
public bool Equals(EGID x, EGID y) | |||
{ | |||
return x == y; | |||
} | |||
public int GetHashCode(EGID obj) | |||
{ | |||
return _GID.GetHashCode(); | |||
} | |||
public int CompareTo(EGID other) | |||
{ | |||
return _GID.CompareTo(other._GID); | |||
} | |||
internal EGID(uint entityID, uint groupID) : this() | |||
{ | |||
_GID = MAKE_GLOBAL_ID(entityID, groupID); | |||
} | |||
public override string ToString() | |||
{ | |||
return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID); | |||
} | |||
readonly ulong _GID; | |||
} | |||
} |
@@ -1,37 +0,0 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public struct EGIDMapper<T> where T : struct, IEntityStruct | |||
{ | |||
internal FasterDictionary<uint, T> map; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T Entity(uint entityID) | |||
{ | |||
#if DEBUG && !PROFILER | |||
if (map.TryFindIndex(entityID, out var findIndex) == false) | |||
throw new Exception("Entity not found in this group ".FastConcat(typeof(T).ToString())); | |||
#else | |||
map.TryFindIndex(entityID, out var findIndex); | |||
#endif | |||
return ref map.valuesArray[findIndex]; | |||
} | |||
public bool TryGetEntity(uint entityID, out T value) | |||
{ | |||
if (map.TryFindIndex(entityID, out var index)) | |||
{ | |||
value = map.GetDirectValue(index); | |||
return true; | |||
} | |||
value = default; | |||
return false; | |||
} | |||
} | |||
} | |||
@@ -1,86 +0,0 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
internal class DoubleBufferedEntitiesToAdd | |||
{ | |||
const int MaximumNumberOfItemsPerFrameBeforeToClear = 100; | |||
internal void Swap() | |||
{ | |||
Swap(ref current, ref other); | |||
Swap(ref currentEntitiesCreatedPerGroup, ref otherEntitiesCreatedPerGroup); | |||
} | |||
void Swap<T>(ref T item1, ref T item2) | |||
{ | |||
var toSwap = item2; | |||
item2 = item1; | |||
item1 = toSwap; | |||
} | |||
public void ClearOther() | |||
{ | |||
//do not clear the groups created so far, they will be reused, unless they are too many! | |||
var otherCount = other.Count; | |||
if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear) | |||
{ | |||
otherEntitiesCreatedPerGroup.FastClear(); | |||
other.FastClear(); | |||
return; | |||
} | |||
var otherValuesArray = other.valuesArray; | |||
for (int i = 0; i < otherCount; ++i) | |||
{ | |||
var safeDictionariesCount = otherValuesArray[i].Count; | |||
var safeDictionaries = otherValuesArray[i].valuesArray; | |||
//do not remove the dictionaries of entities per type created so far, they will be reused | |||
if (safeDictionariesCount <= MaximumNumberOfItemsPerFrameBeforeToClear) | |||
{ | |||
for (int j = 0; j < safeDictionariesCount; ++j) | |||
{ | |||
//clear the dictionary of entities create do far (it won't allocate though) | |||
safeDictionaries[j].FastClear(); | |||
} | |||
} | |||
else | |||
{ | |||
otherValuesArray[i].FastClear(); | |||
} | |||
} | |||
otherEntitiesCreatedPerGroup.FastClear(); | |||
} | |||
internal FasterDictionary<uint, uint> currentEntitiesCreatedPerGroup; | |||
internal FasterDictionary<uint, uint> otherEntitiesCreatedPerGroup; | |||
internal FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> current; | |||
internal FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> other; | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> | |||
_entityViewsToAddBufferA = | |||
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>(); | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> | |||
_entityViewsToAddBufferB = | |||
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>(); | |||
readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupA = new FasterDictionary<uint, uint>(); | |||
readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupB = new FasterDictionary<uint, uint>(); | |||
public DoubleBufferedEntitiesToAdd() | |||
{ | |||
currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA; | |||
otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB; | |||
current = _entityViewsToAddBufferA; | |||
other = _entityViewsToAddBufferB; | |||
} | |||
} | |||
} | |||
} |
@@ -1,143 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
using Svelto.ECS.Schedulers; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
public struct EntitiesSubmitter | |||
{ | |||
public EntitiesSubmitter(EnginesRoot enginesRoot) | |||
{ | |||
_weakReference = new DataStructures.WeakReference<EnginesRoot>(enginesRoot); | |||
} | |||
public void Invoke() | |||
{ | |||
if (_weakReference.IsValid) | |||
_weakReference.Target.SubmitEntityViews(); | |||
} | |||
readonly DataStructures.WeakReference<EnginesRoot> _weakReference; | |||
} | |||
/// <summary> | |||
/// Engines root contextualize your engines and entities. You don't need to limit yourself to one EngineRoot | |||
/// as multiple engines root could promote separation of scopes. The EntitySubmissionScheduler checks | |||
/// periodically if new entity must be submitted to the database and the engines. It's an external | |||
/// dependencies to be independent by the running platform as the user can define it. | |||
/// The EntitySubmissionScheduler cannot hold an EnginesRoot reference, that's why | |||
/// it must receive a weak reference of the EnginesRoot callback. | |||
/// </summary> | |||
public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler) | |||
{ | |||
_entitiesOperations = new FasterDictionary<ulong, EntitySubmitOperation>(); | |||
_reactiveEnginesAddRemove = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>(); | |||
_reactiveEnginesSwap = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>(); | |||
_enginesSet = new FasterList<IEngine>(); | |||
_enginesTypeSet = new HashSet<Type>(); | |||
_disposableEngines = new FasterList<IDisposable>(); | |||
_transientEntitiesOperations = new FasterList<EntitySubmitOperation>(); | |||
_groupEntityViewsDB = new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>(); | |||
_groupsPerEntity = new FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>>(); | |||
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd(); | |||
_entitiesStream = new EntitiesStream(); | |||
_entitiesDB = new EntitiesDB(_groupEntityViewsDB, _groupsPerEntity, _entitiesStream); | |||
_scheduler = entityViewScheduler; | |||
_scheduler.onTick = new EntitiesSubmitter(this); | |||
} | |||
public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler, bool isDeserializationOnly):this(entityViewScheduler) | |||
{ | |||
_isDeserializationOnly = isDeserializationOnly; | |||
} | |||
public void AddEngine(IEngine engine) | |||
{ | |||
var type = engine.GetType(); | |||
var refWrapper = new RefWrapper<Type>(type); | |||
DBC.ECS.Check.Require( | |||
_enginesTypeSet.Contains(refWrapper) == false || | |||
type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true, | |||
"The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute " | |||
.FastConcat(engine.ToString())); | |||
try | |||
{ | |||
if (engine is IReactOnAddAndRemove viewEngine) | |||
CheckEntityViewsEngine(viewEngine, _reactiveEnginesAddRemove); | |||
if (engine is IReactOnSwap viewEngineSwap) | |||
CheckEntityViewsEngine(viewEngineSwap, _reactiveEnginesSwap); | |||
_enginesTypeSet.Add(refWrapper); | |||
_enginesSet.Add(engine); | |||
if (engine is IDisposable) | |||
_disposableEngines.Add(engine as IDisposable); | |||
if (engine is IQueryingEntitiesEngine queryableEntityViewEngine) | |||
{ | |||
queryableEntityViewEngine.entitiesDB = _entitiesDB; | |||
queryableEntityViewEngine.Ready(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " "), e); | |||
} | |||
} | |||
void CheckEntityViewsEngine<T>(T engine, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines) | |||
where T : class, IEngine | |||
{ | |||
var interfaces = engine.GetType().GetInterfaces(); | |||
foreach (var interf in interfaces) | |||
{ | |||
if (interf.IsGenericTypeEx() && typeof(T).IsAssignableFrom(interf)) | |||
{ | |||
var genericArguments = interf.GetGenericArgumentsEx(); | |||
AddEngine(engine, genericArguments, engines); | |||
} | |||
} | |||
} | |||
static void AddEngine<T>(T engine, Type[] entityViewTypes, | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines) | |||
where T : class, IEngine | |||
{ | |||
for (var i = 0; i < entityViewTypes.Length; i++) | |||
{ | |||
var type = entityViewTypes[i]; | |||
AddEngine(engine, engines, type); | |||
} | |||
} | |||
static void AddEngine<T>(T engine, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, Type type) | |||
where T : class, IEngine | |||
{ | |||
if (engines.TryGetValue(new RefWrapper<Type>(type), out var list) == false) | |||
{ | |||
list = new FasterList<IEngine>(); | |||
engines.Add(new RefWrapper<Type>(type), list); | |||
} | |||
list.Add(engine); | |||
} | |||
readonly FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> _reactiveEnginesAddRemove; | |||
readonly FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> _reactiveEnginesSwap; | |||
readonly FasterList<IDisposable> _disposableEngines; | |||
readonly FasterList<IEngine> _enginesSet; | |||
readonly HashSet<Type> _enginesTypeSet; | |||
} | |||
} |
@@ -1,305 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot : IDisposable | |||
{ | |||
/// <summary> | |||
/// Dispose an EngineRoot once not used anymore, so that all the | |||
/// engines are notified with the entities removed. | |||
/// It's a clean up process. | |||
/// </summary> | |||
public void Dispose() | |||
{ | |||
using (var profiler = new PlatformProfiler("Final Dispose")) | |||
{ | |||
foreach (var groups in _groupEntityViewsDB) | |||
{ | |||
foreach (var entityList in groups.Value) | |||
{ | |||
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, | |||
profiler, new ExclusiveGroup.ExclusiveGroupStruct(groups.Key)); | |||
} | |||
} | |||
_groupEntityViewsDB.Clear(); | |||
_groupsPerEntity.Clear(); | |||
foreach (var engine in _disposableEngines) | |||
engine.Dispose(); | |||
_disposableEngines.Clear(); | |||
_enginesSet.Clear(); | |||
_enginesTypeSet.Clear(); | |||
_reactiveEnginesSwap.Clear(); | |||
_reactiveEnginesAddRemove.Clear(); | |||
_entitiesOperations.Clear(); | |||
_transientEntitiesOperations.Clear(); | |||
_scheduler.Dispose(); | |||
#if DEBUG && !PROFILER | |||
_idCheckers.Clear(); | |||
#endif | |||
_groupedEntityToAdd = null; | |||
_entitiesStream.Dispose(); | |||
} | |||
GC.SuppressFinalize(this); | |||
} | |||
~EnginesRoot() | |||
{ | |||
Console.LogWarning("Engines Root has been garbage collected, don't forget to call Dispose()!"); | |||
Dispose(); | |||
} | |||
///-------------------------------------------- | |||
/// | |||
public IEntityStreamConsumerFactory GenerateConsumerFactory() | |||
{ | |||
return new GenericEntityStreamConsumerFactory(this); | |||
} | |||
public IEntityFactory GenerateEntityFactory() | |||
{ | |||
return new GenericEntityFactory(this); | |||
} | |||
public IEntityFunctions GenerateEntityFunctions() | |||
{ | |||
return new GenericEntityFunctions(this); | |||
} | |||
///-------------------------------------------- | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
EntityStructInitializer BuildEntity(EGID entityID, IEntityBuilder[] entitiesToBuild, | |||
IEnumerable<object> implementors = null) | |||
{ | |||
CheckAddEntityID(entityID); | |||
var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, | |||
entitiesToBuild, implementors); | |||
return new EntityStructInitializer(entityID, dic); | |||
} | |||
///-------------------------------------------- | |||
void Preallocate<T>(uint groupID, uint size) where T : IEntityDescriptor, new() | |||
{ | |||
var entityViewsToBuild = EntityDescriptorTemplate<T>.descriptor.entitiesToBuild; | |||
var numberOfEntityViews = entityViewsToBuild.Length; | |||
//reserve space in the database | |||
if (_groupEntityViewsDB.TryGetValue(groupID, out var group) == false) | |||
group = _groupEntityViewsDB[groupID] = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>(); | |||
for (var index = 0; index < numberOfEntityViews; index++) | |||
{ | |||
var entityViewBuilder = entityViewsToBuild[index]; | |||
var entityViewType = entityViewBuilder.GetEntityType(); | |||
var refWrapper = new RefWrapper<Type>(entityViewType); | |||
if (group.TryGetValue(refWrapper, out var dbList) == false) | |||
group[refWrapper] = entityViewBuilder.Preallocate(ref dbList, size); | |||
else | |||
dbList.SetCapacity(size); | |||
if (_groupsPerEntity.TryGetValue(refWrapper, out var groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[refWrapper] = | |||
new FasterDictionary<uint, ITypeSafeDictionary>(); | |||
groupedGroup[groupID] = dbList; | |||
} | |||
} | |||
///-------------------------------------------- | |||
/// | |||
void MoveEntityFromAndToEngines(IEntityBuilder[] entityBuilders, EGID fromEntityGID, EGID? toEntityGID) | |||
{ | |||
using (var sampler = new PlatformProfiler("Move Entity From Engines")) | |||
{ | |||
//for each entity view generated by the entity descriptor | |||
if (_groupEntityViewsDB.TryGetValue(fromEntityGID.groupID, out var fromGroup) == false) | |||
throw new ECSException("from group not found eid: ".FastConcat(fromEntityGID.entityID) | |||
.FastConcat(" group: ").FastConcat(fromEntityGID.groupID)); | |||
//Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor! | |||
if (fromGroup.TryGetValue(new RefWrapper<Type>(EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW), | |||
out var entityInfoViewDic) && | |||
(entityInfoViewDic as TypeSafeDictionary<EntityStructInfoView>).TryGetValue( | |||
fromEntityGID.entityID, out var entityInfoView)) | |||
MoveEntities(fromEntityGID, toEntityGID, entityInfoView.entitiesToBuild, fromGroup, sampler); | |||
//otherwise it's a normal static entity descriptor | |||
else | |||
MoveEntities(fromEntityGID, toEntityGID, entityBuilders, fromGroup, sampler); | |||
} | |||
} | |||
void MoveEntities(EGID fromEntityGID, EGID? toEntityGID, IEntityBuilder[] entitiesToMove, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, PlatformProfiler sampler) | |||
{ | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = null; | |||
if (toEntityGID != null) | |||
{ | |||
var toGroupID = toEntityGID.Value.groupID; | |||
if (_groupEntityViewsDB.TryGetValue(toGroupID, out toGroup) == false) | |||
toGroup = _groupEntityViewsDB[toGroupID] = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>(); | |||
//Add all the entities to the dictionary | |||
for (var i = 0; i < entitiesToMove.Length; i++) | |||
CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup, | |||
entitiesToMove[i].GetEntityType()); | |||
} | |||
//call all the callbacks | |||
for (var i = 0; i < entitiesToMove.Length; i++) | |||
MoveEntityViewFromAndToEngines(fromEntityGID, toEntityGID, fromGroup, toGroup, | |||
entitiesToMove[i].GetEntityType(), sampler); | |||
//then remove all the entities from the dictionary | |||
for (var i = 0; i < entitiesToMove.Length; i++) | |||
RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityType(), sampler); | |||
} | |||
void CopyEntityToDictionary(EGID entityGID, EGID toEntityGID, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityViewType) | |||
{ | |||
var wrapper = new RefWrapper<Type>(entityViewType); | |||
if (fromGroup.TryGetValue(wrapper, out var fromTypeSafeDictionary) == false) | |||
{ | |||
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID) | |||
.FastConcat(" group: ").FastConcat(entityGID.groupID)); | |||
} | |||
#if DEBUG && !PROFILER | |||
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) | |||
{ | |||
throw new EntityNotFoundException(entityGID, entityViewType); | |||
} | |||
#endif | |||
if (toGroup.TryGetValue(wrapper, out var toEntitiesDictionary) == false) | |||
{ | |||
toEntitiesDictionary = fromTypeSafeDictionary.Create(); | |||
toGroup.Add(wrapper, toEntitiesDictionary); | |||
} | |||
//todo: this must be unit tested properly | |||
if (_groupsPerEntity.TryGetValue(wrapper, out var groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[wrapper] = | |||
new FasterDictionary<uint, ITypeSafeDictionary>(); | |||
groupedGroup[toEntityGID.groupID] = toEntitiesDictionary; | |||
fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary); | |||
} | |||
void MoveEntityViewFromAndToEngines(EGID entityGID, EGID? toEntityGID, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityViewType, | |||
in PlatformProfiler profiler) | |||
{ | |||
//add all the entities | |||
var refWrapper = new RefWrapper<Type>(entityViewType); | |||
if (fromGroup.TryGetValue(refWrapper, out var fromTypeSafeDictionary) == false) | |||
{ | |||
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID) | |||
.FastConcat(" group: ").FastConcat(entityGID.groupID)); | |||
} | |||
ITypeSafeDictionary toEntitiesDictionary = null; | |||
if (toGroup != null) | |||
toEntitiesDictionary = toGroup[refWrapper]; //this is guaranteed to exist by AddEntityToDictionary | |||
#if DEBUG && !PROFILER | |||
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) | |||
throw new EntityNotFoundException(entityGID, entityViewType); | |||
#endif | |||
fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID, | |||
toEntitiesDictionary, toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap, | |||
in profiler); | |||
} | |||
void RemoveEntityFromDictionary(EGID entityGID, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, Type entityViewType, | |||
in PlatformProfiler profiler) | |||
{ | |||
var refWrapper = new RefWrapper<Type>(entityViewType); | |||
if (fromGroup.TryGetValue(refWrapper, out var fromTypeSafeDictionary) == false) | |||
{ | |||
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID) | |||
.FastConcat(" group: ").FastConcat(entityGID.groupID)); | |||
} | |||
fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID, profiler); | |||
if (fromTypeSafeDictionary.Count == 0) //clean up | |||
{ | |||
//todo: this must be unit tested properly | |||
_groupsPerEntity[refWrapper].Remove(entityGID.groupID); | |||
//I don't remove the group if empty on purpose, in case it needs to be reused | |||
} | |||
} | |||
/// <summary> | |||
/// Todo: I should keep the group, but I need to mark the group as deleted for the Exist function to work | |||
/// </summary> | |||
/// <param name="groupID"></param> | |||
/// <param name="profiler"></param> | |||
void RemoveGroupAndEntitiesFromDB(uint groupID, in PlatformProfiler profiler) | |||
{ | |||
var dictionariesOfEntities = _groupEntityViewsDB[groupID]; | |||
foreach (var dictionaryOfEntities in dictionariesOfEntities) | |||
{ | |||
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler, | |||
new ExclusiveGroup.ExclusiveGroupStruct(groupID)); | |||
var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key]; | |||
groupedGroupOfEntities.Remove(groupID); | |||
} | |||
//careful, in this case I assume you really don't want to use this group anymore | |||
//so I remove it from the database | |||
_groupEntityViewsDB.Remove(groupID); | |||
} | |||
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct | |||
{ | |||
return _entitiesStream.GenerateConsumer<T>(name, capacity); | |||
} | |||
public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) where T : unmanaged, | |||
IEntityStruct | |||
{ | |||
return _entitiesStream.GenerateConsumer<T>(group, name, capacity); | |||
} | |||
//one datastructure rule them all: | |||
//split by group | |||
//split by type per group. It's possible to get all the entities of a give type T per group thanks | |||
//to the FasterDictionary capabilities OR it's possible to get a specific entityView indexed by | |||
//ID. This ID doesn't need to be the EGID, it can be just the entityID | |||
//for each group id, save a dictionary indexed by entity type of entities indexed by id | |||
//ITypeSafeDictionary = Key = entityID, Value = EntityStruct | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> _groupEntityViewsDB; | |||
//for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are | |||
//found indexed by group id | |||
//EntityViewType //groupID //entityID, EntityStruct | |||
readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity; | |||
readonly EntitiesDB _entitiesDB; | |||
readonly EntitiesStream _entitiesStream; | |||
} | |||
} |
@@ -1,57 +0,0 @@ | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
class GenericEntityFactory : IEntityFactory | |||
{ | |||
public GenericEntityFactory(EnginesRoot weakReference) | |||
{ | |||
_enginesRoot = new WeakReference<EnginesRoot>(weakReference); | |||
} | |||
public EntityStructInitializer BuildEntity<T>(uint entityID, | |||
ExclusiveGroup.ExclusiveGroupStruct groupStructId, IEnumerable<object> implementors = null) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId), | |||
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, implementors); | |||
} | |||
public EntityStructInitializer BuildEntity<T>(EGID egid, IEnumerable<object> implementors = null) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
return _enginesRoot.Target.BuildEntity(egid, | |||
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, implementors); | |||
} | |||
public EntityStructInitializer BuildEntity<T>(EGID egid, T entityDescriptor, | |||
IEnumerable<object> implementors) | |||
where T : IEntityDescriptor | |||
{ | |||
return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.entitiesToBuild, implementors); | |||
} | |||
public EntityStructInitializer BuildEntity<T>(uint entityID, | |||
ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, IEnumerable<object> implementors) | |||
where T : IEntityDescriptor | |||
{ | |||
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId), | |||
descriptorEntity.entitiesToBuild, | |||
implementors); | |||
} | |||
public void PreallocateEntitySpace<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
_enginesRoot.Target.Preallocate<T>(groupStructId, size); | |||
} | |||
//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside | |||
//engines of other enginesRoot | |||
readonly WeakReference<EnginesRoot> _enginesRoot; | |||
} | |||
} | |||
} |
@@ -1,130 +0,0 @@ | |||
using System.Diagnostics; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
/// <summary> | |||
/// todo: EnginesRoot was a weakreference to give the change to inject | |||
/// entityfunctions from other engines root. It probably should be reverted | |||
/// </summary> | |||
sealed class GenericEntityFunctions : IEntityFunctions | |||
{ | |||
internal GenericEntityFunctions(EnginesRoot weakReference) | |||
{ | |||
_enginesRoot = new WeakReference<EnginesRoot>(weakReference); | |||
} | |||
[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() | |||
{ | |||
_enginesRoot.Target.CheckRemoveEntityID(entityEGID); | |||
_enginesRoot.Target.QueueEntitySubmitOperation<T>( | |||
new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID, | |||
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) | |||
{ | |||
_enginesRoot.Target.RemoveGroupID(groupID); | |||
_enginesRoot.Target.QueueEntitySubmitOperation( | |||
new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(0, groupID), new EGID())); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, | |||
ExclusiveGroup.ExclusiveGroupStruct toGroupID) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
SwapEntityGroup<T>(new EGID(entityID, fromGroupID), toGroupID); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
SwapEntityGroup<T>(fromID, new EGID(fromID.entityID, (uint) toGroupID)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(EGID fromID, ExclusiveGroup.ExclusiveGroupStruct toGroupID | |||
, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
if (fromID.groupID != mustBeFromGroup) | |||
throw new ECSException("Entity is not coming from the expected group"); | |||
SwapEntityGroup<T>(fromID, toGroupID); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(EGID fromID, EGID toID | |||
, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
if (fromID.groupID != mustBeFromGroup) | |||
throw new ECSException("Entity is not coming from the expected group"); | |||
SwapEntityGroup<T>(fromID, toID); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(EGID fromID, EGID toID) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
_enginesRoot.Target.CheckRemoveEntityID(fromID); | |||
_enginesRoot.Target.CheckAddEntityID(toID); | |||
_enginesRoot.Target.QueueEntitySubmitOperation<T>( | |||
new EntitySubmitOperation(EntitySubmitOperationType.Swap, | |||
fromID, toID, EntityDescriptorTemplate<T>.descriptor.entitiesToBuild)); | |||
} | |||
//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside | |||
//engines of other enginesRoot | |||
readonly WeakReference<EnginesRoot> _enginesRoot; | |||
} | |||
void QueueEntitySubmitOperation(EntitySubmitOperation entitySubmitOperation) | |||
{ | |||
#if DEBUG && !PROFILER | |||
entitySubmitOperation.trace = new StackFrame(1, true); | |||
#endif | |||
_entitiesOperations.Add((ulong) entitySubmitOperation.fromID, entitySubmitOperation); | |||
} | |||
void QueueEntitySubmitOperation<T>(EntitySubmitOperation entitySubmitOperation) where T : IEntityDescriptor | |||
{ | |||
#if DEBUG && !PROFILER | |||
entitySubmitOperation.trace = new StackFrame(1, true); | |||
if (_entitiesOperations.TryGetValue((ulong) entitySubmitOperation.fromID, out var entitySubmitedOperation)) | |||
{ | |||
if (entitySubmitedOperation != entitySubmitOperation) | |||
throw new ECSException("Only one entity operation per submission is allowed" | |||
.FastConcat(" entityViewType: ") | |||
.FastConcat(typeof(T).Name) | |||
.FastConcat(" submission type ", entitySubmitOperation.type.ToString(), | |||
" from ID: ", entitySubmitOperation.fromID.entityID.ToString()) | |||
.FastConcat(" previous operation type: ", | |||
_entitiesOperations[(ulong) entitySubmitOperation.fromID].type | |||
.ToString())); | |||
} | |||
else | |||
#endif | |||
_entitiesOperations.Set((ulong) entitySubmitOperation.fromID, entitySubmitOperation); | |||
} | |||
} | |||
} |
@@ -1,162 +0,0 @@ | |||
using System; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
using Svelto.ECS.Schedulers; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
readonly FasterList<EntitySubmitOperation> _transientEntitiesOperations; | |||
void SubmitEntityViews() | |||
{ | |||
using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission")) | |||
{ | |||
int iterations = 0; | |||
do | |||
{ | |||
SingleSubmission(profiler); | |||
} while ((_groupedEntityToAdd.currentEntitiesCreatedPerGroup.Count > 0 || | |||
_entitiesOperations.Count > 0) && ++iterations < 5); | |||
#if DEBUG && !PROFILER | |||
if (iterations == 5) | |||
throw new ECSException("possible circular submission detected"); | |||
#endif | |||
} | |||
} | |||
void SingleSubmission(in PlatformProfiler profiler) | |||
{ | |||
if (_entitiesOperations.Count > 0) | |||
{ | |||
using (profiler.Sample("Remove and Swap operations")) | |||
{ | |||
_transientEntitiesOperations.FastClear(); | |||
var entitySubmitOperations = _entitiesOperations.GetValuesArray(out var count); | |||
_transientEntitiesOperations.AddRange(entitySubmitOperations, count); | |||
_entitiesOperations.FastClear(); | |||
var entitiesOperations = _transientEntitiesOperations.ToArrayFast(); | |||
for (var i = 0; i < _transientEntitiesOperations.Count; i++) | |||
{ | |||
try | |||
{ | |||
switch (entitiesOperations[i].type) | |||
{ | |||
case EntitySubmitOperationType.Swap: | |||
MoveEntityFromAndToEngines(entitiesOperations[i].builders, | |||
entitiesOperations[i].fromID, | |||
entitiesOperations[i].toID); | |||
break; | |||
case EntitySubmitOperationType.Remove: | |||
MoveEntityFromAndToEngines(entitiesOperations[i].builders, | |||
entitiesOperations[i].fromID, null); | |||
break; | |||
case EntitySubmitOperationType.RemoveGroup: | |||
RemoveGroupAndEntitiesFromDB( | |||
entitiesOperations[i].fromID.groupID, profiler); | |||
break; | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
var str = "Crash while executing Entity Operation " | |||
.FastConcat(entitiesOperations[i].type.ToString()); | |||
throw new ECSException(str.FastConcat(" ") | |||
#if DEBUG && !PROFILER | |||
.FastConcat(entitiesOperations[i].trace.ToString()) | |||
#endif | |||
, e); | |||
} | |||
} | |||
} | |||
} | |||
_groupedEntityToAdd.Swap(); | |||
if (_groupedEntityToAdd.otherEntitiesCreatedPerGroup.Count > 0) | |||
{ | |||
using (profiler.Sample("Add operations")) | |||
{ | |||
try | |||
{ | |||
AddEntityViewsToTheDBAndSuitableEngines(profiler); | |||
} | |||
finally | |||
{ | |||
using (profiler.Sample("clear operates double buffering")) | |||
{ | |||
//other can be cleared now, but let's avoid deleting the dictionary every time | |||
_groupedEntityToAdd.ClearOther(); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
void AddEntityViewsToTheDBAndSuitableEngines(in PlatformProfiler profiler) | |||
{ | |||
using (profiler.Sample("Add entities to database")) | |||
{ | |||
//each group is indexed by entity view type. for each type there is a dictionary indexed by entityID | |||
foreach (var groupOfEntitiesToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup) | |||
{ | |||
var groupID = groupOfEntitiesToSubmit.Key; | |||
//if the group doesn't exist in the current DB let's create it first | |||
if (_groupEntityViewsDB.TryGetValue(groupID, out var groupDB) == false) | |||
groupDB = _groupEntityViewsDB[groupID] = | |||
new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>(); | |||
//add the entityViews in the group | |||
foreach (var entityViewsToSubmit in _groupedEntityToAdd.other[groupID]) | |||
{ | |||
var type = entityViewsToSubmit.Key; | |||
var typeSafeDictionary = entityViewsToSubmit.Value; | |||
var wrapper = new RefWrapper<Type>(type); | |||
if (groupDB.TryGetValue(wrapper, out var dbDic) == false) | |||
dbDic = groupDB[wrapper] = typeSafeDictionary.Create(); | |||
//Fill the DB with the entity views generate this frame. | |||
dbDic.AddEntitiesFromDictionary(typeSafeDictionary, groupID); | |||
if (_groupsPerEntity.TryGetValue(wrapper, out var groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[wrapper] = | |||
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 | |||
using (profiler.Sample("Add entities to engines")) | |||
{ | |||
foreach (var groupToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup) | |||
{ | |||
var groupID = groupToSubmit.Key; | |||
var groupDB = _groupEntityViewsDB[groupID]; | |||
foreach (var entityViewsToSubmit in _groupedEntityToAdd.other[groupID]) | |||
{ | |||
var realDic = groupDB[new RefWrapper<Type>(entityViewsToSubmit.Key)]; | |||
entityViewsToSubmit.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, realDic, in profiler, | |||
new ExclusiveGroup.ExclusiveGroupStruct(groupToSubmit.Key)); | |||
} | |||
} | |||
} | |||
} | |||
DoubleBufferedEntitiesToAdd _groupedEntityToAdd; | |||
readonly IEntitySubmissionScheduler _scheduler; | |||
readonly FasterDictionary<ulong, EntitySubmitOperation> _entitiesOperations; | |||
} | |||
} |
@@ -1,301 +0,0 @@ | |||
#if DEBUG && !PROFILER | |||
#define ENABLE_DEBUG_FUNC | |||
#endif | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
partial class EntitiesDB : IEntitiesDB | |||
{ | |||
internal EntitiesDB( | |||
FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> groupEntityViewsDB, | |||
FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> groupsPerEntity, | |||
EntitiesStream entityStream) | |||
{ | |||
_groupEntityViewsDB = groupEntityViewsDB; | |||
_groupsPerEntity = groupsPerEntity; | |||
_entityStream = entityStream; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T QueryUniqueEntity<T>(ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct | |||
{ | |||
var entities = QueryEntities<T>(group, out var count); | |||
if (count == 0) | |||
throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'")); | |||
if (count != 1) | |||
throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString()) | |||
.FastConcat("'")); | |||
return ref entities[0]; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T QueryEntity<T>(EGID entityGID) where T : struct, IEntityStruct | |||
{ | |||
T[] array; | |||
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out var index)) != null) | |||
return ref array[index]; | |||
throw new EntityNotFoundException(entityGID, typeof(T)); | |||
} | |||
public ref T QueryEntity<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct | |||
{ | |||
return ref QueryEntity<T>(new EGID(id, group)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public T[] QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) | |||
where T : struct, IEntityStruct | |||
{ | |||
uint group = groupStruct; | |||
count = 0; | |||
if (SafeQueryEntityDictionary(group, out TypeSafeDictionary<T> typeSafeDictionary) == false) | |||
return RetrieveEmptyEntityViewArray<T>(); | |||
return typeSafeDictionary.GetValuesArray(out count); | |||
} | |||
public EntityCollection<T> QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) | |||
where T : struct, IEntityStruct | |||
{ | |||
return new EntityCollection<T>(QueryEntities<T>(groupStruct, out var count), count); | |||
} | |||
public EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) | |||
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct | |||
{ | |||
return new EntityCollection<T1, T2>(QueryEntities<T1, T2>(groupStruct, out var count), count); | |||
} | |||
public EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) | |||
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct | |||
{ | |||
return new EntityCollection<T1, T2, T3>(QueryEntities<T1, T2, T3>(groupStruct, out var count), count); | |||
} | |||
public EntityCollections<T> QueryEntities<T>(ExclusiveGroup[] groups) where T : struct, IEntityStruct | |||
{ | |||
return new EntityCollections<T>(this, groups); | |||
} | |||
public EntityCollections<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup[] groups) | |||
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct | |||
{ | |||
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: ' count: " | |||
.FastConcat(countCheck) | |||
.FastConcat(typeof(T1).ToString()) | |||
.FastConcat("'. Entity 2: ' count: ".FastConcat(count) | |||
.FastConcat(typeof(T2).ToString()) | |||
.FastConcat("'"))); | |||
} | |||
return (T1entities, T2entities); | |||
} | |||
[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 | |||
{ | |||
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(" count: ").FastConcat(countCheck1).FastConcat( | |||
" Entity 2: ".FastConcat(typeof(T2).ToString()) | |||
.FastConcat(" count: ").FastConcat(countCheck2) | |||
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())).FastConcat(" count: ") | |||
.FastConcat(count))); | |||
return (T1entities, T2entities, T3entities); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId) | |||
where T : struct, IEntityStruct | |||
{ | |||
if (SafeQueryEntityDictionary(groupStructId, out TypeSafeDictionary<T> typeSafeDictionary) == false) | |||
throw new EntityGroupNotFoundException(groupStructId, typeof(T)); | |||
EGIDMapper<T> mapper; | |||
mapper.map = typeSafeDictionary; | |||
return mapper; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryQueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, | |||
out EGIDMapper<T> mapper) | |||
where T : struct, IEntityStruct | |||
{ | |||
mapper = default; | |||
if (SafeQueryEntityDictionary(groupStructId, out TypeSafeDictionary<T> typeSafeDictionary) == false) | |||
return false; | |||
mapper.map = typeSafeDictionary; | |||
return true; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public T[] QueryEntitiesAndIndex<T>(EGID entityGID, out uint index) where T : struct, IEntityStruct | |||
{ | |||
T[] array; | |||
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out index)) != null) | |||
return array; | |||
throw new EntityNotFoundException(entityGID, typeof(T)); | |||
} | |||
[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; | |||
} | |||
[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); | |||
} | |||
[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); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Exists<T>(EGID entityGID) where T : struct, IEntityStruct | |||
{ | |||
if (SafeQueryEntityDictionary(entityGID.groupID, out TypeSafeDictionary<T> casted) == false) return false; | |||
return casted != null && casted.ContainsKey(entityGID.entityID); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Exists<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct | |||
{ | |||
if (SafeQueryEntityDictionary(group, out TypeSafeDictionary<T> casted) == false) return false; | |||
return casted != null && casted.ContainsKey(id); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid) | |||
{ | |||
return _groupEntityViewsDB.ContainsKey(gid); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool HasAny<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct | |||
{ | |||
QueryEntities<T>(groupStruct, out var count); | |||
return count > 0; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public uint Count<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct | |||
{ | |||
QueryEntities<T>(groupStruct, out var count); | |||
return count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityStruct | |||
{ | |||
_entityStream.PublishEntity(ref QueryEntity<T>(egid), egid); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
T[] QueryEntitiesAndIndexInternal<T>(EGID entityGID, out uint index) where T : struct, IEntityStruct | |||
{ | |||
index = 0; | |||
if (SafeQueryEntityDictionary(entityGID.groupID, out TypeSafeDictionary<T> safeDictionary) == false) | |||
return null; | |||
if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false) | |||
return null; | |||
return safeDictionary.GetValuesArray(out _); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
bool SafeQueryEntityDictionary<T>(uint group, out TypeSafeDictionary<T> typeSafeDictionary) | |||
where T : struct, IEntityStruct | |||
{ | |||
if (UnsafeQueryEntityDictionary(group, TypeCache<T>.type, out var safeDictionary) == false) | |||
{ | |||
typeSafeDictionary = default; | |||
return false; | |||
} | |||
//return the indexes entities if they exist | |||
typeSafeDictionary = safeDictionary as TypeSafeDictionary<T>; | |||
return true; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal bool UnsafeQueryEntityDictionary(uint group, Type type, out ITypeSafeDictionary typeSafeDictionary) | |||
{ | |||
//search for the group | |||
if (_groupEntityViewsDB.TryGetValue(group, out var entitiesInGroupPerType) == false) | |||
{ | |||
typeSafeDictionary = null; | |||
return false; | |||
} | |||
//search for the indexed entities in the group | |||
return entitiesInGroupPerType.TryGetValue(new RefWrapper<Type>(type), out typeSafeDictionary); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
static T[] RetrieveEmptyEntityViewArray<T>() | |||
{ | |||
return EmptyList<T>.emptyArray; | |||
} | |||
//grouped set of entity views, this is the standard way to handle entity views entity views are grouped per | |||
//group, then indexable per type, then indexable per EGID. however the TypeSafeDictionary can return an array of | |||
//values directly, that can be iterated over, so that is possible to iterate over all the entity views of | |||
//a specific type inside a specific group. | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> _groupEntityViewsDB; | |||
//needed to be able to iterate over all the entities of the same type regardless the group | |||
//may change in future | |||
readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity; | |||
readonly EntitiesStream _entityStream; | |||
static class EmptyList<T> | |||
{ | |||
internal static readonly T[] emptyArray = new T[0]; | |||
} | |||
} | |||
} |
@@ -1,138 +0,0 @@ | |||
#if !DEBUG || PROFILER | |||
#define DISABLE_CHECKS | |||
using System.Diagnostics; | |||
#endif | |||
using System; | |||
using System.Reflection; | |||
namespace Svelto.ECS | |||
{ | |||
internal static class EntityBuilderUtilities | |||
{ | |||
const string MSG = "Entity Structs field and Entity View Struct components must hold value types."; | |||
#if DISABLE_CHECKS | |||
[Conditional("_CHECKS_DISABLED")] | |||
#endif | |||
public static void CheckFields(Type entityStructType, bool needsReflection, bool isStringAllowed = false) | |||
{ | |||
if (entityStructType == ENTITY_STRUCT_INFO_VIEW || | |||
entityStructType == EGIDType || | |||
entityStructType == EXCLUSIVEGROUPSTRUCTTYPE || | |||
entityStructType == SERIALIZABLE_ENTITY_STRUCT) | |||
{ | |||
return; | |||
} | |||
if (needsReflection == false) | |||
{ | |||
if (entityStructType.IsClass) | |||
{ | |||
throw new EntityStructException("EntityStructs must be structs.", entityStructType); | |||
} | |||
FieldInfo[] fields = entityStructType.GetFields(BindingFlags.Public | BindingFlags.Instance); | |||
for (var i = fields.Length - 1; i >= 0; --i) | |||
{ | |||
FieldInfo fieldInfo = fields[i]; | |||
Type fieldType = fieldInfo.FieldType; | |||
SubCheckFields(fieldType, entityStructType, isStringAllowed); | |||
} | |||
} | |||
else | |||
{ | |||
FieldInfo[] fields = entityStructType.GetFields(BindingFlags.Public | BindingFlags.Instance); | |||
if (fields.Length < 1) | |||
{ | |||
ProcessError("Entity View Structs must hold only entity components interfaces.", entityStructType); | |||
} | |||
for (int i = fields.Length - 1; i >= 0; --i) | |||
{ | |||
FieldInfo fieldInfo = fields[i]; | |||
if (fieldInfo.FieldType.IsInterfaceEx() == false) | |||
{ | |||
ProcessError("Entity View Structs must hold only entity components interfaces.", | |||
entityStructType); | |||
} | |||
PropertyInfo[] properties = fieldInfo.FieldType.GetProperties( | |||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); | |||
for (int j = properties.Length - 1; j >= 0; --j) | |||
{ | |||
if (properties[j].PropertyType.IsGenericType) | |||
{ | |||
Type genericTypeDefinition = properties[j].PropertyType.GetGenericTypeDefinition(); | |||
if (genericTypeDefinition == DISPATCHONSETTYPE || | |||
genericTypeDefinition == DISPATCHONCHANGETYPE) | |||
{ | |||
continue; | |||
} | |||
} | |||
Type propertyType = properties[j].PropertyType; | |||
if (propertyType != STRINGTYPE) | |||
{ | |||
//for EntityViewStructs, component fields that are structs that hold strings | |||
//are allowed | |||
SubCheckFields(propertyType, entityStructType, isStringAllowed: true); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
static void SubCheckFields(Type fieldType, Type entityStructType, bool isStringAllowed = false) | |||
{ | |||
if (fieldType.IsPrimitive || fieldType.IsValueType || (isStringAllowed == true && fieldType == STRINGTYPE)) | |||
{ | |||
if (fieldType.IsValueType && !fieldType.IsEnum && fieldType.IsPrimitive == false) | |||
{ | |||
CheckFields(fieldType, false, isStringAllowed); | |||
} | |||
return; | |||
} | |||
ProcessError(MSG, entityStructType, fieldType); | |||
} | |||
static void ProcessError(string message, Type entityViewType, Type fieldType = null) | |||
{ | |||
if (fieldType != null) | |||
{ | |||
throw new EntityStructException(message, entityViewType, fieldType); | |||
} | |||
throw new EntityStructException(message, entityViewType); | |||
} | |||
static readonly Type DISPATCHONCHANGETYPE = typeof(DispatchOnChange<>); | |||
static readonly Type DISPATCHONSETTYPE = typeof(DispatchOnSet<>); | |||
static readonly Type EGIDType = typeof(EGID); | |||
static readonly Type EXCLUSIVEGROUPSTRUCTTYPE = typeof(ExclusiveGroup.ExclusiveGroupStruct); | |||
static readonly Type SERIALIZABLE_ENTITY_STRUCT = typeof(SerializableEntityStruct); | |||
static readonly Type STRINGTYPE = typeof(string); | |||
internal static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityStructInfoView); | |||
} | |||
public class EntityStructException : Exception | |||
{ | |||
public EntityStructException(string message, Type entityViewType, Type type) : | |||
base(message.FastConcat(" entity view: '", entityViewType.ToString(), "', field: '", type.ToString())) | |||
{ | |||
} | |||
public EntityStructException(string message, Type entityViewType) : | |||
base(message.FastConcat(" entity view: ", entityViewType.ToString())) | |||
{ | |||
} | |||
} | |||
} |
@@ -1,146 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Reflection; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Hybrid; | |||
using Svelto.ECS.Internal; | |||
using Svelto.Utilities; | |||
namespace Svelto.ECS | |||
{ | |||
public class EntityBuilder<T> : IEntityBuilder where T : struct, IEntityStruct | |||
{ | |||
static class EntityView | |||
{ | |||
internal static readonly FasterList<KeyValuePair<Type, ActionCast<T>>> cachedFields; | |||
internal static readonly Dictionary<Type, Type[]> cachedTypes; | |||
#if DEBUG && !PROFILER | |||
internal static readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType; | |||
#else | |||
internal static readonly Dictionary<Type, object> implementorsByType; | |||
#endif | |||
static EntityView() | |||
{ | |||
cachedFields = new FasterList<KeyValuePair<Type, ActionCast<T>>>(); | |||
var type = typeof(T); | |||
var fields = type.GetFields(BindingFlags.Public | | |||
BindingFlags.Instance); | |||
for (var i = fields.Length - 1; i >= 0; --i) | |||
{ | |||
var field = fields[i]; | |||
var setter = FastInvoke<T>.MakeSetter(field); | |||
cachedFields.Add(new KeyValuePair<Type, ActionCast<T>>(field.FieldType, setter)); | |||
} | |||
cachedTypes = new Dictionary<Type, Type[]>(); | |||
#if DEBUG && !PROFILER | |||
implementorsByType = new Dictionary<Type, ECSTuple<object, int>>(); | |||
#else | |||
implementorsByType = new Dictionary<Type, object>(); | |||
#endif | |||
} | |||
internal static void InitCache() | |||
{} | |||
internal static void BuildEntityView(out T entityView) | |||
{ | |||
entityView = new T(); | |||
} | |||
} | |||
public EntityBuilder() | |||
{ | |||
_initializer = DEFAULT_IT; | |||
EntityBuilderUtilities.CheckFields(ENTITY_VIEW_TYPE, NEEDS_REFLECTION); | |||
if (NEEDS_REFLECTION) | |||
EntityView.InitCache(); | |||
} | |||
public EntityBuilder(in T initializer) : this() | |||
{ | |||
_initializer = initializer; | |||
} | |||
public void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID egid, | |||
IEnumerable<object> implementors) | |||
{ | |||
if (dictionary == null) | |||
dictionary = new TypeSafeDictionary<T>(); | |||
var castedDic = dictionary as TypeSafeDictionary<T>; | |||
if (NEEDS_REFLECTION) | |||
{ | |||
DBC.ECS.Check.Require(implementors != null, | |||
$"Implementors not found while building an EntityView `{typeof(T)}`"); | |||
DBC.ECS.Check.Require(castedDic.ContainsKey(egid.entityID) == false, | |||
$"building an entity with already used entity id! id: '{(ulong) egid}', {ENTITY_VIEW_NAME}"); | |||
EntityView.BuildEntityView(out var entityView); | |||
this.FillEntityView(ref entityView, entityViewBlazingFastReflection, implementors, | |||
EntityView.implementorsByType, EntityView.cachedTypes); | |||
castedDic.Add(egid.entityID, entityView); | |||
} | |||
else | |||
{ | |||
DBC.ECS.Check.Require(!castedDic.ContainsKey(egid.entityID), | |||
$"building an entity with already used entity id! id: '{egid.entityID}'"); | |||
castedDic.Add(egid.entityID, _initializer); | |||
} | |||
} | |||
ITypeSafeDictionary IEntityBuilder.Preallocate(ref ITypeSafeDictionary dictionary, uint size) | |||
{ | |||
return Preallocate(ref dictionary, size); | |||
} | |||
static ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size) | |||
{ | |||
if (dictionary == null) | |||
dictionary = new TypeSafeDictionary<T>(size); | |||
else | |||
dictionary.SetCapacity(size); | |||
return dictionary; | |||
} | |||
public Type GetEntityType() | |||
{ | |||
return ENTITY_VIEW_TYPE; | |||
} | |||
static EntityBuilder() | |||
{ | |||
ENTITY_VIEW_TYPE = typeof(T); | |||
DEFAULT_IT = default; | |||
NEEDS_REFLECTION = typeof(IEntityViewStruct).IsAssignableFrom(ENTITY_VIEW_TYPE); | |||
HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_VIEW_TYPE); | |||
ENTITY_VIEW_NAME = ENTITY_VIEW_TYPE.ToString(); | |||
SetEGIDWithoutBoxing<T>.Warmup(); | |||
} | |||
readonly T _initializer; | |||
static FasterList<KeyValuePair<Type, ActionCast<T>>> entityViewBlazingFastReflection => | |||
EntityView.cachedFields; | |||
internal static readonly Type ENTITY_VIEW_TYPE; | |||
public static readonly bool HAS_EGID; | |||
static readonly T DEFAULT_IT; | |||
static readonly bool NEEDS_REFLECTION; | |||
static readonly string ENTITY_VIEW_NAME; | |||
} | |||
} |
@@ -1,8 +0,0 @@ | |||
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; |
@@ -1,326 +0,0 @@ | |||
using System; | |||
using System.Collections; | |||
using System.Collections.Generic; | |||
namespace Svelto.ECS | |||
{ | |||
public struct EntityCollection<T> | |||
{ | |||
public EntityCollection(T[] array, uint count) | |||
{ | |||
_array = array; | |||
_count = count; | |||
} | |||
public EntityIterator GetEnumerator() | |||
{ | |||
return new EntityIterator(_array, _count); | |||
} | |||
readonly T[] _array; | |||
readonly uint _count; | |||
public struct EntityIterator : 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; | |||
} | |||
} | |||
public struct EntityCollection<T1, T2> | |||
{ | |||
public EntityCollection(in (T1[], T2[]) array, uint count) | |||
{ | |||
_array = array; | |||
_count = count; | |||
} | |||
public EntityIterator GetEnumerator() | |||
{ | |||
return new EntityIterator(_array, _count); | |||
} | |||
readonly (T1[], T2[]) _array; | |||
readonly uint _count; | |||
public struct EntityIterator : IEnumerator<ValueRef<T1, T2>> | |||
{ | |||
public EntityIterator((T1[], T2[]) array, uint count) : this() | |||
{ | |||
_array = array; | |||
_count = count; | |||
_index = -1; | |||
} | |||
public bool MoveNext() | |||
{ | |||
return ++_index < _count; | |||
} | |||
public void Reset() | |||
{ | |||
_index = -1; | |||
} | |||
public ValueRef<T1, T2> Current => new ValueRef<T1, T2>(_array, (uint) _index); | |||
ValueRef<T1, T2> IEnumerator<ValueRef<T1, T2>>. Current => throw new NotImplementedException(); | |||
object IEnumerator.Current => throw new NotImplementedException(); | |||
public void Dispose() {} | |||
readonly (T1[], T2[]) _array; | |||
readonly uint _count; | |||
int _index; | |||
} | |||
} | |||
public struct EntityCollection<T1, T2, T3> | |||
{ | |||
public EntityCollection(in (T1[], T2[], T3[]) array, uint count) | |||
{ | |||
_array = array; | |||
_count = count; | |||
} | |||
public EntityIterator GetEnumerator() | |||
{ | |||
return new EntityIterator(_array, _count); | |||
} | |||
readonly (T1[], T2[], T3[]) _array; | |||
readonly uint _count; | |||
public struct EntityIterator : IEnumerator<ValueRef<T1, T2, T3>> | |||
{ | |||
public EntityIterator((T1[], T2[], T3[]) array, uint count) : this() | |||
{ | |||
_array = array; | |||
_count = count; | |||
_index = -1; | |||
} | |||
public bool MoveNext() | |||
{ | |||
return ++_index < _count; | |||
} | |||
public void Reset() | |||
{ | |||
_index = -1; | |||
} | |||
public ValueRef<T1, T2, T3> Current => new ValueRef<T1, T2, T3>(_array, (uint) _index); | |||
ValueRef<T1, T2, T3> IEnumerator<ValueRef<T1, T2, T3>>.Current => throw new NotImplementedException(); | |||
object IEnumerator. Current => throw new NotImplementedException(); | |||
public void Dispose() {} | |||
readonly (T1[], T2[], T3[]) _array; | |||
readonly uint _count; | |||
int _index; | |||
} | |||
} | |||
public struct EntityCollections<T> where T : struct, IEntityStruct | |||
{ | |||
public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
public EntityGroupsIterator GetEnumerator() | |||
{ | |||
return new EntityGroupsIterator(_db, _groups); | |||
} | |||
readonly IEntitiesDB _db; | |||
readonly ExclusiveGroup[] _groups; | |||
public struct EntityGroupsIterator : IEnumerator<T> | |||
{ | |||
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); | |||
} | |||
return ++_index < _count; | |||
} | |||
public void Reset() | |||
{ | |||
_index = -1; | |||
_indexGroup = -1; | |||
_count = 0; | |||
} | |||
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 EntityCollections<T1, T2> where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct | |||
{ | |||
public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
public EntityGroupsIterator GetEnumerator() | |||
{ | |||
return new EntityGroupsIterator(_db, _groups); | |||
} | |||
readonly IEntitiesDB _db; | |||
readonly ExclusiveGroup[] _groups; | |||
public struct EntityGroupsIterator : IEnumerator<ValueRef<T1, T2>> | |||
{ | |||
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; | |||
var array1 = _db.QueryEntities<T1>(_groups[_indexGroup], out _count); | |||
var array2 = _db.QueryEntities<T2>(_groups[_indexGroup], out var count1); | |||
_array = (array1, array2); | |||
#if DEBUG && !PROFILER | |||
if (_count != count1) | |||
throw new ECSException("number of entities in group doesn't match"); | |||
#endif | |||
} | |||
return ++_index < _count; | |||
} | |||
public void Reset() | |||
{ | |||
_index = -1; | |||
_indexGroup = -1; | |||
var array1 = _db.QueryEntities<T1>(_groups[0], out _count); | |||
var array2 = _db.QueryEntities<T2>(_groups[0], out var count1); | |||
_array = (array1, array2); | |||
#if DEBUG && !PROFILER | |||
if (_count != count1) | |||
throw new ECSException("number of entities in group doesn't match"); | |||
#endif | |||
} | |||
public ValueRef<T1, T2> Current | |||
{ | |||
get | |||
{ | |||
var valueRef = new ValueRef<T1, T2>(_array, (uint) _index); | |||
return valueRef; | |||
} | |||
} | |||
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; | |||
(T1[], T2[]) _array; | |||
} | |||
} | |||
public struct ValueRef<T1, T2> | |||
{ | |||
readonly (T1[], T2[]) array; | |||
readonly uint index; | |||
public ValueRef(in (T1[], T2[]) entity1, uint i) | |||
{ | |||
array = entity1; | |||
index = i; | |||
} | |||
public ref T1 entityStructA => ref array.Item1[index]; | |||
public ref T2 entityStructB => ref array.Item2[index]; | |||
} | |||
public struct ValueRef<T1, T2, T3> | |||
{ | |||
readonly (T1[], T2[], T3[]) array; | |||
readonly uint index; | |||
public ValueRef(in (T1[], T2[], T3[]) entity1, uint i) | |||
{ | |||
array = entity1; | |||
index = i; | |||
} | |||
public ref T1 entityStructA => ref array.Item1[index]; | |||
public ref T2 entityStructB => ref array.Item2[index]; | |||
public ref T3 entityStructC => ref array.Item3[index]; | |||
} | |||
} |
@@ -1,17 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public interface IEntityDescriptor | |||
{ | |||
IEntityBuilder[] entitiesToBuild { get; } | |||
} | |||
static class EntityDescriptorTemplate<TType> where TType : IEntityDescriptor, new() | |||
{ | |||
static EntityDescriptorTemplate() | |||
{ | |||
descriptor = new TType(); | |||
} | |||
public static IEntityDescriptor descriptor { get; } | |||
} | |||
} |
@@ -1,97 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
static class EntityFactory | |||
{ | |||
public static FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> BuildGroupedEntities(EGID egid, | |||
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd, | |||
IEntityBuilder[] entitiesToBuild, | |||
IEnumerable<object> implementors) | |||
{ | |||
var group = FetchEntityGroup(egid.groupID, groupEntitiesToAdd); | |||
BuildEntitiesAndAddToGroup(egid, group, entitiesToBuild, implementors); | |||
return group; | |||
} | |||
static FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> FetchEntityGroup(uint groupID, | |||
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityViewsByType) | |||
{ | |||
if (groupEntityViewsByType.current.TryGetValue(groupID, out var group) == false) | |||
{ | |||
group = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>(); | |||
groupEntityViewsByType.current.Add(groupID, group); | |||
} | |||
if (groupEntityViewsByType.currentEntitiesCreatedPerGroup.TryGetValue(groupID, out var value) == false) | |||
groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = 0; | |||
else | |||
groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = value+1; | |||
return group; | |||
} | |||
static void BuildEntitiesAndAddToGroup(EGID entityID, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group, | |||
IEntityBuilder[] entityBuilders, IEnumerable<object> implementors) | |||
{ | |||
#if DEBUG && !PROFILER | |||
HashSet<Type> types = new HashSet<Type>(); | |||
#endif | |||
InternalBuild(entityID, group, entityBuilders, implementors | |||
#if DEBUG && !PROFILER | |||
, types | |||
#endif | |||
); | |||
} | |||
static void InternalBuild(EGID entityID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group, | |||
IEntityBuilder[] entityBuilders, IEnumerable<object> implementors | |||
#if DEBUG && !PROFILER | |||
, HashSet<Type> types | |||
#endif | |||
) | |||
{ | |||
var count = entityBuilders.Length; | |||
#if DEBUG && !PROFILER | |||
for (var index = 0; index < count; ++index) | |||
{ | |||
var entityViewType = entityBuilders[index].GetEntityType(); | |||
if (types.Contains(entityViewType)) | |||
{ | |||
throw new ECSException("EntityBuilders must be unique inside an EntityDescriptor"); | |||
} | |||
types.Add(entityViewType); | |||
} | |||
#endif | |||
for (var index = 0; index < count; ++index) | |||
{ | |||
var entityStructBuilder = entityBuilders[index]; | |||
var entityViewType = entityStructBuilder.GetEntityType(); | |||
BuildEntity(entityID, group, entityViewType, entityStructBuilder, implementors); | |||
} | |||
} | |||
static void BuildEntity(EGID entityID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group, | |||
Type entityViewType, IEntityBuilder entityBuilder, IEnumerable<object> implementors) | |||
{ | |||
var entityViewsPoolWillBeCreated = | |||
group.TryGetValue(new RefWrapper<Type>(entityViewType), out var safeDictionary) == false; | |||
//passing the undefined entityViewsByType inside the entityViewBuilder will allow it to be created with the | |||
//correct type and casted back to the undefined list. that's how the list will be eventually of the target | |||
//type. | |||
entityBuilder.BuildEntityAndAddToList(ref safeDictionary, entityID, implementors); | |||
if (entityViewsPoolWillBeCreated) | |||
group.Add(new RefWrapper<Type>(entityViewType), safeDictionary); | |||
} | |||
} | |||
} |
@@ -1,12 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
class EntityGroupNotFoundException : Exception | |||
{ | |||
public EntityGroupNotFoundException(uint groupId, Type type) | |||
: base("entity group not found ".FastConcat(type.ToString())) | |||
{ | |||
} | |||
} | |||
} |
@@ -1,11 +0,0 @@ | |||
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; } | |||
} | |||
} |
@@ -1,7 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
struct EntityStructInfoView: IEntityStruct | |||
{ | |||
public IEntityBuilder[] entitiesToBuild; | |||
} | |||
} |
@@ -1,12 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS | |||
{ | |||
public class EntityNotFoundException : Exception | |||
{ | |||
public EntityNotFoundException(EGID entityEGID, Type entityType) : base( | |||
$"entity of type '{entityType}' with ID '{entityEGID.entityID}', group '{(uint) entityEGID.groupID}' not found!") | |||
{ | |||
} | |||
} | |||
} |
@@ -1,163 +0,0 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
/// <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. | |||
/// Using groups, you can have always an optimal set of entity components to poll, so EntityStreams must be used | |||
/// only if: | |||
/// - you want to polling engine to be able to track all the entity changes happening in between polls and not | |||
/// just the current state | |||
/// - you want a thread-safe way to read entity states, which includes all the state changes and not the last | |||
/// one only | |||
/// - you want to communicate between EnginesRoots | |||
/// </summary> | |||
class EntitiesStream : IDisposable | |||
{ | |||
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct | |||
{ | |||
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false) _streams[TypeRefWrapper<T>.wrapper] = new EntityStream<T>(); | |||
return (_streams[TypeRefWrapper<T>.wrapper] as EntityStream<T>).GenerateConsumer(name, capacity); | |||
} | |||
public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) | |||
where T : unmanaged, IEntityStruct | |||
{ | |||
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false) _streams[TypeRefWrapper<T>.wrapper] = new EntityStream<T>(); | |||
return (_streams[TypeRefWrapper<T>.wrapper] as EntityStream<T>).GenerateConsumer(group, name, capacity); | |||
} | |||
internal void PublishEntity<T>(ref T entity, EGID egid) where T : unmanaged, IEntityStruct | |||
{ | |||
if (_streams.TryGetValue(TypeRefWrapper<T>.wrapper, out var typeSafeStream)) | |||
(typeSafeStream as EntityStream<T>).PublishEntity(ref entity, egid); | |||
else | |||
Console.LogDebug("No Consumers are waiting for this entity to change ", typeof(T)); | |||
} | |||
readonly ThreadSafeDictionary<RefWrapper<Type>, ITypeSafeStream> _streams = | |||
new ThreadSafeDictionary<RefWrapper<Type>, ITypeSafeStream>(); | |||
public void Dispose() | |||
{ | |||
_streams.Clear(); | |||
} | |||
} | |||
interface ITypeSafeStream | |||
{} | |||
class EntityStream<T> : ITypeSafeStream where T : unmanaged, IEntityStruct | |||
{ | |||
public void PublishEntity(ref T entity, EGID egid) | |||
{ | |||
for (int i = 0; i < _consumers.Count; i++) | |||
{ | |||
if (_consumers[i]._hasGroup) | |||
{ | |||
if (egid.groupID == _consumers[i]._group) | |||
{ | |||
_consumers[i].Enqueue(entity, egid); | |||
} | |||
} | |||
else | |||
{ | |||
_consumers[i].Enqueue(entity, egid); | |||
} | |||
} | |||
} | |||
public Consumer<T> GenerateConsumer(string name, uint capacity) | |||
{ | |||
var consumer = new Consumer<T>(name, capacity, this); | |||
_consumers.Add(consumer); | |||
return consumer; | |||
} | |||
public Consumer<T> GenerateConsumer(ExclusiveGroup group, string name, uint capacity) | |||
{ | |||
var consumer = new Consumer<T>(group, name, capacity, this); | |||
_consumers.Add(consumer); | |||
return consumer; | |||
} | |||
public void RemoveConsumer(Consumer<T> consumer) | |||
{ | |||
_consumers.UnorderedRemove(consumer); | |||
} | |||
readonly FasterListThreadSafe<Consumer<T>> _consumers = new FasterListThreadSafe<Consumer<T>>(); | |||
} | |||
public struct Consumer<T> : IDisposable where T : unmanaged, IEntityStruct | |||
{ | |||
internal Consumer(string name, uint capacity, EntityStream<T> stream):this() | |||
{ | |||
#if DEBUG && !PROFILER | |||
_name = name; | |||
#endif | |||
_ringBuffer = new RingBuffer<ValueTuple<T, EGID>>((int) capacity, | |||
#if DEBUG && !PROFILER | |||
_name | |||
#else | |||
string.Empty | |||
#endif | |||
); | |||
_stream = stream; | |||
} | |||
internal Consumer(ExclusiveGroup group, string name, uint capacity, EntityStream<T> stream) : this(name, | |||
capacity, stream) | |||
{ | |||
_group = group; | |||
_hasGroup = true; | |||
} | |||
internal void Enqueue(in T entity, in EGID egid) | |||
{ | |||
_ringBuffer.Enqueue((entity, egid)); | |||
} | |||
public bool TryDequeue(out T entity) | |||
{ | |||
var tryDequeue = _ringBuffer.TryDequeue(out var values); | |||
entity = values.Item1; | |||
return tryDequeue; | |||
} | |||
public bool TryDequeue(out T entity, out EGID id) | |||
{ | |||
var tryDequeue = _ringBuffer.TryDequeue(out var values); | |||
entity = values.Item1; | |||
id = values.Item2; | |||
return tryDequeue; | |||
} | |||
public void Flush() { _ringBuffer.Reset(); } | |||
public void Dispose() { _stream.RemoveConsumer(this); } | |||
public uint Count() { return (uint) _ringBuffer.Count; } | |||
readonly RingBuffer<ValueTuple<T, EGID>> _ringBuffer; | |||
readonly EntityStream<T> _stream; | |||
internal readonly ExclusiveGroup _group; | |||
internal readonly bool _hasGroup; | |||
#if DEBUG && !PROFILER | |||
readonly string _name; | |||
#endif | |||
} | |||
} |
@@ -1,75 +0,0 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public ref struct EntityStructInitializer | |||
{ | |||
public EntityStructInitializer(EGID id, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group) | |||
{ | |||
_group = group; | |||
_ID = id; | |||
} | |||
public void Init<T>(T initializer) where T : struct, IEntityStruct | |||
{ | |||
if (_group.TryGetValue(new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE), | |||
out var typeSafeDictionary) == false) return; | |||
var dictionary = (TypeSafeDictionary<T>) typeSafeDictionary; | |||
if (EntityBuilder<T>.HAS_EGID) | |||
SetEGIDWithoutBoxing<T>.SetIDWithoutBoxing(ref initializer, _ID); | |||
if (dictionary.TryFindIndex(_ID.entityID, out var findElementIndex)) | |||
dictionary.GetDirectValue(findElementIndex) = initializer; | |||
} | |||
public void CopyFrom<T>(T initializer) where T : struct, IEntityStruct | |||
{ | |||
var dictionary = (TypeSafeDictionary<T>) _group[new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE)]; | |||
if (EntityBuilder<T>.HAS_EGID) | |||
SetEGIDWithoutBoxing<T>.SetIDWithoutBoxing(ref initializer, _ID); | |||
dictionary[_ID.entityID] = initializer; | |||
} | |||
public ref T GetOrCreate<T>() where T : struct, IEntityStruct | |||
{ | |||
ref var entityDictionary = ref _group.GetOrCreate(new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE) | |||
, () => new TypeSafeDictionary<T>()); | |||
var dictionary = (TypeSafeDictionary<T>) entityDictionary; | |||
return ref dictionary.GetOrCreate(_ID.entityID); | |||
} | |||
public T Get<T>() where T : struct, IEntityStruct | |||
{ | |||
return (_group[new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE)] as TypeSafeDictionary<T>)[_ID.entityID]; | |||
} | |||
public bool Has<T>() where T : struct, IEntityStruct | |||
{ | |||
if (_group.TryGetValue(new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE), | |||
out var typeSafeDictionary)) | |||
{ | |||
var dictionary = (TypeSafeDictionary<T>) typeSafeDictionary; | |||
if (dictionary.ContainsKey(_ID.entityID)) | |||
return true; | |||
} | |||
return false; | |||
} | |||
public static EntityStructInitializer CreateEmptyInitializer() | |||
{ | |||
return new EntityStructInitializer(new EGID(), new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>()); | |||
} | |||
readonly EGID _ID; | |||
readonly FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> _group; | |||
} | |||
} |
@@ -1,9 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS.Schedulers | |||
{ | |||
public interface IEntitySubmissionScheduler: IDisposable | |||
{ | |||
EnginesRoot.EntitiesSubmitter onTick { set; } | |||
} | |||
} |
@@ -1,53 +0,0 @@ | |||
using System; | |||
using System.Diagnostics; | |||
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 EGID fromID; | |||
public readonly EGID toID; | |||
#if DEBUG && !PROFILER | |||
public StackFrame trace; | |||
#endif | |||
public EntitySubmitOperation(EntitySubmitOperationType operation, EGID from, EGID to, | |||
IEntityBuilder[] builders = null) | |||
{ | |||
type = operation; | |||
this.builders = builders; | |||
fromID = from; | |||
toID = to; | |||
#if DEBUG && !PROFILER | |||
trace = default; | |||
#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 | |||
} | |||
} |
@@ -1,125 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
using Svelto.Utilities; | |||
namespace Svelto.ECS | |||
{ | |||
#if DEBUG && !PROFILER | |||
struct ECSTuple<T1, T2> | |||
{ | |||
public readonly T1 implementorType; | |||
public T2 numberOfImplementations; | |||
public ECSTuple(T1 implementor, T2 v) | |||
{ | |||
implementorType = implementor; | |||
numberOfImplementations = v; | |||
} | |||
} | |||
#endif | |||
static class EntityViewUtility | |||
{ | |||
public static void FillEntityView<T>(this IEntityBuilder entityBuilder | |||
, ref T entityView | |||
, FasterList<KeyValuePair<Type, ActionCast<T>>> | |||
entityViewBlazingFastReflection | |||
, IEnumerable<object> implementors, | |||
#if DEBUG && !PROFILER | |||
Dictionary<Type, ECSTuple<object, int>> implementorsByType | |||
#else | |||
Dictionary<Type, object> implementorsByType | |||
#endif | |||
, Dictionary<Type, Type[]> cachedTypes | |||
) | |||
{ | |||
//efficient way to collect the fields of every EntityViewType | |||
var setters = | |||
FasterList<KeyValuePair<Type, ActionCast<T>>>.NoVirt.ToArrayFast(entityViewBlazingFastReflection, out var count); | |||
foreach (var implementor in implementors) | |||
{ | |||
if (implementor != null) | |||
{ | |||
var type = implementor.GetType(); | |||
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 | |||
if (implementorsByType.TryGetValue(componentType, out var implementorData)) | |||
{ | |||
implementorData.numberOfImplementations++; | |||
implementorsByType[componentType] = implementorData; | |||
} | |||
else | |||
implementorsByType[componentType] = new ECSTuple<object, int>(implementor, 1); | |||
#else | |||
implementorsByType[componentType] = implementor; | |||
#endif | |||
} | |||
} | |||
#if DEBUG && !PROFILER | |||
else | |||
{ | |||
Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityView ", | |||
entityBuilder.GetEntityType().ToString())); | |||
} | |||
#endif | |||
} | |||
for (var i = 0; i < count; i++) | |||
{ | |||
var fieldSetter = setters[i]; | |||
var fieldType = fieldSetter.Key; | |||
#if DEBUG && !PROFILER | |||
ECSTuple<object, int> component; | |||
#else | |||
object component; | |||
#endif | |||
if (implementorsByType.TryGetValue(fieldType, out component) == false) | |||
{ | |||
var e = new ECSException(NOT_FOUND_EXCEPTION + " Component Type: " + fieldType.Name + | |||
" - EntityView: " + entityBuilder.GetEntityType().Name); | |||
throw e; | |||
} | |||
#if DEBUG && !PROFILER | |||
if (component.numberOfImplementations > 1) | |||
throw new ECSException(DUPLICATE_IMPLEMENTOR_ERROR.FastConcat( | |||
"Component Type: ", fieldType.Name, | |||
" implementor: ", | |||
component.implementorType | |||
.ToString()) + | |||
" - EntityView: " + | |||
entityBuilder.GetEntityType().Name); | |||
#endif | |||
#if DEBUG && !PROFILER | |||
fieldSetter.Value(ref entityView, component.implementorType); | |||
#else | |||
fieldSetter.Value(ref entityView, component); | |||
#endif | |||
} | |||
implementorsByType.Clear(); | |||
} | |||
const string DUPLICATE_IMPLEMENTOR_ERROR = | |||
"<color=teal>Svelto.ECS</color> the same component is implemented with more than one implementor. This is " + | |||
"considered an error and MUST be fixed. "; | |||
const string NULL_IMPLEMENTOR_ERROR = | |||
"<color=teal>Svelto.ECS</color> Null implementor, please be careful about the implementors passed to avoid " + | |||
"performance loss "; | |||
const string NOT_FOUND_EXCEPTION = "<color=teal>Svelto.ECS</color> Implementor not found for an EntityView. "; | |||
} | |||
} |
@@ -1,265 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
#pragma warning disable 660,661 | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// Exclusive Groups guarantee that the GroupID is unique. | |||
/// | |||
/// The best way to use it is like: | |||
/// | |||
/// public static class MyExclusiveGroups //(can be as many as you want) | |||
/// { | |||
/// public static ExclusiveGroup MyExclusiveGroup1 = new ExclusiveGroup(); | |||
/// | |||
/// public static ExclusiveGroup[] GroupOfGroups = { MyExclusiveGroup1, ...}; //for each on this! | |||
/// } | |||
/// </summary> | |||
/// | |||
///use this like: | |||
/// public class TriggersGroup : ExclusiveGroup<TriggersGroup> {} | |||
public abstract class NamedExclusiveGroup<T>:ExclusiveGroup | |||
{ | |||
public static ExclusiveGroup Group = new ExclusiveGroup(); | |||
public static string name = typeof(T).FullName; | |||
public NamedExclusiveGroup() { } | |||
public NamedExclusiveGroup(string recognizeAs) : base(recognizeAs) | |||
{} | |||
public NamedExclusiveGroup(ushort range) : base(range) | |||
{} | |||
} | |||
public class ExclusiveGroup | |||
{ | |||
public ExclusiveGroup() | |||
{ | |||
_group = ExclusiveGroupStruct.Generate(); | |||
} | |||
public ExclusiveGroup(string recognizeAs) | |||
{ | |||
_group = ExclusiveGroupStruct.Generate(); | |||
_serialisedGroups.Add(recognizeAs, _group); | |||
} | |||
public ExclusiveGroup(ushort range) | |||
{ | |||
_group = new ExclusiveGroupStruct(range); | |||
#if DEBUG | |||
_range = range; | |||
#endif | |||
} | |||
public static implicit operator ExclusiveGroupStruct(ExclusiveGroup group) | |||
{ | |||
return group._group; | |||
} | |||
public static explicit operator uint(ExclusiveGroup group) | |||
{ | |||
return group._group; | |||
} | |||
public static ExclusiveGroupStruct operator+(ExclusiveGroup a, uint b) | |||
{ | |||
#if DEBUG | |||
if (a._range == 0) | |||
throw new ECSException("adding values to a not ranged ExclusiveGroup"); | |||
if (b >= a._range) | |||
throw new ECSException("Using out of range group"); | |||
#endif | |||
return a._group + b; | |||
} | |||
readonly ExclusiveGroupStruct _group; | |||
//I use this as parameter because it must not be possible to pass null Exclusive Groups. | |||
public struct ExclusiveGroupStruct : IEquatable<ExclusiveGroupStruct>, IComparable<ExclusiveGroupStruct>, | |||
IEqualityComparer<ExclusiveGroupStruct> | |||
{ | |||
public static bool operator ==(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2) | |||
{ | |||
return c1.Equals(c2); | |||
} | |||
public static bool operator !=(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2) | |||
{ | |||
return c1.Equals(c2) == false; | |||
} | |||
public bool Equals(ExclusiveGroupStruct other) | |||
{ | |||
return other._id == _id; | |||
} | |||
public int CompareTo(ExclusiveGroupStruct other) | |||
{ | |||
return other._id.CompareTo(_id); | |||
} | |||
public bool Equals(ExclusiveGroupStruct x, ExclusiveGroupStruct y) | |||
{ | |||
return x._id == y._id; | |||
} | |||
public int GetHashCode(ExclusiveGroupStruct obj) | |||
{ | |||
return _id.GetHashCode(); | |||
} | |||
internal static ExclusiveGroupStruct Generate() | |||
{ | |||
ExclusiveGroupStruct groupStruct; | |||
groupStruct._id = _globalId; | |||
DBC.ECS.Check.Require(_globalId + 1 < ushort.MaxValue, "too many exclusive groups created"); | |||
_globalId++; | |||
return groupStruct; | |||
} | |||
/// <summary> | |||
/// Use this constructor to reserve N groups | |||
/// </summary> | |||
internal ExclusiveGroupStruct(ushort range) | |||
{ | |||
_id = _globalId; | |||
DBC.ECS.Check.Require(_globalId + range < ushort.MaxValue, "too many exclusive groups created"); | |||
_globalId += range; | |||
} | |||
internal ExclusiveGroupStruct(uint groupID) | |||
{ | |||
_id = groupID; | |||
} | |||
public ExclusiveGroupStruct(byte[] data, uint pos) | |||
{ | |||
_id = (uint)( | |||
data[pos++] | |||
| data[pos++] << 8 | |||
| data[pos++] << 16 | |||
| data[pos++] << 24 | |||
); | |||
DBC.ECS.Check.Ensure(_id < _globalId, "Invalid group ID deserialiased"); | |||
} | |||
public static implicit operator uint(ExclusiveGroupStruct groupStruct) | |||
{ | |||
return groupStruct._id; | |||
} | |||
public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, uint b) | |||
{ | |||
var group = new ExclusiveGroupStruct(); | |||
group._id = a._id + b; | |||
return group; | |||
} | |||
uint _id; | |||
static uint _globalId; | |||
} | |||
public static ExclusiveGroupStruct Search(string holderGroupName) | |||
{ | |||
if (_serialisedGroups.ContainsKey(holderGroupName) == false) | |||
throw new Exception("Named Group Not Found ".FastConcat(holderGroupName)); | |||
return _serialisedGroups[holderGroupName]; | |||
} | |||
static readonly Dictionary<string, ExclusiveGroupStruct> _serialisedGroups = new Dictionary<string, | |||
ExclusiveGroupStruct>(); | |||
#if DEBUG | |||
readonly ushort _range; | |||
#endif | |||
} | |||
} | |||
#if future | |||
public static void ConstructStaticGroups() | |||
{ | |||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); | |||
// Assemblies or types aren't guaranteed to be returned in the same order, | |||
// and I couldn't find proof that `GetTypes()` returns them in fixed order either, | |||
// even for builds made with the exact same source code. | |||
// So will sort reflection results by name before constructing groups. | |||
var groupFields = new List<KeyValuePair<string, FieldInfo>>(); | |||
foreach (Assembly assembly in assemblies) | |||
{ | |||
Type[] types = GetTypesSafe(assembly); | |||
foreach (Type type in types) | |||
{ | |||
if (type == null || !type.IsClass) | |||
{ | |||
continue; | |||
} | |||
// Groups defined as static members in static classes | |||
if (type.IsSealed && type.IsAbstract) | |||
{ | |||
FieldInfo[] fields = type.GetFields(); | |||
foreach(var field in fields) | |||
{ | |||
if (field.IsStatic && typeof(ExclusiveGroup).IsAssignableFrom(field.FieldType)) | |||
{ | |||
groupFields.Add(new KeyValuePair<string, FieldInfo>($"{type.FullName}.{field.Name}", field)); | |||
} | |||
} | |||
} | |||
// Groups defined as classes | |||
else if (type.BaseType != null | |||
&& type.BaseType.IsGenericType | |||
&& type.BaseType.GetGenericTypeDefinition() == typeof(ExclusiveGroup<>)) | |||
{ | |||
FieldInfo field = type.GetField("Group", | |||
BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy); | |||
groupFields.Add(new KeyValuePair<string, FieldInfo>(type.FullName, field)); | |||
} | |||
} | |||
} | |||
groupFields.Sort((a, b) => string.CompareOrdinal(a.Key, b.Key)); | |||
for (int i = 0; i < groupFields.Count; ++i) | |||
{ | |||
groupFields[i].Value.GetValue(null); | |||
#if DEBUG | |||
var group = (ExclusiveGroup) groupFields[i].Value.GetValue(null); | |||
groupNames[(uint) group] = groupFields[i].Key; | |||
#endif | |||
} | |||
} | |||
static Type[] GetTypesSafe(Assembly assembly) | |||
{ | |||
try | |||
{ | |||
Type[] types = assembly.GetTypes(); | |||
return types; | |||
} | |||
catch (ReflectionTypeLoadException e) | |||
{ | |||
return e.Types; | |||
} | |||
} | |||
#if DEBUG | |||
static string[] groupNames = new string[ushort.MaxValue]; | |||
#endif | |||
#endif |
@@ -1,61 +0,0 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
partial class EntitiesDB | |||
{ | |||
public void ExecuteOnAllEntities<T>(Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB> action) | |||
where T : struct, IEntityStruct | |||
{ | |||
var type = typeof(T); | |||
if (_groupsPerEntity.TryGetValue(new RefWrapper<Type>(type), out var dictionary)) | |||
{ | |||
foreach (var pair in dictionary) | |||
{ | |||
var entities = (pair.Value as TypeSafeDictionary<T>).GetValuesArray(out var innerCount); | |||
if (innerCount > 0) | |||
action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this); | |||
} | |||
} | |||
} | |||
public void ExecuteOnAllEntities | |||
<T, W>(W value, Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB, W> action) | |||
where T : struct, IEntityStruct | |||
{ | |||
var type = typeof(T); | |||
if (_groupsPerEntity.TryGetValue(new RefWrapper<Type>(type), out var dic)) | |||
{ | |||
foreach (var pair in dic) | |||
{ | |||
var entities = (pair.Value as TypeSafeDictionary<T>).GetValuesArray(out var innerCount); | |||
if (innerCount > 0) | |||
action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this, value); | |||
} | |||
} | |||
} | |||
public void ExecuteOnAllEntities | |||
<T, W>(ref W value, ExecuteOnAllEntitiesAction<T, W> action) | |||
where T : struct, IEntityStruct | |||
{ | |||
var type = typeof(T); | |||
if (_groupsPerEntity.TryGetValue(new RefWrapper<Type>(type), out var dic)) | |||
{ | |||
foreach (var pair in dic) | |||
{ | |||
var entities = (pair.Value as TypeSafeDictionary<T>).GetValuesArray(out var innerCount); | |||
if (innerCount > 0) | |||
action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this, ref value); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -1,48 +0,0 @@ | |||
using System; | |||
using Svelto.ECS.Serialization; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// Inherit from an ExtendibleEntityDescriptor to extend a base entity descriptor that can be used | |||
/// to swap and remove specialized entities from abstract engines | |||
/// </summary> | |||
/// <typeparam name="TType"></typeparam> | |||
public class ExtendibleEntityDescriptor<TType> : IEntityDescriptor where TType : IEntityDescriptor, new() | |||
{ | |||
static ExtendibleEntityDescriptor() | |||
{ | |||
if (typeof(ISerializableEntityDescriptor).IsAssignableFrom(typeof(TType))) | |||
throw new Exception( | |||
$"SerializableEntityDescriptors cannot be used as base entity descriptor: {typeof(TType)}"); | |||
} | |||
public ExtendibleEntityDescriptor(IEntityBuilder[] extraEntities) | |||
{ | |||
_dynamicDescriptor = new DynamicEntityDescriptor<TType>(extraEntities); | |||
} | |||
public ExtendibleEntityDescriptor() | |||
{ | |||
_dynamicDescriptor = new DynamicEntityDescriptor<TType>(true); | |||
} | |||
public ExtendibleEntityDescriptor<TType> ExtendWith<T>() where T : IEntityDescriptor, new() | |||
{ | |||
_dynamicDescriptor.ExtendWith<T>(); | |||
return this; | |||
} | |||
public ExtendibleEntityDescriptor<TType> ExtendWith(IEntityBuilder[] extraEntities) | |||
{ | |||
_dynamicDescriptor.ExtendWith(extraEntities); | |||
return this; | |||
} | |||
public IEntityBuilder[] entitiesToBuild => _dynamicDescriptor.entitiesToBuild; | |||
DynamicEntityDescriptor<TType> _dynamicDescriptor; | |||
} | |||
} |
@@ -1,24 +0,0 @@ | |||
#if UNITY_5 || UNITY_5_3_OR_NEWER | |||
using UnityEngine; | |||
namespace Svelto.ECS.Unity | |||
{ | |||
public abstract class GenericEntityDescriptorHolder<T>: | |||
MonoBehaviour , IEntityDescriptorHolder | |||
where T: IEntityDescriptor, new() | |||
{ | |||
public IEntityDescriptor GetDescriptor() | |||
{ | |||
return EntityDescriptorTemplate<T>.descriptor; | |||
} | |||
public string groupName => _groupName; | |||
public ushort id => _id; | |||
#pragma warning disable 649 | |||
[SerializeField] string _groupName; | |||
[SerializeField] ushort _id; | |||
#pragma warning restore 649 | |||
} | |||
} | |||
#endif |
@@ -1,99 +0,0 @@ | |||
#if UNITY_5 || UNITY_5_3_OR_NEWER | |||
using UnityEngine; | |||
namespace Svelto.ECS.Unity | |||
{ | |||
public static class SveltoGUIHelper | |||
{ | |||
public static T CreateFromPrefab<T>(ref uint startIndex, Transform contextHolder, IEntityFactory factory, | |||
ExclusiveGroup group, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holder = Create<T>(new EGID(startIndex++, group), contextHolder, factory); | |||
var childs = contextHolder.GetComponentsInChildren<IEntityDescriptorHolder>(true); | |||
foreach (var child in childs) | |||
{ | |||
if (child.GetType() != typeof(T)) | |||
{ | |||
var monoBehaviour = child as MonoBehaviour; | |||
var childImplementors = monoBehaviour.GetComponents<IImplementor>(); | |||
startIndex = InternalBuildAll( | |||
startIndex, | |||
child, | |||
factory, | |||
group, | |||
childImplementors, | |||
groupNamePostfix); | |||
} | |||
} | |||
return holder; | |||
} | |||
public static T Create<T>(EGID ID, Transform contextHolder, IEntityFactory factory) | |||
where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holder = contextHolder.GetComponentInChildren<T>(true); | |||
DBC.ECS.Check.Assert(holder != null, $"`{nameof(holder)}` is null! No component of type " + | |||
$"`{typeof(T)}` was found between its children."); | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
return holder; | |||
} | |||
public static EntityStructInitializer CreateWithEntity<T>(EGID ID, Transform contextHolder, | |||
IEntityFactory factory, out T holder) | |||
where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
holder = contextHolder.GetComponentInChildren<T>(true); | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
} | |||
public static uint CreateAll<T>(uint startIndex, ExclusiveGroup group, | |||
Transform contextHolder, IEntityFactory factory, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holders = contextHolder.GetComponentsInChildren<T>(true); | |||
foreach (var holder in holders) | |||
{ | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
startIndex = InternalBuildAll(startIndex, holder, factory, group, implementors, groupNamePostfix); | |||
} | |||
return startIndex; | |||
} | |||
static uint InternalBuildAll(uint startIndex, IEntityDescriptorHolder descriptorHolder, | |||
IEntityFactory factory, ExclusiveGroup group, IImplementor[] implementors, string groupNamePostfix) | |||
{ | |||
ExclusiveGroup.ExclusiveGroupStruct realGroup = group; | |||
if (string.IsNullOrEmpty(descriptorHolder.groupName) == false) | |||
{ | |||
realGroup = ExclusiveGroup.Search(!string.IsNullOrEmpty(groupNamePostfix) | |||
? $"{descriptorHolder.groupName}{groupNamePostfix}" | |||
: 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)); | |||
return startIndex; | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,66 +0,0 @@ | |||
#if UNITY_5 || UNITY_5_3_OR_NEWER | |||
using Object = UnityEngine.Object; | |||
using System; | |||
using System.Collections; | |||
using UnityEngine; | |||
namespace Svelto.ECS.Schedulers.Unity | |||
{ | |||
//The EntitySubmissionScheduler has been introduced to make the entity views submission logic platform independent | |||
//You can customize the scheduler if you wish | |||
public class UnityEntitySubmissionScheduler : IEntitySubmissionScheduler | |||
{ | |||
class Scheduler : MonoBehaviour | |||
{ | |||
public Scheduler() | |||
{ | |||
_coroutine = Coroutine(); | |||
} | |||
void Update() | |||
{ | |||
_coroutine.MoveNext(); | |||
} | |||
IEnumerator Coroutine() | |||
{ | |||
while (true) | |||
{ | |||
yield return _wait; | |||
onTick.Invoke(); | |||
} | |||
} | |||
readonly WaitForEndOfFrame _wait = new WaitForEndOfFrame(); | |||
readonly IEnumerator _coroutine; | |||
public EnginesRoot.EntitiesSubmitter onTick; | |||
} | |||
public UnityEntitySubmissionScheduler(string name = "ECSScheduler") { _name = name; } | |||
public void Dispose() | |||
{ | |||
Object.Destroy(_scheduler.gameObject); | |||
} | |||
public EnginesRoot.EntitiesSubmitter onTick | |||
{ | |||
set | |||
{ | |||
if (_scheduler == null) | |||
{ | |||
_scheduler = new GameObject(_name).AddComponent<Scheduler>(); | |||
GameObject.DontDestroyOnLoad(_scheduler.gameObject); | |||
} | |||
_scheduler.onTick = value; | |||
} | |||
} | |||
Scheduler _scheduler; | |||
readonly string _name; | |||
} | |||
} | |||
#endif |
@@ -1,104 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public abstract class GenericEntityDescriptor<T> : IEntityDescriptor where T : struct, IEntityStruct | |||
{ | |||
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 : struct, IEntityStruct where U : struct, IEntityStruct | |||
{ | |||
static readonly IEntityBuilder[] _entityBuilders; | |||
static GenericEntityDescriptor() | |||
{ | |||
_entityBuilders = new IEntityBuilder[] {new EntityBuilder<T>(), new EntityBuilder<U>()}; | |||
} | |||
public IEntityBuilder[] entitiesToBuild => _entityBuilders; | |||
} | |||
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[] | |||
{ | |||
new EntityBuilder<T>(), | |||
new EntityBuilder<U>(), | |||
new EntityBuilder<V>() | |||
}; | |||
} | |||
public IEntityBuilder[] entitiesToBuild => _entityBuilders; | |||
} | |||
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[] | |||
{ | |||
new EntityBuilder<T>(), | |||
new EntityBuilder<U>(), | |||
new EntityBuilder<V>(), | |||
new EntityBuilder<W>() | |||
}; | |||
} | |||
public IEntityBuilder[] entitiesToBuild => _entityBuilders; | |||
} | |||
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[] | |||
{ | |||
new EntityBuilder<T>(), | |||
new EntityBuilder<U>(), | |||
new EntityBuilder<V>(), | |||
new EntityBuilder<W>(), | |||
new EntityBuilder<X>() | |||
}; | |||
} | |||
public IEntityBuilder[] entitiesToBuild => _entityBuilders; | |||
} | |||
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[] | |||
{ | |||
new EntityBuilder<T>(), | |||
new EntityBuilder<U>(), | |||
new EntityBuilder<V>(), | |||
new EntityBuilder<W>(), | |||
new EntityBuilder<X>(), | |||
new EntityBuilder<Y>() | |||
}; | |||
} | |||
public IEntityBuilder[] entitiesToBuild => _entityBuilders; | |||
} | |||
} |
@@ -1,33 +0,0 @@ | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
class GenericEntityStreamConsumerFactory : IEntityStreamConsumerFactory | |||
{ | |||
public GenericEntityStreamConsumerFactory(EnginesRoot weakReference) | |||
{ | |||
_enginesRoot = new WeakReference<EnginesRoot>(weakReference); | |||
} | |||
public Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct | |||
{ | |||
return _enginesRoot.Target.GenerateConsumer<T>(name, capacity); | |||
} | |||
public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) where T : unmanaged, IEntityStruct | |||
{ | |||
return _enginesRoot.Target.GenerateConsumer<T>(group, name, capacity); | |||
} | |||
//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside | |||
//engines of other enginesRoot | |||
readonly WeakReference<EnginesRoot> _enginesRoot; | |||
} | |||
public interface IEntityStreamConsumerFactory | |||
{ | |||
Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct; | |||
Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) | |||
where T : unmanaged, IEntityStruct; | |||
} | |||
} |
@@ -1,6 +0,0 @@ | |||
namespace Svelto.ECS.Hybrid | |||
{ | |||
public interface IEntityViewStruct:IEntityStruct, INeedEGID | |||
{} | |||
} | |||
@@ -1,6 +0,0 @@ | |||
namespace Svelto.ECS.Unity | |||
{ | |||
public interface IImplementor | |||
{ | |||
} | |||
} |
@@ -1,17 +0,0 @@ | |||
namespace Svelto.ECS.Internal | |||
{ | |||
public interface IReactEngine: IEngine | |||
{} | |||
public interface IReactOnAddAndRemove : IReactEngine | |||
{} | |||
public interface IReactOnSwap : IReactEngine | |||
{} | |||
} | |||
namespace Svelto.ECS | |||
{ | |||
public interface IEngine | |||
{} | |||
} |
@@ -1,193 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS | |||
{ | |||
public delegate void ExecuteOnAllEntitiesAction<T, W>(T[] entities, ExclusiveGroup.ExclusiveGroupStruct group, | |||
uint count, IEntitiesDB db, ref W value); | |||
public interface IEntitiesDB | |||
{ | |||
/////////////////////////////////////////////////// | |||
// Query entities | |||
// ECS systems are meant to work on a set of Entities. These methods allow to iterate over entity | |||
// structs inside a given group or an array of groups | |||
/////////////////////////////////////////////////// | |||
/// <summary> | |||
/// Fast and raw return of entities buffer. | |||
/// </summary> | |||
/// <param name="groupStruct"></param> | |||
/// <param name="count"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
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; | |||
/// <summary> | |||
/// return entities that can be iterated through the EntityCollection iterator | |||
/// </summary> | |||
/// <param name="groupStruct"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
EntityCollection<T> QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) | |||
where T : struct, IEntityStruct; | |||
EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) | |||
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; | |||
EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) | |||
where T1 : struct, IEntityStruct | |||
where T2 : struct, IEntityStruct | |||
where T3 : struct, IEntityStruct; | |||
/// <summary> | |||
/// return entities found in multiple groups, that can be iterated through the EntityCollection iterator | |||
/// This method is useful to write abstracted engines | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
EntityCollections<T> QueryEntities<T>(ExclusiveGroup[] groups) where T : struct, IEntityStruct; | |||
EntityCollections<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup[] groups) | |||
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; | |||
/////////////////////////////////////////////////// | |||
// Query entities regardless the group | |||
// these methods are necessary to create abstracted engines. Engines that can iterate over entities regardless | |||
// the group | |||
/////////////////////////////////////////////////// | |||
/// <summary> | |||
/// Execute an action on ALL the entities regardless the group. This function doesn't guarantee cache | |||
/// friendliness even if just EntityStructs are used. Safety checks are in place, | |||
/// </summary> | |||
/// <param name="action"></param> | |||
/// <typeparam name="T"></typeparam> | |||
void ExecuteOnAllEntities<T>(Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB> action) | |||
where T : struct, IEntityStruct; | |||
/// <summary> | |||
/// same as above, but can pass some external data to avoid allocations | |||
/// </summary> | |||
/// <param name="value"></param> | |||
/// <param name="action"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <typeparam name="W"></typeparam> | |||
void ExecuteOnAllEntities<T, W>(ref W value, ExecuteOnAllEntitiesAction<T, W> action) | |||
where T : struct, IEntityStruct; | |||
void ExecuteOnAllEntities<T, W>(W value, Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB, W> action) | |||
where T : struct, IEntityStruct; | |||
/////////////////////////////////////////////////// | |||
// Query single entities | |||
// ECS systems are meant to work on a set of Entities. Working on a single entity is sometime necessary, hence | |||
// the following methods | |||
// However Because of the double hashing required to identify a specific entity, these function are slower than | |||
// other query methods when used multiple times! | |||
/////////////////////////////////////////////////// | |||
/// <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> | |||
/// return a specific entity by reference. | |||
/// </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> | |||
/// | |||
///QueryEntitiesAndIndex is useful to optimize cases when multiple entity structs from the same entity must | |||
/// be queried. This is the use case: | |||
/// | |||
///ref var ghostPosition = ref entitiesDB.QueryEntitiesAndIndex<PositionEntityStruct> | |||
/// (MockupRenderingGroups.GhostCubeID, out var index)[index]; | |||
///ref var ghostScaling = ref entitiesDB.QueryEntities<ScalingEntityStruct> | |||
/// (MockupRenderingGroups.GhostCubeID.groupID, out _)[index]; | |||
///ref var ghostRotation = ref entitiesDB.QueryEntities<RotationEntityStruct> | |||
/// (MockupRenderingGroups.GhostCubeID.groupID, out _)[index]; | |||
///ref var ghostResource = ref entitiesDB.QueryEntities<GFXPrefabEntityStruct> | |||
/// (MockupRenderingGroups.GhostCubeID.groupID, out _)[index]; | |||
/// | |||
/// </summary> | |||
/// <param name="entityGid"></param> | |||
/// <param name="index"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
T[] QueryEntitiesAndIndex<T>(EGID entityGid, out uint index) where T : struct, IEntityStruct; | |||
T[] QueryEntitiesAndIndex<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) | |||
where T : struct, IEntityStruct; | |||
/// <summary> | |||
/// Like QueryEntitiesAndIndex and only way to get an index only if exists | |||
/// </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> | |||
/// this method returns a mapped version of the entity array so that is possible to work on multiple entities | |||
/// inside the group through their EGID. This version skip a level of indirection so it's a bit faster than | |||
/// using QueryEntity multiple times (with different EGIDs). | |||
/// However mapping can be slow so it must be used for not performance critical paths | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId) | |||
where T : struct, IEntityStruct; | |||
bool TryQueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper) | |||
where T : struct, IEntityStruct; | |||
/////////////////////////////////////////////////// | |||
// Utility methods | |||
/////////////////////////////////////////////////// | |||
/// <summary> | |||
/// check if a specific entity exists | |||
/// </summary> | |||
/// <param name="egid"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
bool Exists<T>(EGID egid) where T : struct, IEntityStruct; | |||
bool Exists<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct; | |||
bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid); | |||
/// <summary> | |||
/// know if there is any entity struct in a specific group | |||
/// </summary> | |||
/// <param name="groupStruct"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
bool HasAny<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; | |||
/// <summary> | |||
/// Count the number of entity structs in a specific group | |||
/// </summary> | |||
/// <param name="groupStruct"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
uint Count<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; | |||
/// <summary> | |||
/// </summary> | |||
/// <param name="egid"></param> | |||
/// <typeparam name="T"></typeparam> | |||
void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityStruct; | |||
} | |||
} |
@@ -1,15 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IEntityBuilder | |||
{ | |||
void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID egid, | |||
IEnumerable<object> implementors); | |||
ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size); | |||
Type GetEntityType(); | |||
} | |||
} |
@@ -1,10 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public interface IEntityDescriptorHolder | |||
{ | |||
IEntityDescriptor GetDescriptor(); | |||
string groupName { get; } | |||
ushort id { get; } | |||
} | |||
} |
@@ -1,66 +0,0 @@ | |||
using System.Collections.Generic; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// Entities are always built in group. Where the group is not specificed, a special standard group is used | |||
/// ID can be reused within groups | |||
/// an EnginesRoot reference cannot be held by anything else than the Composition Root | |||
/// where it has been created. IEntityFactory and IEntityFunctions allow a weakreference | |||
/// of the EnginesRoot to be passed around. | |||
/// | |||
/// ExclusiveGroups must be used in your game like: | |||
/// static class GameExclusiveGroup | |||
///{ | |||
/// public static readonly ExclusiveGroups PlayerEntitiesGroup = new ExclusiveGroups(); | |||
///} | |||
/// | |||
/// </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="groupStructId"></param> | |||
/// <param name="size"></param> | |||
void PreallocateEntitySpace<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size) | |||
where T : IEntityDescriptor, new(); | |||
/// <summary> | |||
/// The EntityDescriptor doesn't need to be ever instantiated. It just describes the Entity | |||
/// itself in terms of EntityViews to build. The Implementors are passed to fill the | |||
/// references of the EntityViews components. Please read the articles on my blog | |||
/// to understand better the terminologies | |||
/// Using this function is like building a normal entity, but the entity views | |||
/// are grouped by groupID to be more efficiently processed inside engines and | |||
/// improve cache locality. Either class entityViews and struct entityViews can be | |||
/// grouped. | |||
/// </summary> | |||
/// <param name="entityID"></param> | |||
/// <param name="groupStructId"></param> | |||
/// <param name="ed"></param> | |||
/// <param name="implementors"></param> | |||
EntityStructInitializer BuildEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, | |||
IEnumerable<object> implementors = null) | |||
where T : IEntityDescriptor, new(); | |||
EntityStructInitializer BuildEntity<T>(EGID egid, IEnumerable<object> implementors = null) | |||
where T:IEntityDescriptor, new(); | |||
/// <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. | |||
/// </summary> | |||
/// <param name="entityID"></param> | |||
/// <param name="entityDescriptor"></param> | |||
/// <param name="implementors"></param> | |||
/// | |||
EntityStructInitializer BuildEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId, | |||
T descriptorEntity, IEnumerable<object> implementors = null) where T : IEntityDescriptor; | |||
EntityStructInitializer BuildEntity<T>(EGID egid, T entityDescriptor, IEnumerable<object> implementors = null) | |||
where T : IEntityDescriptor; | |||
} | |||
} |
@@ -1,20 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public interface IEntityFunctions | |||
{ | |||
//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>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); | |||
void RemoveEntity<T>(EGID entityegid) where T : IEntityDescriptor, new(); | |||
void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID); | |||
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 fromID, EGID toId) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID fromID, EGID toId, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new(); | |||
} | |||
} |
@@ -1,15 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
///<summary>EntityStruct MUST implement IEntityStruct</summary> | |||
public interface IEntityStruct | |||
{ | |||
} | |||
/// <summary> | |||
/// use INeedEGID on an IEntityStruct only if you need the EGID | |||
/// </summary> | |||
public interface INeedEGID | |||
{ | |||
EGID ID { get; set; } | |||
} | |||
} |
@@ -1,9 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public interface IQueryingEntitiesEngine : IEngine | |||
{ | |||
IEntitiesDB entitiesDB { set; } | |||
void Ready(); | |||
} | |||
} |
@@ -1,10 +0,0 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IReactOnAddAndRemove<T> : IReactOnAddAndRemove where T : IEntityStruct | |||
{ | |||
void Add(ref T entityView, EGID egid); | |||
void Remove(ref T entityView, EGID egid); | |||
} | |||
} |
@@ -1,12 +0,0 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IReactOnSwap<T> : IReactOnSwap where T : IEntityStruct | |||
{ | |||
void MovedTo(ref T entityView, ExclusiveGroup.ExclusiveGroupStruct previousGroup, EGID egid); | |||
#if SEEMS_UNNECESSARY | |||
void MovedFrom(ref T entityView, EGID egid); | |||
#endif | |||
} | |||
} |
@@ -1,22 +0,0 @@ | |||
The MIT License (MIT) | |||
Copyright (c) 2015 Sebastiano Mandalà | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. | |||
@@ -1,50 +0,0 @@ | |||
Svelto Entity Component System for Unity | |||
===================================== | |||
**Note: The alpha stage of Svelto 2.0 is almost completed, so if you are here to experiment with it, please use the current alpha branch** | |||
Real Entity-Component-System for c# and Unity (it can be adapted for other c# platforms too). Enables to write encapsulated, uncoupled, highly efficient, data oriented, cache friendly, multi-threaded, code without pain. | |||
you can find working examples to learn how to use the framework here: | |||
https://github.com/sebas77/Svelto-ECS-Example (unity) | |||
https://github.com/sebas77/Svelto.ECS.Vanilla.Example (.net core and standard) | |||
I advise to clone the example repositories separately from the framework one, both under the same Unity project Assets folder. | |||
relative article: | |||
http://www.sebaslab.com/svelto-ecs-2-0-almost-production-ready/ | |||
http://www.sebaslab.com/ecs-1-0/ | |||
If you want to know more about the theory and rationale behind this framework: | |||
http://www.sebaslab.com/ioc-container-for-unity3d-part-1/ | |||
http://www.sebaslab.com/ioc-container-for-unity3d-part-2/ | |||
http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-i-dependency-injection/ | |||
http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-ii-inversion-of-control/ | |||
http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-iii-entity-component-systems/ | |||
http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-iv-dependency-inversion-principle/ | |||
http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-v-drifting-away-from-ioc-containers/ | |||
new article on optimizations: | |||
http://www.sebaslab.com/svelto-ecs-svelto-tasks-to-write-data-oriented-cache-friendly-multi-threaded-code-in-unity/ | |||
Note: if you ever build something with Svelto.ECS that you can share with the community, please do and let me know. Other coders need more examples. | |||
Copyright (c) Sebastiano Mandalà | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -1,132 +0,0 @@ | |||
using System; | |||
using System.Collections; | |||
using System.Collections.Generic; | |||
namespace Svelto.ECS | |||
{ | |||
public class Steps | |||
{ | |||
internal readonly Dictionary<IEngine, To> _steps; | |||
public Steps(params Step[] values) | |||
{ | |||
_steps = new Dictionary<IEngine, To>(); | |||
for (var i = 0; i < values.Length; i++) | |||
_steps.Add(values[i].from, values[i].to); | |||
} | |||
} | |||
public class To | |||
{ | |||
public To(IStep engine) | |||
{ | |||
this.engine = engine; | |||
} | |||
public To(params IStep[] engines) | |||
{ | |||
this.engines = engines; | |||
} | |||
public IStep engine { get; set; } | |||
public IStep[] engines { get; set; } | |||
} | |||
public class To<C>:To, IEnumerable where C : struct, IConvertible | |||
{ | |||
internal readonly Dictionary<C, IStep<C>[]> _tos = new Dictionary<C, IStep<C>[]>(); | |||
public IEnumerator GetEnumerator() | |||
{ | |||
throw new NotImplementedException(); | |||
} | |||
public void Add(C condition, params IStep<C>[] engine) | |||
{ | |||
_tos[condition] = engine; | |||
} | |||
} | |||
public interface IStep | |||
{ | |||
void Step(EGID id); | |||
} | |||
public interface IStep<in C> where C:struct,IConvertible | |||
{ | |||
void Step(C condition, EGID id); | |||
} | |||
public struct Step | |||
{ | |||
public IEngine from { get; set; } | |||
public To to { get; set; } | |||
} | |||
/// <summary> | |||
/// The sequencer has just one goal: define a complex sequence of engines to call. The sequence is not | |||
/// just "sequential", but can create branches and loops. | |||
/// With the time, I figure out that the majority of the times this class is useful in the rare cases where | |||
/// order execution of the engine is necessary/ | |||
/// Using branching is even rarer, but still useful sometimes. | |||
/// I used loops only once. | |||
/// There is the chance that this class will become obsolete over the time, as by design engines should | |||
/// never rely on the order of execution | |||
/// Using this class to let engines from different EnginesRoot communicate will also become obsolete, as | |||
/// better solution will be implemented once I have the time | |||
/// Trying to work out how to initialize this structure can be painful. This is by design as this class must | |||
/// be initialized using the following pattern: | |||
/// instancedSequence.SetSequence( | |||
/// new Steps //list of steps | |||
/// ( | |||
/// new Step // first step | |||
/// { | |||
/// from = menuOptionEnablerEngine, //starting engine | |||
/// to = new To<ItemActionsPanelEnableCondition> //targets | |||
/// { | |||
/// { | |||
/// ItemActionsPanelEnableCondition.Enable, //condition 1 | |||
/// menuPanelEnablerEngine //targets for condition 1 | |||
/// }, | |||
/// { | |||
/// ItemActionsPanelEnableCondition.Disable,//condition 2 | |||
/// menuPanelEnablerEngine //targets for condition 2 | |||
/// } | |||
/// } | |||
/// }) | |||
/// ); | |||
/// </summary> | |||
public class Sequencer<S> where S: Sequencer<S>, new() | |||
{ | |||
public void SetSequence(Steps steps) | |||
{ | |||
_steps = steps; | |||
} | |||
public void Next<C>(IEngine engine, C condition, EGID id) where C:struct, IConvertible | |||
{ | |||
var branch = condition; | |||
var to = (_steps._steps[engine] as To<C>); | |||
var steps = to._tos[branch]; | |||
for (var i = 0; i < steps.Length; i++) | |||
steps[i].Step(condition, id); | |||
} | |||
public void Next(IEngine engine, EGID id) | |||
{ | |||
var to = _steps._steps[engine]; | |||
var steps = to.engines; | |||
if (steps != null && steps.Length > 1) | |||
for (var i = 0; i < steps.Length; i++) | |||
steps[i].Step(id); | |||
else | |||
to.engine.Step(id); | |||
} | |||
Steps _steps; | |||
} | |||
} |
@@ -1,43 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public class ComposedSerializer<T, X, Y> : ISerializer<T> | |||
where T : unmanaged, IEntityStruct | |||
where X : class, ISerializer<T>, new() | |||
where Y : class, ISerializer<T>, new() | |||
{ | |||
public ComposedSerializer() | |||
{ | |||
_serializers = new ISerializer<T>[2]; | |||
_serializers[0] = new X(); | |||
_serializers[1] = new Y(); | |||
} | |||
public bool Serialize(in T value, ISerializationData serializationData) | |||
{ | |||
foreach (ISerializer<T> s in _serializers) | |||
{ | |||
serializationData.data.ExpandBy(s.size); | |||
if (s.SerializeSafe(value, serializationData)) | |||
return true; | |||
} | |||
throw new Exception($"ComposedSerializer for {typeof(T)} did not serialize any data!"); | |||
} | |||
public bool Deserialize(ref T value, ISerializationData serializationData) | |||
{ | |||
foreach (ISerializer<T> s in _serializers) | |||
{ | |||
if (s.DeserializeSafe(ref value, serializationData)) | |||
return true; | |||
} | |||
throw new Exception($"ComposedSerializer for {typeof(T)} did not deserialize any data!"); | |||
} | |||
public uint size => 0; | |||
ISerializer<T>[] _serializers; | |||
} | |||
} |
@@ -1,43 +0,0 @@ | |||
using Svelto.Common; | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public class DefaultSerializer<T> : ISerializer<T> where T : unmanaged, IEntityStruct | |||
{ | |||
public static readonly uint SIZEOFT = SerializableEntityBuilder<T>.SIZE; | |||
public uint size => SIZEOFT; | |||
static DefaultSerializer() | |||
{ | |||
var _type = typeof(T); | |||
foreach (var field in _type.GetFields()) | |||
{ | |||
if (field.FieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) && field.IsPrivate == false) | |||
throw new ECSException("field cannot be serialised ".FastConcat(_type.FullName)); | |||
} | |||
if (_type.GetProperties().Length > (EntityBuilder<T>.HAS_EGID ? 1 : 0)) | |||
throw new ECSException("serializable entity struct must be property less ".FastConcat(_type.FullName)); | |||
} | |||
public bool Serialize(in T value, ISerializationData serializationData) | |||
{ | |||
DefaultSerializerUtils.CopyToByteArray(value, serializationData.data.ToArrayFast(), serializationData.dataPos); | |||
serializationData.dataPos += SIZEOFT; | |||
return true; | |||
} | |||
public bool Deserialize(ref T value, ISerializationData serializationData) | |||
{ | |||
value = DefaultSerializerUtils.CopyFromByteArray<T>(serializationData.data.ToArrayFast(), serializationData.dataPos); | |||
serializationData.dataPos += SIZEOFT; | |||
return true; | |||
} | |||
} | |||
} |
@@ -1,46 +0,0 @@ | |||
using System; | |||
using Svelto.ECS; | |||
public static class DefaultSerializerUtils | |||
{ | |||
public static unsafe void CopyToByteArray<T>(in T src, byte[] data, uint offsetDst) where T : unmanaged, IEntityStruct | |||
{ | |||
fixed (void* dstPtr = data) | |||
{ | |||
void* dstOffsetPtr; | |||
if (IntPtr.Size == sizeof(int)) | |||
{ | |||
dstOffsetPtr = (void*) (((IntPtr) dstPtr).ToInt32() + ((IntPtr) offsetDst).ToInt32()); | |||
} | |||
else | |||
{ | |||
dstOffsetPtr = (void*) (((IntPtr) dstPtr).ToInt64() + ((IntPtr) offsetDst).ToInt64()); | |||
} | |||
*(T*) dstOffsetPtr = src; | |||
} | |||
} | |||
public static unsafe T CopyFromByteArray<T>(byte[] data, uint offsetSrc) where T : unmanaged, IEntityStruct | |||
{ | |||
T dst = new T(); | |||
void* dstPtr = &dst; | |||
fixed (void* srcPtr = data) | |||
{ | |||
void* srcOffsetPtr; | |||
if (IntPtr.Size == sizeof(int)) | |||
{ | |||
srcOffsetPtr = (void*) (((IntPtr) srcPtr).ToInt32() + ((IntPtr) offsetSrc).ToInt32()); | |||
} | |||
else | |||
{ | |||
srcOffsetPtr = (void*) (((IntPtr) srcPtr).ToInt64() + ((IntPtr) offsetSrc).ToInt64()); | |||
} | |||
*(T*) dstPtr = *(T*) srcOffsetPtr; | |||
} | |||
return dst; | |||
} | |||
} |
@@ -1,28 +0,0 @@ | |||
using System.Collections.Generic; | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public class DefaultVersioningFactory<T> : IDeserializationFactory where T : IEntityDescriptor, new() | |||
{ | |||
public EntityStructInitializer BuildDeserializedEntity(EGID egid, | |||
ISerializationData serializationData, | |||
ISerializableEntityDescriptor entityDescriptor, | |||
SerializationType serializationType, | |||
IEntitySerialization entitySerialization) | |||
{ | |||
var initializer = _factory.BuildEntity<T>(egid, _implementors); | |||
entitySerialization.DeserializeEntityStructs(serializationData, entityDescriptor, ref initializer, serializationType); | |||
return initializer; | |||
} | |||
public DefaultVersioningFactory(IEntityFactory factory) { _factory = factory; } | |||
public DefaultVersioningFactory(IEntityFactory factory, IEnumerable<object> implementors) { _factory = factory; | |||
_implementors = implementors; | |||
} | |||
readonly IEntityFactory _factory; | |||
readonly IEnumerable<object> _implementors; | |||
} | |||
} |
@@ -1,8 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public class DoNotSerializeAttribute : Attribute | |||
{ | |||
} | |||
} |
@@ -1,17 +0,0 @@ | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public class DontSerialize<T> : ISerializer<T> where T : unmanaged, IEntityStruct | |||
{ | |||
public uint size => 0; | |||
public bool Serialize(in T value, ISerializationData serializationData) | |||
{ | |||
return false; | |||
} | |||
public bool Deserialize(ref T value, ISerializationData serializationData) | |||
{ | |||
return false; | |||
} | |||
} | |||
} |
@@ -1,198 +0,0 @@ | |||
using System; | |||
using Svelto.Common; | |||
using Svelto.ECS.Internal; | |||
using Svelto.ECS.Serialization; | |||
namespace Svelto.ECS | |||
{ | |||
//todo: this should not be at framework level | |||
public enum SerializationType | |||
{ | |||
Network, | |||
Storage, | |||
Length | |||
} | |||
public partial class EnginesRoot | |||
{ | |||
readonly bool _isDeserializationOnly; | |||
sealed class EntitySerialization : IEntitySerialization | |||
{ | |||
public void SerializeEntity(EGID egid, ISerializationData serializationData, | |||
SerializationType serializationType) | |||
{ | |||
var entitiesDb = _enginesRoot._entitiesDB; | |||
//needs to retrieve the meta data associated with the entity | |||
ref var serializableEntityStruct = ref entitiesDb.QueryEntity<SerializableEntityStruct>(egid); | |||
uint descriptorHash = serializableEntityStruct.descriptorHash; | |||
SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; | |||
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); | |||
var entityStructsToSerialise = entityDescriptor.entitiesToSerialize; | |||
var header = | |||
new SerializableEntityHeader(descriptorHash, egid, (byte) entityStructsToSerialise.Length); | |||
header.Copy(serializationData); | |||
for (int index = 0; index < entityStructsToSerialise.Length; index++) | |||
{ | |||
var entityBuilder = entityStructsToSerialise[index]; | |||
serializationData.BeginNextEntityStruct(); | |||
SerializeEntityStruct(egid, entityBuilder, serializationData, serializationType); | |||
} | |||
} | |||
public EntityStructInitializer DeserializeNewEntity(EGID egid, ISerializationData serializationData, | |||
SerializationType serializationType) | |||
{ | |||
//todo: SerializableEntityHeader may be needed to be customizable | |||
var serializableEntityHeader = new SerializableEntityHeader(serializationData); | |||
uint descriptorHash = serializableEntityHeader.descriptorHash; | |||
SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; | |||
var factory = serializationDescriptorMap.GetSerializationFactory(descriptorHash); | |||
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); | |||
//default factory | |||
if (factory == null) | |||
{ | |||
var initializer = _enginesRoot.BuildEntity(egid, | |||
_enginesRoot._isDeserializationOnly | |||
? entityDescriptor.entitiesToSerialize | |||
: entityDescriptor.entitiesToBuild); | |||
DeserializeEntityStructs(serializationData, entityDescriptor, ref initializer, serializationType); | |||
return initializer; | |||
} | |||
//custom factory | |||
return factory.BuildDeserializedEntity(egid, serializationData, entityDescriptor, serializationType, | |||
this); | |||
} | |||
public void DeserializeEntity(ISerializationData serializationData, SerializationType serializationType) | |||
{ | |||
var serializableEntityHeader = new SerializableEntityHeader(serializationData); | |||
EGID egid = serializableEntityHeader.egid; | |||
DeserializeEntityInternal(serializationData, egid, serializableEntityHeader, serializationType); | |||
} | |||
public void DeserializeEntity(EGID egid, ISerializationData serializationData, | |||
SerializationType serializationType) | |||
{ | |||
var serializableEntityHeader = new SerializableEntityHeader(serializationData); | |||
DeserializeEntityInternal(serializationData, egid, serializableEntityHeader, serializationType); | |||
} | |||
public void DeserializeEntityStructs(ISerializationData serializationData, | |||
ISerializableEntityDescriptor entityDescriptor, | |||
ref EntityStructInitializer initializer, SerializationType serializationType) | |||
{ | |||
foreach (var serializableEntityBuilder in entityDescriptor.entitiesToSerialize) | |||
{ | |||
serializationData.BeginNextEntityStruct(); | |||
serializableEntityBuilder.Deserialize(serializationData, initializer, serializationType); | |||
} | |||
} | |||
public void DeserializeEntityToSwap(EGID localEgid, EGID toEgid) | |||
{ | |||
EntitiesDB entitiesDb = _enginesRoot._entitiesDB; | |||
ref var serializableEntityStruct = ref entitiesDb.QueryEntity<SerializableEntityStruct>(localEgid); | |||
SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; | |||
uint descriptorHash = serializableEntityStruct.descriptorHash; | |||
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); | |||
var entitySubmitOperation = new EntitySubmitOperation( | |||
EntitySubmitOperationType.Swap, | |||
localEgid, | |||
toEgid, | |||
entityDescriptor.entitiesToBuild); | |||
_enginesRoot.CheckRemoveEntityID(localEgid); | |||
_enginesRoot.CheckAddEntityID(toEgid); | |||
_enginesRoot.QueueEntitySubmitOperation(entitySubmitOperation); | |||
} | |||
public void DeserializeEntityToDelete(EGID egid) | |||
{ | |||
EntitiesDB entitiesDB = _enginesRoot._entitiesDB; | |||
ref var serializableEntityStruct = ref entitiesDB.QueryEntity<SerializableEntityStruct>(egid); | |||
uint descriptorHash = serializableEntityStruct.descriptorHash; | |||
SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; | |||
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); | |||
_enginesRoot.CheckRemoveEntityID(egid); | |||
var entitySubmitOperation = new EntitySubmitOperation( | |||
EntitySubmitOperationType.Remove, | |||
egid, | |||
egid, | |||
entityDescriptor.entitiesToBuild); | |||
_enginesRoot.QueueEntitySubmitOperation(entitySubmitOperation); | |||
} | |||
public void RegisterSerializationFactory<T>(IDeserializationFactory deserializationFactory) | |||
where T : ISerializableEntityDescriptor, new() | |||
{ | |||
SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; | |||
serializationDescriptorMap.RegisterSerializationFactory<T>(deserializationFactory); | |||
} | |||
internal EntitySerialization(EnginesRoot enginesRoot) | |||
{ | |||
_enginesRoot = enginesRoot; | |||
} | |||
void SerializeEntityStruct(EGID entityGID, ISerializableEntityBuilder entityBuilder, | |||
ISerializationData serializationData, SerializationType serializationType) | |||
{ | |||
uint groupId = entityGID.groupID; | |||
Type entityType = entityBuilder.GetEntityType(); | |||
if (!_enginesRoot._entitiesDB.UnsafeQueryEntityDictionary(groupId, entityType, | |||
out var safeDictionary)) | |||
{ | |||
throw new Exception("Entity Serialization failed"); | |||
} | |||
entityBuilder.Serialize(entityGID.entityID, safeDictionary, serializationData, serializationType); | |||
} | |||
void DeserializeEntityInternal(ISerializationData serializationData, EGID egid, | |||
SerializableEntityHeader serializableEntityHeader, SerializationType serializationType) | |||
{ | |||
SerializationDescriptorMap descriptorMap = _enginesRoot.serializationDescriptorMap; | |||
var entityDescriptor = descriptorMap.GetDescriptorFromHash(serializableEntityHeader.descriptorHash); | |||
foreach (var serializableEntityBuilder in entityDescriptor.entitiesToSerialize) | |||
{ | |||
_enginesRoot._entitiesDB.UnsafeQueryEntityDictionary(egid.groupID, | |||
serializableEntityBuilder.GetEntityType(), out var safeDictionary); | |||
serializationData.BeginNextEntityStruct(); | |||
serializableEntityBuilder.Deserialize(egid.entityID, safeDictionary, serializationData, | |||
serializationType); | |||
} | |||
} | |||
readonly EnginesRoot _enginesRoot; | |||
} | |||
public IEntitySerialization GenerateEntitySerializer() | |||
{ | |||
return new EntitySerialization(this); | |||
} | |||
} | |||
} |
@@ -1,76 +0,0 @@ | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
struct SerializableEntityHeader | |||
{ | |||
public readonly uint descriptorHash; | |||
public readonly byte entityStructsCount; | |||
const uint SIZE = 4 + 4 + 4 + 1; | |||
internal SerializableEntityHeader(uint descriptorHash_, EGID egid_, byte entityStructsCount_) | |||
{ | |||
entityStructsCount = entityStructsCount_; | |||
descriptorHash = descriptorHash_; | |||
egid = egid_; | |||
} | |||
internal SerializableEntityHeader(ISerializationData serializationData) | |||
{ | |||
descriptorHash = (uint) | |||
(serializationData.data[serializationData.dataPos++] | |||
| serializationData.data[serializationData.dataPos++] << 8 | |||
| serializationData.data[serializationData.dataPos++] << 16 | |||
| serializationData.data[serializationData.dataPos++] << 24); | |||
uint entityID = (uint) | |||
(serializationData.data[serializationData.dataPos++] | |||
| serializationData.data[serializationData.dataPos++] << 8 | |||
| serializationData.data[serializationData.dataPos++] << 16 | |||
| serializationData.data[serializationData.dataPos++] << 24); | |||
uint groupID = (uint) | |||
(serializationData.data[serializationData.dataPos++] | |||
| serializationData.data[serializationData.dataPos++] << 8 | |||
| serializationData.data[serializationData.dataPos++] << 16 | |||
| serializationData.data[serializationData.dataPos++] << 24); | |||
entityStructsCount = serializationData.data[serializationData.dataPos++]; | |||
egid = new EGID(entityID, new ExclusiveGroup.ExclusiveGroupStruct(groupID)); | |||
} | |||
internal void Copy(ISerializationData serializationData) | |||
{ | |||
serializationData.data.ExpandBy(SIZE); | |||
// Splitting the descriptorHash_ (uint, 32 bit) into four bytes. | |||
serializationData.data[serializationData.dataPos++] = (byte) (descriptorHash & 0xff); | |||
serializationData.data[serializationData.dataPos++] = (byte) ((descriptorHash >> 8) & 0xff); | |||
serializationData.data[serializationData.dataPos++] = (byte) ((descriptorHash >> 16) & 0xff); | |||
serializationData.data[serializationData.dataPos++] = (byte) ((descriptorHash >> 24) & 0xff); | |||
// Splitting the entityID (uint, 32 bit) into four bytes. | |||
uint entityID = egid.entityID; | |||
serializationData.data[serializationData.dataPos++] = (byte) (entityID & 0xff); | |||
serializationData.data[serializationData.dataPos++] = (byte) ((entityID >> 8) & 0xff); | |||
serializationData.data[serializationData.dataPos++] = (byte) ((entityID >> 16) & 0xff); | |||
serializationData.data[serializationData.dataPos++] = (byte) ((entityID >> 24) & 0xff); | |||
// Splitting the groupID (uint, 32 bit) into four bytes. | |||
uint groupID = egid.groupID; | |||
serializationData.data[serializationData.dataPos++] = (byte) (groupID & 0xff); | |||
serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 8) & 0xff); | |||
serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 16) & 0xff); | |||
serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 24) & 0xff); | |||
serializationData.data[serializationData.dataPos++] = entityStructsCount; | |||
} | |||
internal readonly EGID egid; //this can't be used safely! | |||
} | |||
} | |||
} |
@@ -1,105 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Reflection; | |||
using Svelto.ECS.Serialization; | |||
namespace Svelto.ECS | |||
{ | |||
partial class EnginesRoot | |||
{ | |||
sealed class SerializationDescriptorMap | |||
{ | |||
/// <summary> | |||
/// Here we want to register all the EntityDescriptors that need to be serialized for network play. | |||
/// | |||
/// Remember! This needs to in sync across different clients and server as the values are serialized across | |||
/// the network also want this to not change so we can save to a DB | |||
/// </summary> | |||
internal SerializationDescriptorMap() | |||
{ | |||
_descriptors = new Dictionary<uint, ISerializableEntityDescriptor>(); | |||
_factories = new Dictionary<uint, IDeserializationFactory>(); | |||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); | |||
foreach (Assembly assembly in assemblies) | |||
{ | |||
foreach (Type type in GetTypesSafe(assembly)) | |||
{ | |||
if (type != null && type.IsClass && type.GetConstructor(Type.EmptyTypes) != null && | |||
typeof(ISerializableEntityDescriptor).IsAssignableFrom(type)) | |||
{ | |||
var descriptor = Activator.CreateInstance(type) as ISerializableEntityDescriptor; | |||
RegisterEntityDescriptor(descriptor); | |||
} | |||
} | |||
} | |||
} | |||
static IEnumerable<Type> GetTypesSafe(Assembly assembly) | |||
{ | |||
try | |||
{ | |||
Type[] types = assembly.GetTypes(); | |||
return types; | |||
} | |||
catch (ReflectionTypeLoadException e) | |||
{ | |||
return e.Types; | |||
} | |||
} | |||
void RegisterEntityDescriptor(ISerializableEntityDescriptor descriptor) | |||
{ | |||
if (descriptor == null) | |||
{ | |||
return; | |||
} | |||
uint descriptorHash = descriptor.hash; | |||
#if DEBUG && !PROFILER | |||
if (_descriptors.ContainsKey(descriptorHash)) | |||
{ | |||
throw new Exception($"Hash Collision of '{descriptor.GetType()}' against " + | |||
$"'{_descriptors[descriptorHash]} ::: {descriptorHash}'"); | |||
} | |||
#endif | |||
_descriptors[descriptorHash] = descriptor; | |||
} | |||
public ISerializableEntityDescriptor GetDescriptorFromHash(uint descriptorID) | |||
{ | |||
#if DEBUG && !PROFILER | |||
DBC.ECS.Check.Require(_descriptors.ContainsKey(descriptorID), | |||
$"Could not find descriptor with ID '{descriptorID}'!"); | |||
#endif | |||
return _descriptors[descriptorID]; | |||
} | |||
public IDeserializationFactory GetSerializationFactory(uint descriptorID) | |||
{ | |||
return _factories.TryGetValue(descriptorID, out var factory) ? factory : null; | |||
} | |||
public void RegisterSerializationFactory<Descriptor>(IDeserializationFactory deserializationFactory) | |||
where Descriptor : ISerializableEntityDescriptor, new() | |||
{ | |||
_factories.Add(SerializationEntityDescriptorTemplate<Descriptor>.hash, deserializationFactory); | |||
} | |||
readonly Dictionary<uint, ISerializableEntityDescriptor> _descriptors; | |||
readonly Dictionary<uint, IDeserializationFactory> _factories; | |||
} | |||
/// <summary> | |||
/// The map of serializable entity hashes to the serializable entity builders (to know the entity structs | |||
/// to serialize) | |||
/// </summary> | |||
SerializationDescriptorMap serializationDescriptorMap { get; } = new SerializationDescriptorMap(); | |||
} | |||
} |
@@ -1,15 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
[AttributeUsage(AttributeTargets.Class)] | |||
public class HashNameAttribute:Attribute | |||
{ | |||
public HashNameAttribute(string name) | |||
{ | |||
_name = name; | |||
} | |||
internal string _name; | |||
} | |||
} |
@@ -1,9 +0,0 @@ | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public interface IDeserializationFactory | |||
{ | |||
EntityStructInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, | |||
ISerializableEntityDescriptor entityDescriptor, SerializationType serializationType, | |||
IEntitySerialization entitySerialization); | |||
} | |||
} |
@@ -1,69 +0,0 @@ | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public interface IEntitySerialization | |||
{ | |||
/// <summary> | |||
/// Fill the serializationData of the entitiesToSerialize of this descriptor | |||
/// </summary> | |||
/// <param name="egid"></param> | |||
/// <param name="serializedData"></param> | |||
/// <param name="serializationType"></param> | |||
/// <returns>Size in bytes of the newly instantiated entity</returns> | |||
void SerializeEntity(EGID egid, ISerializationData serializationData, SerializationType serializationType); | |||
/// <summary> | |||
/// Deserialize a serializationData and copy directly onto the appropriate entities | |||
/// </summary> | |||
/// <param name="data"></param> | |||
/// <param name="dataPos"></param> | |||
/// <param name="serializationType"></param> | |||
void DeserializeEntity(ISerializationData serializationData, SerializationType serializationType); | |||
/// <summary> | |||
/// Deserialize a serializationData and copy directly onto the appropriate entities with explicit EGID | |||
/// </summary> | |||
/// <param name="egid"></param> | |||
/// <param name="data"></param> | |||
/// <param name="dataPos"></param> | |||
/// <param name="serializationType"></param> | |||
void DeserializeEntity(EGID egid, ISerializationData serializationData, SerializationType serializationType); | |||
/// <summary> | |||
/// Deserialize a serializationData and copy directly to an previously created EntityStructInitializer | |||
/// </summary> | |||
/// <param name="serializationData"></param> | |||
/// <param name="entityDescriptor"></param> | |||
/// <param name="initializer"></param> | |||
/// <param name="serializationType"></param> | |||
void DeserializeEntityStructs(ISerializationData serializationData, | |||
ISerializableEntityDescriptor entityDescriptor, | |||
ref EntityStructInitializer initializer, SerializationType serializationType); | |||
/// <summary> | |||
/// Contrary to the other Deserialize methods that assume that the entity exists, this method is used to deserialise | |||
/// a new Entity | |||
/// </summary> | |||
/// <param name="egid"></param> | |||
/// <param name="serializationData"></param> | |||
/// <param name="serializationType"></param> | |||
/// <returns></returns> | |||
EntityStructInitializer DeserializeNewEntity(EGID egid, ISerializationData serializationData, | |||
SerializationType serializationType); | |||
/// <summary> | |||
/// Special Entity Swap method that works without knowing the EntityDescriptor to swap | |||
/// </summary> | |||
/// <param name="localEgid"></param> | |||
/// <param name="toEgid"></param> | |||
void DeserializeEntityToSwap(EGID localEgid, EGID toEgid); | |||
/// <summary> | |||
/// Special Entity delete method that works without knowing the EntityDescriptor to delete | |||
/// </summary> | |||
/// <param name="egid"></param> | |||
void DeserializeEntityToDelete(EGID egid); | |||
void RegisterSerializationFactory<T>(IDeserializationFactory deserializationFactory) | |||
where T : ISerializableEntityDescriptor, new(); | |||
} | |||
} |
@@ -1,15 +0,0 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public interface ISerializableEntityBuilder : IEntityBuilder | |||
{ | |||
void Serialize(uint id, ITypeSafeDictionary dictionary, ISerializationData serializationData, SerializationType serializationType); | |||
void Deserialize(uint id, ITypeSafeDictionary dictionary, ISerializationData serializationData, SerializationType serializationType); | |||
void Deserialize(ISerializationData serializationData, in EntityStructInitializer initializer, SerializationType serializationType); | |||
void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer, in EntityStructInitializer destinationInitializer, SerializationType serializationType); | |||
} | |||
} |
@@ -1,10 +0,0 @@ | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public interface ISerializableEntityDescriptor : IEntityDescriptor | |||
{ | |||
uint hash { get; } | |||
ISerializableEntityBuilder[] entitiesToSerialize { get; } | |||
void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer, in EntityStructInitializer destinationInitializer, SerializationType serializationType); | |||
} | |||
} |
@@ -1,14 +0,0 @@ | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public interface ISerializationData | |||
{ | |||
uint dataPos { get; set; } | |||
FasterList<byte> data { get; } | |||
void ReuseAsNew(); | |||
void Reset(); | |||
void BeginNextEntityStruct(); | |||
} | |||
} |
@@ -1,55 +0,0 @@ | |||
#if DEBUG && !PROFILER | |||
using System; | |||
#endif | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public interface ISerializer<T> | |||
where T : unmanaged, IEntityStruct | |||
{ | |||
bool Serialize(in T value, ISerializationData serializationData); | |||
bool Deserialize(ref T value, ISerializationData serializationData); | |||
uint size { get; } | |||
} | |||
public static class SerializerExt | |||
{ | |||
public static bool SerializeSafe<T>(this ISerializer<T> serializer, in T value, ISerializationData serializationData) | |||
where T : unmanaged, IEntityStruct | |||
{ | |||
#if DEBUG && !PROFILER | |||
uint posBefore = serializationData.dataPos; | |||
#endif | |||
bool res = serializer.Serialize(value, serializationData); | |||
#if DEBUG && !PROFILER | |||
// size == 0 is a special case when we don't know the size in advance | |||
if (serializer.size != 0 && serializationData.dataPos != posBefore + serializer.size) | |||
{ | |||
throw new IndexOutOfRangeException( | |||
$"Size mismatch when serializing {typeof(T).FullName} using {serializer.GetType().FullName}, " + | |||
$"expected offset {posBefore + serializer.size}, got {serializationData.dataPos}"); | |||
} | |||
#endif | |||
return res; | |||
} | |||
public static bool DeserializeSafe<T>(this ISerializer<T> serializer, ref T value, ISerializationData serializationData) | |||
where T : unmanaged, IEntityStruct | |||
{ | |||
#if DEBUG && !PROFILER | |||
uint posBefore = serializationData.dataPos; | |||
#endif | |||
bool res = serializer.Deserialize(ref value, serializationData); | |||
#if DEBUG && !PROFILER | |||
if (serializer.size != 0 && serializationData.dataPos != posBefore + serializer.size) | |||
{ | |||
throw new IndexOutOfRangeException( | |||
$"Size mismatch when deserializing {typeof(T).FullName} using {serializer.GetType().FullName}, " + | |||
$"expected offset {posBefore + serializer.size}, got {serializationData.dataPos}"); | |||
} | |||
#endif | |||
return res; | |||
} | |||
} | |||
} |
@@ -1,89 +0,0 @@ | |||
using System; | |||
using System.Reflection; | |||
using System.Runtime.InteropServices; | |||
using Svelto.DataStructures; | |||
using Attribute = System.Attribute; | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
[AttributeUsage(AttributeTargets.Field)] | |||
public class PartialSerializerFieldAttribute : Attribute | |||
{} | |||
public class PartialSerializer<T> : ISerializer<T> | |||
where T : unmanaged, IEntityStruct | |||
{ | |||
static PartialSerializer() | |||
{ | |||
Type myType = typeof(T); | |||
FieldInfo[] myMembers = myType.GetFields(); | |||
for (int i = 0; i < myMembers.Length; i++) | |||
{ | |||
Object[] myAttributes = myMembers[i].GetCustomAttributes(true); | |||
for (int j = 0; j < myAttributes.Length; j++) | |||
{ | |||
if (myAttributes[j] is PartialSerializerFieldAttribute) | |||
{ | |||
if (myMembers[i].FieldType == typeof(EGID)) | |||
throw new ECSException("EGID fields cannot be serialised ".FastConcat(myType.FullName)); | |||
var offset = Marshal.OffsetOf<T>(myMembers[i].Name); | |||
var sizeOf = (uint)Marshal.SizeOf(myMembers[i].FieldType); | |||
offsets.Add(((uint) offset.ToInt32(), sizeOf)); | |||
totalSize += sizeOf; | |||
} | |||
} | |||
} | |||
if (myType.GetProperties().Length > (EntityBuilder<T>.HAS_EGID ? 1 : 0)) | |||
throw new ECSException("serializable entity struct must be property less ".FastConcat(myType.FullName)); | |||
} | |||
public bool Serialize(in T value, ISerializationData serializationData) | |||
{ | |||
unsafe | |||
{ | |||
fixed (byte* dataptr = serializationData.data.ToArrayFast()) | |||
{ | |||
var entityStruct = value; | |||
foreach ((uint offset, uint size) offset in offsets) | |||
{ | |||
byte* srcPtr = (byte*) &entityStruct + offset.offset; | |||
//todo move to Unsafe Copy when available as it is faster | |||
Buffer.MemoryCopy(srcPtr, dataptr + serializationData.dataPos, | |||
serializationData.data.Count - serializationData.dataPos, offset.size); | |||
serializationData.dataPos += offset.size; | |||
} | |||
} | |||
} | |||
return true; | |||
} | |||
public bool Deserialize(ref T value, ISerializationData serializationData) | |||
{ | |||
unsafe | |||
{ | |||
T tempValue = value; //todo: temporary solution I want to get rid of this copy | |||
fixed (byte* dataptr = serializationData.data.ToArrayFast()) | |||
foreach ((uint offset, uint size) offset in offsets) | |||
{ | |||
byte* dstPtr = (byte*) &tempValue + offset.offset; | |||
//todo move to Unsafe Copy when available as it is faster | |||
Buffer.MemoryCopy(dataptr + serializationData.dataPos, dstPtr, offset.size, offset.size); | |||
serializationData.dataPos += offset.size; | |||
} | |||
value = tempValue; //todo: temporary solution I want to get rid of this copy | |||
} | |||
return true; | |||
} | |||
public uint size => totalSize; | |||
static readonly FasterList<(uint, uint)> offsets = new FasterList<(uint, uint)>(); | |||
static readonly uint totalSize; | |||
} | |||
} |
@@ -1,95 +0,0 @@ | |||
using System; | |||
using Svelto.Common; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public class SerializableEntityBuilder<T> : EntityBuilder<T>, ISerializableEntityBuilder | |||
where T : unmanaged, IEntityStruct | |||
{ | |||
public static readonly uint SIZE = UnsafeUtils.SizeOf<T>(); | |||
static SerializableEntityBuilder() | |||
{} | |||
public SerializableEntityBuilder() | |||
{ | |||
_serializers = new ISerializer<T>[(int) SerializationType.Length]; | |||
for (int i = 0; i < (int) SerializationType.Length; i++) | |||
{ | |||
_serializers[i] = new DefaultSerializer<T>(); | |||
} | |||
} | |||
public SerializableEntityBuilder(params ValueTuple<SerializationType, ISerializer<T>>[] serializers) | |||
{ | |||
_serializers = new ISerializer<T>[(int) SerializationType.Length]; | |||
for (int i = 0; i < serializers.Length; i++) | |||
{ | |||
ref (SerializationType, ISerializer<T>) s = ref serializers[i]; | |||
_serializers[(int) s.Item1] = s.Item2; | |||
} | |||
// Just in case the above are the same type | |||
for (int i = 0; i < (int) SerializationType.Length; i++) | |||
{ | |||
if (_serializers[i] == null) _serializers[i] = new DontSerialize<T>(); | |||
} | |||
} | |||
public void Serialize(uint entityID, ITypeSafeDictionary dictionary, | |||
ISerializationData serializationData, SerializationType serializationType) | |||
{ | |||
ISerializer<T> serializer = _serializers[(int)serializationType]; | |||
var safeDictionary = (TypeSafeDictionary<T>) dictionary; | |||
if (safeDictionary.TryFindIndex(entityID, out uint index) == false) | |||
{ | |||
throw new ECSException("Entity Serialization failed"); | |||
} | |||
T[] values = safeDictionary.GetValuesArray(out _); | |||
ref T val = ref values[index]; | |||
serializationData.dataPos = (uint) serializationData.data.Count; | |||
serializationData.data.ExpandBy(serializer.size); | |||
serializer.SerializeSafe(val, serializationData); | |||
} | |||
public void Deserialize(uint entityID, ITypeSafeDictionary dictionary, | |||
ISerializationData serializationData, SerializationType serializationType) | |||
{ | |||
ISerializer<T> serializer = _serializers[(int) serializationType]; | |||
// Handle the case when an entity struct is gone | |||
var safeDictionary = (TypeSafeDictionary<T>) dictionary; | |||
if (safeDictionary.TryFindIndex(entityID, out uint index) == false) | |||
{ | |||
throw new ECSException("Entity Deserialization failed"); | |||
} | |||
T[] values = safeDictionary.GetValuesArray(out _); | |||
ref T val = ref values[index]; | |||
serializer.DeserializeSafe(ref val, serializationData); | |||
} | |||
public void Deserialize(ISerializationData serializationData | |||
, in EntityStructInitializer initializer, SerializationType serializationType) | |||
{ | |||
ISerializer<T> serializer = _serializers[(int) serializationType]; | |||
serializer.DeserializeSafe(ref initializer.GetOrCreate<T>(), serializationData); | |||
} | |||
public void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer, | |||
in EntityStructInitializer destinationInitializer, SerializationType serializationType) | |||
{ | |||
if ((_serializers[(int) serializationType] is DontSerialize<T>) == false) | |||
destinationInitializer.CopyFrom(sourceInitializer.Get<T>()); | |||
} | |||
readonly ISerializer<T>[] _serializers; | |||
} | |||
} |
@@ -1,122 +0,0 @@ | |||
using System; | |||
using System.Reflection; | |||
using System.Text; | |||
using Svelto.DataStructures; | |||
using Svelto.Utilities; | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public static class DesignatedHash | |||
{ | |||
public static readonly Func<byte[], uint> Hash = Murmur3.MurmurHash3_x86_32; | |||
} | |||
public abstract class SerializableEntityDescriptor<TType> : ISerializableEntityDescriptor | |||
where TType : IEntityDescriptor, new() | |||
{ | |||
static SerializableEntityDescriptor() | |||
{ | |||
IEntityBuilder[] defaultEntities = EntityDescriptorTemplate<TType>.descriptor.entitiesToBuild; | |||
var hashNameAttribute = _type.GetCustomAttribute<HashNameAttribute>(); | |||
if (hashNameAttribute == null) | |||
{ | |||
throw new Exception("HashName attribute not found on the serializable type ".FastConcat(_type.FullName)); | |||
} | |||
_hash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(hashNameAttribute._name)); | |||
var (index, dynamicIndex) = SetupSpecialEntityStruct(defaultEntities, out _entitiesToBuild); | |||
if (index == -1) | |||
{ | |||
index = _entitiesToBuild.Length - 1; | |||
} | |||
// Stores the hash of this EntityDescriptor | |||
_entitiesToBuild[index] = new EntityBuilder<SerializableEntityStruct> | |||
( | |||
new SerializableEntityStruct | |||
{ | |||
descriptorHash = _hash | |||
} | |||
); | |||
// If the current serializable is an ExtendibleDescriptor, I have to update it. | |||
if (dynamicIndex != -1) | |||
{ | |||
_entitiesToBuild[dynamicIndex] = new EntityBuilder<EntityStructInfoView> | |||
( | |||
new EntityStructInfoView | |||
{ | |||
entitiesToBuild = _entitiesToBuild | |||
} | |||
); | |||
} | |||
///// | |||
var entitiesToSerialize = new FasterList<ISerializableEntityBuilder>(); | |||
_entitiesToSerializeMap = new FasterDictionary<RefWrapper<Type>, ISerializableEntityBuilder>(); | |||
foreach (IEntityBuilder e in defaultEntities) | |||
{ | |||
if (e is ISerializableEntityBuilder serializableEntityBuilder) | |||
{ | |||
var entityType = serializableEntityBuilder.GetEntityType(); | |||
_entitiesToSerializeMap[new RefWrapper<Type>(entityType)] = serializableEntityBuilder; | |||
entitiesToSerialize.Add(serializableEntityBuilder); | |||
} | |||
} | |||
_entitiesToSerialize = entitiesToSerialize.ToArray(); | |||
} | |||
static (int indexSerial, int indexDynamic) SetupSpecialEntityStruct(IEntityBuilder[] defaultEntities, | |||
out IEntityBuilder[] entitiesToBuild) | |||
{ | |||
int length = defaultEntities.Length; | |||
int newLenght = length + 1; | |||
int indexSerial = -1; | |||
int indexDynamic = -1; | |||
for (var i = 0; i < length; ++i) | |||
{ | |||
if (defaultEntities[i].GetEntityType() == _serializableStructType) | |||
{ | |||
indexSerial = i; | |||
--newLenght; | |||
} | |||
if (defaultEntities[i].GetEntityType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) | |||
{ | |||
indexDynamic = i; | |||
} | |||
} | |||
entitiesToBuild = new IEntityBuilder[newLenght]; | |||
Array.Copy(defaultEntities, 0, entitiesToBuild, 0, length); | |||
return (indexSerial, indexDynamic); | |||
} | |||
public void CopySerializedEntityStructs(in EntityStructInitializer sourceInitializer, in EntityStructInitializer destinationInitializer, SerializationType serializationType) | |||
{ | |||
foreach (ISerializableEntityBuilder e in entitiesToSerialize) | |||
{ | |||
e.CopySerializedEntityStructs(sourceInitializer, destinationInitializer, serializationType); | |||
} | |||
} | |||
public IEntityBuilder[] entitiesToBuild => _entitiesToBuild; | |||
public uint hash => _hash; | |||
public ISerializableEntityBuilder[] entitiesToSerialize => _entitiesToSerialize; | |||
static readonly IEntityBuilder[] _entitiesToBuild; | |||
static readonly FasterDictionary<RefWrapper<Type>, ISerializableEntityBuilder> _entitiesToSerializeMap; | |||
static readonly ISerializableEntityBuilder[] _entitiesToSerialize; | |||
static readonly uint _hash; | |||
static readonly Type _serializableStructType = typeof(SerializableEntityStruct); | |||
static readonly Type _type = typeof(TType); | |||
} | |||
} |
@@ -1,9 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
internal struct SerializableEntityStruct : IEntityStruct, INeedEGID | |||
{ | |||
public uint descriptorHash; | |||
public EGID ID { get; set; } | |||
} | |||
} |
@@ -1,16 +0,0 @@ | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public static class SerializationEntityDescriptorTemplate<TType> where TType : ISerializableEntityDescriptor, new() | |||
{ | |||
static SerializationEntityDescriptorTemplate() | |||
{ | |||
var serializableEntityDescriptor = new TType(); | |||
hash = serializableEntityDescriptor.hash; | |||
entityDescriptor = (ISerializableEntityDescriptor) EntityDescriptorTemplate<TType>.descriptor; | |||
} | |||
public static uint hash { get; } | |||
public static ISerializableEntityDescriptor entityDescriptor { get; } | |||
} | |||
} |
@@ -1,37 +0,0 @@ | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public class SimpleSerializationData : ISerializationData | |||
{ | |||
public uint dataPos { get; set; } | |||
public FasterList<byte> data { get; set; } | |||
public SimpleSerializationData(FasterList<byte> d) | |||
{ | |||
data = d; | |||
} | |||
public void ResetWithNewData(FasterList<byte> newData) | |||
{ | |||
dataPos = 0; | |||
data = newData; | |||
} | |||
public void ReuseAsNew() | |||
{ | |||
dataPos = 0; | |||
data.ResetToReuse(); | |||
} | |||
public void Reset() | |||
{ | |||
dataPos = 0; | |||
} | |||
public void BeginNextEntityStruct() | |||
{} | |||
} | |||
} |
@@ -1,15 +0,0 @@ | |||
using Svelto.ECS; | |||
namespace Svelto.Common | |||
{ | |||
public class UnsafeUtils | |||
{ | |||
public static uint SizeOf<T>() where T : unmanaged, IEntityStruct | |||
{ | |||
unsafe | |||
{ | |||
return (uint) sizeof(T); | |||
} | |||
} | |||
} | |||
} |
@@ -1,25 +0,0 @@ | |||
using System; | |||
using Svelto.ECS.Schedulers; | |||
namespace Svelto.ECS | |||
{ | |||
//This scheduler shouldn't be used in production and it's meant to be used for Unit Tests only | |||
public class SimpleSubmissionEntityViewScheduler : IEntitySubmissionScheduler | |||
{ | |||
public void SubmitEntities() | |||
{ | |||
_onTick.Invoke(); | |||
} | |||
EnginesRoot.EntitiesSubmitter IEntitySubmissionScheduler.onTick | |||
{ | |||
set => _onTick = value; | |||
} | |||
EnginesRoot.EntitiesSubmitter _onTick; | |||
public void Dispose() | |||
{ | |||
} | |||
} | |||
} |
@@ -1,15 +0,0 @@ | |||
{ | |||
"name": "Svelto.ECS", | |||
"references": [ | |||
"Svelto.Common" | |||
], | |||
"optionalUnityReferences": [], | |||
"includePlatforms": [], | |||
"excludePlatforms": [], | |||
"allowUnsafeCode": true, | |||
"overrideReferences": false, | |||
"precompiledReferences": [], | |||
"autoReferenced": true, | |||
"defineConstraints": [], | |||
"versionDefines": [] | |||
} |
@@ -1,24 +0,0 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<AssemblyName>Svelto.ECS</AssemblyName> | |||
<LangVersion>7.3</LangVersion> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> | |||
<PlatformTarget>AnyCPU</PlatformTarget> | |||
<UseSharedCompilation>false</UseSharedCompilation> | |||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> | |||
<PlatformTarget>AnyCPU</PlatformTarget> | |||
<UseSharedCompilation>false</UseSharedCompilation> | |||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\Svelto.Common\Svelto.Common.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="System.Memory" Version="4.5.2" /> | |||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.6.0-preview8.19405.3" /> | |||
</ItemGroup> | |||
</Project> |
@@ -1,9 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
public class TypeCache<T> | |||
{ | |||
public static Type type = typeof(T); | |||
} | |||
} |
@@ -1,64 +0,0 @@ | |||
using System; | |||
using System.Collections; | |||
namespace Svelto.ECS | |||
{ | |||
public class WaitForSubmissionEnumerator : IEnumerator | |||
{ | |||
class SubmissionEntityDescriptor : GenericEntityDescriptor<SubmissionSignalStruct> | |||
{ | |||
internal static readonly ExclusiveGroup SubmissionGroup = new ExclusiveGroup(); | |||
} | |||
readonly IEntityFactory _entityFactory; | |||
readonly IEntitiesDB _entitiesDB; | |||
readonly IEntityFunctions _entityFunctions; | |||
int _state; | |||
public WaitForSubmissionEnumerator(IEntityFunctions entityFunctions, IEntityFactory entityFactory, | |||
IEntitiesDB entitiesDb) | |||
{ | |||
_entityFactory = entityFactory; | |||
_entityFunctions = entityFunctions; | |||
_entitiesDB = entitiesDb; | |||
} | |||
public bool MoveNext() | |||
{ | |||
switch (_state) | |||
{ | |||
case 0: | |||
_counter = _COUNTER++; | |||
_entityFactory.BuildEntity<SubmissionEntityDescriptor>(new EGID((uint) _counter, | |||
SubmissionEntityDescriptor.SubmissionGroup)); | |||
_state = 1; | |||
return true; | |||
case 1: | |||
if (_entitiesDB.Exists<SubmissionSignalStruct>(new EGID((uint) _counter, | |||
SubmissionEntityDescriptor.SubmissionGroup)) == false) | |||
return true; | |||
_entityFunctions.RemoveEntity<SubmissionEntityDescriptor>(new EGID((uint) _counter, | |||
SubmissionEntityDescriptor.SubmissionGroup)); | |||
_state = 0; | |||
return false; | |||
} | |||
throw new Exception("something is wrong"); | |||
} | |||
void IEnumerator.Reset() | |||
{ | |||
throw new NotImplementedException(); | |||
} | |||
public object Current { get; } | |||
struct SubmissionSignalStruct : IEntityStruct | |||
{} | |||
int _counter; | |||
static int _COUNTER; | |||
} | |||
} |
@@ -1,15 +0,0 @@ | |||
{ | |||
"displayName": "Svelto ECS", | |||
"category": "Svelto", | |||
"description": "Svelto ECS C# Lightweight Data Oriented Entity Component System Framework", | |||
"dependencies": { | |||
"com.sebaslab.svelto.common": "2.9.2" | |||
}, | |||
"keywords": [ | |||
"svelto" | |||
], | |||
"name": "com.sebaslab.svelto.ecs", | |||
"unity": "2019.2", | |||
"version": "2.9.2", | |||
"type": "library" | |||
} |