using System; using System.Collections; using System.Collections.Generic; using Svelto.DataStructures; using UnityEngine; using WeakReference = Svelto.DataStructures.WeakReference; using Svelto.ECS.NodeSchedulers; using Svelto.ECS.Internal; #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>(); _engineRootWeakReference = new WeakReference(this); _otherEnginesReferences = new FasterList(); _nodesDB = new Dictionary>(); _nodesDBdic = new Dictionary>(); _nodesToAdd = new FasterList(); _groupNodesToAdd = new FasterList(); _nodesDBgroups = new Dictionary>(); _scheduler = nodeScheduler; _scheduler.Schedule(SubmitNodes); #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR GameObject debugEngineObject = new GameObject("Engine Debugger"); debugEngineObject.gameObject.AddComponent(); #endif } void SubmitNodes() { int groupNodesCount; int nodesCount; bool newNodesHaveBeenAddedWhileIterating; int startNodes = 0; int startGroupNodes = 0; int numberOfReenteringLoops = 0; do { groupNodesCount = _groupNodesToAdd.Count; nodesCount = _nodesToAdd.Count; for (int i = startNodes; i < nodesCount; i++) { var node = _nodesToAdd[i]; AddNodeToTheDB(node, node.GetType()); } for (int i = startGroupNodes; i < groupNodesCount; i++) { var node = _groupNodesToAdd[i]; AddNodeToGroupDB(node, node.GetType()); } for (int i = startNodes; i < nodesCount; i++) { var node = _nodesToAdd[i]; AddNodeToTheSuitableEngines(node, node.GetType()); } for (int i = startGroupNodes; i < groupNodesCount; i++) { var node = _groupNodesToAdd[i]; AddNodeToTheSuitableEngines(node, node.GetType()); } newNodesHaveBeenAddedWhileIterating = _groupNodesToAdd.Count > groupNodesCount || _nodesToAdd.Count > nodesCount; startNodes = nodesCount; startGroupNodes = groupNodesCount; 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++; } while (newNodesHaveBeenAddedWhileIterating); _nodesToAdd.Clear(); _groupNodesToAdd.Clear(); } public void AddEngine(INodeEngine engine) where T:class, INode { AddEngine(new NodeEngineWrapper(engine)); if (engine is IQueryableNodeEngine) (engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _nodesDBgroups); } public void AddEngine(INodeEngine engine) where T:class, INode where U:class, INode { AddEngine(new NodeEngineWrapper(engine)); AddEngine((INodeEngine)(engine)); } public void AddEngine(INodeEngine engine) where T:class, INode where U:class, INode where V:class, INode { AddEngine(new NodeEngineWrapper(engine)); AddEngine((INodeEngine)(engine)); } public void AddEngine(IEngine engine) { #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR EngineProfiler.AddEngine(engine); #endif if (engine is IQueryableNodeEngine) (engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _nodesDBgroups); if (engine is INodesEngine) { var nodesEngine = engine as INodesEngine; AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines); } else { var engineType = engine.GetType(); #if !NETFX_CORE var baseType = engineType.BaseType; if (baseType.IsGenericType #else var baseType = engineType.GetTypeInfo().BaseType; if (baseType.IsConstructedGenericType #endif && baseType.GetGenericTypeDefinition() == typeof(SingleNodeEngine<>)) { AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines); } else { bool found = false; for (int i = 0, maxLength = engineType.GetInterfaces().Length; i < maxLength; i++) { var type = engineType.GetInterfaces()[i]; if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(INodeEngine<>)) { AddEngine(engine as INodeEngine, type.GetGenericArguments(), _nodeEngines); found = true; } } if (found == false) _otherEnginesReferences.Add(engine); } } if (engine is ICallBackOnAddEngine) (engine as ICallBackOnAddEngine).Ready(); } public void BuildEntity(int ID, EntityDescriptor ed) { var entityNodes = ed.BuildNodes(ID, (nodes) => { if (_engineRootWeakReference.IsValid == true) InternalRemove(nodes); }); _nodesToAdd.AddRange(entityNodes); } /// /// An entity group is a meta entity. It's a way to create a set of entitites that /// are not easily queriable otherwise. For example you may group existing entities /// by size and type and then use the groupID to retrieve a single node that is shared /// among the single entities of the same type and size. This willwd 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 entity group is 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 is then used by engines that are meant to manage a group of entities /// through a single node. The same engine can manage several groups of entitites. /// /// /// public void BuildEntityGroup(int groupID, EntityDescriptor ed) { var entityNodes = ed.BuildNodes(groupID, (nodes) => { if (_engineRootWeakReference.IsValid == true) InternalGroupRemove(nodes); }); _groupNodesToAdd.AddRange(entityNodes); } static void AddEngine(INodeEngine 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 AddNodeToGroupDB(INode node, Type nodeType) { FasterList nodes; if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == false) nodes = _nodesDBgroups[nodeType] = new FasterList(); nodes.Add(node); AddNodeToNodesDictionary(node, nodeType); } void AddNodeToTheDB(INode node, Type nodeType) { FasterList nodes; if (_nodesDB.TryGetValue(nodeType, out nodes) == false) nodes = _nodesDB[nodeType] = new FasterList(); nodes.Add(node); AddNodeToNodesDictionary(node, nodeType); } void AddNodeToNodesDictionary(INode node, Type nodeType) { if (node is NodeWithID) { Dictionary nodesDic; if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false) nodesDic = _nodesDBdic[nodeType] = new Dictionary(); nodesDic[(node as NodeWithID).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], node); #else enginesForNode[j].Add(node); #endif } } } void RemoveNodesFromDB(Dictionary> DB, FasterReadOnlyList nodes) { for (int i = 0; i < nodes.Count; i++) { FasterList nodesInDB; var node = nodes[i]; var nodeType = node.GetType(); if (DB.TryGetValue(nodeType, out nodesInDB) == true) nodesInDB.UnorderredRemove(node); //should I remove it from the dictionary if length is zero? if (node is NodeWithID) { Dictionary nodesDic; if (_nodesDBdic.TryGetValue(nodeType, out nodesDic)) nodesDic.Remove((node as NodeWithID).ID); } } } void RemoveNodesFromEngines(FasterReadOnlyList nodes) { for (int i = 0; i < nodes.Count; i++) { FasterList enginesForNode; var node = nodes[i]; if (_nodeEngines.TryGetValue(node.GetType(), out enginesForNode)) { for (int j = 0; j < enginesForNode.Count; j++) { #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, enginesForNode[j], node); #else enginesForNode[j].Remove(node); #endif } } } } #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR static void AddNodeToEngine(INodeEngine engine, INode node) { engine.Add(node); } static void RemoveNodeFromEngine(INodeEngine engine, INode node) { engine.Remove(node); } #endif void InternalRemove(FasterReadOnlyList nodes) { RemoveNodesFromEngines(nodes); RemoveNodesFromDB(_nodesDB, nodes); } void InternalGroupRemove(FasterReadOnlyList nodes) { RemoveNodesFromEngines(nodes); RemoveNodesFromDB(_nodesDBgroups, nodes); } Dictionary> _nodeEngines; FasterList _otherEnginesReferences; Dictionary> _nodesDB; Dictionary> _nodesDBgroups; Dictionary> _nodesDBdic; FasterList _nodesToAdd; FasterList _groupNodesToAdd; WeakReference _engineRootWeakReference; NodeSubmissionScheduler _scheduler; } }