More debug info Better memory usage Query entity must throw an exception if entity is not found runtime validation for EntityStructstags/2.6a
@@ -29,10 +29,11 @@ namespace Svelto.ECS.Internal | |||
void AddEntitiesToEngines(Dictionary<Type, FasterList<IHandleEntityViewEngineAbstracted>> entityViewEnginesDB); | |||
void AddCapacity(int size); | |||
bool Remove(int idGid); | |||
int Count { get; } | |||
void Trim(); | |||
void Clear(); | |||
bool Has(int entityIdEntityId); | |||
} | |||
class TypeSafeDictionary<TValue> : FasterDictionary<int, TValue>, ITypeSafeDictionary where TValue : IEntityStruct | |||
@@ -74,6 +75,11 @@ namespace Svelto.ECS.Internal | |||
} | |||
} | |||
public bool Has(int entityIdEntityId) | |||
{ | |||
return ContainsKey(entityIdEntityId); | |||
} | |||
void AddEntityViewToEngines(Dictionary<Type, FasterList<IHandleEntityViewEngineAbstracted>> entityViewEnginesDB, ref TValue entity) | |||
{ | |||
FasterList<IHandleEntityViewEngineAbstracted> entityViewsEngines; | |||
@@ -1,4 +1,7 @@ | |||
using System.Collections; | |||
using System; | |||
using System.Collections; | |||
using System.Collections.Generic; | |||
using Svelto.ECS.Internal; | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
using Svelto.ECS.Profiler; | |||
@@ -8,12 +11,12 @@ namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
class DoubleBufferedEntityViews<T> where T : class, IDictionary, new() | |||
class DoubleBufferedEntitiesToAdd<T> where T : Dictionary<int, Dictionary<Type, ITypeSafeDictionary>>, new() | |||
{ | |||
readonly T _entityViewsToAddBufferA = new T(); | |||
readonly T _entityViewsToAddBufferB = new T(); | |||
internal DoubleBufferedEntityViews() | |||
internal DoubleBufferedEntitiesToAdd() | |||
{ | |||
this.other = _entityViewsToAddBufferA; | |||
this.current = _entityViewsToAddBufferB; | |||
@@ -21,13 +24,24 @@ namespace Svelto.ECS | |||
internal T other; | |||
internal T current; | |||
internal void Swap() | |||
{ | |||
var toSwap = other; | |||
other = current; | |||
current = toSwap; | |||
} | |||
public void ClearOther() | |||
{ | |||
foreach (var item in other) | |||
{ | |||
foreach (var subitem in item.Value) | |||
{ | |||
subitem.Value.Clear(); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -42,7 +42,7 @@ namespace Svelto.ECS | |||
_groupEntityDB = new Dictionary<int, Dictionary<Type, ITypeSafeDictionary>>(); | |||
_groupEntityDB[ExclusiveGroup.StandardEntitiesGroup] = new Dictionary<Type, ITypeSafeDictionary>(); | |||
_groupedGroups = new Dictionary<Type, FasterDictionary<int, ITypeSafeDictionary>>(); | |||
_groupedEntityToAdd = new DoubleBufferedEntityViews<Dictionary<int, Dictionary<Type, ITypeSafeDictionary>>>(); | |||
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd<Dictionary<int, Dictionary<Type, ITypeSafeDictionary>>>(); | |||
_DB = new entitiesDB(_groupEntityDB, _groupedGroups); | |||
@@ -1,5 +1,6 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using Svelto.DataStructures.Experimental; | |||
using Svelto.ECS.Internal; | |||
@@ -40,10 +41,16 @@ namespace Svelto.ECS | |||
EntityStructInitializer BuildEntity<T>(EGID entityID, object[] implementors) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
var descriptorEntitiesToBuild = EntityDescriptorTemplate<T>.descriptor.entitiesToBuild; | |||
#if DEBUG && !PROFILER | |||
CheckEntityID(entityID, descriptorEntitiesToBuild); | |||
#endif | |||
var dic = EntityFactory.BuildGroupedEntityViews(entityID, | |||
_groupedEntityToAdd.current, | |||
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, | |||
descriptorEntitiesToBuild, | |||
implementors); | |||
_newEntitiesBuiltToProcess++; | |||
return new EntityStructInitializer(entityID, dic); | |||
} | |||
@@ -52,13 +59,48 @@ namespace Svelto.ECS | |||
IEntityBuilder[] entityToBuild, | |||
object[] implementors) | |||
{ | |||
#if DEBUG && !PROFILER | |||
CheckEntityID(entityID, entityToBuild); | |||
#endif | |||
var dic = EntityFactory.BuildGroupedEntityViews(entityID, | |||
_groupedEntityToAdd.current, | |||
entityToBuild, | |||
implementors); | |||
_newEntitiesBuiltToProcess++; | |||
return new EntityStructInitializer(entityID, dic); | |||
} | |||
void CheckEntityID(EGID entityID, IEntityBuilder[] descriptorEntitiesToBuild) | |||
{ | |||
Dictionary<Type, ITypeSafeDictionary> @group; | |||
if (_groupEntityDB.TryGetValue(entityID.groupID, out @group) == true) | |||
{ | |||
for (int i = 0; i < descriptorEntitiesToBuild.Length; i++) | |||
{ | |||
CheckEntityID(entityID, descriptorEntitiesToBuild[i].GetEntityType(), @group); | |||
} | |||
CheckEntityID(entityID, _typeEntityInfoView, @group); | |||
} | |||
} | |||
static void CheckEntityID(EGID entityID, Type entityType, Dictionary<Type, ITypeSafeDictionary> @group) | |||
{ | |||
ITypeSafeDictionary entities; | |||
if (@group.TryGetValue(entityType, out entities)) | |||
{ | |||
if (entities.Has(entityID.entityID) == true) | |||
{ | |||
Utility.Console.LogError("Entity with used ID is about to be built: " | |||
.FastConcat(entityType) | |||
.FastConcat(" id: ") | |||
.FastConcat(entityID.entityID) | |||
.FastConcat(" groupid: ") | |||
.FastConcat(entityID.groupID)); | |||
} | |||
} | |||
} | |||
///-------------------------------------------- | |||
@@ -181,7 +223,7 @@ namespace Svelto.ECS | |||
MoveEntity(new EGID(entityID, fromGroupID), toGroupID, toGroup); | |||
} | |||
EGID SwapFirstEntityGroup(int fromGroupID, int toGroupId) | |||
EGID SwapFirstEntityInGroup(int fromGroupID, int toGroupId) | |||
{ | |||
var firstID = | |||
((TypeSafeDictionary<EntityInfoView>) _groupEntityDB[fromGroupID][_typeEntityInfoView]).FasterValues[0].ID.entityID; | |||
@@ -191,10 +233,10 @@ namespace Svelto.ECS | |||
return new EGID(firstID, toGroupId); | |||
} | |||
readonly entitiesDB _DB; | |||
readonly DoubleBufferedEntityViews<Dictionary<int, Dictionary<Type, ITypeSafeDictionary>>> _groupedEntityToAdd; | |||
readonly entitiesDB _DB; | |||
static readonly Type _typeEntityInfoView = typeof(EntityInfoView); | |||
static readonly Type _typeEntityInfoView = typeof(EntityInfoView); | |||
int _newEntitiesBuiltToProcess; | |||
} | |||
public struct EntityStructInitializer | |||
@@ -54,7 +54,7 @@ namespace Svelto.ECS | |||
public EGID SwapFirstEntityGroup(int fromGroupID, int toGroupID) | |||
{ | |||
return _weakReference.Target.SwapFirstEntityGroup( fromGroupID, toGroupID); | |||
return _weakReference.Target.SwapFirstEntityInGroup( fromGroupID, toGroupID); | |||
} | |||
} | |||
} |
@@ -14,11 +14,10 @@ namespace Svelto.ECS | |||
{ | |||
void SubmitEntityViews() | |||
{ | |||
bool newEntityViewsHaveBeenAddedWhileIterating = _groupedEntityToAdd.current.Count > 0; | |||
int numberOfReenteringLoops = 0; | |||
while (newEntityViewsHaveBeenAddedWhileIterating) | |||
//are there new entities built to process? | |||
while ( _newEntitiesBuiltToProcess > 0) | |||
{ | |||
//use other as source from now on | |||
//current will be use to write new entityViews | |||
@@ -33,15 +32,13 @@ namespace Svelto.ECS | |||
if (_groupedEntityToAdd.other.Count > 0) | |||
AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd.other); | |||
//other can be cleared now | |||
_groupedEntityToAdd.other.Clear(); | |||
//has current new entityViews? | |||
newEntityViewsHaveBeenAddedWhileIterating = _groupedEntityToAdd.current.Count > 0; | |||
//other can be cleared now, but let's avoid deleting the dictionary every time | |||
_groupedEntityToAdd.ClearOther(); | |||
if (numberOfReenteringLoops > 5) | |||
throw new Exception("possible infinite loop found creating Entities inside IEntityViewsEngine Add method, please consider building entities outside IEntityViewsEngine Add method"); | |||
_newEntitiesBuiltToProcess = 0; | |||
numberOfReenteringLoops++; | |||
} | |||
} | |||
@@ -70,7 +67,7 @@ namespace Svelto.ECS | |||
if (_groupedGroups.TryGetValue(entityViewTypeSafeDictionary.Key, out groupedGroup) == false) | |||
groupedGroup = _groupedGroups[entityViewTypeSafeDictionary.Key] = new FasterDictionary<int, ITypeSafeDictionary>(); | |||
//type safe copy | |||
//Fill the DB with the entity views generate this frame. | |||
dbDic.FillWithIndexedEntities(entityViewTypeSafeDictionary.Value); | |||
groupedGroup[groupID] = dbDic; | |||
} | |||
@@ -95,6 +92,7 @@ namespace Svelto.ECS | |||
readonly Dictionary<int, Dictionary<Type, ITypeSafeDictionary>> _groupEntityDB; | |||
readonly Dictionary<Type, FasterDictionary<int, ITypeSafeDictionary>> _groupedGroups; //yes I am being sarcastic | |||
readonly DoubleBufferedEntitiesToAdd<Dictionary<int, Dictionary<Type, ITypeSafeDictionary>>> _groupedEntityToAdd; | |||
readonly EntitySubmissionScheduler _scheduler; | |||
} | |||
} |
@@ -57,27 +57,16 @@ namespace Svelto.ECS.Internal | |||
public T[] QueryEntitiesAndIndex<T>(EGID entityGID, out uint index) where T : IEntityStruct | |||
{ | |||
TypeSafeDictionary<T> casted; | |||
if (!FindSafeDictionary(entityGID, out casted)) | |||
{ | |||
index = 0; | |||
return null; | |||
} | |||
if (casted == null || casted.TryFindElementIndex(entityGID.entityID, out index) == false) | |||
{ | |||
index = 0; | |||
return null; | |||
} | |||
int count; | |||
T[] array; | |||
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out index)) != null) | |||
return array; | |||
return QueryEntities<T>(entityGID.groupID, out count); | |||
throw new Exception("Entity not found id: ".FastConcat(entityGID.entityID).FastConcat(" groupID: ").FastConcat(entityGID.groupID)); | |||
} | |||
public bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array) where T : IEntityStruct | |||
{ | |||
if ((array = QueryEntitiesAndIndex<T>(entityGid, out index)) != null) | |||
if ((array = QueryEntitiesAndIndexInternal<T>(entityGid, out index)) != null) | |||
return true; | |||
return false; | |||
@@ -275,7 +264,26 @@ namespace Svelto.ECS.Internal | |||
return false; | |||
} | |||
T[] QueryEntitiesAndIndexInternal<T>(EGID entityGID, out uint index) where T : IEntityStruct | |||
{ | |||
TypeSafeDictionary<T> casted; | |||
if (!FindSafeDictionary(entityGID, out casted)) | |||
{ | |||
index = 0; | |||
return null; | |||
} | |||
if (casted == null || casted.TryFindElementIndex(entityGID.entityID, out index) == false) | |||
{ | |||
index = 0; | |||
return null; | |||
} | |||
int count; | |||
return QueryEntities<T>(entityGID.groupID, out count); | |||
} | |||
static ReadOnlyCollectionStruct<T> RetrieveEmptyEntityViewList<T>() | |||
{ | |||
return ReadOnlyCollectionStruct<T>.DefaultList; | |||
@@ -48,6 +48,7 @@ namespace Svelto.ECS.Internal | |||
} | |||
_builder._initializer = new EntityInfoView {entityToBuild = entityToBuild}; | |||
BuildEntityView(entityID, @group, _viewType, _builder, null); | |||
} | |||
@@ -62,6 +63,7 @@ namespace Svelto.ECS.Internal | |||
//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.BuildEntityViewAndAddToList(ref safeDictionary, entityID, implementors); | |||
if (entityViewsPoolWillBeCreated) | |||
@@ -18,20 +18,7 @@ namespace Svelto.ECS | |||
#if DEBUG && !PROFILER | |||
if (needsReflection == false && typeof(T) != typeof(EntityInfoView)) | |||
{ | |||
var type = typeof(T); | |||
var fields = type.GetFields(BindingFlags.Public | | |||
BindingFlags.Instance); | |||
for (int i = fields.Length - 1; i >= 0; --i) | |||
{ | |||
var field = fields[i]; | |||
if (field.FieldType.IsPrimitive == true || field.FieldType.IsValueType == true) | |||
continue; | |||
throw new EntityStructException(field.FieldType); | |||
} | |||
CheckFields(typeof(T)); | |||
} | |||
#endif | |||
if (needsReflection == true) | |||
@@ -39,7 +26,32 @@ namespace Svelto.ECS | |||
EntityView<T>.InitCache(); | |||
} | |||
} | |||
#if DEBUG && !PROFILER | |||
static void CheckFields(Type type) | |||
{ | |||
var fields = type.GetFields(BindingFlags.Public | | |||
BindingFlags.Instance); | |||
for (int i = fields.Length - 1; i >= 0; --i) | |||
{ | |||
var field = fields[i]; | |||
var fieldFieldType = field.FieldType; | |||
if (fieldFieldType.IsPrimitive == true || fieldFieldType.IsValueType == true) | |||
{ | |||
if (fieldFieldType.IsValueType && !fieldFieldType.IsEnum) | |||
{ | |||
CheckFields(fieldFieldType); | |||
} | |||
continue; | |||
} | |||
throw new EntityStructException(fieldFieldType); | |||
} | |||
} | |||
#endif | |||
public void BuildEntityViewAndAddToList(ref ITypeSafeDictionary dictionary, EGID entityID, object[] implementors) | |||
{ | |||
if (dictionary == null) | |||
@@ -20,7 +20,8 @@ static class EntityViewUtility | |||
.NoVirt.ToArrayFast(entityViewBlazingFastReflection, out count); | |||
#if DEBUG && !PROFILER | |||
if (count == 0) | |||
throw new Exception(NO_COMPONENTS_EXCEPTION.FastConcat("Type ", entityDescriptorName, " entityView ", entityBuilder.GetEntityType().ToString())); | |||
throw new Exception(NO_COMPONENTS_EXCEPTION.FastConcat("Type ", entityDescriptorName, " entityView ", | |||
entityBuilder.GetEntityType().ToString())); | |||
#endif | |||
for (var index = 0; index < implementors.Length; index++) | |||
{ | |||
@@ -52,7 +53,8 @@ static class EntityViewUtility | |||
#if DEBUG && !PROFILER | |||
else | |||
{ | |||
Utility.Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat("Type ", entityDescriptorName, " entityView ", entityBuilder.GetEntityType().ToString())); | |||
Utility.Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat("Type ", entityDescriptorName, " entityView ", | |||
entityBuilder.GetEntityType().ToString())); | |||
} | |||
#endif | |||
} | |||
@@ -71,8 +73,8 @@ static class EntityViewUtility | |||
if (implementorsByType.TryGetValue(fieldType, out component) == false) | |||
{ | |||
var e = new Exception(NOT_FOUND_EXCEPTION + " Component Type: " + fieldType.Name + | |||
" - EntityView: " + | |||
entityBuilder.GetEntityType().Name + " - EntityDescriptor " + entityDescriptorName); | |||
" - EntityView: " + entityBuilder.GetEntityType().Name + | |||
" - EntityDescriptor " + entityDescriptorName); | |||
throw e; | |||
} | |||
@@ -40,7 +40,6 @@ namespace Svelto.ECS | |||
//to use with EntityViews, EntityStructs and EntityViewStructs | |||
void ExecuteOnEntity<T>(EGID egid, ActionRef<T> action) where T : IEntityStruct; | |||
void ExecuteOnEntity<T>(int id, ActionRef<T> action) where T : IEntityStruct; | |||
void ExecuteOnEntity<T>(int id, int groupid, ActionRef<T> action) where T : IEntityStruct; | |||
@@ -48,7 +47,6 @@ namespace Svelto.ECS | |||
void ExecuteOnEntities<T>(ActionRef<T> action) where T : IEntityStruct; | |||
void ExecuteOnEntity<T, W>(EGID egid, ref W value, ActionRef<T, W> action) where T : IEntityStruct; | |||
void ExecuteOnEntity<T, W>(int id, ref W value, ActionRef<T, W> action) where T : IEntityStruct; | |||
void ExecuteOnEntity<T, W>(int id, int groupid, ref W value, ActionRef<T, W> action) where T : IEntityStruct; | |||
@@ -7,39 +7,30 @@ namespace Svelto.ECS | |||
public class Steps : Dictionary<IEngine, IDictionary> | |||
{} | |||
public class To : Dictionary<int, IStep[]> | |||
public class To<C> : Dictionary<C, IStep[]> where C : struct, IConvertible | |||
{ | |||
public void Add(IStep engine) | |||
public void Add(C condition, IStep engine) | |||
{ | |||
Add(Condition.Always, new [] {engine}); | |||
Add(condition, new [] {engine}); | |||
} | |||
public void Add(IStep[] engines) | |||
public void Add(C condition, params IStep[] engines) | |||
{ | |||
Add(Condition.Always, engines); | |||
Add(condition, engines); | |||
} | |||
public void Add(params IStep[] engines) | |||
{ | |||
Add(default(C), engines); | |||
} | |||
} | |||
public class To<C> : Dictionary<C, IStep[]> where C:struct,IConvertible | |||
{} | |||
public interface IStep | |||
{} | |||
public interface IStep<T>:IStep | |||
public interface IStep<in C>:IStep where C:struct,IConvertible | |||
{ | |||
void Step(ref T token, int condition); | |||
void Step(C condition, EGID id); | |||
} | |||
public interface IStep<T, in C>:IStep where C:struct,IConvertible | |||
{ | |||
void Step(ref T token, C condition); | |||
} | |||
public interface IEnumStep<T>:IStep | |||
{ | |||
void Step(ref T token, Enum condition); | |||
} | |||
public abstract class Sequencer | |||
{ | |||
public void SetSequence(Steps steps) | |||
@@ -47,39 +38,25 @@ namespace Svelto.ECS | |||
_steps = steps; | |||
} | |||
public void Next<T>(IEngine engine, ref T param) | |||
{ | |||
Next(engine, ref param, Condition.Always); | |||
} | |||
public void Next<T>(IEngine engine, ref T param, int condition) | |||
public void Next(IEngine engine, EGID id) | |||
{ | |||
int branch = condition; | |||
var steps = (_steps[engine] as Dictionary<int, IStep[]>)[branch]; | |||
if (steps != null) | |||
for (int i = 0; i < steps.Length; i++) | |||
((IStep<T>)steps[i]).Step(ref param, condition); | |||
Next(engine, Condition.Always, id); | |||
} | |||
public void Next<T>(IEngine engine, ref T param, Enum condition) | |||
public void Next(IEngine engine) | |||
{ | |||
int branch = Convert.ToInt32(condition); | |||
var steps = (_steps[engine] as Dictionary<int, IStep[]>)[branch]; | |||
if (steps != null) | |||
for (int i = 0; i < steps.Length; i++) | |||
((IEnumStep<T>)steps[i]).Step(ref param, condition); | |||
Next(engine, Condition.Always); | |||
} | |||
public void Next<T, C>(IEngine engine, ref T param, C condition) where C:struct,IConvertible | |||
public void Next<C>(IEngine engine, C condition, EGID id = new EGID()) where C:struct,IConvertible | |||
{ | |||
C branch = condition; | |||
var steps = (_steps[engine] as Dictionary<C, IStep[]>)[branch]; | |||
if (steps != null) | |||
for (int i = 0; i < steps.Length; i++) | |||
((IStep<T, C>)steps[i]).Step(ref param, condition); | |||
if (steps == null) return; | |||
for (var i = 0; i < steps.Length; i++) | |||
((IStep<C>)steps[i]).Step(condition, id); | |||
} | |||
Steps _steps; | |||
@@ -89,4 +66,4 @@ namespace Svelto.ECS | |||
{ | |||
public const int Always = 0; | |||
} | |||
} | |||
} |