diff --git a/DataStructures/CircularBufferIndexer.cs b/DataStructures/CircularBufferIndexer.cs index cc9d397..a01d8ba 100644 --- a/DataStructures/CircularBufferIndexer.cs +++ b/DataStructures/CircularBufferIndexer.cs @@ -9,7 +9,7 @@ namespace Svelto.DataStructures // isn't taken into account (we would have to do a shift in both arrays) // Could be added as an option? - class CircularBufferIndexer : IDictionary + public class CircularBufferIndexer : IDictionary { public ICollection Keys { diff --git a/DataStructures/FasterList.cs b/DataStructures/FasterList.cs index bc96c16..40407e4 100644 --- a/DataStructures/FasterList.cs +++ b/DataStructures/FasterList.cs @@ -352,12 +352,12 @@ namespace Svelto.DataStructures } } - public void UnorderredRemoveAt(int index) + public void UnorderedRemoveAt(int index) { _lockQ.EnterWriteLock(); try { - _list.UnorderredRemoveAt(index); + _list.UnorderedRemoveAt(index); } finally { @@ -450,10 +450,12 @@ namespace Svelto.DataStructures readonly FasterList _list; } - public class FasterList : IList + public interface IFasterList + {} + + public class FasterList : IList, IFasterList { public static FasterList DefaultList = new FasterList(); - const int MIN_SIZE = 4; public int Count @@ -567,6 +569,11 @@ namespace Svelto.DataStructures _count += count; } + public void AddRange(T[] items) + { + AddRange(items, items.Length); + } + public FasterReadOnlyList AsReadOnly() { return new FasterReadOnlyList(this); @@ -703,32 +710,30 @@ namespace Svelto.DataStructures return _buffer; } - public bool UnorderredRemove(T item) + public bool UnorderedRemove(T item) { var index = IndexOf(item); if (index == -1) return false; - UnorderredRemoveAt(index); + UnorderedRemoveAt(index); return true; } - public T UnorderredRemoveAt(int index) + public bool UnorderedRemoveAt(int index) { DesignByContract.Check.Require(index < _count && _count > 0, "out of bound index"); - T item = _buffer[index]; - if (index == --_count) - return item; + return false; T swap = _buffer[index]; _buffer[index] = _buffer[_count]; _buffer[_count] = swap; - return item; + return true; } IEnumerator IEnumerable.GetEnumerator() diff --git a/DataStructures/Priority Queue/HeapPriorityQueue.cs b/DataStructures/Priority Queue/HeapPriorityQueue.cs index 2774d57..c30e06d 100644 --- a/DataStructures/Priority Queue/HeapPriorityQueue.cs +++ b/DataStructures/Priority Queue/HeapPriorityQueue.cs @@ -13,9 +13,9 @@ namespace Svelto.DataStructures public sealed class HeapPriorityQueue : IPriorityQueue where T : PriorityQueueNode { - int _numNodes; - private readonly FasterList _nodes; - long _numNodesEverEnqueued; + private int _numNodes; + private readonly FasterList _nodes; + private long _numNodesEverEnqueued; /// /// Instantiate a new Priority Queue @@ -101,10 +101,10 @@ namespace Svelto.DataStructures CascadeUp(_nodes[_numNodes]); } -#if NET_VERSION_4_5 + #if NET_VERSION_4_5 [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - void Swap(T node1, T node2) + #endif + private void Swap(T node1, T node2) { //Swap the nodes _nodes[node1.QueueIndex] = node2; @@ -117,14 +117,14 @@ namespace Svelto.DataStructures } //Performance appears to be slightly better when this is NOT inlined o_O - void CascadeUp(T node) + private void CascadeUp(T node) { //aka Heapify-up int parent = node.QueueIndex / 2; - while (parent >= 1) + while(parent >= 1) { T parentNode = _nodes[parent]; - if (HasHigherPriority(parentNode, node)) + if(HasHigherPriority(parentNode, node)) break; //Node has lower priority value, so move it up the heap @@ -134,9 +134,9 @@ namespace Svelto.DataStructures } } -#if NET_VERSION_4_5 + #if NET_VERSION_4_5 [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif + #endif private void CascadeDown(T node) { //aka Heapify-down @@ -198,10 +198,10 @@ namespace Svelto.DataStructures /// Returns true if 'higher' has higher priority than 'lower', false otherwise. /// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false /// -#if NET_VERSION_4_5 + #if NET_VERSION_4_5 [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - bool HasHigherPriority(T higher, T lower) + #endif + private bool HasHigherPriority(T higher, T lower) { return (higher.Priority < lower.Priority || (higher.Priority == lower.Priority && higher.InsertionIndex < lower.InsertionIndex)); @@ -242,13 +242,13 @@ namespace Svelto.DataStructures OnNodeUpdated(node); } - void OnNodeUpdated(T node) + private void OnNodeUpdated(T node) { //Bubble the updated node up or down as appropriate int parentIndex = node.QueueIndex / 2; T parentNode = _nodes[parentIndex]; - if (parentIndex > 0 && HasHigherPriority(node, parentNode)) + if(parentIndex > 0 && HasHigherPriority(node, parentNode)) { CascadeUp(node); } diff --git a/ECS/EngineNodeDB.cs b/ECS/EngineNodeDB.cs index 4a31542..36bef54 100644 --- a/ECS/EngineNodeDB.cs +++ b/ECS/EngineNodeDB.cs @@ -1,77 +1,71 @@ using System; using System.Collections.Generic; using Svelto.DataStructures; +using Svelto.ECS.Internal; namespace Svelto.ECS { - class EngineNodeDB : IEngineNodeDB + public class EngineNodeDB : IEngineNodeDB { internal EngineNodeDB( Dictionary> nodesDB, Dictionary> nodesDBdic, - Dictionary> nodesDBgroups) + Dictionary> metaNodesDB, + Dictionary structNodesDB) { - _nodesDB = new DataStructures.WeakReference>>(nodesDB); - _nodesDBdic = new DataStructures.WeakReference>>(nodesDBdic); - _nodesDBgroups = new DataStructures.WeakReference>>(nodesDBgroups); + _nodesDB = nodesDB; + _nodesDBdic = nodesDBdic; + _metaNodesDB = metaNodesDB; + _structNodesDB = structNodesDB; } public FasterReadOnlyListCast QueryNodes() where T:INode { var type = typeof(T); - if (_nodesDB.IsValid == false || _nodesDB.Target.ContainsKey(type) == false) + if (_nodesDB.ContainsKey(type) == false) return RetrieveEmptyNodeList(); - return new FasterReadOnlyListCast(_nodesDB.Target[type]); + return new FasterReadOnlyListCast(_nodesDB[type]); } - /* public FasterReadOnlyList QueryStructNodes() where T:struct - { - var type = typeof(T); - - if (_nodesDBStructs.ContainsKey(type) == false) - return RetrieveEmptyStructNodeList(); - - return new FasterReadOnlyList(((StructNodeList)(_nodesDBStructs[type])).list); - }*/ - public ReadOnlyDictionary QueryIndexableNodes() where T:INode { var type = typeof(T); - if (_nodesDB.IsValid == false || _nodesDBdic.Target.ContainsKey(type) == false) + if (_nodesDBdic.ContainsKey(type) == false) return _defaultEmptyNodeDict; - return new ReadOnlyDictionary(_nodesDBdic.Target[type]); + return new ReadOnlyDictionary(_nodesDBdic[type]); } - public T QueryNodeFromGroup(int groupID) where T : INode + public T QueryMetaNode(int metaEntityID) where T : INode { - return QueryNode(groupID); + return QueryNode(metaEntityID); } - public bool QueryNodeFromGroup(int groupID, out T node) where T : INode + public bool TryQueryMetaNode(int metaEntityID, out T node) where T : INode { - return QueryNode(groupID, out node); + return TryQueryNode(metaEntityID, out node); } - public FasterReadOnlyListCast QueryNodesFromGroups() where T : INode + public FasterReadOnlyListCast QueryMetaNodes() where T : INode { var type = typeof(T); - if (_nodesDBgroups.IsValid == false || _nodesDBgroups.Target.ContainsKey(type) == false) + if (_metaNodesDB.ContainsKey(type) == false) return RetrieveEmptyNodeList(); - return new FasterReadOnlyListCast(_nodesDBgroups.Target[type]); + return new FasterReadOnlyListCast(_metaNodesDB[type]); } - public bool QueryNode(int ID, out T node) where T:INode + public bool TryQueryNode(int ID, out T node) where T:INode { var type = typeof(T); INode internalNode; - if (_nodesDBdic.IsValid && _nodesDBdic.Target.ContainsKey(type) && _nodesDBdic.Target[type].TryGetValue(ID, out internalNode)) + if (_nodesDBdic.ContainsKey(type) && + _nodesDBdic[type].TryGetValue(ID, out internalNode)) { node = (T)internalNode; @@ -89,39 +83,33 @@ namespace Svelto.ECS INode internalNode; - if (_nodesDBdic.IsValid && _nodesDBdic.Target.ContainsKey(type) && _nodesDBdic.Target[type].TryGetValue(ID, out internalNode)) + if (_nodesDBdic.ContainsKey(type) && + _nodesDBdic[type].TryGetValue(ID, out internalNode)) return (T)internalNode; throw new Exception("Node Not Found"); } - static FasterReadOnlyListCast RetrieveEmptyNodeList() where T : INode + public FasterReadOnlyListCast QueryGroupNodes(int groupID) where T : INode { - return new FasterReadOnlyListCast(FasterList.DefaultList); + var type = typeof(T); + + if (_nodesDB.ContainsKey(type) == false) + return RetrieveEmptyNodeList(); + + return new FasterReadOnlyListCast(_nodesDB[type]); } - static FasterReadOnlyList RetrieveEmptyStructNodeList() where T : struct + static FasterReadOnlyListCast RetrieveEmptyNodeList() where T : INode { - return new FasterReadOnlyList(FasterList.DefaultList); + return new FasterReadOnlyListCast(FasterList.DefaultList); } - Svelto.DataStructures.WeakReference>> _nodesDB; - Svelto.DataStructures.WeakReference>> _nodesDBdic; - Svelto.DataStructures.WeakReference>> _nodesDBgroups; -// Dictionary _nodesDBStructs; - - //Dictionary> _nodesDB; - //Dictionary> _nodesDBdic; -// Dictionary> _nodesDBgroups; + readonly Dictionary> _nodesDB; + readonly Dictionary> _nodesDBdic; + readonly Dictionary> _metaNodesDB; + readonly Dictionary _structNodesDB; - ReadOnlyDictionary _defaultEmptyNodeDict = new ReadOnlyDictionary(new Dictionary()); - - class StructNodeList - { } - - class StructNodeList : StructNodeList where T : struct - { - public FasterList list = new FasterList(); - } + readonly ReadOnlyDictionary _defaultEmptyNodeDict = new ReadOnlyDictionary(new Dictionary()); } } diff --git a/ECS/EnginesRoot.cs b/ECS/EnginesRoot.cs index 0807d13..a70807b 100644 --- a/ECS/EnginesRoot.cs +++ b/ECS/EnginesRoot.cs @@ -2,10 +2,11 @@ using System; using System.Collections; using System.Collections.Generic; using Svelto.DataStructures; +using Svelto.ECS.Internal; +using Svelto.ECS.NodeSchedulers; 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 @@ -15,25 +16,54 @@ using System.Reflection; namespace Svelto.ECS { + class Scheduler : MonoBehaviour + { + IEnumerator Start() + { + while (true) + { + yield return _waitForEndOfFrame; + + OnTick(); + } + } + + internal Action OnTick; + + readonly WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame(); + } + public sealed class EnginesRoot : IEnginesRoot, IEntityFactory { public EnginesRoot(NodeSubmissionScheduler nodeScheduler) { - _nodeEngines = new Dictionary>(); + _nodeEngines = new Dictionary>(); + _activableEngines = new Dictionary>(); + _otherEngines = new FasterList(); + _engineRootWeakReference = new WeakReference(this); - _otherEnginesReferences = new FasterList(); _nodesDB = new Dictionary>(); _nodesDBdic = new Dictionary>(); _nodesToAdd = new FasterList(); - _groupNodesToAdd = new FasterList(); + _metaNodesToAdd = new FasterList(); + + _metaNodesDB = new Dictionary>(); + _structNodesDB = new Dictionary(); - _nodesDBgroups = new Dictionary>(); + _internalRemove = InternalRemove; + _internalDisable = InternalDisable; + _internalEnable = InternalEnable; + _internalMetaRemove = InternalMetaRemove; _scheduler = nodeScheduler; _scheduler.Schedule(SubmitNodes); + _activableNodeEngineType = typeof(IActivableNodeEngine<>); + + _implementedInterfaceTypes = new Dictionary(); + #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR GameObject debugEngineObject = new GameObject("Engine Debugger"); debugEngineObject.gameObject.AddComponent(); @@ -42,28 +72,38 @@ namespace Svelto.ECS void SubmitNodes() { - int groupNodesCount; - int nodesCount; + int metaNodesCount = _metaNodesToAdd.Count; + int nodesCount = _nodesToAdd.Count; + + if (metaNodesCount + nodesCount == 0) return; + bool newNodesHaveBeenAddedWhileIterating; int startNodes = 0; - int startGroupNodes = 0; + int startMetaNodes = 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()); + var nodeType = node.GetType(); + + AddNodeToTheDB(node, nodeType); + var nodeWithId = node as INodeWithID; + if (nodeWithId != null) + AddNodeToNodesDictionary(nodeWithId, nodeType); } - for (int i = startGroupNodes; i < groupNodesCount; i++) + for (int i = startMetaNodes; i < metaNodesCount; i++) { - var node = _groupNodesToAdd[i]; - AddNodeToGroupDB(node, node.GetType()); + 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++) @@ -72,151 +112,223 @@ namespace Svelto.ECS AddNodeToTheSuitableEngines(node, node.GetType()); } - for (int i = startGroupNodes; i < groupNodesCount; i++) + for (int i = startMetaNodes; i < metaNodesCount; i++) { - var node = _groupNodesToAdd[i]; + var node = _metaNodesToAdd[i]; AddNodeToTheSuitableEngines(node, node.GetType()); } - newNodesHaveBeenAddedWhileIterating = _groupNodesToAdd.Count > groupNodesCount || _nodesToAdd.Count > nodesCount; + newNodesHaveBeenAddedWhileIterating = + _metaNodesToAdd.Count > metaNodesCount || + _nodesToAdd.Count > nodesCount; startNodes = nodesCount; - startGroupNodes = groupNodesCount; + 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++; + numberOfReenteringLoops++; + + metaNodesCount = _metaNodesToAdd.Count; + nodesCount = _nodesToAdd.Count; } while (newNodesHaveBeenAddedWhileIterating); _nodesToAdd.Clear(); - _groupNodesToAdd.Clear(); + _metaNodesToAdd.Clear(); } - public void AddEngine(INodeEngine engine) where T:class, INode + public void AddEngine(IEngine engine) { - AddEngine(new NodeEngineWrapper(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, _structNodesDB); - if (engine is IQueryableNodeEngine) - (engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _nodesDBgroups); - } + var engineType = engine.GetType(); + var implementedInterfaces = engineType.GetInterfaces(); - public void AddEngine(INodeEngine engine) where T:class, INode where U:class, INode - { - AddEngine(new NodeEngineWrapper(engine)); - AddEngine((INodeEngine)(engine)); + 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(); } - public void AddEngine(INodeEngine engine) where T:class, INode - where U:class, INode - where V:class, INode + void CollectImplementedInterfaces(Type[] implementedInterfaces) { - AddEngine(new NodeEngineWrapper(engine)); - AddEngine((INodeEngine)(engine)); + _implementedInterfaceTypes.Clear(); + + for (int index = 0; index < implementedInterfaces.Length; index++) + { + var interfaceType = implementedInterfaces[index]; +#if !NETFX_CORE + + if (false == interfaceType.IsGenericType) +#else + if (false == interfaceType.IsConstructedGenericType) +#endif + { + continue; + } + + var genericTypeDefinition = interfaceType.GetGenericTypeDefinition(); + var a = interfaceType.GetGenericArguments(); + var b = genericTypeDefinition.GetGenericArguments(); + + _implementedInterfaceTypes.Add(genericTypeDefinition, + interfaceType.GetGenericArguments()); + } } - public void AddEngine(IEngine engine) + bool CheckGenericEngines(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 (_implementedInterfaceTypes.Count == 0) return false; - if (engine is INodesEngine) + bool engineAdded = false; + + Type[] arguments; + if (_implementedInterfaceTypes.TryGetValue(_activableNodeEngineType, + out arguments)) { - var nodesEngine = engine as INodesEngine; + AddEngine(engine, arguments, _activableEngines); - AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines); + engineAdded = true; } - else + + return engineAdded; + } + + bool CheckLegacyNodesEngine(IEngine engine, ref bool engineAdded) + { + var nodesEngine = engine as INodesEngine; + if (nodesEngine != null) { - var engineType = engine.GetType(); + AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines); -#if !NETFX_CORE - var baseType = engineType.BaseType; + engineAdded = true; + + return true; + } + + return false; + } - if (baseType.IsGenericType + 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 - && 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); + && engine is INodeEngine) + { + AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines); - found = true; - } - } + engineAdded = true; - if (found == false) - _otherEnginesReferences.Add(engine); - } + return true; } - - if (engine is ICallBackOnAddEngine) - (engine as ICallBackOnAddEngine).Ready(); + + return false; } public void BuildEntity(int ID, EntityDescriptor ed) { - var entityNodes = ed.BuildNodes(ID, (nodes) => - { - if (_engineRootWeakReference.IsValid == true) - InternalRemove(nodes); - }); - + var entityNodes = ed.BuildNodes(ID, + _internalRemove, + _internalEnable, + _internalDisable + ); + _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 + /// 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 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. + /// 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 BuildEntityGroup(int groupID, EntityDescriptor ed) + public void BuildEntityInGroup(int entityID, int groupID, + EntityDescriptor ed) { - var entityNodes = ed.BuildNodes(groupID, (nodes) => - { - if (_engineRootWeakReference.IsValid == true) - InternalGroupRemove(nodes); - }); + var entityNodes = ed.BuildNodes(entityID, + _internalRemove, + _internalEnable, + _internalDisable + ); + + _nodesToAdd.AddRange(entityNodes); - _groupNodesToAdd.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(INodeEngine engine, Type[] types, Dictionary> engines) + static void AddEngine(IEngine engine, Type[] types, + Dictionary> engines) { for (int i = 0; i < types.Length; i++) { - FasterList list; + FasterList list; var type = types[i]; if (engines.TryGetValue(type, out list) == false) { - list = new FasterList(); + list = new FasterList(); engines.Add(type, list); } @@ -225,136 +337,223 @@ namespace Svelto.ECS } } - void AddNodeToGroupDB(INode node, Type nodeType) + void AddNodeToMetaDB(INode node, Type nodeType) { FasterList nodes; - if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == false) - nodes = _nodesDBgroups[nodeType] = new FasterList(); + if (_metaNodesDB.TryGetValue(nodeType, out nodes) == false) + nodes = _metaNodesDB[nodeType] = new FasterList(); nodes.Add(node); - - AddNodeToNodesDictionary(node, nodeType); } - void AddNodeToTheDB(INode node, Type 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); + if (node is IStructNodeWithID == false) + { + if (_nodesDB.TryGetValue(nodeType, out nodes) == false) + nodes = _nodesDB[nodeType] = new FasterList(); - AddNodeToNodesDictionary(node, nodeType); + nodes.Add(node); + } + else + { + if (_nodesDB.TryGetValue(nodeType, out nodes) == false) + nodes = _nodesDB[nodeType] = new FasterList(); + } } - void AddNodeToNodesDictionary(INode node, Type nodeType) + void AddNodeToNodesDictionary(T node, Type nodeType) where T : INodeWithID { - if (node is NodeWithID) - { - Dictionary nodesDic; - if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false) - nodesDic = _nodesDBdic[nodeType] = new Dictionary(); + Dictionary nodesDic; - nodesDic[(node as NodeWithID).ID] = node; - } + 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; + 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); + EngineProfiler.MonitorAddDuration(AddNodeToEngine, enginesForNode[j] as INodeEngine, node); #else - enginesForNode[j].Add(node); + (enginesForNode[j] as INodeEngine).Add(node); #endif } } } - void RemoveNodesFromDB(Dictionary> DB, FasterReadOnlyList nodes) + void RemoveNodeFromTheDB(T node, Type nodeType) where T : INode { - for (int i = 0; i < nodes.Count; i++) - { - FasterList nodesInDB; + FasterList nodes; + if (_nodesDB.TryGetValue(nodeType, out nodes) == true) + nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero? + } - 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? + 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? + } - if (node is NodeWithID) - { - Dictionary nodesDic; + void RemoveNodeFromNodesDictionary(T node, Type nodeType) where T : INodeWithID + { + Dictionary nodesDic; - if (_nodesDBdic.TryGetValue(nodeType, out nodesDic)) - nodesDic.Remove((node as NodeWithID).ID); - } - } + if (_nodesDBdic.TryGetValue(nodeType, out nodesDic)) + nodesDic.Remove(node.ID); } - void RemoveNodesFromEngines(FasterReadOnlyList nodes) + void RemoveNodeFromEngines(T node, Type nodeType) where T : INode { - for (int i = 0; i < nodes.Count; i++) - { - FasterList enginesForNode; - var node = nodes[i]; + FasterList enginesForNode; - if (_nodeEngines.TryGetValue(node.GetType(), out enginesForNode)) + if (_nodeEngines.TryGetValue(nodeType, out enginesForNode)) + { + for (int j = 0; j < enginesForNode.Count; j++) { - for (int j = 0; j < enginesForNode.Count; j++) - { #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR - EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, enginesForNode[j], node); + EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, (enginesForNode[j] as INodeEngine), node); #else - enginesForNode[j].Remove(node); + (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 - static void AddNodeToEngine(INodeEngine engine, INode node) + void AddNodeToEngine(IEngine engine, INode node) { - engine.Add(node); + (engine as INodeEngine).Add(node); } - static void RemoveNodeFromEngine(INodeEngine engine, INode node) + void RemoveNodeFromEngine(IEngine engine, INode node) { - engine.Remove(node); + (engine as INodeEngine).Remove(node); } #endif - void InternalRemove(FasterReadOnlyList nodes) + 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) { - RemoveNodesFromEngines(nodes); - RemoveNodesFromDB(_nodesDB, 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 InternalGroupRemove(FasterReadOnlyList nodes) + void InternalMetaRemove(FasterList nodes) { - RemoveNodesFromEngines(nodes); - RemoveNodesFromDB(_nodesDBgroups, 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); + } } - Dictionary> _nodeEngines; - FasterList _otherEnginesReferences; + readonly Dictionary> _nodeEngines; + readonly Dictionary> _activableEngines; + + readonly FasterList _otherEngines; + + readonly Dictionary> _nodesDB; + readonly Dictionary> _metaNodesDB; + readonly Dictionary _structNodesDB; + + readonly Dictionary> _nodesDBdic; + + readonly FasterList _nodesToAdd; + readonly FasterList _metaNodesToAdd; + + readonly WeakReference _engineRootWeakReference; - Dictionary> _nodesDB; - Dictionary> _nodesDBgroups; + readonly NodeSubmissionScheduler _scheduler; - Dictionary> _nodesDBdic; + readonly Action> _internalRemove; + readonly Action> _internalEnable; + readonly Action> _internalDisable; + readonly Action> _internalMetaRemove; - FasterList _nodesToAdd; - FasterList _groupNodesToAdd; + readonly Type _activableNodeEngineType; - WeakReference _engineRootWeakReference; - NodeSubmissionScheduler _scheduler; + readonly Dictionary _implementedInterfaceTypes; } } diff --git a/ECS/EntityDescriptor.cs b/ECS/EntityDescriptor.cs index d818dce..1550f60 100644 --- a/ECS/EntityDescriptor.cs +++ b/ECS/EntityDescriptor.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Reflection; using Svelto.DataStructures; #if NETFX_CORE @@ -11,39 +12,53 @@ namespace Svelto.ECS { protected EntityDescriptor(INodeBuilder[] nodesToBuild, params object[] componentsImplementor) { - _implementors = componentsImplementor; - _nodesToBuild = nodesToBuild; + _nodesToBuild = new FasterList(nodesToBuild); + + ProcessImplementors(componentsImplementor); } - /* protected EntityDescriptor(IStructNodeBuilder[] structNodesToBuild) + public void AddImplementors(params object[] componentsImplementor) { - _structNodesToBuild = structNodesToBuild; - }*/ + ProcessImplementors(componentsImplementor); + } - public void AddImplementors(params object[] componentsImplementor) + void ProcessImplementors(object[] implementors) { - var implementors = new object[componentsImplementor.Length + _implementors.Length]; + for (int index = 0; index < implementors.Length; index++) + { + var implementor = implementors[index]; + if (implementor is IRemoveEntityComponent) + _removingImplementors.Add(implementor as IRemoveEntityComponent); + if (implementor is IDisableEntityComponent) + _disablingImplementors.Add(implementor as IDisableEntityComponent); + if (implementor is IEnableEntityComponent) + _enablingImplementors.Add(implementor as IEnableEntityComponent); - Array.Copy(_implementors, implementors, _implementors.Length); - Array.Copy(componentsImplementor, 0, implementors, _implementors.Length, componentsImplementor.Length); + var interfaces = implementor.GetType().GetInterfaces(); - _implementors = implementors; + for (int iindex = 0; iindex < interfaces.Length; iindex++) + { + _implementorsByType[interfaces[iindex]] = implementor; + } + } + } + + public void AddNodes(params INodeBuilder[] nodesWithID) + { + _nodesToBuild.AddRange(nodesWithID); } - public virtual FasterList BuildNodes(int ID, Action> removeAction) + public virtual FasterList BuildNodes(int ID) { var nodes = new FasterList(); - for (int index = _nodesToBuild.Length - 1; index >= 0; index--) + for (int index = 0; index < _nodesToBuild.Count; index++) { var nodeBuilder = _nodesToBuild[index]; - var node = FillNode(nodeBuilder.Build(ID), () => - { - removeAction(new FasterReadOnlyList()); + var node = nodeBuilder.Build(ID); - nodes.Clear(); - } - ); + if (nodeBuilder.reflects != FillNodeMode.None) + node = FillNode(node, nodeBuilder.reflects); nodes.Add(node); } @@ -51,74 +66,116 @@ namespace Svelto.ECS return nodes; } - TNode FillNode(TNode node, Action removeAction) where TNode: INode + internal FasterList BuildNodes(int ID, + Action> removeEntity, + Action> enableEntity, + Action> disableEntity) { - var fields = node.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); - - for (int i = fields.Length - 1; i >=0 ; --i) - { - var field = fields[i]; - Type fieldType = field.FieldType; - object component = null; + var nodes = BuildNodes(ID); - for (int j = 0; j < _implementors.Length; j++) - { - var implementor = _implementors[j]; + SetupImplementors(removeEntity, enableEntity, disableEntity, nodes); - if (implementor != null && fieldType.IsAssignableFrom(implementor.GetType())) - { - component = implementor; + return nodes; + } - if (fieldType.IsAssignableFrom(typeof(IRemoveEntityComponent))) - (component as IRemoveEntityComponent).removeEntity = removeAction; + void SetupImplementors( + Action> removeEntity, + Action> enableEntity, + Action> disableEntity, + FasterList nodes) + { + Action removeEntityAction = () => + { removeEntity(nodes); nodes.Clear(); }; + Action disableEntityAction = () => + { disableEntity(nodes); }; + Action enableEntityAction = () => + { enableEntity(nodes); }; + + for (int index = 0; index < _removingImplementors.Count; index++) + _removingImplementors[index].removeEntity = removeEntityAction; + for (int index = 0; index < _disablingImplementors.Count; index++) + _disablingImplementors[index].disableEntity = disableEntityAction; + for (int index = 0; index < _enablingImplementors.Count; index++) + _enablingImplementors[index].enableEntity = enableEntityAction; + } - break; - } - } + INode FillNode(INode node, FillNodeMode mode) + { + var fields = node.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); - if (component == null) + for (int i = fields.Length - 1; i >= 0; --i) + { + var field = fields[i]; + Type fieldType = field.FieldType; + object component; + + if ((_implementorsByType.TryGetValue(fieldType, out component)) == false) { - Exception e = new Exception("Svelto.ECS: Implementor not found for a Node. " + - "Implementor Type: " + field.FieldType.Name + " - Node: " + node.GetType().Name + " - EntityDescriptor " + this); + if (mode == FillNodeMode.Strict) + { + Exception e = + new Exception("Svelto.ECS: Implementor not found for a Node. " + "Implementor Type: " + + field.FieldType.Name + " - Node: " + node.GetType().Name + + " - EntityDescriptor " + this); - throw e; + throw e; + } } - - field.SetValue(node, component); + else + field.SetValue(node, component); } return node; } - object[] _implementors; - - readonly INodeBuilder[] _nodesToBuild; - // readonly IStructNodeBuilder[] _structNodesToBuild; + readonly FasterList _disablingImplementors = new FasterList(); + readonly FasterList _removingImplementors = new FasterList(); + readonly FasterList _enablingImplementors = new FasterList(); + + readonly Dictionary _implementorsByType = new Dictionary(); + readonly FasterList _nodesToBuild; } public interface INodeBuilder { - NodeWithID Build(int ID); + INodeWithID Build(int ID); + + FillNodeMode reflects { get; } } - public class NodeBuilder : INodeBuilder where NodeType:NodeWithID, new() + public class NodeBuilder : INodeBuilder where NodeType : NodeWithID, new() { - public NodeWithID Build(int ID) + public INodeWithID Build(int ID) { NodeWithID node = NodeWithID.BuildNode(ID); return (NodeType)node; } + + public FillNodeMode reflects { get { return FillNodeMode.Strict; } } } -/* - public interface IStructNodeBuilder - {} - public class StructNodeBuilder : IStructNodeBuilder where NodeType : struct + //To Do: Probably I will need to add an + //FastStructNodeBuilder where reflects is false + public class StructNodeBuilder : INodeBuilder + where NodeType : struct, IStructNodeWithID { - public NodeType Build() + public INodeWithID Build(int ID) { - return new NodeType(); + IStructNodeWithID node = default(NodeType); + node.ID = ID; + + return node; } - }*/ + + public FillNodeMode reflects { get { return FillNodeMode.Relaxed; } } + } + + public enum FillNodeMode + { + Strict, + Relaxed, + + None + } } diff --git a/Extensions/Unity/UnitySumbmissionNodeScheduler.cs b/ECS/Extensions/Unity/UnitySumbmissionNodeScheduler.cs similarity index 82% rename from Extensions/Unity/UnitySumbmissionNodeScheduler.cs rename to ECS/Extensions/Unity/UnitySumbmissionNodeScheduler.cs index e79a425..c211f73 100644 --- a/Extensions/Unity/UnitySumbmissionNodeScheduler.cs +++ b/ECS/Extensions/Unity/UnitySumbmissionNodeScheduler.cs @@ -1,4 +1,5 @@ -using System; +#if UNITY_5 || UNITY_5_3_OR_NEWER +using System; using System.Collections; using UnityEngine; @@ -24,15 +25,17 @@ namespace Svelto.ECS.NodeSchedulers { while (true) { - yield return new WaitForEndOfFrame(); + yield return _wait; OnTick(); } } internal Action OnTick; + WaitForEndOfFrame _wait = new WaitForEndOfFrame(); } Scheduler _scheduler; } } +#endif \ No newline at end of file diff --git a/ECS/GenericEntityDescriptor.cs b/ECS/GenericEntityDescriptor.cs index 62ddd73..1be1793 100644 --- a/ECS/GenericEntityDescriptor.cs +++ b/ECS/GenericEntityDescriptor.cs @@ -1,6 +1,6 @@ namespace Svelto.ECS { - class GenericEntityDescriptor : EntityDescriptor + public class GenericEntityDescriptor : EntityDescriptor where T : NodeWithID, new() { static GenericEntityDescriptor() @@ -10,10 +10,10 @@ public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) {} - static INodeBuilder[] _nodesToBuild; + static readonly INodeBuilder[] _nodesToBuild; } - class GenericEntityDescriptor : EntityDescriptor + public class GenericEntityDescriptor : EntityDescriptor where T : NodeWithID, new() where U : NodeWithID, new() { @@ -29,10 +29,10 @@ public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) {} - static INodeBuilder[] _nodesToBuild; + static readonly INodeBuilder[] _nodesToBuild; } - class GenericEntityDescriptor : EntityDescriptor + public class GenericEntityDescriptor : EntityDescriptor where T : NodeWithID, new() where U : NodeWithID, new() where V : NodeWithID, new() @@ -47,12 +47,12 @@ }; } public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) - { - } - static INodeBuilder[] _nodesToBuild; + {} + + static readonly INodeBuilder[] _nodesToBuild; } - class GenericEntityDescriptor : EntityDescriptor + public class GenericEntityDescriptor : EntityDescriptor where T : NodeWithID, new() where U : NodeWithID, new() where V : NodeWithID, new() @@ -69,12 +69,12 @@ }; } public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) - { - } - static INodeBuilder[] _nodesToBuild; + {} + + static readonly INodeBuilder[] _nodesToBuild; } - class GenericEntityDescriptor : EntityDescriptor + public class GenericEntityDescriptor : EntityDescriptor where T : NodeWithID, new() where U : NodeWithID, new() where V : NodeWithID, new() @@ -93,11 +93,12 @@ }; } public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) - { - } - static INodeBuilder[] _nodesToBuild; + {} + + static readonly INodeBuilder[] _nodesToBuild; } - class GenericEntityDescriptor : EntityDescriptor + + public class GenericEntityDescriptor : EntityDescriptor where T : NodeWithID, new() where U : NodeWithID, new() where V : NodeWithID, new() @@ -118,8 +119,121 @@ }; } public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + {} + + static readonly INodeBuilder[] _nodesToBuild; + } + + public class GenericMixedEntityDescriptor : EntityDescriptor + where T : INodeBuilder, new() + { + static GenericMixedEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new T(), + }; + } + public GenericMixedEntityDescriptor(params object[] componentsImplementor) : + base(_nodesToBuild, componentsImplementor) + { } + + static readonly INodeBuilder[] _nodesToBuild; + } + + public class GenericMixedEntityDescriptor : EntityDescriptor + where T : INodeBuilder, new() + where U : INodeBuilder, new() + where V : INodeBuilder, new() + { + static GenericMixedEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new T(), + new U(), + new V() + }; + } + public GenericMixedEntityDescriptor(params object[] componentsImplementor) : + base(_nodesToBuild, componentsImplementor) + { } + + static readonly INodeBuilder[] _nodesToBuild; + } + + public class GenericMixedEntityDescriptor : EntityDescriptor + where T : INodeBuilder, new() + where U : INodeBuilder, new() + where V : INodeBuilder, new() + where W : INodeBuilder, new() + { + static GenericMixedEntityDescriptor() { + _nodesToBuild = new INodeBuilder[] + { + new T(), + new U(), + new V(), + new W() + }; } - static INodeBuilder[] _nodesToBuild; + public GenericMixedEntityDescriptor(params object[] componentsImplementor) : + base(_nodesToBuild, componentsImplementor) + { } + + static readonly INodeBuilder[] _nodesToBuild; + } + + public class GenericMixedEntityDescriptor : EntityDescriptor + where T : INodeBuilder, new() + where U : INodeBuilder, new() + where V : INodeBuilder, new() + where W : INodeBuilder, new() + where X : INodeBuilder, new() + { + static GenericMixedEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new T(), + new U(), + new V(), + new W(), + new X() + }; + } + public GenericMixedEntityDescriptor(params object[] componentsImplementor) : + base(_nodesToBuild, componentsImplementor) + { } + + static readonly INodeBuilder[] _nodesToBuild; + } + + public class GenericMixedEntityDescriptor : EntityDescriptor + where T : INodeBuilder, new() + where U : INodeBuilder, new() + where V : INodeBuilder, new() + where W : INodeBuilder, new() + where X : INodeBuilder, new() + where Y : INodeBuilder, new() + { + static GenericMixedEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new T(), + new U(), + new V(), + new W(), + new X(), + new Y() + }; + } + public GenericMixedEntityDescriptor(params object[] componentsImplementor) : + base(_nodesToBuild, componentsImplementor) + { } + + static readonly INodeBuilder[] _nodesToBuild; } } diff --git a/ECS/ICallBackOnAddEngine.cs b/ECS/ICallBackOnAddEngine.cs index 30ac6de..815d59d 100644 --- a/ECS/ICallBackOnAddEngine.cs +++ b/ECS/ICallBackOnAddEngine.cs @@ -1,7 +1,7 @@ namespace Svelto.ECS { - interface ICallBackOnAddEngine + public interface ICallBackOnAddEngine { void Ready(); } -} \ No newline at end of file +} diff --git a/ECS/IEngine.cs b/ECS/IEngine.cs index 3d67fcd..b39e4df 100644 --- a/ECS/IEngine.cs +++ b/ECS/IEngine.cs @@ -1,27 +1,14 @@ -namespace Svelto.ECS -{ - public interface IEngine - {} - - public interface INodeEngine:IEngine where TNodeType:INode - { - void Add(TNodeType obj); - void Remove(TNodeType obj); - } - - public interface INodeEngine:INodeEngine where T:INode where U:INode - { - void Add(T obj); - void Remove(T obj); - } +using Svelto.ECS.Internal; - public interface INodeEngine:INodeEngine where T:INode where U:INode where V:INode +namespace Svelto.ECS.Internal +{ + public interface IActivableNodeEngine : IEngine { - void Add(T obj); - void Remove(T obj); + void Enable(INode obj); + void Disable(INode obj); } - public interface INodeEngine:IEngine + public interface INodeEngine : IEngine { void Add(INode obj); void Remove(INode obj); @@ -31,9 +18,19 @@ namespace Svelto.ECS { System.Type[] AcceptedNodes(); } +} + +namespace Svelto.ECS +{ + public interface IEngine + {} + + public interface IActivableNodeEngine : IActivableNodeEngine where TNodeType : INode + { } public interface IQueryableNodeEngine:IEngine { IEngineNodeDB nodesDB { set; } } } + diff --git a/ECS/IEngineNodeDB.cs b/ECS/IEngineNodeDB.cs index 6103ad0..5e8a154 100644 --- a/ECS/IEngineNodeDB.cs +++ b/ECS/IEngineNodeDB.cs @@ -6,16 +6,14 @@ namespace Svelto.ECS { ReadOnlyDictionary QueryIndexableNodes() where T:INode; - bool QueryNode(int ID, out T node) where T:INode; + bool TryQueryNode(int ID, out T node) where T:INode; T QueryNode(int ID) where T:INode; FasterReadOnlyListCast QueryNodes() where T:INode; -// FasterReadOnlyList QueryStructNodes() where T : struct; - - bool QueryNodeFromGroup(int ID, out T node) where T : INode; - T QueryNodeFromGroup(int ID) where T : INode; - FasterReadOnlyListCast QueryNodesFromGroups() where T : INode; + bool TryQueryMetaNode(int metaEntityID, out T node) where T : INode; + T QueryMetaNode(int metaEntityID) where T : INode; + FasterReadOnlyListCast QueryMetaNodes() where T : INode; } } diff --git a/ECS/IEnginesRoot.cs b/ECS/IEnginesRoot.cs index 72c317a..5535222 100644 --- a/ECS/IEnginesRoot.cs +++ b/ECS/IEnginesRoot.cs @@ -1,3 +1,5 @@ +using Svelto.ECS.Internal; + namespace Svelto.ECS { public interface IEnginesRoot @@ -9,6 +11,8 @@ namespace Svelto.ECS { void BuildEntity(int ID, EntityDescriptor ED); - void BuildEntityGroup(int ID, EntityDescriptor ED); + void BuildMetaEntity(int metaEntityID, EntityDescriptor ED); + + void BuildEntityInGroup(int entityID, int groupID, EntityDescriptor ED); } } diff --git a/ECS/INode.cs b/ECS/INode.cs index 30abb9f..ac85ef8 100644 --- a/ECS/INode.cs +++ b/ECS/INode.cs @@ -3,7 +3,22 @@ namespace Svelto.ECS public interface INode {} - public class NodeWithID: INode + public interface INodeWithID:INode + { + int ID { get; } + } + + public interface IStructNodeWithID : INodeWithID + { + new int ID { get; set; } + } + + public interface IGroupedStructNodeWithID : IStructNodeWithID + { + int groupID { get; set; } + } + + public class NodeWithID: INodeWithID { public static TNodeType BuildNode(int ID) where TNodeType: NodeWithID, new() { diff --git a/ECS/IRemoveEntityComponent.cs b/ECS/IRemoveEntityComponent.cs index a3f1525..5a3ee3b 100644 --- a/ECS/IRemoveEntityComponent.cs +++ b/ECS/IRemoveEntityComponent.cs @@ -6,4 +6,14 @@ namespace Svelto.ECS { Action removeEntity { get; set; } } + + public interface IDisableEntityComponent + { + Action disableEntity { get; set; } + } + + public interface IEnableEntityComponent + { + Action enableEntity { get; set; } + } } diff --git a/ECS/MultipleNodesEngine.cs b/ECS/MultipleNodesEngine.cs new file mode 100644 index 0000000..70586c1 --- /dev/null +++ b/ECS/MultipleNodesEngine.cs @@ -0,0 +1,47 @@ +using Svelto.ECS.Internal; + +namespace Svelto.ECS.Internal +{ + public abstract class MultiNodesEngine + where T : INode + { + protected internal abstract void Add(T node); + protected internal abstract void Remove(T node); + } +} + +namespace Svelto.ECS +{ + public abstract class MultiNodesEngine : INodesEngine + { + public abstract System.Type[] AcceptedNodes(); + + public abstract void Add(INode node); + public abstract void Remove(INode node); + } + + public abstract class MultiNodesEngine : MultiNodesEngine, + INodeEngine + where T : INode + where U : INode + { + protected abstract void Add(T node); + protected abstract void Remove(T node); + + public void Add(INode node) + { + if (node is T) + Add((T) node); + else + ((MultiNodesEngine)(this)).Add((U)node); + } + + public void Remove(INode node) + { + if (node is T) + Remove((T)node); + else + ((MultiNodesEngine)(this)).Remove((U)node); + } + } +} \ No newline at end of file diff --git a/ECS/NodeEngineWrapper.cs b/ECS/NodeEngineWrapper.cs deleted file mode 100644 index 49ec673..0000000 --- a/ECS/NodeEngineWrapper.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; - -namespace Svelto.ECS.Internal -{ - class NodeEngineWrapper : SingleNodeEngine where T : class, INode - { - INodeEngine engine; - - public NodeEngineWrapper(INodeEngine engine) - { - this.engine = engine; - } - - protected override void Add(T node) - { - engine.Add((T)node); - } - - protected override void Remove(T node) - { - engine.Remove((T)node); - } - } - - class NodeEngineWrapper: SingleNodeEngine where T : class, INode where U : class, INode - { - INodeEngine engine; - - public NodeEngineWrapper(INodeEngine engine) - { - this.engine = engine; - } - - protected override void Add(T node) - { - engine.Add((T)node); - } - - protected override void Remove(T node) - { - engine.Remove((T)node); - } - } - - class NodeEngineWrapper: SingleNodeEngine where T : class, INode - where U : class, INode - where V : class, INode - { - INodeEngine engine; - - public NodeEngineWrapper(INodeEngine engine) - { - this.engine = engine; - } - - protected override void Add(T node) - { - engine.Add((T)node); - } - - protected override void Remove(T node) - { - engine.Remove((T)node); - } - } -} \ No newline at end of file diff --git a/ECS/Profiler/EngineProfiler.cs b/ECS/Profiler/EngineProfiler.cs index 53e7296..0612925 100644 --- a/ECS/Profiler/EngineProfiler.cs +++ b/ECS/Profiler/EngineProfiler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using Svelto.ECS.Internal; //This profiler is based on the Entitas Visual Debugging tool //https://github.com/sschmid/Entitas-CSharp diff --git a/ECS/Profiler/EngineProfilerBehaviour.cs b/ECS/Profiler/EngineProfilerBehaviour.cs index 9039a2a..c924d6c 100644 --- a/ECS/Profiler/EngineProfilerBehaviour.cs +++ b/ECS/Profiler/EngineProfilerBehaviour.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using UnityEngine; diff --git a/ECS/SingleNodeEngine.cs b/ECS/SingleNodeEngine.cs index d92b840..3d7a45f 100644 --- a/ECS/SingleNodeEngine.cs +++ b/ECS/SingleNodeEngine.cs @@ -1,15 +1,18 @@ -namespace Svelto.ECS +using Svelto.ECS.Internal; + +namespace Svelto.ECS { - public abstract class SingleNodeEngine : INodeEngine where TNodeType : class, INode + public abstract class SingleNodeEngine : INodeEngine + where TNodeType : class, INode { - void INodeEngine.Add(INode obj) + public void Add(INode obj) { - Add(obj as TNodeType); + Add((TNodeType) obj); } - void INodeEngine.Remove(INode obj) + public void Remove(INode obj) { - Remove(obj as TNodeType); + Remove((TNodeType) obj); } protected abstract void Add(TNodeType node); diff --git a/ECS/StructNodes.cs b/ECS/StructNodes.cs new file mode 100644 index 0000000..aa708f4 --- /dev/null +++ b/ECS/StructNodes.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using Svelto.DataStructures; +using Svelto.ECS.Internal; + +namespace Svelto.ECS +{ + public class StructNodes: IStructNodes where T:struct, IStructNodeWithID + { + public FasterList list + { + get + { + return _internalList; + } + } + + public StructNodes() + { + _internalList = new FasterList(); + } + + public void Add(T node) + { + T convert = (T)node; + + _internalList.Add(convert); + } + + readonly FasterList _internalList; + } + + public class StructGroupNodes: IStructGroupNodes + where T : struct, IGroupedStructNodeWithID + { + public void Add(int groupID, T node) + { + T convert = (T)node; + + var fasterList = GetList(groupID); + _indices[node.ID] = fasterList.Count; + + fasterList.Add(convert); + } + + public void Remove(int groupID, T node) + { + var fasterList = GetList(groupID); + var index = _indices[node.ID]; + _indices.Remove(node.ID); + + if (fasterList.UnorderedRemoveAt(index)) + _indices[fasterList[index].ID] = index; + } + + public FasterList GetList(int groupID) + { + return _nodes[groupID]; + } + + readonly Dictionary _indices = new Dictionary(); + Dictionary> _nodes = new Dictionary>(); + } +} + +namespace Svelto.ECS.Internal +{ + public interface IStructGroupNodes + { + } + + public interface IStructNodes + { + } +} \ No newline at end of file diff --git a/ECS/note.txt b/ECS/note.txt new file mode 100644 index 0000000..c041193 --- /dev/null +++ b/ECS/note.txt @@ -0,0 +1,111 @@ +systems do not hold component data, but only system states +systems cannot be injected +systems are SRP and OCP +systems communicates between component, mediators, producer/consumer, observers. producer/consumer and observers must be defined in the layer of the engine. +systems can have injected dependencies + +components don't have logic +components can have only getter and setter +components cannot define patterns that requires logic. +components cannot issues commands + +High Cohesion[edit] +Main article: Cohesion (computer science) +High Cohesion is an evaluative pattern that attempts to keep objects appropriately focused, manageable and understandable. High cohesion is generally used in support of Low Coupling. High cohesion means that the responsibilities of a given element are strongly related and highly focused. Breaking programs into classes and subsystems is an example of activities that increase the cohesive properties of a system. Alternatively, low cohesion is a situation in which a given element has too many unrelated responsibilities. Elements with low cohesion often suffer from being hard to comprehend, hard to reuse, hard to maintain and averse to change.[3] + +Low Coupling[edit] +Main article: Loose coupling +Low Coupling is an evaluative pattern, which dictates how to assign responsibilities to support: + +lower dependency between the classes, +change in one class having lower impact on other classes, +higher reuse potential. + +events, observers and mediators have the inconvience to hold the reference to the engine, which forces to use cleanup if the engine must be removed. +Observers are easy to clean up from the engine. Mediators needs to be integrated to the framework to be simple to clean up. Component events need the clean up function. +producer/consumer has the inconvienent to force check the number of jobs available everyframe + +Engine can't be removed, they can only be disabled, but the logic of disabling must be handled by the engine itself + +Should components have just one element? Should engines use just nodes? Components are ditacted by the entities and Nodes by the engines + +http://thelinuxlich.github.io/artemis_CSharp/ + +differences: components no events, everthing must be update +give more responsabiltiy to the user, semplicity + +https://github.com/sschmid/Entitas-CSharp/wiki/Overview + +no groups, no queries + +http://entity-systems.wikidot.com/es-articles + +http://www.ashframework.org/ + +it's very important to give a namespace to the engines. In this way it's impossible to create semantically wrong nodes (PlayerNode Vs TargetNode) + +ToDo: + +it's not safe to remove an engine without having called being denitialised internal states. A special ClearOnRemove function must be added for each engine + +namespace GameFramework.RayCast +{ + public class RayCastEngineEngine + { + public RayCastEngine(RayCastEmployer jobList) + { + jobList.onJobassigned += OnRaycastRequested; + } + + public void Add(IComponent obj) + {} + + public void Remove(IComponent obj) + {} + + void OnRaycastRequested(RayCastJob job) + { + RaycastHit shootHit; + + Physics.Raycast(job.rayVector, out shootHit, job.range, _enemyMask); + + job.Done(shootHit); + } + + RayCastEmployer _employer; + + int _enemyMask; + } + + public struct RayCastJob + { + readonly public Ray rayVector; + readonly public float range; + readonly public Action Done; + + public RayCastJob(Ray direction, float distance, Action OnDone) + { + rayVector = direction; + range = distance; + Done = OnDone; + } + } + + public class RayCastEmployer + { + public event Action onJobassigned; + + public void AssignJob(RayCastJob data, Action onJobdone) + { + onJobassigned(data); + } + } +} + +if your code can be read as + +A tells B to do something is direct +A register B event is indirect +althoggh if B tells A something through event is direct again. B must say something like I don't know who you are, but this just happened. you say B.SomethingHappenedToMe() not B.YouMustDoThis(); + +un engine non deve mai avere concetti di un altro engine. dire all'engine sonoro suona morte � sbagliato. � l'engine death che triggera l'evento e l'engine sound ad ascoltarlo. diff --git a/Utilities/DesignByContract.cs b/Utilities/DesignByContract.cs index 9a76411..d34861e 100644 --- a/Utilities/DesignByContract.cs +++ b/Utilities/DesignByContract.cs @@ -324,18 +324,18 @@ namespace DesignByContract useAssertions = value; } } + + #endregion // Interface - #endregion // Interface - - #region Implementation + #region Implementation - // No creation - Check() { } + // No creation + private Check() {} - /// - /// Is exception handling being used? - /// - private static bool UseExceptions + /// + /// Is exception handling being used? + /// + private static bool UseExceptions { get { @@ -343,15 +343,15 @@ namespace DesignByContract } } - // Are trace assertion statements being used? - // Default is to use exception handling. - static bool useAssertions = false; + // Are trace assertion statements being used? + // Default is to use exception handling. + private static bool useAssertions = false; - #endregion // Implementation + #endregion // Implementation - } // End Check + } // End Check - class Trace + internal class Trace { internal static void Assert(bool assertion, string v) { diff --git a/Utilities/Print.cs b/Utilities/Print.cs index 0d7e601..095c110 100644 --- a/Utilities/Print.cs +++ b/Utilities/Print.cs @@ -136,7 +136,7 @@ namespace Utility logger.Log(txt); } - public static void LogError(string txt, bool showCurrentStack = true) + public static void LogError(string txt) { string toPrint; @@ -148,11 +148,8 @@ namespace Utility toPrint = _stringBuilder.ToString(); } -#if !NETFX_CORE - logger.Log(toPrint, showCurrentStack == true ? new StackTrace().ToString() : null, LogType.Error); -#else + logger.Log(toPrint, null, LogType.Error); -#endif } public static void LogError(string txt, string stack) diff --git a/Utilities/WeakActionStruct.cs b/Utilities/WeakActionStruct.cs index dff59f6..318027b 100644 --- a/Utilities/WeakActionStruct.cs +++ b/Utilities/WeakActionStruct.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/Utilities/WeakEvent.cs b/Utilities/WeakEvent.cs index e912490..91aa235 100644 --- a/Utilities/WeakEvent.cs +++ b/Utilities/WeakEvent.cs @@ -1,5 +1,6 @@ using Svelto.DataStructures; using System; +using System.Collections.Generic; namespace BetterWeakEvents { @@ -13,7 +14,7 @@ namespace BetterWeakEvents public static WeakEvent operator-(WeakEvent c1, Action x) { - c1._subscribers.UnorderredRemove(new WeakAction(x)); + c1._subscribers.UnorderedRemove(new WeakAction(x)); return c1; } @@ -21,7 +22,7 @@ namespace BetterWeakEvents { for (int i = 0; i < _subscribers.Count; i++) if (_subscribers[i].Invoke(arg1, arg2) == false) - _subscribers.UnorderredRemoveAt(i--); + _subscribers.UnorderedRemoveAt(i--); } ~WeakEvent() @@ -32,4 +33,4 @@ namespace BetterWeakEvents protected FasterList> _subscribers = new FasterList>(); } -} \ No newline at end of file +}