From cbde7d8f4a8bc5df79c5bc8c3d6cebd2be11d66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastiano=20Mandal=C3=A0?= Date: Sat, 11 Nov 2017 10:37:41 +0000 Subject: [PATCH] working on the new typesafe svelto.ecs --- DataStructures/FasterList.cs | 20 +- DataStructures/IGraphNode.cs | 16 + .../LeftLeaningKeyedRedBlackTree.cs | 627 ++++++++++++++ DataStructures/LeftLeaningRedBlackTree.cs | 789 ++++++++++++++++++ DataStructures/PriorityQueue.cs | 108 +++ ECS/DataStructures/TypeSafeDictionary.cs | 23 + ECS/DataStructures/TypeSafeList.cs | 51 ++ ECS/EngineNodeDB.cs | 60 +- ECS/EnginesRoot.cs | 592 ++++++------- ECS/EntityDescriptor.cs | 274 +++--- .../Unity/UnitySumbmissionNodeScheduler.cs | 8 + ECS/GenericEntityDescriptor.cs | 19 + ECS/GenericEntityDescriptorHolder.cs | 3 +- ECS/IEngine.cs | 11 +- ECS/IEngineNodeDB.cs | 15 +- ECS/INode.cs | 9 +- ECS/MultiNodesEngine.cs | 87 +- ECS/NodeBuilder.cs | 77 ++ .../EngineProfiler/EngineProfilerInspector.cs | 2 +- ECS/Profiler/EngineProfiler.cs | 18 +- ECS/Profiler/EngineProfilerBehaviour.cs | 2 +- ECS/SingleNodeEngine.cs | 26 +- ECS/StructNodes.cs | 91 +- Factories/IGameObjectFactory.cs | 2 + Factories/IMonoBehaviourFactory.cs | 3 + Observer/Observable.cs | 42 + Observer/Observer.cs | 111 +++ Utilities/Console.cs | 37 +- Utilities/NetFXCoreWrappers.cs | 125 +++ Utilities/ThreadUtility.cs | 16 + WeakEvents/WeakAction.cs | 9 +- WeakEvents/WeakActionStruct.cs | 10 +- WeakEvents/WeakEvent.cs | 47 +- 33 files changed, 2708 insertions(+), 622 deletions(-) create mode 100644 DataStructures/IGraphNode.cs create mode 100644 DataStructures/LeftLeaningKeyedRedBlackTree.cs create mode 100644 DataStructures/LeftLeaningRedBlackTree.cs create mode 100644 DataStructures/PriorityQueue.cs create mode 100644 ECS/DataStructures/TypeSafeDictionary.cs create mode 100644 ECS/DataStructures/TypeSafeList.cs create mode 100644 ECS/NodeBuilder.cs create mode 100644 Observer/Observable.cs create mode 100644 Observer/Observer.cs create mode 100644 Utilities/NetFXCoreWrappers.cs create mode 100644 Utilities/ThreadUtility.cs diff --git a/DataStructures/FasterList.cs b/DataStructures/FasterList.cs index d87c13b..cfd9825 100644 --- a/DataStructures/FasterList.cs +++ b/DataStructures/FasterList.cs @@ -466,10 +466,7 @@ namespace Svelto.DataStructures readonly FasterList _list; } - public interface IFasterList - {} - - public class FasterList : IList, IFasterList + public class FasterList : IList { const int MIN_SIZE = 4; @@ -562,7 +559,7 @@ namespace Svelto.DataStructures while (items.MoveNext()) _buffer[_count++] = items.Current; } - + public void AddRange(ICollection items) { AddRange(items.GetEnumerator(), items.Count); @@ -803,5 +800,18 @@ namespace Svelto.DataStructures T[] _buffer; int _count; + + public static class NoVirt + { + public static int Count(FasterList fasterList) + { + return fasterList._count; + } + + public static T[] ToArrayFast(FasterList fasterList) + { + return fasterList._buffer; + } + } } } diff --git a/DataStructures/IGraphNode.cs b/DataStructures/IGraphNode.cs new file mode 100644 index 0000000..05a8c8e --- /dev/null +++ b/DataStructures/IGraphNode.cs @@ -0,0 +1,16 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18408 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +using System; + +public interface IGraphNode +{ + void VisitNeighbours(System.Action onVisiting); +} + diff --git a/DataStructures/LeftLeaningKeyedRedBlackTree.cs b/DataStructures/LeftLeaningKeyedRedBlackTree.cs new file mode 100644 index 0000000..0de1ae1 --- /dev/null +++ b/DataStructures/LeftLeaningKeyedRedBlackTree.cs @@ -0,0 +1,627 @@ +// Uncomment this to enable the following debugging aids: +// LeftLeaningRedBlackTree.HtmlFragment +// LeftLeaningRedBlackTree.Node.HtmlFragment +// LeftLeaningRedBlackTree.AssertInvariants +// #define DEBUGGING + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +/// +/// Implements a left-leaning red-black tree. +/// +/// +/// Based on the research paper "Left-leaning Red-Black Trees" +/// by Robert Sedgewick. More information available at: +/// http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf +/// http://www.cs.princeton.edu/~rs/talks/LLRB/08Penn.pdf +/// +/// Type of keys. +public class LeftLeaningKeyedRedBlackTree where TKey: IComparable +{ + /// + /// Stores the root node of the tree. + /// + private Node _rootNode; + + /// + /// Represents a node of the tree. + /// + /// + /// Using fields instead of properties drops execution time by about 40%. + /// + [DebuggerDisplay("Key={Key}")] + private class Node + { + /// + /// Gets or sets the node's key. + /// + public TKey Key; + + /// + /// Gets or sets the left node. + /// + public Node Left; + + /// + /// Gets or sets the right node. + /// + public Node Right; + + /// + /// Gets or sets the color of the node. + /// + public bool IsBlack; + +#if DEBUGGING + /// + /// Gets an HTML fragment representing the node and its children. + /// + public string HtmlFragment + { + get + { + return + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
" + Key + ", " + Value + " [" + Siblings + "]
" + (null != Left ? Left.HtmlFragment : "[null]") + "" + (null != Right ? Right.HtmlFragment : "[null]") + "
"; + } + } +#endif + } + + /// + /// Adds a key/value pair to the tree. + /// + /// Key to add. + public void Add(TKey key) + { + _rootNode = Add(_rootNode, key); + _rootNode.IsBlack = true; +#if DEBUGGING + AssertInvariants(); +#endif + } + + /// + /// Removes a key/value pair from the tree. + /// + /// Key to remove. + /// True if key/value present and removed. + public bool Remove(TKey key) + { + int initialCount = Count; + if (null != _rootNode) + { + _rootNode = Remove(_rootNode, key); + if (null != _rootNode) + { + _rootNode.IsBlack = true; + } + } +#if DEBUGGING + AssertInvariants(); +#endif + return initialCount != Count; + } + + /// + /// Removes all nodes in the tree. + /// + public void Clear() + { + _rootNode = null; + Count = 0; +#if DEBUGGING + AssertInvariants(); +#endif + } + + /// + /// Gets a sorted list of keys in the tree. + /// + /// Sorted list of keys. + public IEnumerable GetKeys() + { + TKey lastKey = default(TKey); + bool lastKeyValid = false; + return Traverse( + _rootNode, + n => !lastKeyValid || !object.Equals(lastKey, n.Key), + n => + { + lastKey = n.Key; + lastKeyValid = true; + return lastKey; + }); + } + + /// + /// Gets the count of key/value pairs in the tree. + /// + public int Count { get; private set; } + + /// + /// Gets the minimum key in the tree. + /// + public TKey MinimumKey + { + get { return GetExtreme(_rootNode, n => n.Left, n => n.Key); } + } + + /// + /// Gets the maximum key in the tree. + /// + public TKey MaximumKey + { + get { return GetExtreme(_rootNode, n => n.Right, n => n.Key); } + } + + /// + /// Returns true if the specified node is red. + /// + /// Specified node. + /// True if specified node is red. + private static bool IsRed(Node node) + { + if (null == node) + { + // "Virtual" leaf nodes are always black + return false; + } + return !node.IsBlack; + } + + /// + /// Adds the specified key/value pair below the specified root node. + /// + /// Specified node. + /// Key to add. + /// Value to add. + /// New root node. + private Node Add(Node node, TKey key) + { + if (null == node) + { + // Insert new node + Count++; + return new Node { Key = key }; + } + + if (IsRed(node.Left) && IsRed(node.Right)) + { + // Split node with two red children + FlipColor(node); + } + + // Find right place for new node + int comparisonResult = KeyComparison(key, node.Key); + if (comparisonResult < 0) + { + node.Left = Add(node.Left, key); + } + else if (0 < comparisonResult) + { + node.Right = Add(node.Right, key); + } + + if (IsRed(node.Right)) + { + // Rotate to prevent red node on right + node = RotateLeft(node); + } + + if (IsRed(node.Left) && IsRed(node.Left.Left)) + { + // Rotate to prevent consecutive red nodes + node = RotateRight(node); + } + + return node; + } + + /// + /// Removes the specified key/value pair from below the specified node. + /// + /// Specified node. + /// Key to remove. + /// True if key/value present and removed. + private Node Remove(Node node, TKey key) + { + int comparisonResult = KeyComparison(key, node.Key); + if (comparisonResult < 0) + { + // * Continue search if left is present + if (null != node.Left) + { + if (!IsRed(node.Left) && !IsRed(node.Left.Left)) + { + // Move a red node over + node = MoveRedLeft(node); + } + + // Remove from left + node.Left = Remove(node.Left, key); + } + } + else + { + if (IsRed(node.Left)) + { + // Flip a 3 node or unbalance a 4 node + node = RotateRight(node); + } + if ((0 == KeyComparison(key, node.Key)) && (null == node.Right)) + { + // Remove leaf node + Debug.Assert(null == node.Left, "About to remove an extra node."); + Count--; + // Leaf node is gone + return null; + } + // * Continue search if right is present + if (null != node.Right) + { + if (!IsRed(node.Right) && !IsRed(node.Right.Left)) + { + // Move a red node over + node = MoveRedRight(node); + } + if (0 == KeyComparison(key, node.Key)) + { + // Remove leaf node + Count--; + // Find the smallest node on the right, swap, and remove it + Node m = GetExtreme(node.Right, n => n.Left, n => n); + node.Key = m.Key; + node.Right = DeleteMinimum(node.Right); + } + else + { + // Remove from right + node.Right = Remove(node.Right, key); + } + } + } + + // Maintain invariants + return FixUp(node); + } + + /// + /// Flip the colors of the specified node and its direct children. + /// + /// Specified node. + private static void FlipColor(Node node) + { + node.IsBlack = !node.IsBlack; + node.Left.IsBlack = !node.Left.IsBlack; + node.Right.IsBlack = !node.Right.IsBlack; + } + + /// + /// Rotate the specified node "left". + /// + /// Specified node. + /// New root node. + private static Node RotateLeft(Node node) + { + Node x = node.Right; + node.Right = x.Left; + x.Left = node; + x.IsBlack = node.IsBlack; + node.IsBlack = false; + return x; + } + + /// + /// Rotate the specified node "right". + /// + /// Specified node. + /// New root node. + private static Node RotateRight(Node node) + { + Node x = node.Left; + node.Left = x.Right; + x.Right = node; + x.IsBlack = node.IsBlack; + node.IsBlack = false; + return x; + } + + /// + /// Moves a red node from the right child to the left child. + /// + /// Parent node. + /// New root node. + private static Node MoveRedLeft(Node node) + { + FlipColor(node); + if (IsRed(node.Right.Left)) + { + node.Right = RotateRight(node.Right); + node = RotateLeft(node); + FlipColor(node); + + // * Avoid creating right-leaning nodes + if (IsRed(node.Right.Right)) + { + node.Right = RotateLeft(node.Right); + } + } + return node; + } + + /// + /// Moves a red node from the left child to the right child. + /// + /// Parent node. + /// New root node. + private static Node MoveRedRight(Node node) + { + FlipColor(node); + if (IsRed(node.Left.Left)) + { + node = RotateRight(node); + FlipColor(node); + } + return node; + } + + /// + /// Deletes the minimum node under the specified node. + /// + /// Specified node. + /// New root node. + private Node DeleteMinimum(Node node) + { + if (null == node.Left) + { + // Nothing to do + return null; + } + + if (!IsRed(node.Left) && !IsRed(node.Left.Left)) + { + // Move red node left + node = MoveRedLeft(node); + } + + // Recursively delete + node.Left = DeleteMinimum(node.Left); + + // Maintain invariants + return FixUp(node); + } + + /// + /// Maintains invariants by adjusting the specified nodes children. + /// + /// Specified node. + /// New root node. + private static Node FixUp(Node node) + { + if (IsRed(node.Right)) + { + // Avoid right-leaning node + node = RotateLeft(node); + } + + if (IsRed(node.Left) && IsRed(node.Left.Left)) + { + // Balance 4-node + node = RotateRight(node); + } + + if (IsRed(node.Left) && IsRed(node.Right)) + { + // Push red up + FlipColor(node); + } + + // * Avoid leaving behind right-leaning nodes + if ((null != node.Left) && IsRed(node.Left.Right) && !IsRed(node.Left.Left)) + { + node.Left = RotateLeft(node.Left); + if (IsRed(node.Left)) + { + // Balance 4-node + node = RotateRight(node); + } + } + + return node; + } + + /// + /// Gets the (first) node corresponding to the specified key. + /// + /// Key to search for. + /// Corresponding node or null if none found. + private Node GetNodeForKey(TKey key) + { + // Initialize + Node node = _rootNode; + while (null != node) + { + // Compare keys and go left/right + int comparisonResult = key.CompareTo(node.Key); + if (comparisonResult < 0) + { + node = node.Left; + } + else if (0 < comparisonResult) + { + node = node.Right; + } + else + { + // Match; return node + return node; + } + } + + // No match found + return null; + } + + /// + /// Gets an extreme (ex: minimum/maximum) value. + /// + /// Type of value. + /// Node to start from. + /// Successor function. + /// Selector function. + /// Extreme value. + private static T GetExtreme(Node node, Func successor, Func selector) + { + // Initialize + T extreme = default(T); + Node current = node; + while (null != current) + { + // Go to extreme + extreme = selector(current); + current = successor(current); + } + return extreme; + } + + /// + /// Traverses a subset of the sequence of nodes in order and selects the specified nodes. + /// + /// Type of elements. + /// Starting node. + /// Condition method. + /// Selector method. + /// Sequence of selected nodes. + private IEnumerable Traverse(Node node, Func condition, Func selector) + { + // Create a stack to avoid recursion + Stack stack = new Stack(); + Node current = node; + while (null != current) + { + if (null != current.Left) + { + // Save current state and go left + stack.Push(current); + current = current.Left; + } + else + { + do + { + // Select current node if relevant + if (condition(current)) + { + yield return selector(current); + } + // Go right - or up if nothing to the right + current = current.Right; + } + while ((null == current) && + (0 < stack.Count) && + (null != (current = stack.Pop()))); + } + } + } + + /// + /// Compares the specified keys (primary) and values (secondary). + /// + /// The left key. + /// The right key. + /// CompareTo-style results: -1 if left is less, 0 if equal, and 1 if greater than right. + private int KeyComparison(TKey leftKey, TKey rightKey) + { + return leftKey.CompareTo(rightKey); + } + +#if DEBUGGING + /// + /// Asserts that tree invariants are not violated. + /// + private void AssertInvariants() + { + // Root is black + Debug.Assert((null == _rootNode) || _rootNode.IsBlack, "Root is not black"); + // Every path contains the same number of black nodes + Dictionary parents = new Dictionary.Node, LeftLeaningRedBlackTree.Node>(); + foreach (Node node in Traverse(_rootNode, n => true, n => n)) + { + if (null != node.Left) + { + parents[node.Left] = node; + } + if (null != node.Right) + { + parents[node.Right] = node; + } + } + if (null != _rootNode) + { + parents[_rootNode] = null; + } + int treeCount = -1; + foreach (Node node in Traverse(_rootNode, n => (null == n.Left) || (null == n.Right), n => n)) + { + int pathCount = 0; + Node current = node; + while (null != current) + { + if (current.IsBlack) + { + pathCount++; + } + current = parents[current]; + } + Debug.Assert((-1 == treeCount) || (pathCount == treeCount), "Not all paths have the same number of black nodes."); + treeCount = pathCount; + } + // Verify node properties... + foreach (Node node in Traverse(_rootNode, n => true, n => n)) + { + // Left node is less + if (null != node.Left) + { + Debug.Assert(0 > KeyAndValueComparison(node.Left.Key, node.Left.Value, node.Key, node.Value), "Left node is greater than its parent."); + } + // Right node is greater + if (null != node.Right) + { + Debug.Assert(0 < KeyAndValueComparison(node.Right.Key, node.Right.Value, node.Key, node.Value), "Right node is less than its parent."); + } + // Both children of a red node are black + Debug.Assert(!IsRed(node) || (!IsRed(node.Left) && !IsRed(node.Right)), "Red node has a red child."); + // Always left-leaning + Debug.Assert(!IsRed(node.Right) || IsRed(node.Left), "Node is not left-leaning."); + // No consecutive reds (subset of previous rule) + //Debug.Assert(!(IsRed(node) && IsRed(node.Left))); + } + } + + /// + /// Gets an HTML fragment representing the tree. + /// + public string HtmlDocument + { + get + { + return + "" + + "" + + (null != _rootNode ? _rootNode.HtmlFragment : "[null]") + + "" + + ""; + } + } +#endif +} diff --git a/DataStructures/LeftLeaningRedBlackTree.cs b/DataStructures/LeftLeaningRedBlackTree.cs new file mode 100644 index 0000000..b6e3712 --- /dev/null +++ b/DataStructures/LeftLeaningRedBlackTree.cs @@ -0,0 +1,789 @@ +// Uncomment this to enable the following debugging aids: +// LeftLeaningRedBlackTree.HtmlFragment +// LeftLeaningRedBlackTree.Node.HtmlFragment +// LeftLeaningRedBlackTree.AssertInvariants +// #define DEBUGGING + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +/// +/// Implements a left-leaning red-black tree. +/// +/// +/// Based on the research paper "Left-leaning Red-Black Trees" +/// by Robert Sedgewick. More information available at: +/// http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf +/// http://www.cs.princeton.edu/~rs/talks/LLRB/08Penn.pdf +/// +/// Type of keys. +/// Type of values. +public class LeftLeaningRedBlackTree +{ + /// + /// Stores the key comparison function. + /// + private Comparison _keyComparison; + + /// + /// Stores the value comparison function. + /// + private Comparison _valueComparison; + + /// + /// Stores the root node of the tree. + /// + private Node _rootNode; + + /// + /// Represents a node of the tree. + /// + /// + /// Using fields instead of properties drops execution time by about 40%. + /// + [DebuggerDisplay("Key={Key}, Value={Value}, Siblings={Siblings}")] + private class Node + { + /// + /// Gets or sets the node's key. + /// + public TKey Key; + + /// + /// Gets or sets the node's value. + /// + public TValue Value; + + /// + /// Gets or sets the left node. + /// + public Node Left; + + /// + /// Gets or sets the right node. + /// + public Node Right; + + /// + /// Gets or sets the color of the node. + /// + public bool IsBlack; + + /// + /// Gets or sets the number of "siblings" (nodes with the same key/value). + /// + public int Siblings; + +#if DEBUGGING + /// + /// Gets an HTML fragment representing the node and its children. + /// + public string HtmlFragment + { + get + { + return + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
" + Key + ", " + Value + " [" + Siblings + "]
" + (null != Left ? Left.HtmlFragment : "[null]") + "" + (null != Right ? Right.HtmlFragment : "[null]") + "
"; + } + } +#endif + } + + /// + /// Initializes a new instance of the LeftLeaningRedBlackTree class implementing a normal dictionary. + /// + /// The key comparison function. + public LeftLeaningRedBlackTree(Comparison keyComparison) + { + if (null == keyComparison) + { + throw new ArgumentNullException("keyComparison"); + } + _keyComparison = keyComparison; + } + + /// + /// Initializes a new instance of the LeftLeaningRedBlackTree class implementing an ordered multi-dictionary. + /// + /// The key comparison function. + /// The value comparison function. + public LeftLeaningRedBlackTree(Comparison keyComparison, Comparison valueComparison) + : this(keyComparison) + { + if (null == valueComparison) + { + throw new ArgumentNullException("valueComparison"); + } + _valueComparison = valueComparison; + } + + /// + /// Gets a value indicating whether the tree is acting as an ordered multi-dictionary. + /// + private bool IsMultiDictionary + { + get { return null != _valueComparison; } + } + + /// + /// Adds a key/value pair to the tree. + /// + /// Key to add. + /// Value to add. + public void Add(TKey key, TValue value) + { + _rootNode = Add(_rootNode, key, value); + _rootNode.IsBlack = true; +#if DEBUGGING + AssertInvariants(); +#endif + } + + /// + /// Removes a key (and its associated value) from a normal (non-multi) dictionary. + /// + /// Key to remove. + /// True if key present and removed. + public bool Remove(TKey key) + { + if (IsMultiDictionary) + { + throw new InvalidOperationException("Remove is only supported when acting as a normal (non-multi) dictionary."); + } + return Remove(key, default(TValue)); + } + + /// + /// Removes a key/value pair from the tree. + /// + /// Key to remove. + /// Value to remove. + /// True if key/value present and removed. + public bool Remove(TKey key, TValue value) + { + int initialCount = Count; + if (null != _rootNode) + { + _rootNode = Remove(_rootNode, key, value); + if (null != _rootNode) + { + _rootNode.IsBlack = true; + } + } +#if DEBUGGING + AssertInvariants(); +#endif + return initialCount != Count; + } + + /// + /// Removes all nodes in the tree. + /// + public void Clear() + { + _rootNode = null; + Count = 0; +#if DEBUGGING + AssertInvariants(); +#endif + } + + /// + /// Gets a sorted list of keys in the tree. + /// + /// Sorted list of keys. + public IEnumerable GetKeys() + { + TKey lastKey = default(TKey); + bool lastKeyValid = false; + return Traverse( + _rootNode, + n => !lastKeyValid || !object.Equals(lastKey, n.Key), + n => + { + lastKey = n.Key; + lastKeyValid = true; + return lastKey; + }); + } + + /// + /// Gets the value associated with the specified key in a normal (non-multi) dictionary. + /// + /// Specified key. + /// Value associated with the specified key. + public TValue GetValueForKey(TKey key) + { + if (IsMultiDictionary) + { + throw new InvalidOperationException("GetValueForKey is only supported when acting as a normal (non-multi) dictionary."); + } + Node node = GetNodeForKey(key); + if (null != node) + { + return node.Value; + } + else + { + throw new KeyNotFoundException(); + } + } + + /// + /// Gets a sequence of the values associated with the specified key. + /// + /// Specified key. + /// Sequence of values. + public IEnumerable GetValuesForKey(TKey key) + { + return Traverse(GetNodeForKey(key), n => 0 == _keyComparison(n.Key, key), n => n.Value); + } + + /// + /// Gets a sequence of all the values in the tree. + /// + /// Sequence of all values. + public IEnumerable GetValuesForAllKeys() + { + return Traverse(_rootNode, n => true, n => n.Value); + } + + /// + /// Gets the count of key/value pairs in the tree. + /// + public int Count { get; private set; } + + /// + /// Gets the minimum key in the tree. + /// + public TKey MinimumKey + { + get { return GetExtreme(_rootNode, n => n.Left, n => n.Key); } + } + + /// + /// Gets the maximum key in the tree. + /// + public TKey MaximumKey + { + get { return GetExtreme(_rootNode, n => n.Right, n => n.Key); } + } + + /// + /// Returns true if the specified node is red. + /// + /// Specified node. + /// True if specified node is red. + private static bool IsRed(Node node) + { + if (null == node) + { + // "Virtual" leaf nodes are always black + return false; + } + return !node.IsBlack; + } + + /// + /// Adds the specified key/value pair below the specified root node. + /// + /// Specified node. + /// Key to add. + /// Value to add. + /// New root node. + private Node Add(Node node, TKey key, TValue value) + { + if (null == node) + { + // Insert new node + Count++; + return new Node { Key = key, Value = value }; + } + + if (IsRed(node.Left) && IsRed(node.Right)) + { + // Split node with two red children + FlipColor(node); + } + + // Find right place for new node + int comparisonResult = KeyAndValueComparison(key, value, node.Key, node.Value); + if (comparisonResult < 0) + { + node.Left = Add(node.Left, key, value); + } + else if (0 < comparisonResult) + { + node.Right = Add(node.Right, key, value); + } + else + { + if (IsMultiDictionary) + { + // Store the presence of a "duplicate" node + node.Siblings++; + Count++; + } + else + { + // Replace the value of the existing node + node.Value = value; + } + } + + if (IsRed(node.Right)) + { + // Rotate to prevent red node on right + node = RotateLeft(node); + } + + if (IsRed(node.Left) && IsRed(node.Left.Left)) + { + // Rotate to prevent consecutive red nodes + node = RotateRight(node); + } + + return node; + } + + /// + /// Removes the specified key/value pair from below the specified node. + /// + /// Specified node. + /// Key to remove. + /// Value to remove. + /// True if key/value present and removed. + private Node Remove(Node node, TKey key, TValue value) + { + int comparisonResult = KeyAndValueComparison(key, value, node.Key, node.Value); + if (comparisonResult < 0) + { + // * Continue search if left is present + if (null != node.Left) + { + if (!IsRed(node.Left) && !IsRed(node.Left.Left)) + { + // Move a red node over + node = MoveRedLeft(node); + } + + // Remove from left + node.Left = Remove(node.Left, key, value); + } + } + else + { + if (IsRed(node.Left)) + { + // Flip a 3 node or unbalance a 4 node + node = RotateRight(node); + } + if ((0 == KeyAndValueComparison(key, value, node.Key, node.Value)) && (null == node.Right)) + { + // Remove leaf node + Debug.Assert(null == node.Left, "About to remove an extra node."); + Count--; + if (0 < node.Siblings) + { + // Record the removal of the "duplicate" node + Debug.Assert(IsMultiDictionary, "Should not have siblings if tree is not a multi-dictionary."); + node.Siblings--; + return node; + } + else + { + // Leaf node is gone + return null; + } + } + // * Continue search if right is present + if (null != node.Right) + { + if (!IsRed(node.Right) && !IsRed(node.Right.Left)) + { + // Move a red node over + node = MoveRedRight(node); + } + if (0 == KeyAndValueComparison(key, value, node.Key, node.Value)) + { + // Remove leaf node + Count--; + if (0 < node.Siblings) + { + // Record the removal of the "duplicate" node + Debug.Assert(IsMultiDictionary, "Should not have siblings if tree is not a multi-dictionary."); + node.Siblings--; + } + else + { + // Find the smallest node on the right, swap, and remove it + Node m = GetExtreme(node.Right, n => n.Left, n => n); + node.Key = m.Key; + node.Value = m.Value; + node.Siblings = m.Siblings; + node.Right = DeleteMinimum(node.Right); + } + } + else + { + // Remove from right + node.Right = Remove(node.Right, key, value); + } + } + } + + // Maintain invariants + return FixUp(node); + } + + /// + /// Flip the colors of the specified node and its direct children. + /// + /// Specified node. + private static void FlipColor(Node node) + { + node.IsBlack = !node.IsBlack; + node.Left.IsBlack = !node.Left.IsBlack; + node.Right.IsBlack = !node.Right.IsBlack; + } + + /// + /// Rotate the specified node "left". + /// + /// Specified node. + /// New root node. + private static Node RotateLeft(Node node) + { + Node x = node.Right; + node.Right = x.Left; + x.Left = node; + x.IsBlack = node.IsBlack; + node.IsBlack = false; + return x; + } + + /// + /// Rotate the specified node "right". + /// + /// Specified node. + /// New root node. + private static Node RotateRight(Node node) + { + Node x = node.Left; + node.Left = x.Right; + x.Right = node; + x.IsBlack = node.IsBlack; + node.IsBlack = false; + return x; + } + + /// + /// Moves a red node from the right child to the left child. + /// + /// Parent node. + /// New root node. + private static Node MoveRedLeft(Node node) + { + FlipColor(node); + if (IsRed(node.Right.Left)) + { + node.Right = RotateRight(node.Right); + node = RotateLeft(node); + FlipColor(node); + + // * Avoid creating right-leaning nodes + if (IsRed(node.Right.Right)) + { + node.Right = RotateLeft(node.Right); + } + } + return node; + } + + /// + /// Moves a red node from the left child to the right child. + /// + /// Parent node. + /// New root node. + private static Node MoveRedRight(Node node) + { + FlipColor(node); + if (IsRed(node.Left.Left)) + { + node = RotateRight(node); + FlipColor(node); + } + return node; + } + + /// + /// Deletes the minimum node under the specified node. + /// + /// Specified node. + /// New root node. + private Node DeleteMinimum(Node node) + { + if (null == node.Left) + { + // Nothing to do + return null; + } + + if (!IsRed(node.Left) && !IsRed(node.Left.Left)) + { + // Move red node left + node = MoveRedLeft(node); + } + + // Recursively delete + node.Left = DeleteMinimum(node.Left); + + // Maintain invariants + return FixUp(node); + } + + /// + /// Maintains invariants by adjusting the specified nodes children. + /// + /// Specified node. + /// New root node. + private static Node FixUp(Node node) + { + if (IsRed(node.Right)) + { + // Avoid right-leaning node + node = RotateLeft(node); + } + + if (IsRed(node.Left) && IsRed(node.Left.Left)) + { + // Balance 4-node + node = RotateRight(node); + } + + if (IsRed(node.Left) && IsRed(node.Right)) + { + // Push red up + FlipColor(node); + } + + // * Avoid leaving behind right-leaning nodes + if ((null != node.Left) && IsRed(node.Left.Right) && !IsRed(node.Left.Left)) + { + node.Left = RotateLeft(node.Left); + if (IsRed(node.Left)) + { + // Balance 4-node + node = RotateRight(node); + } + } + + return node; + } + + /// + /// Gets the (first) node corresponding to the specified key. + /// + /// Key to search for. + /// Corresponding node or null if none found. + private Node GetNodeForKey(TKey key) + { + // Initialize + Node node = _rootNode; + while (null != node) + { + // Compare keys and go left/right + int comparisonResult = _keyComparison(key, node.Key); + if (comparisonResult < 0) + { + node = node.Left; + } + else if (0 < comparisonResult) + { + node = node.Right; + } + else + { + // Match; return node + return node; + } + } + + // No match found + return null; + } + + /// + /// Gets an extreme (ex: minimum/maximum) value. + /// + /// Type of value. + /// Node to start from. + /// Successor function. + /// Selector function. + /// Extreme value. + private static T GetExtreme(Node node, Func successor, Func selector) + { + // Initialize + T extreme = default(T); + Node current = node; + while (null != current) + { + // Go to extreme + extreme = selector(current); + current = successor(current); + } + return extreme; + } + + /// + /// Traverses a subset of the sequence of nodes in order and selects the specified nodes. + /// + /// Type of elements. + /// Starting node. + /// Condition method. + /// Selector method. + /// Sequence of selected nodes. + private IEnumerable Traverse(Node node, Func condition, Func selector) + { + // Create a stack to avoid recursion + Stack stack = new Stack(); + Node current = node; + while (null != current) + { + if (null != current.Left) + { + // Save current state and go left + stack.Push(current); + current = current.Left; + } + else + { + do + { + for (int i = 0; i <= current.Siblings; i++) + { + // Select current node if relevant + if (condition(current)) + { + yield return selector(current); + } + } + // Go right - or up if nothing to the right + current = current.Right; + } + while ((null == current) && + (0 < stack.Count) && + (null != (current = stack.Pop()))); + } + } + } + + /// + /// Compares the specified keys (primary) and values (secondary). + /// + /// The left key. + /// The left value. + /// The right key. + /// The right value. + /// CompareTo-style results: -1 if left is less, 0 if equal, and 1 if greater than right. + private int KeyAndValueComparison(TKey leftKey, TValue leftValue, TKey rightKey, TValue rightValue) + { + // Compare keys + int comparisonResult = _keyComparison(leftKey, rightKey); + if ((0 == comparisonResult) && (null != _valueComparison)) + { + // Keys match; compare values + comparisonResult = _valueComparison(leftValue, rightValue); + } + return comparisonResult; + } + +#if DEBUGGING + /// + /// Asserts that tree invariants are not violated. + /// + private void AssertInvariants() + { + // Root is black + Debug.Assert((null == _rootNode) || _rootNode.IsBlack, "Root is not black"); + // Every path contains the same number of black nodes + Dictionary parents = new Dictionary.Node, LeftLeaningRedBlackTree.Node>(); + foreach (Node node in Traverse(_rootNode, n => true, n => n)) + { + if (null != node.Left) + { + parents[node.Left] = node; + } + if (null != node.Right) + { + parents[node.Right] = node; + } + } + if (null != _rootNode) + { + parents[_rootNode] = null; + } + int treeCount = -1; + foreach (Node node in Traverse(_rootNode, n => (null == n.Left) || (null == n.Right), n => n)) + { + int pathCount = 0; + Node current = node; + while (null != current) + { + if (current.IsBlack) + { + pathCount++; + } + current = parents[current]; + } + Debug.Assert((-1 == treeCount) || (pathCount == treeCount), "Not all paths have the same number of black nodes."); + treeCount = pathCount; + } + // Verify node properties... + foreach (Node node in Traverse(_rootNode, n => true, n => n)) + { + // Left node is less + if (null != node.Left) + { + Debug.Assert(0 > KeyAndValueComparison(node.Left.Key, node.Left.Value, node.Key, node.Value), "Left node is greater than its parent."); + } + // Right node is greater + if (null != node.Right) + { + Debug.Assert(0 < KeyAndValueComparison(node.Right.Key, node.Right.Value, node.Key, node.Value), "Right node is less than its parent."); + } + // Both children of a red node are black + Debug.Assert(!IsRed(node) || (!IsRed(node.Left) && !IsRed(node.Right)), "Red node has a red child."); + // Always left-leaning + Debug.Assert(!IsRed(node.Right) || IsRed(node.Left), "Node is not left-leaning."); + // No consecutive reds (subset of previous rule) + //Debug.Assert(!(IsRed(node) && IsRed(node.Left))); + } + } + + /// + /// Gets an HTML fragment representing the tree. + /// + public string HtmlDocument + { + get + { + return + "" + + "" + + (null != _rootNode ? _rootNode.HtmlFragment : "[null]") + + "" + + ""; + } + } +#endif +} diff --git a/DataStructures/PriorityQueue.cs b/DataStructures/PriorityQueue.cs new file mode 100644 index 0000000..f314d94 --- /dev/null +++ b/DataStructures/PriorityQueue.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Collections; + +// By James McCaffrey11/02/2012 + +sealed public class PriorityQueue:IEnumerable where T : IComparable +{ + private List data; + + public PriorityQueue () + { + this.data = new List (); + } + + IEnumerator System.Collections.IEnumerable.GetEnumerator () + { + // Lets call the generic version here + return this.GetEnumerator (); + } + + public IEnumerator GetEnumerator () + { + return data.GetEnumerator () as IEnumerator; + } + + public void Enqueue (T item) + { + data.Add (item); + int ci = data.Count - 1; // child index; start at end + while (ci > 0) { + int pi = (ci - 1) / 2; // parent index + if (data [ci].CompareTo (data [pi]) >= 0) + break; // child item is larger than (or equal) parent so we're done + T tmp = data [ci]; + data [ci] = data [pi]; + data [pi] = tmp; + ci = pi; + } + } + + public T Dequeue() + { + // assumes pq is not empty; up to calling code + int li = data.Count - 1; // last index (before removal) + T frontItem = data [0]; // fetch the front + data [0] = data [li]; + data.RemoveAt (li); + + --li; // last index (after removal) + int pi = 0; // parent index. start at front of pq + while (true) + { + int ci = pi * 2 + 1; // left child index of parent + if (ci > li) + break; // no children so done + int rc = ci + 1; // right child + if (rc <= li && data [rc].CompareTo (data [ci]) < 0) // if there is a rc (ci + 1), and it is smaller than left child, use the rc instead + ci = rc; + if (data [pi].CompareTo (data [ci]) <= 0) + break; // parent is smaller than (or equal to) smallest child so done + T tmp = data [pi]; + data [pi] = data [ci]; + data [ci] = tmp; // swap parent and child + pi = ci; + } + + return frontItem; + } + + public T Peek () + { + T frontItem = data [0]; + return frontItem; + } + + public int Count () + { + return data.Count; + } + + public override string ToString () + { + string s = ""; + for (int i = 0; i < data.Count; ++i) + s += data [i].ToString () + " "; + s += "count = " + data.Count; + return s; + } + + public bool IsConsistent () + { + // is the heap property true for all data? + if (data.Count == 0) + return true; + int li = data.Count - 1; // last index + for (int pi = 0; pi < data.Count; ++pi) { // each parent index + int lci = 2 * pi + 1; // left child index + int rci = 2 * pi + 2; // right child index + + if (lci <= li && data [pi].CompareTo (data [lci]) > 0) + return false; // if lc exists and it's greater than parent then bad. + if (rci <= li && data [pi].CompareTo (data [rci]) > 0) + return false; // check the right child too. + } + return true; // passed all checks + } // IsConsistent +} // PriorityQueue diff --git a/ECS/DataStructures/TypeSafeDictionary.cs b/ECS/DataStructures/TypeSafeDictionary.cs new file mode 100644 index 0000000..022ebe5 --- /dev/null +++ b/ECS/DataStructures/TypeSafeDictionary.cs @@ -0,0 +1,23 @@ +using Svelto.DataStructures; +using System.Collections.Generic; + +namespace Svelto.ECS +{ + /// + /// This is just a place holder at the moment + /// I always wanted to create my own Dictionary + /// data structure as excercise, but never had the + /// time to. At the moment I need the custom interface + /// wrapped though. + /// + + public interface ITypeSafeDictionary + { + + } + + public class TypeSafeDictionary : Dictionary, ITypeSafeDictionary + { + internal static readonly ReadOnlyDictionary Default = new ReadOnlyDictionary(new TypeSafeDictionary()); + } +} diff --git a/ECS/DataStructures/TypeSafeList.cs b/ECS/DataStructures/TypeSafeList.cs new file mode 100644 index 0000000..cac7c4b --- /dev/null +++ b/ECS/DataStructures/TypeSafeList.cs @@ -0,0 +1,51 @@ +using Svelto.DataStructures; +using Svelto.ECS.Internal; + +namespace Svelto.ECS +{ + public interface ITypeSafeList + { + void Clear(); + void AddRange(ITypeSafeList nodeListValue); + + ITypeSafeList Create(); + ITypeSafeDictionary CreateIndexedDictionary(); + void AddToIndexedDictionary(ITypeSafeDictionary nodesDic); + } + + public class TypeSafeFasterList : FasterList, ITypeSafeList + { + public TypeSafeFasterList() + { + } + + public void AddRange(ITypeSafeList nodeListValue) + { + AddRange(nodeListValue as FasterList); + } + + public ITypeSafeList Create() + { + return new TypeSafeFasterList(); + } + + public ITypeSafeDictionary CreateIndexedDictionary() + { + return new TypeSafeDictionary(); + } + + public void AddToIndexedDictionary(ITypeSafeDictionary nodesDic) + { + var dic = nodesDic as TypeSafeDictionary; + + var buffer = NoVirt.ToArrayFast(this); + + for (int i = 0; i < Count; i++) + { + T node = buffer[i]; + + dic[(node as NodeWithID).ID] = node; + } + } + } +} diff --git a/ECS/EngineNodeDB.cs b/ECS/EngineNodeDB.cs index 0bb5bb8..9e26351 100644 --- a/ECS/EngineNodeDB.cs +++ b/ECS/EngineNodeDB.cs @@ -6,104 +6,102 @@ namespace Svelto.ECS { public class EngineNodeDB : IEngineNodeDB { - internal EngineNodeDB( Dictionary> nodesDB, - Dictionary> nodesDBdic, - Dictionary> metaNodesDB) + internal EngineNodeDB( Dictionary nodesDB, + Dictionary nodesDBdic, + Dictionary metaNodesDB) { _nodesDB = nodesDB; _nodesDBdic = nodesDBdic; _metaNodesDB = metaNodesDB; } - public FasterReadOnlyListCast QueryNodes() where T:INode + public FasterReadOnlyList QueryNodes() { var type = typeof(T); - FasterList nodes; + ITypeSafeList nodes; if (_nodesDB.TryGetValue(type, out nodes) == false) return RetrieveEmptyNodeList(); - return new FasterReadOnlyListCast(nodes); + return new FasterReadOnlyList((FasterList)nodes); } - public ReadOnlyDictionary QueryIndexableNodes() where T:INode + public ReadOnlyDictionary QueryIndexableNodes() { var type = typeof(T); - Dictionary nodes; + ITypeSafeDictionary nodes; if (_nodesDBdic.TryGetValue(type, out nodes) == false) - return _defaultEmptyNodeDict; + return TypeSafeDictionary.Default; - return new ReadOnlyDictionary(nodes); + return new ReadOnlyDictionary(nodes as Dictionary); } - public T QueryMetaNode(int metaEntityID) where T : INode + public T QueryMetaNode(int metaEntityID) { return QueryNode(metaEntityID); } - public bool TryQueryMetaNode(int metaEntityID, out T node) where T : INode + public bool TryQueryMetaNode(int metaEntityID, out T node) { return TryQueryNode(metaEntityID, out node); } - public FasterReadOnlyListCast QueryMetaNodes() where T : INode + public FasterReadOnlyList QueryMetaNodes() { var type = typeof(T); - FasterList nodes; + ITypeSafeList nodes; if (_metaNodesDB.TryGetValue(type, out nodes) == false) return RetrieveEmptyNodeList(); - return new FasterReadOnlyListCast(nodes); + return new FasterReadOnlyList((FasterList)nodes); } - public bool TryQueryNode(int ID, out T node) where T:INode + public bool TryQueryNode(int ID, out T node) { var type = typeof(T); - INode internalNode; + T internalNode; - Dictionary nodes; + ITypeSafeDictionary nodes; if (_nodesDBdic.TryGetValue(type, out nodes) && - nodes.TryGetValue(ID, out internalNode)) + (nodes as Dictionary).TryGetValue(ID, out internalNode)) { - node = (T)internalNode; + node = internalNode; return true; } - + node = default(T); return false; } - public T QueryNode(int ID) where T:INode + public T QueryNode(int ID) { var type = typeof(T); - INode internalNode; Dictionary nodes; + T internalNode; ITypeSafeDictionary nodes; if (_nodesDBdic.TryGetValue(type, out nodes) && - nodes.TryGetValue(ID, out internalNode)) + (nodes as Dictionary).TryGetValue(ID, out internalNode)) return (T)internalNode; throw new Exception("Node Not Found"); } - static FasterReadOnlyListCast RetrieveEmptyNodeList() where T : INode + static FasterReadOnlyList RetrieveEmptyNodeList() { - return FasterReadOnlyListCast.DefaultList; + return FasterReadOnlyList.DefaultList; } - readonly Dictionary> _nodesDB; - readonly Dictionary> _nodesDBdic; - readonly Dictionary> _metaNodesDB; - - readonly ReadOnlyDictionary _defaultEmptyNodeDict = new ReadOnlyDictionary(new Dictionary()); + readonly Dictionary _nodesDB; + readonly Dictionary _nodesDBdic; + readonly Dictionary _metaNodesDB; } } diff --git a/ECS/EnginesRoot.cs b/ECS/EnginesRoot.cs index bc9c6df..3deadf8 100644 --- a/ECS/EnginesRoot.cs +++ b/ECS/EnginesRoot.cs @@ -3,14 +3,24 @@ using System.Collections.Generic; using Svelto.DataStructures; using Svelto.ECS.Internal; using Svelto.ECS.NodeSchedulers; -using WeakReference = Svelto.DataStructures.WeakReference; +using System.Reflection; +using Svelto.Utilities; +using UnityEngine.UI; +using UnityEngine.XR.WSA.Persistence; #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR using Svelto.ECS.Profiler; #endif -#if NETFX_CORE -using System.Reflection; -#endif + +namespace Svelto.ECS.Internal +{ + struct BuildNodeCallbackStruct + { + public Action _internalRemove; + public Action _internalEnable; + public Action _internalDisable; + } +} namespace Svelto.ECS { @@ -22,22 +32,22 @@ namespace Svelto.ECS _activableEngines = new Dictionary>(); _otherEngines = new FasterList(); - _engineRootWeakReference = new WeakReference(this); + //_engineRootWeakReference = new DataStructures.WeakReference(this); - _nodesDB = new Dictionary>(); - _nodesDBdic = new Dictionary>(); + _nodesDB = new Dictionary(); + _metaNodesDB = new Dictionary(); + _nodesDBdic = new Dictionary(); - _nodesToAdd = new FasterList(); - _metaNodesToAdd = new FasterList(); - - _metaNodesDB = new Dictionary>(); - _sharedStructNodeLists = new SharedStructNodeLists(); - _sharedGroupedStructNodeLists = new SharedGroupedStructNodesLists(); + _nodesToAdd = new Dictionary(); + _metaNodesToAdd = new Dictionary(); + _groupedNodesToAdd = new Dictionary>(); + + _callBackStruct = new BuildNodeCallbackStruct(); - _internalRemove = InternalRemove; - _internalDisable = InternalDisable; - _internalEnable = InternalEnable; - _internalMetaRemove = InternalMetaRemove; + /* _callBackStruct._internalRemove = InternalRemove; + _callBackStruct._internalDisable = InternalDisable; + _callBackStruct._internalEnable = InternalEnable; + _callBackStruct._internalMetaRemove = InternalMetaRemove;*/ _scheduler = nodeScheduler; _scheduler.Schedule(SubmitNodes); @@ -54,77 +64,53 @@ namespace Svelto.ECS #endif } - void SubmitNodes() + public void BuildEntity(int ID, EntityDescriptor ed) { - 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; + ed.BuildNodes(ID, _nodesToAdd, ref _callBackStruct); + } - } while (newNodesHaveBeenAddedWhileIterating); + /// + /// 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, ref _callBackStruct); + } - _nodesToAdd.Clear(); - _metaNodesToAdd.Clear(); + /// + /// 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, ref _callBackStruct); } public void AddEngine(IEngine engine) @@ -133,9 +119,9 @@ namespace Svelto.ECS EngineProfiler.AddEngine(engine); #endif var queryableNodeEngine = engine as IQueryableNodeEngine; - if (queryableNodeEngine != null) - queryableNodeEngine.nodesDB = - new EngineNodeDB(_nodesDB, _nodesDBdic, _metaNodesDB); + if (queryableNodeEngine != null) + queryableNodeEngine.nodesDB = + new EngineNodeDB(_nodesDB, _nodesDBdic, _metaNodesDB); var engineType = engine.GetType(); var implementedInterfaces = engineType.GetInterfaces(); @@ -167,12 +153,8 @@ namespace Svelto.ECS if (type.IsAssignableFrom(interfaceType) == false) continue; -#if !NETFX_CORE - if (false == interfaceType.IsGenericType) -#else - if (false == interfaceType.IsConstructedGenericType) -#endif + if (false == interfaceType.IsGenericTypeEx()) { continue; } @@ -189,21 +171,9 @@ namespace Svelto.ECS 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)) + out arguments)) { AddEngine(engine, arguments, _activableEngines); @@ -230,15 +200,9 @@ namespace Svelto.ECS bool CheckNodesEngine(IEngine engine, Type engineType, ref bool engineAdded) { -#if !NETFX_CORE - var baseType = engineType.BaseType; + var baseType = engineType.GetBaseType(); - if (baseType.IsGenericType -#else - var baseType = engineType.GetTypeInfo().BaseType; - - if (baseType.IsConstructedGenericType -#endif + if (baseType.IsGenericTypeEx() && engine is INodeEngine) { AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines); @@ -251,76 +215,8 @@ namespace Svelto.ECS 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) + Dictionary> engines) { for (int i = 0; i < types.Length; i++) { @@ -339,186 +235,229 @@ namespace Svelto.ECS } } - void AddNodeToMetaDB(INode node, Type nodeType) + static void AddNodesToTheDBAndSuitableEngines(Dictionary nodesToAdd, + Dictionary> nodeEngines, + Dictionary nodesDBdic, + Dictionary nodesDB) { - FasterList nodes; - if (_metaNodesDB.TryGetValue(nodeType, out nodes) == false) - nodes = _metaNodesDB[nodeType] = new FasterList(); + foreach (var nodeList in nodesToAdd) + { + ITypeSafeList dbList; - nodes.Add(node); - } + if (nodesDB.TryGetValue(nodeList.Key, out dbList) == false) + dbList = nodesDB[nodeList.Key] = nodeList.Value.Create(); - void AddNodeToTheDB(T node, Type nodeType) where T : INode - { - FasterList nodes; - if (_nodesDB.TryGetValue(nodeType, out nodes) == false) - nodes = _nodesDB[nodeType] = new FasterList(); + dbList.AddRange(nodeList.Value); - nodes.Add(node); + AddNodeToNodesDictionary(nodesDBdic, nodeList.Value, nodeList.Key); + AddNodesToTheSuitableEngines(nodeEngines, nodeList.Value, nodeList.Key); + } } - - void AddNodeToNodesDictionary(T node, Type nodeType) where T : INodeWithID + + static void AddNodeToNodesDictionary(Dictionary nodesDBdic, ITypeSafeList nodes, Type nodeType) { - Dictionary nodesDic; + ITypeSafeDictionary nodesDic; - if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false) - nodesDic = _nodesDBdic[nodeType] = new Dictionary(); + if (nodesDBdic.TryGetValue(nodeType, out nodesDic) == false) + nodesDic = nodesDBdic[nodeType] = nodes.CreateIndexedDictionary(); - nodesDic.Add(node.ID, node); + nodes.AddToIndexedDictionary(nodesDic); } - void AddNodeToTheSuitableEngines(INode node, Type nodeType) + static void AddNodesToTheSuitableEngines(Dictionary> nodeEngines, ITypeSafeList nodes, Type nodeType) { FasterList enginesForNode; - if (_nodeEngines.TryGetValue(nodeType, out 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); + (enginesForNode[j] as INodeEngine).Add(nodes); #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 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 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; - void RemoveNodeFromNodesDictionary(T node, Type nodeType) where T : INodeWithID - { - Dictionary nodesDic; + if (_nodesDBdic.TryGetValue(nodeType, out nodesDic)) + nodesDic.Remove(node.ID); + } - 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 RemoveNodeFromEngines(T node, Type nodeType) where T : INode - { - FasterList enginesForNode; + 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); + } + } + } - if (_nodeEngines.TryGetValue(nodeType, out enginesForNode)) - { - for (int j = 0; j < enginesForNode.Count; j++) + void EnableNodeFromEngines(INode node, Type nodeType) { -#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR - EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, (enginesForNode[j] as INodeEngine), node); -#else - (enginesForNode[j] as INodeEngine).Remove(node); -#endif + 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 DisableNodeFromEngines(INode node, Type nodeType) - { - FasterList enginesForNode; + 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); + } + } - if (_activableEngines.TryGetValue(nodeType, out enginesForNode)) - { - for (int j = 0; j < enginesForNode.Count; j++) + void InternalEnable(FasterList nodes) { - (enginesForNode[j] as IActivableNodeEngine).Disable(node); + 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 EnableNodeFromEngines(INode node, Type nodeType) - { - FasterList enginesForNode; + void InternalRemove(IFasterList nodes) + { + if (_engineRootWeakReference.IsValid == false) + return; - if (_activableEngines.TryGetValue(nodeType, out enginesForNode)) - { - for (int j = 0; j < enginesForNode.Count; j++) + 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) { - (enginesForNode[j] as IActivableNodeEngine).Enable(node); + 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); + } } - } - } -#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 + readonly DataStructures.WeakReference _engineRootWeakReference; + + */ - void InternalDisable(FasterList nodes) + void SubmitNodes() { - if (_engineRootWeakReference.IsValid == false) - return; + int metaNodesCount = _metaNodesToAdd.Count; + int nodesCount = _nodesToAdd.Count; - for (int i = 0; i < nodes.Count; i++) - { - var node = nodes[i]; - Type nodeType = node.GetType(); - DisableNodeFromEngines(node, nodeType); - } - } + if (metaNodesCount + nodesCount == 0) return; - void InternalEnable(FasterList nodes) - { - if (_engineRootWeakReference.IsValid == false) - return; + bool newNodesHaveBeenAddedWhileIterating; + int startNodes = 0; + int startMetaNodes = 0; + int numberOfReenteringLoops = 0; - for (int i = 0; i < nodes.Count; i++) + do { - var node = nodes[i]; - Type nodeType = node.GetType(); - EnableNodeFromEngines(node, nodeType); - } - } + AddNodesToTheDBAndSuitableEngines(_nodesToAdd, _nodeEngines, _nodesDBdic, _nodesDB); + AddNodesToTheDBAndSuitableEngines(_metaNodesToAdd, _nodeEngines, _nodesDBdic, _metaNodesDB); - void InternalRemove(FasterList nodes) - { - if (_engineRootWeakReference.IsValid == false) - return; + newNodesHaveBeenAddedWhileIterating = + _metaNodesToAdd.Count > metaNodesCount || + _nodesToAdd.Count > nodesCount; - for (int i = 0; i < nodes.Count; i++) - { - var node = nodes[i]; - Type nodeType = node.GetType(); + startNodes = nodesCount; + startMetaNodes = metaNodesCount; - RemoveNodeFromEngines(node, nodeType); - RemoveNodeFromTheDB(node, node.GetType()); + if (numberOfReenteringLoops > 5) + throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method"); - var nodeWithId = node as INodeWithID; - if (nodeWithId != null) - RemoveNodeFromNodesDictionary(nodeWithId, nodeType); - } - } + numberOfReenteringLoops++; - void InternalMetaRemove(FasterList nodes) - { - for (int i = 0; i < nodes.Count; i++) - { - var node = nodes[i]; - Type nodeType = node.GetType(); + metaNodesCount = _metaNodesToAdd.Count; + nodesCount = _nodesToAdd.Count; - RemoveNodeFromEngines(node, nodeType); - RemoveNodeFromMetaDB(node, nodeType); + } while (newNodesHaveBeenAddedWhileIterating); - var nodeWithId = node as INodeWithID; - if (nodeWithId != null) - RemoveNodeFromNodesDictionary(nodeWithId, nodeType); - } + _nodesToAdd.Clear(); + _metaNodesToAdd.Clear(); } readonly Dictionary> _nodeEngines; @@ -526,30 +465,27 @@ namespace Svelto.ECS 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 Dictionary _nodesDB; + readonly Dictionary _metaNodesDB; + readonly Dictionary> _groupNodesDB; + + readonly Dictionary _nodesDBdic; + /// + /// Need to think about how to make BuildEntity thread safe as well + /// + readonly Dictionary _nodesToAdd; + readonly Dictionary _metaNodesToAdd; + readonly Dictionary> _groupedNodesToAdd; + 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; + + BuildNodeCallbackStruct _callBackStruct; } -} - +} \ No newline at end of file diff --git a/ECS/EntityDescriptor.cs b/ECS/EntityDescriptor.cs index cf459b2..6c96c06 100644 --- a/ECS/EntityDescriptor.cs +++ b/ECS/EntityDescriptor.cs @@ -2,22 +2,26 @@ using System; using System.Collections.Generic; using System.Reflection; using Svelto.DataStructures; -#if NETFX_CORE -using BindingFlags = System.Reflection.BindingFlags; -#endif +using Svelto.ECS.Internal; namespace Svelto.ECS { public class EntityDescriptor { protected EntityDescriptor() - { - } + {} + + /// + /// if you want to avoid allocation in run-time, you can prebuild + /// EntityDescriptors and use them to build entities at different + /// times + /// protected EntityDescriptor(INodeBuilder[] nodesToBuild) { _nodesToBuild = new FasterList(nodesToBuild); } - protected EntityDescriptor(INodeBuilder[] nodesToBuild, params object[] componentsImplementor):this(nodesToBuild) + protected EntityDescriptor(INodeBuilder[] nodesToBuild, + params object[] componentsImplementor):this(nodesToBuild) { ProcessImplementors(componentsImplementor); } @@ -26,105 +30,160 @@ namespace Svelto.ECS { ProcessImplementors(componentsImplementor); } + + public void AddNodes(params INodeBuilder[] nodesWithID) + { + _nodesToBuild.AddRange(nodesWithID); + } - void ProcessImplementors(object[] implementors) + internal void BuildGroupedNodes + (int entityID, int groupID, + Dictionary> groupNodes, + ref BuildNodeCallbackStruct callBackstruct) { - for (int index = 0; index < implementors.Length; index++) + for (int index = 0; index < _nodesToBuild.Count; index++) { - var implementor = implementors[index]; - if (implementor == null) - { - Utility.Console.LogWarning( - "Null implementor, are you using a wild GetComponents to fetch it? " - .FastConcat(ToString())); - } - else + var nodeBuilder = _nodesToBuild[index]; + var nodeType = nodeBuilder.GetNodeType(); + + Dictionary groupedNodesTyped; + + if (groupNodes.TryGetValue(nodeType, out groupedNodesTyped) == false) { - 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); + groupedNodesTyped = new Dictionary(); + + groupNodes.Add(nodeType, groupedNodesTyped); + }; + + ITypeSafeList nodes; - var interfaces = implementor.GetType().GetInterfaces(); - for (int iindex = 0; iindex < interfaces.Length; iindex++) - { - _implementorsByType[interfaces[iindex]] = implementor; - } + var mustAdd = groupedNodesTyped.TryGetValue(groupID, out nodes) == false; + + var node = nodeBuilder.BuildAndAddToList(ref nodes, entityID); + + if (mustAdd) + groupedNodesTyped[groupID] = nodes; + + if (node != null && nodeBuilder.reflects != FillNodeMode.None) + { + node = FillNode(node, nodeBuilder.reflects); + + SetupImplementors(ref callBackstruct, nodes); } - } - } - public void AddNodes(params INodeBuilder[] nodesWithID) - { - _nodesToBuild.AddRange(nodesWithID); + /* var groupNode = node as IGroupedNode; + if (groupNode != null) + groupNode.groupID = groupID;*/ + } } - public virtual FasterList BuildNodes(int ID) + internal void BuildNodes(int entityID, + Dictionary nodesToAdd, + ref BuildNodeCallbackStruct callBackstruct) { - var nodes = new FasterList(); - for (int index = 0; index < _nodesToBuild.Count; index++) { var nodeBuilder = _nodesToBuild[index]; - var node = nodeBuilder.Build(ID); + var nodeType = nodeBuilder.GetNodeType(); - if (nodeBuilder.reflects != FillNodeMode.None) - node = FillNode(node, nodeBuilder.reflects); + ITypeSafeList nodes; - nodes.Add(node); - } + var mustAdd = nodesToAdd.TryGetValue(nodeType, out nodes) == false; + + var node = nodeBuilder.BuildAndAddToList(ref nodes, entityID); - return nodes; - } + if (mustAdd) + nodesToAdd[nodeType] = nodes; - internal FasterList BuildNodes(int ID, - Action> removeEntity, - Action> enableEntity, - Action> disableEntity) + if (node != null && nodeBuilder.reflects != FillNodeMode.None) + { + FillNode(node, nodeBuilder.reflects); + + SetupImplementors(ref callBackstruct, nodes); + } + } + } + + void ProcessImplementors(object[] implementors) { - var nodes = BuildNodes(ID); - - SetupImplementors(removeEntity, enableEntity, disableEntity, nodes); + for (int index = 0; index < implementors.Length; index++) + { + var implementor = implementors[index]; + + if (implementor != null) + { + if (implementor is IRemoveEntityComponent) + _removingImplementors.Add(new DataStructures.WeakReference(implementor as IRemoveEntityComponent)); + if (implementor is IDisableEntityComponent) + _disablingImplementors.Add(new DataStructures.WeakReference(implementor as IDisableEntityComponent)); + if (implementor is IEnableEntityComponent) + _enablingImplementors.Add(new DataStructures.WeakReference(implementor as IEnableEntityComponent)); - return nodes; + var interfaces = implementor.GetType().GetInterfaces(); + var weakReference = new DataStructures.WeakReference(implementor); + + for (int iindex = 0; iindex < interfaces.Length; iindex++) + { + var componentType = interfaces[iindex]; + + _implementorsByType[componentType] = weakReference; +#if DEBUG && !PROFILER + if (_implementorCounterByType.ContainsKey(componentType) == false) + _implementorCounterByType[componentType] = 1; + else + _implementorCounterByType[componentType]++; +#endif + } + } +#if DEBUG && !PROFILER + else + Utility.Console.LogError(NULL_IMPLEMENTOR_ERROR.FastConcat(ToString())); +#endif + } } void SetupImplementors( - Action> removeEntity, - Action> enableEntity, - Action> disableEntity, - FasterList nodes) + ref BuildNodeCallbackStruct callBackstruct, + ITypeSafeList 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; + var RemoveEntity = callBackstruct._internalRemove; + var DisableEntity = callBackstruct._internalDisable; + var EnableEntity = callBackstruct._internalEnable; + + Action removeEntityAction = () => { RemoveEntity(nodes); nodes.Clear(); }; + Action disableEntityAction = () => DisableEntity(nodes); + Action enableEntityAction = () => EnableEntity(nodes); + + int removingImplementorsCount = _removingImplementors.Count; + for (int index = 0; index < removingImplementorsCount; index++) + _removingImplementors[index].Target.removeEntity = removeEntityAction; + + int disablingImplementorsCount = _disablingImplementors.Count; + for (int index = 0; index < disablingImplementorsCount; index++) + _disablingImplementors[index].Target.disableEntity = disableEntityAction; + + int enablingImplementorsCount = _enablingImplementors.Count; + for (int index = 0; index < enablingImplementorsCount; index++) + _enablingImplementors[index].Target.enableEntity = enableEntityAction; } TNode FillNode(TNode node, FillNodeMode mode) where TNode : INode { - var fields = node.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); + 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; - - if ((_implementorsByType.TryGetValue(fieldType, out component)) == false) + DataStructures.WeakReference component; + + if (_implementorsByType.TryGetValue(fieldType, out component) == false) { if (mode == FillNodeMode.Strict) { Exception e = - new Exception("Svelto.ECS: Implementor not found for a Node. " + "Implementor Type: " + + new Exception(NOT_FOUND_EXCEPTION + field.FieldType.Name + " - Node: " + node.GetType().Name + " - EntityDescriptor " + this); @@ -132,65 +191,36 @@ namespace Svelto.ECS } } else - field.SetValue(node, component); - } - - return node; - } - - 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 - { - INode Build(int ID); - - FillNodeMode reflects { get; } - } - - public class NodeBuilder : INodeBuilder where NodeType : NodeWithID, new() - { - public INode Build(int ID) - { - NodeWithID node = NodeWithID.BuildNode(ID); - - return (NodeType)node; - } - - public FillNodeMode reflects { get { return FillNodeMode.Strict; } } - } + field.SetValue(node, component.Target); + +#if DEBUG && !PROFILER + { + if (_implementorCounterByType[fieldType] != 1) + { + Utility.Console.LogError( + DUPLICATE_IMPLEMENTOR_ERROR.FastConcat("component: ", fieldType.ToString(), + " implementor: ", component.Target.ToString())); + } + } +#endif - public class StructNodeBuilder : INodeBuilder - where NodeType : struct, IStructNodeWithID - { - public INode Build(int ID) - { - var shortID = (short)ID; - IStructNodeWithID node = default(NodeType); - node.ID = shortID; + } return node; } - public virtual FillNodeMode reflects { get { return FillNodeMode.Relaxed; } } - } + readonly FasterList> _disablingImplementors = new FasterList>(); + readonly FasterList> _removingImplementors = new FasterList>(); + readonly FasterList> _enablingImplementors = new FasterList>(); - public class FastStructNodeBuilder : StructNodeBuilder - where NodeType : struct, IStructNodeWithID - { - public override FillNodeMode reflects { get { return FillNodeMode.None; } } - } - - public enum FillNodeMode - { - Strict, - Relaxed, + readonly Dictionary> _implementorsByType = new Dictionary>(); +#if DEBUG && !PROFILER + readonly Dictionary _implementorCounterByType = new Dictionary(); +#endif + readonly FasterList _nodesToBuild; - None + const string DUPLICATE_IMPLEMENTOR_ERROR = "the same component is implemented with more than one implementor. This is considered an error and MUST be fixed. "; + const string NULL_IMPLEMENTOR_ERROR = "Null implementor, are you using a wild GetComponents to fetch it? "; + const string NOT_FOUND_EXCEPTION = "Svelto.ECS: Implementor not found for a Node. Implementor Type: "; } } diff --git a/ECS/Extensions/Unity/UnitySumbmissionNodeScheduler.cs b/ECS/Extensions/Unity/UnitySumbmissionNodeScheduler.cs index 9241fdf..5e0d363 100644 --- a/ECS/Extensions/Unity/UnitySumbmissionNodeScheduler.cs +++ b/ECS/Extensions/Unity/UnitySumbmissionNodeScheduler.cs @@ -5,6 +5,14 @@ using UnityEngine; namespace Svelto.ECS.NodeSchedulers { + //The NodeSubmissionScheduler has been introduced to make + //the node submission logic platform indipendent. + //Please don't be tempted to create your own submission to + //adapt to your game level code design. For example, + //you may be tempted to write a submission logic to submit + //the nodes immediatly just because convenient for your game + //logic. This is not how it works. + public class UnitySumbmissionNodeScheduler : NodeSubmissionScheduler { public UnitySumbmissionNodeScheduler() diff --git a/ECS/GenericEntityDescriptor.cs b/ECS/GenericEntityDescriptor.cs index 472cdff..f0c87ff 100644 --- a/ECS/GenericEntityDescriptor.cs +++ b/ECS/GenericEntityDescriptor.cs @@ -140,6 +140,25 @@ namespace Svelto.ECS static readonly INodeBuilder[] _nodesToBuild; } + + public class GenericMixedEntityDescriptor : EntityDescriptor + where T : INodeBuilder, new() + where U : INodeBuilder, new() + { + static GenericMixedEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new T(), + new U(), + }; + } + public GenericMixedEntityDescriptor(params object[] componentsImplementor) : + base(_nodesToBuild, componentsImplementor) + { } + + static readonly INodeBuilder[] _nodesToBuild; + } public class GenericMixedEntityDescriptor : EntityDescriptor where T : INodeBuilder, new() diff --git a/ECS/GenericEntityDescriptorHolder.cs b/ECS/GenericEntityDescriptorHolder.cs index 4e51570..05c8ac5 100644 --- a/ECS/GenericEntityDescriptorHolder.cs +++ b/ECS/GenericEntityDescriptorHolder.cs @@ -3,7 +3,8 @@ using System; namespace Svelto.ECS { - public class GenericEntityDescriptorHolder: UnityEngine.MonoBehaviour, IEntityDescriptorHolder where T:EntityDescriptor + public class GenericEntityDescriptorHolder: + UnityEngine.MonoBehaviour, IEntityDescriptorHolder where T:EntityDescriptor { public EntityDescriptor BuildDescriptorType(object[] externalImplentors) { diff --git a/ECS/IEngine.cs b/ECS/IEngine.cs index d811e9a..0a4629a 100644 --- a/ECS/IEngine.cs +++ b/ECS/IEngine.cs @@ -1,3 +1,4 @@ +using Svelto.DataStructures; using Svelto.ECS.Internal; namespace Svelto.ECS.Internal @@ -14,14 +15,14 @@ namespace Svelto.ECS.Internal public interface IActivableNodeEngine : IEngine { - void Enable(INode obj); - void Disable(INode obj); + void Enable(ITypeSafeList nodes); + void Disable(ITypeSafeList nodes); } public interface INodeEngine : IEngine { - void Add(INode obj); - void Remove(INode obj); + void Add(ITypeSafeList nodes); + void Remove(ITypeSafeList nodes); } public interface INodesEngine : INodeEngine @@ -56,7 +57,7 @@ namespace Svelto.ECS /// usually the ID is the owner of the nodes of that /// group /// - public interface IGroupedStructNodesEngine : IGroupedStructNodesEngine where T:struct, IGroupedStructNodeWithID + public interface IGroupedStructNodesEngine : IGroupedStructNodesEngine where T:struct, IGroupedNode { } } diff --git a/ECS/IEngineNodeDB.cs b/ECS/IEngineNodeDB.cs index e2ee74f..3906d79 100644 --- a/ECS/IEngineNodeDB.cs +++ b/ECS/IEngineNodeDB.cs @@ -4,16 +4,15 @@ namespace Svelto.ECS { public interface IEngineNodeDB { - ReadOnlyDictionary QueryIndexableNodes() where T:INode; + ReadOnlyDictionary QueryIndexableNodes(); - bool TryQueryNode(int ID, out T node) where T:INode; - T QueryNode(int ID) where T:INode; - - FasterReadOnlyListCast QueryNodes() where T:INode; + bool TryQueryNode(int ID, out T node); + T QueryNode(int ID); + FasterReadOnlyList QueryNodes(); - bool TryQueryMetaNode(int metaEntityID, out T node) where T : INode; - T QueryMetaNode(int metaEntityID) where T : INode; - FasterReadOnlyListCast QueryMetaNodes() where T : INode; + bool TryQueryMetaNode(int metaEntityID, out T node); + T QueryMetaNode(int metaEntityID); + FasterReadOnlyList QueryMetaNodes(); } } diff --git a/ECS/INode.cs b/ECS/INode.cs index f9b0c81..fc53c55 100644 --- a/ECS/INode.cs +++ b/ECS/INode.cs @@ -3,22 +3,17 @@ namespace Svelto.ECS public interface INode {} - public interface INodeWithID:INode - { - int ID { get; } - } - public interface IStructNodeWithID : INode { int ID { get; set; } } - public interface IGroupedStructNodeWithID : IStructNodeWithID + public interface IGroupedNode { int groupID { get; set; } } - public class NodeWithID: INodeWithID + public class NodeWithID: INode { public static TNodeType BuildNode(int ID) where TNodeType: NodeWithID, new() { diff --git a/ECS/MultiNodesEngine.cs b/ECS/MultiNodesEngine.cs index 2907917..97cb3ac 100644 --- a/ECS/MultiNodesEngine.cs +++ b/ECS/MultiNodesEngine.cs @@ -1,9 +1,9 @@ +using Svelto.DataStructures; using Svelto.ECS.Internal; namespace Svelto.ECS.Internal { - public abstract class MultiNodesEngine - where T : INode + public abstract class MultiNodesEngine where T:class { protected abstract void AddNode(T node); protected abstract void RemoveNode(T node); @@ -16,32 +16,89 @@ namespace Svelto.ECS { public abstract System.Type[] AcceptedNodes(); - public abstract void Add(INode node); - public abstract void Remove(INode node); + public abstract void Add(ITypeSafeList nodeWrapper); + public abstract void Remove(ITypeSafeList nodeWrapper); } public abstract class MultiNodesEngine : MultiNodesEngine, - INodeEngine - where T : INode - where U : INode + INodeEngine where T:class where U:class { protected abstract void AddNode(U node); protected abstract void RemoveNode(U node); - public void Add(INode node) + public virtual void Add(ITypeSafeList nodes) { - if (node is T) - AddNode((T)node); + if (nodes is FasterList) + { + var strongTypeNodes = (FasterList)nodes; + + for (int i = 0; i < strongTypeNodes.Count; i++) + { + AddNode(strongTypeNodes[i]); + } + } else - AddNode((U)node); + if (nodes is FasterList) + { + var strongTypeNodes = (FasterList)nodes; + + for (int i = 0; i < strongTypeNodes.Count; i++) + { + AddNode(strongTypeNodes[i]); + } + } } - public void Remove(INode node) + public virtual void Remove(ITypeSafeList nodeWrapper) { - if (node is T) - RemoveNode((T)node); + /* if (nodeWrapper is NodeWrapper) + { + T node; + nodeWrapper.GetNode(out node); + + RemoveNode(ref node); + } + else + { + U node; + nodeWrapper.GetNode(out node); + + RemoveNode(ref node); + }*/ + } + } + + public abstract class MultiNodesEngine : MultiNodesEngine where T: class where U : class + { + protected abstract void AddNode(V node); + protected abstract void RemoveNode(V node); + + public override void Add(ITypeSafeList nodes) + { + if (nodes is FasterList) + { + var strongTypeNodes = (FasterList)nodes; + + for (int i = 0; i < strongTypeNodes.Count; i++) + { + AddNode(strongTypeNodes[i]); + } + } + else + base.Add(nodes); + } + + public override void Remove(ITypeSafeList nodeWrapper) + { + /* if (nodeWrapper is V) + { + V node; + nodeWrapper.GetNode(out node); + + RemoveNode(ref node); + } else - RemoveNode((U)node); + base.Remove(nodeWrapper);*/ } } } \ No newline at end of file diff --git a/ECS/NodeBuilder.cs b/ECS/NodeBuilder.cs new file mode 100644 index 0000000..17b9c34 --- /dev/null +++ b/ECS/NodeBuilder.cs @@ -0,0 +1,77 @@ +using System; +using Svelto.DataStructures; +using Svelto.ECS.Internal; + +namespace Svelto.ECS +{ + public interface INodeBuilder + { + INode BuildAndAddToList(ref ITypeSafeList list, int entityID); + + Type GetNodeType(); + + FillNodeMode reflects { get; } + } + + public class NodeBuilder : INodeBuilder where NodeType : NodeWithID, new() + { + public INode BuildAndAddToList(ref ITypeSafeList list, int entityID) + { + if (list == null) + list = new TypeSafeFasterList(); + + var castedList = list as FasterList; + + var node = NodeWithID.BuildNode(entityID); + + castedList.Add(node); + + return node; + } + + public FillNodeMode reflects + { + get { return FillNodeMode.Strict; } + } + + public Type GetNodeType() + { + return typeof(NodeType); + } + } + + public class StructNodeBuilder : INodeBuilder where NodeType : struct, IStructNodeWithID + { + public INode BuildAndAddToList(ref ITypeSafeList list, int entityID) + { + var node = default(NodeType); + node.ID = entityID; + + if (list == null) + list = new TypeSafeFasterList(); + + var castedList = list as FasterList; + + castedList.Add(node); + + return null; + } + + public Type GetNodeType() + { + return typeof(NodeType); + } + + public virtual FillNodeMode reflects + { + get { return FillNodeMode.None; } + } + } + + public enum FillNodeMode + { + Strict, + + None + } +} \ No newline at end of file diff --git a/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs index b1fb2da..d774c2b 100644 --- a/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs +++ b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR +#if asdUNITY_EDITOR using System; using System.Collections.Generic; using UnityEditor; diff --git a/ECS/Profiler/EngineProfiler.cs b/ECS/Profiler/EngineProfiler.cs index 0612925..c659c87 100644 --- a/ECS/Profiler/EngineProfiler.cs +++ b/ECS/Profiler/EngineProfiler.cs @@ -1,3 +1,4 @@ +#if asdDEBUG using System; using System.Collections.Generic; using System.Diagnostics; @@ -12,31 +13,31 @@ namespace Svelto.ECS.Profiler { static readonly Stopwatch _stopwatch = new Stopwatch(); - public static void MonitorAddDuration(Action addingFunc, INodeEngine engine, INode node) + public static void MonitorAddDuration(Action addingFunc, INodeEngine engine, INodeWrapper node) { EngineInfo info; + if (engineInfos.TryGetValue(engine.GetType(), out info)) { - _stopwatch.Reset(); _stopwatch.Start(); addingFunc(engine, node); - _stopwatch.Stop(); + _stopwatch.Reset(); info.AddAddDuration(_stopwatch.Elapsed.TotalMilliseconds); } } - public static void MonitorRemoveDuration(Action removeFunc, INodeEngine engine, INode node) + public static void MonitorRemoveDuration(Action removeFunc, INodeEngine engine, INodeWrapper node) { EngineInfo info; + if (engineInfos.TryGetValue(engine.GetType(), out info)) { - _stopwatch.Reset(); _stopwatch.Start(); removeFunc(engine, node); - engine.Remove(node); - _stopwatch.Stop(); - + // engine.Remove(node); + _stopwatch.Reset(); + info.AddRemoveDuration(_stopwatch.Elapsed.TotalMilliseconds); } } @@ -60,3 +61,4 @@ namespace Svelto.ECS.Profiler public static readonly Dictionary engineInfos = new Dictionary(); } } +#endif \ No newline at end of file diff --git a/ECS/Profiler/EngineProfilerBehaviour.cs b/ECS/Profiler/EngineProfilerBehaviour.cs index a350ae7..bb046bb 100644 --- a/ECS/Profiler/EngineProfilerBehaviour.cs +++ b/ECS/Profiler/EngineProfilerBehaviour.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR +#if asdUNITY_EDITOR using System; using System.Collections.Generic; using UnityEngine; diff --git a/ECS/SingleNodeEngine.cs b/ECS/SingleNodeEngine.cs index f4c58a1..85ad0d9 100644 --- a/ECS/SingleNodeEngine.cs +++ b/ECS/SingleNodeEngine.cs @@ -1,21 +1,31 @@ +using Svelto.DataStructures; using Svelto.ECS.Internal; namespace Svelto.ECS { - public abstract class SingleNodeEngine : INodeEngine - where TNodeType : INode + public abstract class SingleNodeEngine : INodeEngine where T:class { - public void Add(INode obj) + public void Add(ITypeSafeList nodes) { - Add((TNodeType) obj); + var strongTypeNodes = (FasterList)nodes; + + for (int i = 0; i < strongTypeNodes.Count; i++) + { + Add(strongTypeNodes[i]); //when byref returns will be vailable, this should be passed by reference, not copy! + } } - public void Remove(INode obj) + public void Remove(ITypeSafeList nodes) { - Remove((TNodeType) obj); + /* + T node; + + nodeWrapper.GetNode(out node); + + Remove(node);*/ } - protected abstract void Add(TNodeType node); - protected abstract void Remove(TNodeType node); + protected abstract void Add(T node); + protected abstract void Remove(T node); } } diff --git a/ECS/StructNodes.cs b/ECS/StructNodes.cs index 85ca30d..6a6a60a 100644 --- a/ECS/StructNodes.cs +++ b/ECS/StructNodes.cs @@ -5,7 +5,7 @@ using Svelto.ECS.Internal; namespace Svelto.ECS { - public class StructNodes where T:struct, IStructNodeWithID + public sealed class StructNodes where T:struct, IStructNodeWithID { public T[] GetList(out int numberOfItems) { @@ -15,7 +15,7 @@ namespace Svelto.ECS public StructNodes(SharedStructNodeLists container) { - _internalList = container.GetList(); + _internalList = SharedStructNodeLists.NoVirt.GetList(container); } public void Add(T node) @@ -28,19 +28,20 @@ namespace Svelto.ECS readonly FasterList _internalList; } - public class StructGroupNodes - where T : struct, IGroupedStructNodeWithID + public struct StructGroupNodes + where T : struct, IStructNodeWithID { public StructGroupNodes(SharedGroupedStructNodesLists container) { _container = container; + indices = new Dictionary(); } public void Add(int groupID, T node) { T convert = (T)node; - var fasterList = (_container.GetList(groupID) as FasterList); + var fasterList = (SharedGroupedStructNodesLists.NoVirt.GetList(_container, groupID) as FasterList); indices[node.ID] = fasterList.Count; fasterList.Add(convert); @@ -48,7 +49,7 @@ namespace Svelto.ECS public void Remove(int groupID, T node) { - var fasterList = (_container.GetList(groupID) as FasterList); + var fasterList = (SharedGroupedStructNodesLists.NoVirt.GetList(_container, groupID) as FasterList); var index = indices[node.ID]; indices.Remove(node.ID); @@ -58,77 +59,83 @@ namespace Svelto.ECS public T[] GetList(int groupID, out int numberOfItems) { - var fasterList = (_container.GetList(groupID) as FasterList); - numberOfItems = fasterList.Count; - return fasterList.ToArrayFast(); + var fasterList = (SharedGroupedStructNodesLists.NoVirt.GetList(_container, groupID) as FasterList); + numberOfItems = FasterList.NoVirt.Count(fasterList); + return FasterList.NoVirt.ToArrayFast(fasterList); } readonly SharedGroupedStructNodesLists _container; - readonly Dictionary indices = new Dictionary(); + readonly Dictionary indices; } public class SharedStructNodeLists { - readonly Dictionary _collection; - internal SharedStructNodeLists() { - _collection = new Dictionary(); + _collection = new Dictionary(); } - internal FasterList GetList() where T:struct + internal static class NoVirt { - IFasterList list; - if (_collection.TryGetValue(typeof (T), out list)) + internal static FasterList GetList(SharedStructNodeLists obj) where T : struct { - return list as FasterList; - } + ITypeSafeList list; + if (obj._collection.TryGetValue(typeof(T), out list)) + { + return list as FasterList; + } - list = new FasterList(); + list = new TypeSafeFasterList(); - _collection.Add(typeof (T), list); + obj._collection.Add(typeof(T), list); - return (FasterList) list; + return (FasterList)list; + } } + + readonly Dictionary _collection; } public class SharedGroupedStructNodesLists { internal SharedGroupedStructNodesLists() { - _collection = new Dictionary>(); + _collection = new Dictionary>(); } - internal IFasterList GetList(int groupID) where T : struct + internal static class NoVirt { - Dictionary dic = GetGroup(); - IFasterList localList; - - if (dic.TryGetValue(groupID, out localList)) - return localList; + internal static ITypeSafeList GetList(SharedGroupedStructNodesLists list, int groupID) where T : struct + { + Dictionary dic = GetGroup(list); + ITypeSafeList localList; - localList = new FasterList(); - dic.Add(groupID, localList); + if (dic.TryGetValue(groupID, out localList)) + return localList; - return localList; - } + localList = new TypeSafeFasterList(); + dic.Add(groupID, localList); - internal Dictionary GetGroup() where T : struct - { - Dictionary dic; + return localList; + } - if (_collection.TryGetValue(typeof(T), out dic)) + internal static Dictionary GetGroup(SharedGroupedStructNodesLists list) where T : struct { - return dic; - } + Dictionary dic; + + if (list._collection.TryGetValue(typeof(T), out dic)) + { + return dic; + } - dic = new Dictionary(); + dic = new Dictionary(); - _collection.Add(typeof(T), dic); + list._collection.Add(typeof(T), dic); - return dic; + return dic; + } } - readonly Dictionary> _collection; + readonly Dictionary> _collection; } } \ No newline at end of file diff --git a/Factories/IGameObjectFactory.cs b/Factories/IGameObjectFactory.cs index 7879bf3..03f35ce 100644 --- a/Factories/IGameObjectFactory.cs +++ b/Factories/IGameObjectFactory.cs @@ -1,3 +1,4 @@ +#if UNITY_5_3_OR_NEWER || UNITY_5 using UnityEngine; namespace Svelto.Factories @@ -10,3 +11,4 @@ namespace Svelto.Factories GameObject Build(GameObject prefab); } } +#endif \ No newline at end of file diff --git a/Factories/IMonoBehaviourFactory.cs b/Factories/IMonoBehaviourFactory.cs index cbf5e05..f32a349 100644 --- a/Factories/IMonoBehaviourFactory.cs +++ b/Factories/IMonoBehaviourFactory.cs @@ -1,3 +1,5 @@ +#if UNITY_5_3_OR_NEWER || UNITY_5 + using System; using UnityEngine; @@ -9,3 +11,4 @@ namespace Svelto.Factories } } +#endif \ No newline at end of file diff --git a/Observer/Observable.cs b/Observer/Observable.cs new file mode 100644 index 0000000..0741b1b --- /dev/null +++ b/Observer/Observable.cs @@ -0,0 +1,42 @@ +using System; + +namespace Svelto.Observer +{ + public delegate void ObserverAction(ref DispatchType parameter); + + public interface IObservable + { + event Action Notify; + + void Dispatch(); + } + + public interface IObservable + { + event ObserverAction Notify; + + void Dispatch(ref DispatchType parameter); + } + + public class Observable:IObservable + { + public event ObserverAction Notify; + + public void Dispatch(ref DispatchType parameter) + { + if (Notify != null) + Notify(ref parameter); + } + } + + public class Observable:IObservable + { + public event Action Notify; + + public void Dispatch() + { + if (Notify != null) + Notify(); + } + } +} diff --git a/Observer/Observer.cs b/Observer/Observer.cs new file mode 100644 index 0000000..7871f47 --- /dev/null +++ b/Observer/Observer.cs @@ -0,0 +1,111 @@ +using System; + +namespace Svelto.Observer.InterNamespace +{ + public abstract class Observer : IObserver + { + protected Observer(Observable observable) + { + observable.Notify += OnObservableDispatched; + + _unsubscribe = () => observable.Notify -= OnObservableDispatched; + } + + public void AddAction(ObserverAction action) + { + _actions += action; + } + + public void RemoveAction(ObserverAction action) + { + _actions -= action; + } + + public void Unsubscribe() + { + _unsubscribe(); + } + + void OnObservableDispatched(ref DispatchType dispatchNotification) + { + if (_actions != null) + { + var actionType = TypeMap(ref dispatchNotification); + + _actions(ref actionType); + } + } + + protected abstract ActionType TypeMap(ref DispatchType dispatchNotification); + + ObserverAction _actions; + Action _unsubscribe; + } +} + +namespace Svelto.Observer.IntraNamespace +{ + public class Observer : InterNamespace.Observer + { + public Observer(Observable observable) : base(observable) + { } + + protected override DispatchType TypeMap(ref DispatchType dispatchNotification) + { + return dispatchNotification; + } + } +} + +namespace Svelto.Observer +{ + public class Observer: IObserver + { + public Observer(Observable observable) + { + observable.Notify += OnObservableDispatched; + + _unsubscribe = () => observable.Notify -= OnObservableDispatched; + } + + public void AddAction(Action action) + { + _actions += action; + } + + public void RemoveAction(Action action) + { + _actions -= action; + } + + public void Unsubscribe() + { + _unsubscribe(); + } + + void OnObservableDispatched() + { + if (_actions != null) + _actions(); + } + + Action _actions; + readonly Action _unsubscribe; + } + + public interface IObserver + { + void AddAction(ObserverAction action); + void RemoveAction(ObserverAction action); + + void Unsubscribe(); + } + + public interface IObserver + { + void AddAction(Action action); + void RemoveAction(Action action); + + void Unsubscribe(); + } +} \ No newline at end of file diff --git a/Utilities/Console.cs b/Utilities/Console.cs index cf11720..c5ca1a5 100644 --- a/Utilities/Console.cs +++ b/Utilities/Console.cs @@ -11,22 +11,25 @@ namespace Utility public static class Console { static StringBuilder _stringBuilder = new StringBuilder(256); -#if UNITY_5_3_OR_NEWER || UNITY_5 - public static ILogger logger = new SlowLoggerUnity(); -#else - public static ILogger logger = new SimpleLogger(); -#endif + + public static ILogger logger; + public static volatile bool BatchLog = false; //Hack, have to find the right solution - public static Action onException; + public static Action onException; static Console() { - onException = (e, obj, message, stack) => - { - UnityEngine.Debug.LogException(e, (UnityEngine.Object)obj); - }; +#if UNITY_5_3_OR_NEWER || UNITY_5 + logger = new SlowLoggerUnity(); + onException = (e, message, stack) => + { + UnityEngine.Debug.LogException(e, null); + }; +#else + logger = new SimpleLogger(); +#endif } public static void Log(string txt) @@ -67,11 +70,6 @@ namespace Utility } public static void LogException(Exception e) - { - LogException(e, null); - } - - public static void LogException(Exception e, UnityEngine.Object obj) { string toPrint; string stackTrace; @@ -96,7 +94,8 @@ namespace Utility toPrint = _stringBuilder.ToString(); } - onException(e, obj, toPrint, stackTrace); + if (onException != null) + onException(e, toPrint, stackTrace); } public static void LogWarning(string txt) @@ -142,8 +141,12 @@ namespace Utility toPrint = _stringBuilder.ToString(); } -#if !UNITY_EDITOR && !NETFX_CORE +#if !UNITY_EDITOR +#if !NETFX_CORE System.Console.WriteLine(toPrint); +#else + //find a way to adopt a logger externally, if this is still needed +#endif #else UnityEngine.Debug.Log(toPrint); #endif diff --git a/Utilities/NetFXCoreWrappers.cs b/Utilities/NetFXCoreWrappers.cs new file mode 100644 index 0000000..b172b0b --- /dev/null +++ b/Utilities/NetFXCoreWrappers.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; +using Svelto.DataStructures; + +namespace Svelto.Utilities +{ + public static class NetFXCoreWrappers + { + public static MethodInfo GetMethodInfoEx(this Delegate delegateEx) + { +#if NETFX_CORE + var method = delegateEx.GetMethodInfo(); +#else + var method = delegateEx.Method; +#endif + return method; + } + + public static Type GetDeclaringType(this MemberInfo memberInfo) + { +#if NETFX_CORE + return memberInfo.DeclaringType; +#else + return memberInfo.ReflectedType; +#endif + } + + public static Type GetBaseType(this Type type) + { +#if NETFX_CORE + return type.BaseType(); +#else + return type.BaseType; +#endif + } + + public static IEnumerable GetCustomAttributes(this Type type, bool inherit) + { +#if !NETFX_CORE + return Attribute.GetCustomAttributes(type, inherit); +#else + return type.GetTypeInfo().GetCustomAttributes(inherit); +#endif + } + + public static bool ContainsCustomAttribute(this MemberInfo memberInfo, Type customAttribute, bool inherit) + { +#if !NETFX_CORE + return Attribute.IsDefined(memberInfo, customAttribute, inherit); +#else + return memberInfo.GetCustomAttribute(customAttribute, inherit) != null; +#endif + } + + public static bool IsGenericTypeEx(this Type type) + { +#if !NETFX_CORE + return type.IsGenericType; +#else + return type.IsConstructedGenericType; +#endif + } + + public static MemberInfo[] FindWritablePropertiesWithCustomAttribute(this Type contract, + Type customAttributeType) + { + FasterList propertyList = new FasterList(8); + + do + { + var propertyInfos = contract.GetProperties(System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.NonPublic | + System.Reflection.BindingFlags.DeclaredOnly | + System.Reflection.BindingFlags.Instance); + + for (int i = 0; i < propertyInfos.Length; i++) + { + PropertyInfo propertyInfo = propertyInfos[i]; + + if (propertyInfo.CanWrite && + propertyInfo.ContainsCustomAttribute(customAttributeType, false) == true) + propertyList.Add(propertyInfo); + } + + contract = contract.GetBaseType(); + } while (contract != null); + + if (propertyList.Count > 0) + return propertyList.ToArray(); + + return null; + } + + public static bool IsCompilerGenerated(this Type t) + { +#if NETFX_CORE + var attr = t.GetTypeInfo().GetCustomAttribute(typeof(CompilerGeneratedAttribute)); + + return attr != null; +#else + var attr = Attribute.IsDefined(t, typeof(CompilerGeneratedAttribute)); + + return attr; +#endif + } + + public static bool IsCompilerGenerated(this MemberInfo memberInfo) + { +#if NETFX_CORE + var attr = memberInfo.DeclaringType.GetTypeInfo().GetCustomAttribute(_compilerType); + + return attr != null; +#else + var attr = Attribute.IsDefined(memberInfo, _compilerType); + + return attr; +#endif + } + + static readonly Type _compilerType = typeof(CompilerGeneratedAttribute); + } +} diff --git a/Utilities/ThreadUtility.cs b/Utilities/ThreadUtility.cs new file mode 100644 index 0000000..9c87991 --- /dev/null +++ b/Utilities/ThreadUtility.cs @@ -0,0 +1,16 @@ +using System.Threading; + +namespace Svelto.Utilities +{ + public static class ThreadUtility + { + public static void MemoryBarrier() + { +#if NETFX_CORE || NET_4_6 + Interlocked.MemoryBarrier(); +#else + Thread.MemoryBarrier(); +#endif + } + } +} \ No newline at end of file diff --git a/WeakEvents/WeakAction.cs b/WeakEvents/WeakAction.cs index 8c5ece6..042bf62 100644 --- a/WeakEvents/WeakAction.cs +++ b/WeakEvents/WeakAction.cs @@ -1,6 +1,7 @@ using System; using System.Reflection; using System.Runtime.CompilerServices; +using Svelto.Utilities; namespace Svelto.WeakEvents { @@ -71,14 +72,8 @@ namespace Svelto.WeakEvents Method = method; -#if NETFX_CORE - var attributes = (CompilerGeneratedAttribute[])method.GetType().GetTypeInfo().GetCustomAttributes(typeof(CompilerGeneratedAttribute), false); - if (attributes.Length != 0) - throw new ArgumentException("Cannot create weak event to anonymous method with closure."); -#else - if (method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0) + if (method.IsCompilerGenerated() == true) throw new ArgumentException("Cannot create weak event to anonymous method with closure."); -#endif } protected void Invoke_Internal(object[] data) diff --git a/WeakEvents/WeakActionStruct.cs b/WeakEvents/WeakActionStruct.cs index 7686934..a073cea 100644 --- a/WeakEvents/WeakActionStruct.cs +++ b/WeakEvents/WeakActionStruct.cs @@ -2,6 +2,7 @@ using System; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Svelto.Utilities; //careful, you must handle the destruction of the GCHandles! namespace Svelto.WeakEvents @@ -116,15 +117,8 @@ namespace Svelto.WeakEvents methodOut = method; #if DEBUG && !PROFILER -#if NETFX_CORE - Method = listener.GetMethodInfo(); - var attributes = (CompilerGeneratedAttribute[])Method.GetType().GetTypeInfo().GetCustomAttributes(typeof(CompilerGeneratedAttribute), false); - if(attributes.Length != 0) + if (method.IsCompilerGenerated() == true) throw new ArgumentException("Cannot create weak event to anonymous method with closure."); -#else - if (method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0) - throw new ArgumentException("Cannot create weak event to anonymous method with closure."); -#endif #endif } diff --git a/WeakEvents/WeakEvent.cs b/WeakEvents/WeakEvent.cs index 485df35..814f68b 100644 --- a/WeakEvents/WeakEvent.cs +++ b/WeakEvents/WeakEvent.cs @@ -1,6 +1,7 @@ using Svelto.DataStructures; using System; using System.Reflection; +using Svelto.Utilities; namespace Svelto.WeakEvents { @@ -9,7 +10,7 @@ namespace Svelto.WeakEvents public static WeakEvent operator+(WeakEvent c1, Action x) { if (c1 == null) c1 = new WeakEvent(); - c1._subscribers.Add(new WeakActionStruct(x)); + c1.Add(x); return c1; } @@ -17,11 +18,21 @@ namespace Svelto.WeakEvents public static WeakEvent operator-(WeakEvent c1, Action x) { DesignByContract.Check.Require(x != null); - c1.Remove(x.Target, x.GetMethodInfoEx()); + c1.Remove(x); return c1; } + public void Add(Action x) + { + _subscribers.Add(new WeakActionStruct(x)); + } + + public void Remove(Action x) + { + RemoveInternal(x.Target, x.GetMethodInfoEx()); + } + public void Invoke() { for (int i = 0; i < _subscribers.Count; i++) @@ -29,7 +40,7 @@ namespace Svelto.WeakEvents _subscribers.UnorderedRemoveAt(i--); } - void Remove(object thisObject, MethodInfo thisMethod) + void RemoveInternal(object thisObject, MethodInfo thisMethod) { for (int i = 0; i < _subscribers.Count; ++i) { @@ -58,7 +69,7 @@ namespace Svelto.WeakEvents public static WeakEvent operator+(WeakEvent c1, Action x) { if (c1 == null) c1 = new WeakEvent(); - c1._subscribers.Add(new WeakActionStruct(x)); + c1.Add(x); return c1; } @@ -66,11 +77,21 @@ namespace Svelto.WeakEvents public static WeakEvent operator-(WeakEvent c1, Action x) { DesignByContract.Check.Require(x != null); - c1.Remove(x.Target, x.GetMethodInfoEx()); + c1.Remove(x); return c1; } + public void Add(Action x) + { + _subscribers.Add(new WeakActionStruct(x)); + } + + public void Remove(Action x) + { + RemoveInternal(x.Target, x.GetMethodInfoEx()); + } + public void Invoke(T1 arg1) { args[0] = arg1; @@ -80,7 +101,7 @@ namespace Svelto.WeakEvents _subscribers.UnorderedRemoveAt(i--); } - void Remove(object thisObject, MethodInfo thisMethod) + void RemoveInternal(object thisObject, MethodInfo thisMethod) { for (int i = 0; i < _subscribers.Count; ++i) { @@ -119,11 +140,21 @@ namespace Svelto.WeakEvents public static WeakEvent operator-(WeakEvent c1, Action x) { DesignByContract.Check.Require(x != null); - c1.Remove(x.Target, x.GetMethodInfoEx()); + c1.Remove(x); return c1; } + public void Add(Action x) + { + _subscribers.Add(new WeakActionStruct(x)); + } + + public void Remove(Action x) + { + RemoveInternal(x.Target, x.GetMethodInfoEx()); + } + public void Invoke(T1 arg1, T2 arg2) { args[0] = arg1; @@ -134,7 +165,7 @@ namespace Svelto.WeakEvents _subscribers.UnorderedRemoveAt(i--); } - void Remove(object thisObject, MethodInfo thisMethod) + void RemoveInternal(object thisObject, MethodInfo thisMethod) { for (int i = 0; i < _subscribers.Count; ++i) {