using System; using System.Collections.Generic; using Svelto.DataStructures; using Svelto.ECS.Internal; using Svelto.ECS.NodeSchedulers; using WeakReference = Svelto.DataStructures.WeakReference; #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR using Svelto.ECS.Profiler; #endif #if NETFX_CORE using System.Reflection; #endif namespace Svelto.ECS { public sealed class EnginesRoot : IEnginesRoot, IEntityFactory { public EnginesRoot(NodeSubmissionScheduler nodeScheduler) { _nodeEngines = new Dictionary>(); _activableEngines = new Dictionary>(); _otherEngines = new FasterList(); _engineRootWeakReference = new WeakReference(this); _nodesDB = new Dictionary>(); _nodesDBdic = new Dictionary>(); _nodesToAdd = new FasterList(); _metaNodesToAdd = new FasterList(); _metaNodesDB = new Dictionary>(); _sharedStructNodeLists = new SharedStructNodeLists(); _sharedGroupedStructNodeLists = new SharedGroupedStructNodesLists(); _internalRemove = InternalRemove; _internalDisable = InternalDisable; _internalEnable = InternalEnable; _internalMetaRemove = InternalMetaRemove; _scheduler = nodeScheduler; _scheduler.Schedule(SubmitNodes); _structNodeEngineType = typeof(IStructNodeEngine<>); _groupedStructNodesEngineType = typeof(IGroupedStructNodesEngine<>); _activableNodeEngineType = typeof(IActivableNodeEngine<>); _implementedInterfaceTypes = new Dictionary(); #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR GameObject debugEngineObject = new GameObject("Engine Debugger"); debugEngineObject.gameObject.AddComponent(); #endif } void SubmitNodes() { int metaNodesCount = _metaNodesToAdd.Count; int nodesCount = _nodesToAdd.Count; if (metaNodesCount + nodesCount == 0) return; bool newNodesHaveBeenAddedWhileIterating; int startNodes = 0; int startMetaNodes = 0; int numberOfReenteringLoops = 0; do { var nodesToAdd = _nodesToAdd.ToArrayFast(); for (int i = startNodes; i < nodesCount; i++) { var node = nodesToAdd[i]; var nodeType = node.GetType(); AddNodeToTheDB(node, nodeType); var nodeWithId = node as INodeWithID; if (nodeWithId != null) AddNodeToNodesDictionary(nodeWithId, nodeType); } var metaNodesToAdd = _metaNodesToAdd.ToArrayFast(); for (int i = startMetaNodes; i < metaNodesCount; i++) { var node = metaNodesToAdd[i]; var nodeType = node.GetType(); AddNodeToMetaDB(node, nodeType); var nodeWithId = node as INodeWithID; if (nodeWithId != null) AddNodeToNodesDictionary(nodeWithId, nodeType); } for (int i = startNodes; i < nodesCount; i++) { var node = nodesToAdd[i]; AddNodeToTheSuitableEngines(node, node.GetType()); } for (int i = startMetaNodes; i < metaNodesCount; i++) { var node = metaNodesToAdd[i]; AddNodeToTheSuitableEngines(node, node.GetType()); } newNodesHaveBeenAddedWhileIterating = _metaNodesToAdd.Count > metaNodesCount || _nodesToAdd.Count > nodesCount; startNodes = nodesCount; startMetaNodes = metaNodesCount; if (numberOfReenteringLoops > 5) throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method"); numberOfReenteringLoops++; metaNodesCount = _metaNodesToAdd.Count; nodesCount = _nodesToAdd.Count; } while (newNodesHaveBeenAddedWhileIterating); _nodesToAdd.Clear(); _metaNodesToAdd.Clear(); } public void AddEngine(IEngine engine) { #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR EngineProfiler.AddEngine(engine); #endif var queryableNodeEngine = engine as IQueryableNodeEngine; if (queryableNodeEngine != null) queryableNodeEngine.nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _metaNodesDB); var engineType = engine.GetType(); var implementedInterfaces = engineType.GetInterfaces(); CollectImplementedInterfaces(implementedInterfaces); var engineAdded = CheckGenericEngines(engine); if (CheckLegacyNodesEngine(engine, ref engineAdded) == false) CheckNodesEngine(engine, engineType, ref engineAdded); if (engineAdded == false) _otherEngines.Add(engine); var callBackOnAddEngine = engine as ICallBackOnAddEngine; if (callBackOnAddEngine != null) callBackOnAddEngine.Ready(); } void CollectImplementedInterfaces(Type[] implementedInterfaces) { _implementedInterfaceTypes.Clear(); var type = typeof(IEngine); for (int index = 0; index < implementedInterfaces.Length; index++) { var interfaceType = implementedInterfaces[index]; if (interfaceType.IsAssignableFrom(type) == false) continue; #if !NETFX_CORE if (false == interfaceType.IsGenericType) #else if (false == interfaceType.IsConstructedGenericType) #endif { continue; } var genericTypeDefinition = interfaceType.GetGenericTypeDefinition(); _implementedInterfaceTypes.Add(genericTypeDefinition, interfaceType.GetGenericArguments()); } } bool CheckGenericEngines(IEngine engine) { if (_implementedInterfaceTypes.Count == 0) return false; bool engineAdded = false; if (_implementedInterfaceTypes.ContainsKey(_structNodeEngineType)) { ((IStructNodeEngine)engine).CreateStructNodes (_sharedStructNodeLists); } if (_implementedInterfaceTypes.ContainsKey(_groupedStructNodesEngineType)) { ((IGroupedStructNodesEngine)engine).CreateStructNodes (_sharedGroupedStructNodeLists); } Type[] arguments; if (_implementedInterfaceTypes.TryGetValue(_activableNodeEngineType, out arguments)) { AddEngine(engine, arguments, _activableEngines); engineAdded = true; } return engineAdded; } bool CheckLegacyNodesEngine(IEngine engine, ref bool engineAdded) { var nodesEngine = engine as INodesEngine; if (nodesEngine != null) { AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines); engineAdded = true; return true; } return false; } bool CheckNodesEngine(IEngine engine, Type engineType, ref bool engineAdded) { #if !NETFX_CORE var baseType = engineType.BaseType; if (baseType.IsGenericType #else var baseType = engineType.GetTypeInfo().BaseType; if (baseType.IsConstructedGenericType #endif && engine is INodeEngine) { AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines); engineAdded = true; return true; } return false; } public void BuildEntity(int ID, EntityDescriptor ed) { var entityNodes = ed.BuildNodes(ID, _internalRemove, _internalEnable, _internalDisable ); _nodesToAdd.AddRange(entityNodes); } /// /// 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 node 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 node, the same /// shared node must be found on the single entities of the same type and size. /// The shared node of the meta entity is then used by engines that are meant /// to manage a group of entities through a single node. /// The same engine can manage several meta entities nodes too. /// The Engine manages the logic of the Meta Node data and other engines /// can read back this data through the normal entity as the shared node /// will be present in their descriptor too. /// /// /// public void BuildMetaEntity(int metaEntityID, EntityDescriptor ed) { var entityNodes = ed.BuildNodes(metaEntityID, _internalMetaRemove, _internalEnable, _internalDisable ); _metaNodesToAdd.AddRange(entityNodes); } /// /// Using this function is like building a normal entity, but the nodes /// are grouped by groupID to be better processed inside engines and /// improve cache locality. Only IGroupStructNodeWithID nodes are grouped /// other nodes are managed as usual. /// /// /// /// public void BuildEntityInGroup(int entityID, int groupID, EntityDescriptor ed) { var entityNodes = ed.BuildNodes(entityID, _internalRemove, _internalEnable, _internalDisable ); _nodesToAdd.AddRange(entityNodes); for (int i = 0; i < entityNodes.Count; i++) { var groupNode = entityNodes[i] as IGroupedStructNodeWithID; if (groupNode != null) groupNode.groupID = groupID; } } static void AddEngine(IEngine engine, Type[] types, Dictionary> engines) { for (int i = 0; i < types.Length; i++) { FasterList list; var type = types[i]; if (engines.TryGetValue(type, out list) == false) { list = new FasterList(); engines.Add(type, list); } list.Add(engine); } } void AddNodeToMetaDB(INode node, Type nodeType) { FasterList nodes; if (_metaNodesDB.TryGetValue(nodeType, out nodes) == false) nodes = _metaNodesDB[nodeType] = new FasterList(); nodes.Add(node); } void AddNodeToTheDB(T node, Type nodeType) where T : INode { FasterList nodes; if (_nodesDB.TryGetValue(nodeType, out nodes) == false) nodes = _nodesDB[nodeType] = new FasterList(); nodes.Add(node); } void AddNodeToNodesDictionary(T node, Type nodeType) where T : INodeWithID { Dictionary nodesDic; if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false) nodesDic = _nodesDBdic[nodeType] = new Dictionary(); nodesDic.Add(node.ID, node); } void AddNodeToTheSuitableEngines(INode node, Type nodeType) { FasterList enginesForNode; if (_nodeEngines.TryGetValue(nodeType, out enginesForNode)) { for (int j = 0; j < enginesForNode.Count; j++) { #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR EngineProfiler.MonitorAddDuration(AddNodeToEngine, enginesForNode[j] as INodeEngine, node); #else (enginesForNode[j] as INodeEngine).Add(node); #endif } } } void RemoveNodeFromTheDB(T node, Type nodeType) where T : INode { FasterList nodes; if (_nodesDB.TryGetValue(nodeType, out nodes) == true) nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero? } void RemoveNodeFromMetaDB(T node, Type nodeType) where T : INode { FasterList nodes; if (_metaNodesDB.TryGetValue(nodeType, out nodes) == true) nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero? } void RemoveNodeFromNodesDictionary(T node, Type nodeType) where T : INodeWithID { Dictionary nodesDic; if (_nodesDBdic.TryGetValue(nodeType, out nodesDic)) nodesDic.Remove(node.ID); } void RemoveNodeFromEngines(T node, Type nodeType) where T : INode { FasterList enginesForNode; if (_nodeEngines.TryGetValue(nodeType, out enginesForNode)) { for (int j = 0; j < enginesForNode.Count; j++) { #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, (enginesForNode[j] as INodeEngine), node); #else (enginesForNode[j] as INodeEngine).Remove(node); #endif } } } void DisableNodeFromEngines(INode node, Type nodeType) { FasterList enginesForNode; if (_activableEngines.TryGetValue(nodeType, out enginesForNode)) { for (int j = 0; j < enginesForNode.Count; j++) { (enginesForNode[j] as IActivableNodeEngine).Disable(node); } } } void EnableNodeFromEngines(INode node, Type nodeType) { FasterList enginesForNode; if (_activableEngines.TryGetValue(nodeType, out enginesForNode)) { for (int j = 0; j < enginesForNode.Count; j++) { (enginesForNode[j] as IActivableNodeEngine).Enable(node); } } } #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR void AddNodeToEngine(IEngine engine, INode node) { (engine as INodeEngine).Add(node); } void RemoveNodeFromEngine(IEngine engine, INode node) { (engine as INodeEngine).Remove(node); } #endif void InternalDisable(FasterList nodes) { if (_engineRootWeakReference.IsValid == false) return; for (int i = 0; i < nodes.Count; i++) { var node = nodes[i]; Type nodeType = node.GetType(); DisableNodeFromEngines(node, nodeType); } } void InternalEnable(FasterList nodes) { if (_engineRootWeakReference.IsValid == false) return; for (int i = 0; i < nodes.Count; i++) { var node = nodes[i]; Type nodeType = node.GetType(); EnableNodeFromEngines(node, nodeType); } } void InternalRemove(FasterList nodes) { if (_engineRootWeakReference.IsValid == false) return; for (int i = 0; i < nodes.Count; i++) { var node = nodes[i]; Type nodeType = node.GetType(); RemoveNodeFromEngines(node, nodeType); RemoveNodeFromTheDB(node, node.GetType()); var nodeWithId = node as INodeWithID; if (nodeWithId != null) RemoveNodeFromNodesDictionary(nodeWithId, nodeType); } } void InternalMetaRemove(FasterList nodes) { for (int i = 0; i < nodes.Count; i++) { var node = nodes[i]; Type nodeType = node.GetType(); RemoveNodeFromEngines(node, nodeType); RemoveNodeFromMetaDB(node, nodeType); var nodeWithId = node as INodeWithID; if (nodeWithId != null) RemoveNodeFromNodesDictionary(nodeWithId, nodeType); } } readonly Dictionary> _nodeEngines; readonly Dictionary> _activableEngines; readonly FasterList _otherEngines; readonly Dictionary> _nodesDB; readonly Dictionary> _metaNodesDB; readonly Dictionary> _nodesDBdic; readonly FasterList _nodesToAdd; readonly FasterList _metaNodesToAdd; readonly WeakReference _engineRootWeakReference; readonly SharedStructNodeLists _sharedStructNodeLists; readonly SharedGroupedStructNodesLists _sharedGroupedStructNodeLists; readonly NodeSubmissionScheduler _scheduler; readonly Action> _internalRemove; readonly Action> _internalEnable; readonly Action> _internalDisable; readonly Action> _internalMetaRemove; readonly Type _structNodeEngineType; readonly Type _groupedStructNodesEngineType; readonly Type _activableNodeEngineType; readonly Dictionary _implementedInterfaceTypes; } }