using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Svelto.DataStructures; using Svelto.ECS.Internal; using Svelto.ECS.Legacy; using Svelto.ECS.NodeSchedulers; using Svelto.ECS.Profiler; using Svelto.Utilities; #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR using Svelto.ECS.Profiler; #endif namespace Svelto.ECS.Internal { struct BuildNodeCallbackStruct { public Action, int> internalRemove; public Action, int> internalEnable; public Action, int> internalDisable; } } namespace Svelto.ECS { public sealed class EnginesRoot : IEnginesRoot, IEntityFactory { public EnginesRoot(NodeSubmissionScheduler nodeScheduler) { _nodeEngines = new Dictionary>(); _activableEngines = new Dictionary>(); _otherEngines = new FasterList(); _engineRootWeakReference = new DataStructures.WeakReference(this); _nodesDB = new Dictionary(); _metaNodesDB = new Dictionary(); _groupNodesDB = new Dictionary>(); _nodesDBdic = new Dictionary(); _sharedStructNodeLists = new SharedStructNodeLists(); _sharedGroupedStructNodeLists = new SharedGroupedStructNodesLists(); _nodesToAdd = new DoubleBufferedNodes>(); _metaNodesToAdd = new DoubleBufferedNodes>(); _groupedNodesToAdd = new DoubleBufferedNodes>>(); _callBackStructForBuiltGroupedNodes = new BuildNodeCallbackStruct(); _callBackStructForBuiltGroupedNodes.internalRemove = InternalRemove; _callBackStructForBuiltGroupedNodes.internalDisable = InternalDisable; _callBackStructForBuiltGroupedNodes.internalEnable = InternalEnable; _callBackStructForBuiltNodes = new BuildNodeCallbackStruct(); _callBackStructForBuiltNodes.internalRemove = InternalGroupedRemove; _callBackStructForBuiltNodes.internalDisable = InternalDisable; _callBackStructForBuiltNodes.internalEnable = InternalEnable; _callBackStructForBuiltMetaNodes = new BuildNodeCallbackStruct(); _callBackStructForBuiltMetaNodes.internalRemove = InternalMetaRemove; _callBackStructForBuiltMetaNodes.internalDisable = InternalDisable; _callBackStructForBuiltMetaNodes.internalEnable = InternalEnable; _scheduler = nodeScheduler; _scheduler.Schedule(SubmitNodes); _structNodeEngineType = typeof(IStructNodeEngine<>); _groupedStructNodesEngineType = typeof(IGroupedStructNodesEngine<>); _activableNodeEngineType = typeof(IActivableNodeEngine<>); _implementedInterfaceTypes = new Dictionary(); #if UNITY_EDITOR UnityEngine.GameObject debugEngineObject = new UnityEngine.GameObject("Engine Debugger"); debugEngineObject.gameObject.AddComponent(); #endif } public void BuildEntity(int ID, EntityDescriptor ed) { ed.BuildNodes(ID, _nodesToAdd.other, ref _callBackStructForBuiltNodes); } /// /// 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. /// It's a way to control a group of Entities through a node only. /// This set of entities can share exactly the same node 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 nodes and iterate over /// them to set the same value, you can inject just one node, set the value /// and be sure that the value is shared between entities. /// /// /// public void BuildMetaEntity(int metaEntityID, EntityDescriptor ed) { ed.BuildNodes(metaEntityID, _metaNodesToAdd.other, ref _callBackStructForBuiltMetaNodes); } /// /// 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) { ed.BuildGroupedNodes(entityID, groupID, _groupedNodesToAdd.other, ref _callBackStructForBuiltGroupedNodes); } public void AddEngine(IEngine engine) { #if UNITY_EDITOR EngineProfiler.AddEngine(engine); #endif var queryableNodeEngine = engine as IQueryableNodeEngine; if (queryableNodeEngine != null) queryableNodeEngine.nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _metaNodesDB, _groupNodesDB); 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 (type.IsAssignableFrom(interfaceType) == false) continue; if (false == interfaceType.IsGenericTypeEx()) { 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) { var baseType = engineType.GetBaseType(); if (baseType.IsGenericTypeEx() && engine is INodeEngine) { AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines); engineAdded = true; return true; } return false; } 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); } } static void AddNodesToTheDBAndSuitableEngines(Dictionary nodesToAdd, Dictionary> nodeEngines, Dictionary nodesDBdic, Dictionary nodesDB) { foreach (var nodeList in nodesToAdd) { AddNodeToDB(nodesDB, nodeList); if (nodeList.Value.isQueryiableNode) { AddNodeToNodesDictionary(nodesDBdic, nodeList.Value, nodeList.Key); foreach (var node in nodeList.Value) AddNodeToTheSuitableEngines(nodeEngines, node as NodeWithID, nodeList.Key); } } } static void AddGroupNodesToTheDBAndSuitableEngines(Dictionary> groupedNodesToAdd, Dictionary> nodeEngines, Dictionary nodesDBdic, Dictionary> groupNodesDB, Dictionary nodesDB) { foreach (var group in groupedNodesToAdd) { AddNodesToTheDBAndSuitableEngines(group.Value, nodeEngines, nodesDBdic, nodesDB); AddNodesToGroupDB(groupNodesDB, @group); } } static void AddNodesToGroupDB(Dictionary> groupNodesDB, KeyValuePair> @group) { Dictionary groupedNodesByType; if (groupNodesDB.TryGetValue(@group.Key, out groupedNodesByType) == false) groupedNodesByType = groupNodesDB[@group.Key] = new Dictionary(); foreach (var node in @group.Value) { groupedNodesByType.Add(node.Key, node.Value); } } static void AddNodeToDB(Dictionary nodesDB, KeyValuePair nodeList) { ITypeSafeList dbList; if (nodesDB.TryGetValue(nodeList.Key, out dbList) == false) dbList = nodesDB[nodeList.Key] = nodeList.Value.Create(); dbList.AddRange(nodeList.Value); } static void AddNodeToNodesDictionary(Dictionary nodesDBdic, ITypeSafeList nodes, Type nodeType) { ITypeSafeDictionary nodesDic; if (nodesDBdic.TryGetValue(nodeType, out nodesDic) == false) nodesDic = nodesDBdic[nodeType] = nodes.CreateIndexedDictionary(); nodesDic.FillWithIndexedNodes(nodes); } static void AddNodeToTheSuitableEngines(Dictionary> nodeEngines, NodeWithID 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.MonitorRemoveDuration(RemoveNodeFromEngine, (enginesForNode[j] as INodeEngine), node); #else (enginesForNode[j] as INodeEngine).Add(node); #endif } } } /* void DisableNodeFromEngines(INode node, Type nodeType) { ITypeSafeList 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) { ITypeSafeList 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 nodeBuilders, int entityID) { /* 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 nodeBuilders, int entityID) {/* 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 nodeBuilders, int entityID) { if (_engineRootWeakReference.IsValid == false) return; int nodeBuildersCount = nodeBuilders.Count; for (int i = 0; i < nodeBuildersCount; i++) { Type nodeType = nodeBuilders[i].GetType(); ITypeSafeList nodes; if (_nodesDB.TryGetValue(nodeType, out nodes) == true) nodes.UnorderedRemove(entityID); if (nodes.isQueryiableNode) { var node = _nodesDBdic[nodeType].GetIndexedNode(entityID); _nodesDBdic[nodeType].Remove(entityID); RemoveNodeFromEngines(_nodeEngines, node, nodeType); } } } void InternalGroupedRemove(FasterList nodeBuilders, int entityID) { } void InternalMetaRemove(FasterList nodeBuilders, int entityID) { } static void RemoveNodeFromEngines(Dictionary> nodeEngines, NodeWithID 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.MonitorRemoveDuration(RemoveNodeFromEngine, (enginesForNode[j] as INodeEngine), node); #else (enginesForNode[j] as INodeEngine).Remove(node); #endif } } } void SubmitNodes() { _nodesToAdd.Swap(); _metaNodesToAdd.Swap(); _groupedNodesToAdd.Swap(); bool newNodesHaveBeenAddedWhileIterating = _metaNodesToAdd.Count > 0 || _nodesToAdd.Count > 0 || _groupedNodesToAdd.Count > 0; int numberOfReenteringLoops = 0; while (newNodesHaveBeenAddedWhileIterating) { if ( _nodesToAdd.Count > 0) AddNodesToTheDBAndSuitableEngines(_nodesToAdd.current, _nodeEngines, _nodesDBdic, _nodesDB); if ( _metaNodesToAdd.Count > 0) AddNodesToTheDBAndSuitableEngines(_metaNodesToAdd.current, _nodeEngines, _nodesDBdic, _metaNodesDB); if (_groupedNodesToAdd.Count > 0) AddGroupNodesToTheDBAndSuitableEngines(_groupedNodesToAdd.current, _nodeEngines, _nodesDBdic, _groupNodesDB, _nodesDB); _nodesToAdd.Clear(); _metaNodesToAdd.Clear(); _groupedNodesToAdd.Clear(); _nodesToAdd.Swap(); _metaNodesToAdd.Swap(); _groupedNodesToAdd.Swap(); newNodesHaveBeenAddedWhileIterating = _metaNodesToAdd.Count > 0 || _nodesToAdd.Count > 0 || _groupedNodesToAdd.Count > 0; 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++; } } readonly Dictionary> _nodeEngines; readonly Dictionary> _activableEngines; readonly FasterList _otherEngines; readonly Dictionary _nodesDB; readonly Dictionary _metaNodesDB; readonly Dictionary> _groupNodesDB; readonly Dictionary _nodesDBdic; readonly DoubleBufferedNodes> _nodesToAdd; readonly DoubleBufferedNodes> _metaNodesToAdd; readonly DoubleBufferedNodes>> _groupedNodesToAdd; readonly NodeSubmissionScheduler _scheduler; readonly Type _structNodeEngineType; readonly Type _groupedStructNodesEngineType; readonly Type _activableNodeEngineType; readonly SharedStructNodeLists _sharedStructNodeLists; readonly SharedGroupedStructNodesLists _sharedGroupedStructNodeLists; readonly Dictionary _implementedInterfaceTypes; readonly DataStructures.WeakReference _engineRootWeakReference; BuildNodeCallbackStruct _callBackStructForBuiltNodes; BuildNodeCallbackStruct _callBackStructForBuiltGroupedNodes; BuildNodeCallbackStruct _callBackStructForBuiltMetaNodes; class DoubleBufferedNodes where T : class, IDictionary, new() { readonly T _nodesToAddBufferA = new T(); readonly T _nodesToAddBufferB = new T(); public DoubleBufferedNodes() { this.other = _nodesToAddBufferA; this.current = _nodesToAddBufferB; } public T other { get; private set; } public T current { get; private set; } public int Count { get { return current.Count; } } public void Clear() { current.Clear(); } public void Swap() { var toSwap = other; other = current; current = toSwap; } } } }