using System; using System.Collections; using System.Collections.Generic; using Svelto.DataStructures; using UnityEngine; 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 { class Scheduler : MonoBehaviour { IEnumerator Start() { while (true) { yield return new WaitForEndOfFrame(); OnTick(); } } internal Action OnTick; } public sealed class EnginesRoot : IEnginesRoot, IEntityFactory { public EnginesRoot() { _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>(); GameObject go = new GameObject("ECSScheduler"); go.AddComponent().OnTick += 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(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); return; } #if !NETFX_CORE var baseType = engine.GetType().BaseType; if (baseType.IsGenericType) #else var baseType = engine.GetType().GetTypeInfo().BaseType; if (baseType.IsConstructedGenericType) #endif { var genericType = baseType.GetGenericTypeDefinition(); if (genericType == typeof(SingleNodeEngine<>)) { AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines); return; } } _otherEnginesReferences.Add(engine); } public void BuildEntity(int ID, EntityDescriptor ed) { var entityNodes = ed.BuildNodes(ID, (node) => { if (_engineRootWeakReference.IsValid == true) InternalRemove(node); }); _nodesToAdd.AddRange(entityNodes); } public void BuildEntityGroup(int groupID, EntityDescriptor ed) { var entityNodes = ed.BuildNodes(groupID, (node) => { if (_engineRootWeakReference.IsValid == true) InternalGroupRemove(node); }); _groupNodesToAdd.AddRange(entityNodes); } static void AddEngine(T engine, Type[] types, Dictionary>> engines) where T : INodeEngine { 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(T node, Type nodeType) where T : INode { FasterList nodes; if (_nodesDB.TryGetValue(nodeType, out nodes) == false) nodes = _nodesDB[nodeType] = new FasterList(); nodes.Add(node); AddNodeToNodesDictionary(node, nodeType); } void AddNodeToNodesDictionary(T node, Type nodeType) where T : INode { 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(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.MonitorAddDuration(AddNodeToEngine, enginesForNode[j], node); #else enginesForNode[j].Add(node); #endif } } } void RemoveNodeFromTheDB(T node, Type nodeType) where T : INode { FasterList nodes; if (_nodesDB.TryGetValue(nodeType, out nodes) == true) nodes.UnorderredRemove(node); //should I remove it from the dictionary if length is zero? RemoveNodeFromNodesDictionary(node, nodeType); } void RemoveNodeFromNodesDictionary(T node, Type nodeType) where T : INode { if (node is NodeWithID) { Dictionary nodesDic; if (_nodesDBdic.TryGetValue(nodeType, out nodesDic)) nodesDic.Remove((node as NodeWithID).ID); } } void RemoveNodeFromGroupDB(T node, Type nodeType) where T : INode { FasterList nodes; if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == true) nodes.UnorderredRemove(node); //should I remove it from the dictionary if length is zero? RemoveNodeFromNodesDictionary(node, nodeType); } 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], node); #else enginesForNode[j].Remove(node); #endif } } } #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR void AddNodeToEngine(INodeEngine engine, INode node) { engine.Add(node); } void RemoveNodeFromEngine(INodeEngine engine, INode node) { engine.Remove(node); } #endif void InternalRemove(T node) where T : INode { Type nodeType = node.GetType(); RemoveNodeFromEngines(node, nodeType); RemoveNodeFromTheDB(node, node.GetType()); } void InternalGroupRemove(T node) where T : INode { Type nodeType = node.GetType(); RemoveNodeFromEngines(node, nodeType); RemoveNodeFromGroupDB(node, node.GetType()); } Dictionary>> _nodeEngines; FasterList _otherEnginesReferences; Dictionary> _nodesDB; Dictionary> _nodesDBdic; Dictionary> _nodesDBgroups; FasterList _nodesToAdd; FasterList _groupNodesToAdd; WeakReference _engineRootWeakReference; //integrated pooling system //add debug panel like Entitas has //GCHandle should be used to reduce the number of strong references //datastructure could be thread safe //future enhancements: } }