using System;
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
#if EXPERIMENTAL
using Svelto.ECS.Experimental;
using Svelto.ECS.Experimental.Internal;
#endif
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
using Svelto.ECS.Profiler;
#endif
namespace Svelto.ECS
{
public partial class EnginesRoot : IDisposable
{
///
/// 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.
///
///
public IEntityFactory GenerateEntityFactory()
{
return new GenericEntityFactory(new DataStructures.WeakReference(this));
}
public IEntityFunctions GenerateEntityFunctions()
{
return new GenericEntityFunctions(new DataStructures.WeakReference(this));
}
///
/// 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
///
///
///
///
void BuildEntity(int entityID, object[] implementors = null) where T : IEntityDescriptor, new()
{
EntityFactory.BuildEntityViews
(entityID, _entityViewsToAdd.current, EntityDescriptorTemplate.Default, implementors);
}
///
/// 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.
///
///
///
///
void BuildEntity(int entityID, EntityDescriptorInfo entityDescriptor, object[] implementors = null)
{
EntityFactory.BuildEntityViews
(entityID, _entityViewsToAdd.current, entityDescriptor, implementors);
}
///
/// A meta entity is a way to manage a set of entitites that are not easily
/// queriable otherwise. For example you may want to group existing entities
/// by size and type and then use the meta entity entityView to manage the data
/// shared among the single entities of the same type and size. This will
/// prevent the scenario where the coder is forced to parse all the entities to
/// find the ones of the same size and type.
/// Since the entities are managed through the shared entityView, the same
/// shared entityView must be found on the single entities of the same type and size.
/// The shared entityView of the meta entity is then used by engines that are meant
/// to manage a group of entities through a single entityView.
/// The same engine can manage several meta entities entityViews too.
/// The Engine manages the logic of the Meta EntityView data and other engines
/// can read back this data through the normal entity as the shared entityView
/// will be present in their descriptor too.
/// It's a way to control a group of Entities through a entityView only.
/// This set of entities can share exactly the same entityView reference if
/// built through this function. In this way, if you need to set a variable
/// on a group of entities, instead to inject N entityViews and iterate over
/// them to set the same value, you can inject just one entityView, set the value
/// and be sure that the value is shared between entities.
///
///
///
///
void BuildMetaEntity(int metaEntityID, object[] implementors) where T : IEntityDescriptor, new()
{
EntityFactory.BuildEntityViews(metaEntityID, _entityViewsToAdd.current,
EntityDescriptorTemplate.Default, implementors);
}
///
/// Using this function is like building a normal entity, but the entityViews
/// are grouped by groupID to be more efficently processed inside engines and
/// improve cache locality. Either class entityViews and struct entityViews can be
/// grouped.
///
///
///
///
///
void BuildEntityInGroup(int entityID, int groupID, object[] implementors = null) where T : IEntityDescriptor, new()
{
EntityFactory.BuildGroupedEntityViews(entityID, groupID,
_groupedEntityViewsToAdd.current,
EntityDescriptorTemplate.Default,
implementors);
}
void BuildEntityInGroup(int entityID, int groupID, EntityDescriptorInfo entityDescriptor, object[] implementors = null)
{
EntityFactory.BuildGroupedEntityViews(entityID, groupID,
_groupedEntityViewsToAdd.current,
entityDescriptor, implementors);
}
void Preallocate(int size) where T : IEntityDescriptor, new()
{
var entityViewsToBuild = EntityDescriptorTemplate.Default.entityViewsToBuild;
int count = entityViewsToBuild.Length;
for (int index = 0; index < count; index++)
{
var entityViewBuilder = entityViewsToBuild[index];
var entityViewType = entityViewBuilder.GetEntityViewType();
ITypeSafeList dbList;
if (_entityViewsDB.TryGetValue(entityViewType, out dbList) == false)
_entityViewsDB[entityViewType] = entityViewBuilder.Preallocate(ref dbList, size);
else
dbList.ReserveCapacity(size);
if (_entityViewsToAdd.current.TryGetValue(entityViewType, out dbList) == false)
_entityViewsToAdd.current[entityViewType] = entityViewBuilder.Preallocate(ref dbList, size);
else
dbList.ReserveCapacity(size);
}
}
void RemoveEntity(int entityID, IRemoveEntityComponent removeInfo)
{
var removeEntityImplementor = removeInfo as RemoveEntityImplementor;
if (removeEntityImplementor.isInAGroup)
InternalRemove(removeEntityImplementor.removeEntityInfo.entityViewsToBuild, entityID, removeEntityImplementor.groupID, _entityViewsDB);
else
InternalRemove(removeEntityImplementor.removeEntityInfo.entityViewsToBuild, entityID, _entityViewsDB);
}
void RemoveEntity(int entityID) where T : IEntityDescriptor, new()
{
InternalRemove(EntityDescriptorTemplate.Default.entityViewsToBuild, entityID, _entityViewsDB);
}
void RemoveMetaEntity(int metaEntityID) where T : IEntityDescriptor, new()
{
InternalRemove(EntityDescriptorTemplate.Default.entityViewsToBuild, metaEntityID, _metaEntityViewsDB);
}
void RemoveEntityFromGroup(int entityID, int groupID) where T : IEntityDescriptor, new()
{
InternalRemove(EntityDescriptorTemplate.Default.entityViewsToBuild, entityID, _groupEntityViewsDB[groupID]);
}
void SwapEntityGroup(int entityID, int fromGroupID, int toGroupID) where T : IEntityDescriptor, new()
{
DesignByContract.Check.Require(fromGroupID != toGroupID, "can't move an entity to the same group where it already belongs to");
var entityViewBuilders = EntityDescriptorTemplate.Default.entityViewsToBuild;
int entityViewBuildersCount = entityViewBuilders.Length;
for (int i = 0; i < entityViewBuildersCount; i++)
{
IEntityViewBuilder entityViewBuilder = entityViewBuilders[i];
Type entityViewType = entityViewBuilder.GetEntityViewType();
Dictionary dictionary = _groupEntityViewsDB[fromGroupID];
ITypeSafeList fromSafeList = dictionary[entityViewType];
Dictionary groupedEntityViewsTyped;
if (_groupEntityViewsDB.TryGetValue(toGroupID, out groupedEntityViewsTyped) == false)
{
groupedEntityViewsTyped = new Dictionary();
_groupEntityViewsDB.Add(toGroupID, groupedEntityViewsTyped);
}
ITypeSafeList toSafeList;
if (groupedEntityViewsTyped.TryGetValue(entityViewType, out toSafeList) == false)
{
toSafeList = fromSafeList.Create();
}
entityViewBuilder.MoveEntityView(entityID, fromSafeList, toSafeList);
if (fromSafeList.UnorderedRemove(entityID) == false)
dictionary.Remove(entityViewType);
if (dictionary.Count == 0) _groupEntityViewsDB.Remove(fromGroupID);
}
}
void InternalRemove(IEntityViewBuilder[] entityViewBuilders, int entityID,
Dictionary entityViewsDB)
{
int entityViewBuildersCount = entityViewBuilders.Length;
for (int i = 0; i < entityViewBuildersCount; i++)
{
Type entityViewType = entityViewBuilders[i].GetEntityViewType();
ITypeSafeList entityViews = entityViewsDB[entityViewType];
if (entityViews.UnorderedRemove(entityID) == false)
entityViewsDB.Remove(entityViewType);
if (entityViews.isQueryiableEntityView)
{
var typeSafeDictionary = _entityViewsDBdic[entityViewType];
var entityView = typeSafeDictionary.GetIndexedEntityView(entityID);
if (typeSafeDictionary.Remove(entityID) == false)
_entityViewsDBdic.Remove(entityViewType);
RemoveEntityViewFromEngines(_entityViewEngines, entityView, entityViewType);
}
}
}
void InternalRemove(IEntityViewBuilder[] entityViewBuilders, int entityID, int groupID,
Dictionary entityViewsDB)
{
InternalRemoveFromDB(entityViewBuilders, entityID, groupID);
InternalRemove(entityViewBuilders, entityID, entityViewsDB);
}
void InternalRemoveFromDB(IEntityViewBuilder[] entityViewBuilders, int entityID, int groupID)
{
int entityViewBuildersCount = entityViewBuilders.Length;
for (int i = 0; i < entityViewBuildersCount; i++)
{
Type entityViewType = entityViewBuilders[i].GetEntityViewType();
Dictionary dictionary = _groupEntityViewsDB[groupID];
if (dictionary[entityViewType].UnorderedRemove(entityID) == false)
dictionary.Remove(entityViewType);
if (dictionary.Count == 0) _groupEntityViewsDB.Remove(groupID);
}
}
static void RemoveEntityViewFromEngines(Dictionary> entityViewEngines,
IEntityView entityView, Type entityViewType)
{
FasterList enginesForEntityView;
if (entityViewEngines.TryGetValue(entityViewType, out enginesForEntityView))
{
int count;
var fastList = FasterList.NoVirt.ToArrayFast(enginesForEntityView, out count);
for (int j = 0; j < count; j++)
{
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
EngineProfiler.MonitorRemoveDuration(fastList[j], entityView);
#else
fastList[j].Remove(entityView);
#endif
}
}
}
class GenericEntityFactory : IEntityFactory
{
DataStructures.WeakReference _weakEngine;
public GenericEntityFactory(DataStructures.WeakReference weakReference)
{
_weakEngine = weakReference;
}
public void BuildEntity(int entityID, object[] implementors = null) where T : IEntityDescriptor, new()
{
_weakEngine.Target.BuildEntity(entityID, implementors);
}
public void BuildEntity(int entityID, EntityDescriptorInfo entityDescriptor, object[] implementors = null)
{
_weakEngine.Target.BuildEntity(entityID, entityDescriptor, implementors);
}
public void BuildMetaEntity(int metaEntityID, object[] implementors = null) where T : IEntityDescriptor, new()
{
_weakEngine.Target.BuildMetaEntity(metaEntityID, implementors);
}
public void BuildEntityInGroup(int entityID, int groupID, object[] implementors = null) where T : IEntityDescriptor, new()
{
_weakEngine.Target.BuildEntityInGroup(entityID, groupID, implementors);
}
public void BuildEntityInGroup(int entityID, int groupID, EntityDescriptorInfo entityDescriptor, object[] implementors = null)
{
_weakEngine.Target.BuildEntityInGroup(entityID, groupID, entityDescriptor, implementors);
}
public void Preallocate(int size) where T : IEntityDescriptor, new()
{
_weakEngine.Target.Preallocate(size);
}
}
class GenericEntityFunctions : IEntityFunctions
{
public GenericEntityFunctions(DataStructures.WeakReference weakReference)
{
_weakReference = weakReference;
}
public void RemoveEntity(int entityID, IRemoveEntityComponent removeInfo)
{
_weakReference.Target.RemoveEntity(entityID, removeInfo);
}
public void RemoveEntity(int entityID) where T : IEntityDescriptor, new()
{
_weakReference.Target.RemoveEntity(entityID);
}
public void RemoveMetaEntity(int metaEntityID) where T : IEntityDescriptor, new()
{
_weakReference.Target.RemoveEntity(metaEntityID);
}
public void RemoveEntityFromGroup(int entityID, int groupID) where T : IEntityDescriptor, new()
{
_weakReference.Target.RemoveEntity(entityID);
}
public void SwapEntityGroup(int entityID, int fromGroupID, int toGroupID) where T : IEntityDescriptor, new()
{
_weakReference.Target.SwapEntityGroup(entityID, fromGroupID, toGroupID);
}
readonly DataStructures.WeakReference _weakReference;
}
public void Dispose()
{
foreach (var entity in _entityViewsDB)
{
if (entity.Value.isQueryiableEntityView == true)
{
foreach (var entityView in entity.Value)
{
RemoveEntityViewFromEngines(_entityViewEngines, entityView as EntityView, entity.Key);
}
}
}
foreach (var entity in _metaEntityViewsDB)
{
foreach (var entityView in entity.Value)
{
RemoveEntityViewFromEngines(_entityViewEngines, entityView as EntityView, entity.Key);
}
}
}
}
}