Browse Source

working on the new typesafe svelto.ecs

tags/Rel2b
Sebastiano Mandalà sebas77 6 years ago
parent
commit
cbde7d8f4a
33 changed files with 2708 additions and 622 deletions
  1. +15
    -5
      DataStructures/FasterList.cs
  2. +16
    -0
      DataStructures/IGraphNode.cs
  3. +627
    -0
      DataStructures/LeftLeaningKeyedRedBlackTree.cs
  4. +789
    -0
      DataStructures/LeftLeaningRedBlackTree.cs
  5. +108
    -0
      DataStructures/PriorityQueue.cs
  6. +23
    -0
      ECS/DataStructures/TypeSafeDictionary.cs
  7. +51
    -0
      ECS/DataStructures/TypeSafeList.cs
  8. +29
    -31
      ECS/EngineNodeDB.cs
  9. +264
    -328
      ECS/EnginesRoot.cs
  10. +152
    -122
      ECS/EntityDescriptor.cs
  11. +8
    -0
      ECS/Extensions/Unity/UnitySumbmissionNodeScheduler.cs
  12. +19
    -0
      ECS/GenericEntityDescriptor.cs
  13. +2
    -1
      ECS/GenericEntityDescriptorHolder.cs
  14. +6
    -5
      ECS/IEngine.cs
  15. +7
    -8
      ECS/IEngineNodeDB.cs
  16. +2
    -7
      ECS/INode.cs
  17. +72
    -15
      ECS/MultiNodesEngine.cs
  18. +77
    -0
      ECS/NodeBuilder.cs
  19. +1
    -1
      ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs
  20. +10
    -8
      ECS/Profiler/EngineProfiler.cs
  21. +1
    -1
      ECS/Profiler/EngineProfilerBehaviour.cs
  22. +18
    -8
      ECS/SingleNodeEngine.cs
  23. +49
    -42
      ECS/StructNodes.cs
  24. +2
    -0
      Factories/IGameObjectFactory.cs
  25. +3
    -0
      Factories/IMonoBehaviourFactory.cs
  26. +42
    -0
      Observer/Observable.cs
  27. +111
    -0
      Observer/Observer.cs
  28. +20
    -17
      Utilities/Console.cs
  29. +125
    -0
      Utilities/NetFXCoreWrappers.cs
  30. +16
    -0
      Utilities/ThreadUtility.cs
  31. +2
    -7
      WeakEvents/WeakAction.cs
  32. +2
    -8
      WeakEvents/WeakActionStruct.cs
  33. +39
    -8
      WeakEvents/WeakEvent.cs

+ 15
- 5
DataStructures/FasterList.cs View File

@@ -466,10 +466,7 @@ namespace Svelto.DataStructures
readonly FasterList<T> _list;
}

public interface IFasterList
{}

public class FasterList<T> : IList<T>, IFasterList
public class FasterList<T> : IList<T>
{
const int MIN_SIZE = 4;

@@ -562,7 +559,7 @@ namespace Svelto.DataStructures
while (items.MoveNext())
_buffer[_count++] = items.Current;
}
public void AddRange(ICollection<T> 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<T> fasterList)
{
return fasterList._count;
}

public static T[] ToArrayFast(FasterList<T> fasterList)
{
return fasterList._buffer;
}
}
}
}

+ 16
- 0
DataStructures/IGraphNode.cs View File

@@ -0,0 +1,16 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
using System;

public interface IGraphNode<T>
{
void VisitNeighbours(System.Action<T> onVisiting);
}


+ 627
- 0
DataStructures/LeftLeaningKeyedRedBlackTree.cs View File

@@ -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;

/// <summary>
/// Implements a left-leaning red-black tree.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
/// <typeparam name="TKey">Type of keys.</typeparam>
public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
{
/// <summary>
/// Stores the root node of the tree.
/// </summary>
private Node _rootNode;

/// <summary>
/// Represents a node of the tree.
/// </summary>
/// <remarks>
/// Using fields instead of properties drops execution time by about 40%.
/// </remarks>
[DebuggerDisplay("Key={Key}")]
private class Node
{
/// <summary>
/// Gets or sets the node's key.
/// </summary>
public TKey Key;

/// <summary>
/// Gets or sets the left node.
/// </summary>
public Node Left;

/// <summary>
/// Gets or sets the right node.
/// </summary>
public Node Right;

/// <summary>
/// Gets or sets the color of the node.
/// </summary>
public bool IsBlack;

#if DEBUGGING
/// <summary>
/// Gets an HTML fragment representing the node and its children.
/// </summary>
public string HtmlFragment
{
get
{
return
"<table border='1'>" +
"<tr>" +
"<td colspan='2' align='center' bgcolor='" + (IsBlack ? "gray" : "red") + "'>" + Key + ", " + Value + " [" + Siblings + "]</td>" +
"</tr>" +
"<tr>" +
"<td valign='top'>" + (null != Left ? Left.HtmlFragment : "[null]") + "</td>" +
"<td valign='top'>" + (null != Right ? Right.HtmlFragment : "[null]") + "</td>" +
"</tr>" +
"</table>";
}
}
#endif
}

/// <summary>
/// Adds a key/value pair to the tree.
/// </summary>
/// <param name="key">Key to add.</param>
public void Add(TKey key)
{
_rootNode = Add(_rootNode, key);
_rootNode.IsBlack = true;
#if DEBUGGING
AssertInvariants();
#endif
}

/// <summary>
/// Removes a key/value pair from the tree.
/// </summary>
/// <param name="key">Key to remove.</param>
/// <returns>True if key/value present and removed.</returns>
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;
}

/// <summary>
/// Removes all nodes in the tree.
/// </summary>
public void Clear()
{
_rootNode = null;
Count = 0;
#if DEBUGGING
AssertInvariants();
#endif
}

/// <summary>
/// Gets a sorted list of keys in the tree.
/// </summary>
/// <returns>Sorted list of keys.</returns>
public IEnumerable<TKey> 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;
});
}

/// <summary>
/// Gets the count of key/value pairs in the tree.
/// </summary>
public int Count { get; private set; }

/// <summary>
/// Gets the minimum key in the tree.
/// </summary>
public TKey MinimumKey
{
get { return GetExtreme(_rootNode, n => n.Left, n => n.Key); }
}

/// <summary>
/// Gets the maximum key in the tree.
/// </summary>
public TKey MaximumKey
{
get { return GetExtreme(_rootNode, n => n.Right, n => n.Key); }
}

/// <summary>
/// Returns true if the specified node is red.
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>True if specified node is red.</returns>
private static bool IsRed(Node node)
{
if (null == node)
{
// "Virtual" leaf nodes are always black
return false;
}
return !node.IsBlack;
}

/// <summary>
/// Adds the specified key/value pair below the specified root node.
/// </summary>
/// <param name="node">Specified node.</param>
/// <param name="key">Key to add.</param>
/// <param name="value">Value to add.</param>
/// <returns>New root node.</returns>
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;
}

/// <summary>
/// Removes the specified key/value pair from below the specified node.
/// </summary>
/// <param name="node">Specified node.</param>
/// <param name="key">Key to remove.</param>
/// <returns>True if key/value present and removed.</returns>
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);
}

/// <summary>
/// Flip the colors of the specified node and its direct children.
/// </summary>
/// <param name="node">Specified node.</param>
private static void FlipColor(Node node)
{
node.IsBlack = !node.IsBlack;
node.Left.IsBlack = !node.Left.IsBlack;
node.Right.IsBlack = !node.Right.IsBlack;
}

/// <summary>
/// Rotate the specified node "left".
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
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;
}

/// <summary>
/// Rotate the specified node "right".
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
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;
}

/// <summary>
/// Moves a red node from the right child to the left child.
/// </summary>
/// <param name="node">Parent node.</param>
/// <returns>New root node.</returns>
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;
}

/// <summary>
/// Moves a red node from the left child to the right child.
/// </summary>
/// <param name="node">Parent node.</param>
/// <returns>New root node.</returns>
private static Node MoveRedRight(Node node)
{
FlipColor(node);
if (IsRed(node.Left.Left))
{
node = RotateRight(node);
FlipColor(node);
}
return node;
}

/// <summary>
/// Deletes the minimum node under the specified node.
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
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);
}

/// <summary>
/// Maintains invariants by adjusting the specified nodes children.
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
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;
}

/// <summary>
/// Gets the (first) node corresponding to the specified key.
/// </summary>
/// <param name="key">Key to search for.</param>
/// <returns>Corresponding node or null if none found.</returns>
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;
}

/// <summary>
/// Gets an extreme (ex: minimum/maximum) value.
/// </summary>
/// <typeparam name="T">Type of value.</typeparam>
/// <param name="node">Node to start from.</param>
/// <param name="successor">Successor function.</param>
/// <param name="selector">Selector function.</param>
/// <returns>Extreme value.</returns>
private static T GetExtreme<T>(Node node, Func<Node, Node> successor, Func<Node, T> selector)
{
// Initialize
T extreme = default(T);
Node current = node;
while (null != current)
{
// Go to extreme
extreme = selector(current);
current = successor(current);
}
return extreme;
}

/// <summary>
/// Traverses a subset of the sequence of nodes in order and selects the specified nodes.
/// </summary>
/// <typeparam name="T">Type of elements.</typeparam>
/// <param name="node">Starting node.</param>
/// <param name="condition">Condition method.</param>
/// <param name="selector">Selector method.</param>
/// <returns>Sequence of selected nodes.</returns>
private IEnumerable<T> Traverse<T>(Node node, Func<Node, bool> condition, Func<Node, T> selector)
{
// Create a stack to avoid recursion
Stack<Node> stack = new Stack<Node>();
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())));
}
}
}

/// <summary>
/// Compares the specified keys (primary) and values (secondary).
/// </summary>
/// <param name="leftKey">The left key.</param>
/// <param name="rightKey">The right key.</param>
/// <returns>CompareTo-style results: -1 if left is less, 0 if equal, and 1 if greater than right.</returns>
private int KeyComparison(TKey leftKey, TKey rightKey)
{
return leftKey.CompareTo(rightKey);
}

#if DEBUGGING
/// <summary>
/// Asserts that tree invariants are not violated.
/// </summary>
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<Node, Node> parents = new Dictionary<LeftLeaningRedBlackTree<TKey, TValue>.Node, LeftLeaningRedBlackTree<TKey, TValue>.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)));
}
}

/// <summary>
/// Gets an HTML fragment representing the tree.
/// </summary>
public string HtmlDocument
{
get
{
return
"<html>" +
"<body>" +
(null != _rootNode ? _rootNode.HtmlFragment : "[null]") +
"</body>" +
"</html>";
}
}
#endif
}

+ 789
- 0
DataStructures/LeftLeaningRedBlackTree.cs View File

@@ -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;

/// <summary>
/// Implements a left-leaning red-black tree.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
/// <typeparam name="TKey">Type of keys.</typeparam>
/// <typeparam name="TValue">Type of values.</typeparam>
public class LeftLeaningRedBlackTree<TKey, TValue>
{
/// <summary>
/// Stores the key comparison function.
/// </summary>
private Comparison<TKey> _keyComparison;

/// <summary>
/// Stores the value comparison function.
/// </summary>
private Comparison<TValue> _valueComparison;

/// <summary>
/// Stores the root node of the tree.
/// </summary>
private Node _rootNode;

/// <summary>
/// Represents a node of the tree.
/// </summary>
/// <remarks>
/// Using fields instead of properties drops execution time by about 40%.
/// </remarks>
[DebuggerDisplay("Key={Key}, Value={Value}, Siblings={Siblings}")]
private class Node
{
/// <summary>
/// Gets or sets the node's key.
/// </summary>
public TKey Key;

/// <summary>
/// Gets or sets the node's value.
/// </summary>
public TValue Value;

/// <summary>
/// Gets or sets the left node.
/// </summary>
public Node Left;

/// <summary>
/// Gets or sets the right node.
/// </summary>
public Node Right;

/// <summary>
/// Gets or sets the color of the node.
/// </summary>
public bool IsBlack;

/// <summary>
/// Gets or sets the number of "siblings" (nodes with the same key/value).
/// </summary>
public int Siblings;

#if DEBUGGING
/// <summary>
/// Gets an HTML fragment representing the node and its children.
/// </summary>
public string HtmlFragment
{
get
{
return
"<table border='1'>" +
"<tr>" +
"<td colspan='2' align='center' bgcolor='" + (IsBlack ? "gray" : "red") + "'>" + Key + ", " + Value + " [" + Siblings + "]</td>" +
"</tr>" +
"<tr>" +
"<td valign='top'>" + (null != Left ? Left.HtmlFragment : "[null]") + "</td>" +
"<td valign='top'>" + (null != Right ? Right.HtmlFragment : "[null]") + "</td>" +
"</tr>" +
"</table>";
}
}
#endif
}

/// <summary>
/// Initializes a new instance of the LeftLeaningRedBlackTree class implementing a normal dictionary.
/// </summary>
/// <param name="keyComparison">The key comparison function.</param>
public LeftLeaningRedBlackTree(Comparison<TKey> keyComparison)
{
if (null == keyComparison)
{
throw new ArgumentNullException("keyComparison");
}
_keyComparison = keyComparison;
}

/// <summary>
/// Initializes a new instance of the LeftLeaningRedBlackTree class implementing an ordered multi-dictionary.
/// </summary>
/// <param name="keyComparison">The key comparison function.</param>
/// <param name="valueComparison">The value comparison function.</param>
public LeftLeaningRedBlackTree(Comparison<TKey> keyComparison, Comparison<TValue> valueComparison)
: this(keyComparison)
{
if (null == valueComparison)
{
throw new ArgumentNullException("valueComparison");
}
_valueComparison = valueComparison;
}

/// <summary>
/// Gets a value indicating whether the tree is acting as an ordered multi-dictionary.
/// </summary>
private bool IsMultiDictionary
{
get { return null != _valueComparison; }
}

/// <summary>
/// Adds a key/value pair to the tree.
/// </summary>
/// <param name="key">Key to add.</param>
/// <param name="value">Value to add.</param>
public void Add(TKey key, TValue value)
{
_rootNode = Add(_rootNode, key, value);
_rootNode.IsBlack = true;
#if DEBUGGING
AssertInvariants();
#endif
}

/// <summary>
/// Removes a key (and its associated value) from a normal (non-multi) dictionary.
/// </summary>
/// <param name="key">Key to remove.</param>
/// <returns>True if key present and removed.</returns>
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));
}

/// <summary>
/// Removes a key/value pair from the tree.
/// </summary>
/// <param name="key">Key to remove.</param>
/// <param name="value">Value to remove.</param>
/// <returns>True if key/value present and removed.</returns>
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;
}

/// <summary>
/// Removes all nodes in the tree.
/// </summary>
public void Clear()
{
_rootNode = null;
Count = 0;
#if DEBUGGING
AssertInvariants();
#endif
}

/// <summary>
/// Gets a sorted list of keys in the tree.
/// </summary>
/// <returns>Sorted list of keys.</returns>
public IEnumerable<TKey> 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;
});
}

/// <summary>
/// Gets the value associated with the specified key in a normal (non-multi) dictionary.
/// </summary>
/// <param name="key">Specified key.</param>
/// <returns>Value associated with the specified key.</returns>
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();
}
}

/// <summary>
/// Gets a sequence of the values associated with the specified key.
/// </summary>
/// <param name="key">Specified key.</param>
/// <returns>Sequence of values.</returns>
public IEnumerable<TValue> GetValuesForKey(TKey key)
{
return Traverse(GetNodeForKey(key), n => 0 == _keyComparison(n.Key, key), n => n.Value);
}

/// <summary>
/// Gets a sequence of all the values in the tree.
/// </summary>
/// <returns>Sequence of all values.</returns>
public IEnumerable<TValue> GetValuesForAllKeys()
{
return Traverse(_rootNode, n => true, n => n.Value);
}

/// <summary>
/// Gets the count of key/value pairs in the tree.
/// </summary>
public int Count { get; private set; }

/// <summary>
/// Gets the minimum key in the tree.
/// </summary>
public TKey MinimumKey
{
get { return GetExtreme(_rootNode, n => n.Left, n => n.Key); }
}

/// <summary>
/// Gets the maximum key in the tree.
/// </summary>
public TKey MaximumKey
{
get { return GetExtreme(_rootNode, n => n.Right, n => n.Key); }
}

/// <summary>
/// Returns true if the specified node is red.
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>True if specified node is red.</returns>
private static bool IsRed(Node node)
{
if (null == node)
{
// "Virtual" leaf nodes are always black
return false;
}
return !node.IsBlack;
}

/// <summary>
/// Adds the specified key/value pair below the specified root node.
/// </summary>
/// <param name="node">Specified node.</param>
/// <param name="key">Key to add.</param>
/// <param name="value">Value to add.</param>
/// <returns>New root node.</returns>
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;
}

/// <summary>
/// Removes the specified key/value pair from below the specified node.
/// </summary>
/// <param name="node">Specified node.</param>
/// <param name="key">Key to remove.</param>
/// <param name="value">Value to remove.</param>
/// <returns>True if key/value present and removed.</returns>
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);
}

/// <summary>
/// Flip the colors of the specified node and its direct children.
/// </summary>
/// <param name="node">Specified node.</param>
private static void FlipColor(Node node)
{
node.IsBlack = !node.IsBlack;
node.Left.IsBlack = !node.Left.IsBlack;
node.Right.IsBlack = !node.Right.IsBlack;
}

/// <summary>
/// Rotate the specified node "left".
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
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;
}

/// <summary>
/// Rotate the specified node "right".
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
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;
}

/// <summary>
/// Moves a red node from the right child to the left child.
/// </summary>
/// <param name="node">Parent node.</param>
/// <returns>New root node.</returns>
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;
}

/// <summary>
/// Moves a red node from the left child to the right child.
/// </summary>
/// <param name="node">Parent node.</param>
/// <returns>New root node.</returns>
private static Node MoveRedRight(Node node)
{
FlipColor(node);
if (IsRed(node.Left.Left))
{
node = RotateRight(node);
FlipColor(node);
}
return node;
}

/// <summary>
/// Deletes the minimum node under the specified node.
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
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);
}

/// <summary>
/// Maintains invariants by adjusting the specified nodes children.
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
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;
}

/// <summary>
/// Gets the (first) node corresponding to the specified key.
/// </summary>
/// <param name="key">Key to search for.</param>
/// <returns>Corresponding node or null if none found.</returns>
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;
}

/// <summary>
/// Gets an extreme (ex: minimum/maximum) value.
/// </summary>
/// <typeparam name="T">Type of value.</typeparam>
/// <param name="node">Node to start from.</param>
/// <param name="successor">Successor function.</param>
/// <param name="selector">Selector function.</param>
/// <returns>Extreme value.</returns>
private static T GetExtreme<T>(Node node, Func<Node, Node> successor, Func<Node, T> selector)
{
// Initialize
T extreme = default(T);
Node current = node;
while (null != current)
{
// Go to extreme
extreme = selector(current);
current = successor(current);
}
return extreme;
}

/// <summary>
/// Traverses a subset of the sequence of nodes in order and selects the specified nodes.
/// </summary>
/// <typeparam name="T">Type of elements.</typeparam>
/// <param name="node">Starting node.</param>
/// <param name="condition">Condition method.</param>
/// <param name="selector">Selector method.</param>
/// <returns>Sequence of selected nodes.</returns>
private IEnumerable<T> Traverse<T>(Node node, Func<Node, bool> condition, Func<Node, T> selector)
{
// Create a stack to avoid recursion
Stack<Node> stack = new Stack<Node>();
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())));
}
}
}

/// <summary>
/// Compares the specified keys (primary) and values (secondary).
/// </summary>
/// <param name="leftKey">The left key.</param>
/// <param name="leftValue">The left value.</param>
/// <param name="rightKey">The right key.</param>
/// <param name="rightValue">The right value.</param>
/// <returns>CompareTo-style results: -1 if left is less, 0 if equal, and 1 if greater than right.</returns>
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
/// <summary>
/// Asserts that tree invariants are not violated.
/// </summary>
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<Node, Node> parents = new Dictionary<LeftLeaningRedBlackTree<TKey, TValue>.Node, LeftLeaningRedBlackTree<TKey, TValue>.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)));
}
}

/// <summary>
/// Gets an HTML fragment representing the tree.
/// </summary>
public string HtmlDocument
{
get
{
return
"<html>" +
"<body>" +
(null != _rootNode ? _rootNode.HtmlFragment : "[null]") +
"</body>" +
"</html>";
}
}
#endif
}

+ 108
- 0
DataStructures/PriorityQueue.cs View File

@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Collections;

// By James McCaffrey11/02/2012

sealed public class PriorityQueue<T>:IEnumerable<T> where T : IComparable<T>
{
private List<T> data;

public PriorityQueue ()
{
this.data = new List<T> ();
}
IEnumerator System.Collections.IEnumerable.GetEnumerator ()
{
// Lets call the generic version here
return this.GetEnumerator ();
}
public IEnumerator<T> GetEnumerator ()
{
return data.GetEnumerator () as IEnumerator<T>;
}
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

+ 23
- 0
ECS/DataStructures/TypeSafeDictionary.cs View File

@@ -0,0 +1,23 @@
using Svelto.DataStructures;
using System.Collections.Generic;

namespace Svelto.ECS
{
/// <summary>
/// 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.
/// </summary>

public interface ITypeSafeDictionary
{

}

public class TypeSafeDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ITypeSafeDictionary
{
internal static readonly ReadOnlyDictionary<TKey, TValue> Default = new ReadOnlyDictionary<TKey, TValue>(new TypeSafeDictionary<TKey, TValue>());
}
}

+ 51
- 0
ECS/DataStructures/TypeSafeList.cs View File

@@ -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<T> : FasterList<T>, ITypeSafeList
{
public TypeSafeFasterList()
{
}

public void AddRange(ITypeSafeList nodeListValue)
{
AddRange(nodeListValue as FasterList<T>);
}

public ITypeSafeList Create()
{
return new TypeSafeFasterList<T>();
}

public ITypeSafeDictionary CreateIndexedDictionary()
{
return new TypeSafeDictionary<int, T>();
}

public void AddToIndexedDictionary(ITypeSafeDictionary nodesDic)
{
var dic = nodesDic as TypeSafeDictionary<int, T>;

var buffer = NoVirt.ToArrayFast(this);

for (int i = 0; i < Count; i++)
{
T node = buffer[i];

dic[(node as NodeWithID).ID] = node;
}
}
}
}

+ 29
- 31
ECS/EngineNodeDB.cs View File

@@ -6,104 +6,102 @@ namespace Svelto.ECS
{
public class EngineNodeDB : IEngineNodeDB
{
internal EngineNodeDB( Dictionary<Type, FasterList<INode>> nodesDB,
Dictionary<Type, Dictionary<int, INode>> nodesDBdic,
Dictionary<Type, FasterList<INode>> metaNodesDB)
internal EngineNodeDB( Dictionary<Type, ITypeSafeList> nodesDB,
Dictionary<Type, ITypeSafeDictionary> nodesDBdic,
Dictionary<Type, ITypeSafeList> metaNodesDB)
{
_nodesDB = nodesDB;
_nodesDBdic = nodesDBdic;
_metaNodesDB = metaNodesDB;
}

public FasterReadOnlyListCast<INode, T> QueryNodes<T>() where T:INode
public FasterReadOnlyList<T> QueryNodes<T>()
{
var type = typeof(T);

FasterList<INode> nodes;
ITypeSafeList nodes;

if (_nodesDB.TryGetValue(type, out nodes) == false)
return RetrieveEmptyNodeList<T>();

return new FasterReadOnlyListCast<INode, T>(nodes);
return new FasterReadOnlyList<T>((FasterList<T>)nodes);
}

public ReadOnlyDictionary<int, INode> QueryIndexableNodes<T>() where T:INode
public ReadOnlyDictionary<int, T> QueryIndexableNodes<T>()
{
var type = typeof(T);

Dictionary<int, INode> nodes;
ITypeSafeDictionary nodes;

if (_nodesDBdic.TryGetValue(type, out nodes) == false)
return _defaultEmptyNodeDict;
return TypeSafeDictionary<int, T>.Default;

return new ReadOnlyDictionary<int, INode>(nodes);
return new ReadOnlyDictionary<int, T>(nodes as Dictionary<int, T>);
}

public T QueryMetaNode<T>(int metaEntityID) where T : INode
public T QueryMetaNode<T>(int metaEntityID)
{
return QueryNode<T>(metaEntityID);
}

public bool TryQueryMetaNode<T>(int metaEntityID, out T node) where T : INode
public bool TryQueryMetaNode<T>(int metaEntityID, out T node)
{
return TryQueryNode(metaEntityID, out node);
}

public FasterReadOnlyListCast<INode, T> QueryMetaNodes<T>() where T : INode
public FasterReadOnlyList<T> QueryMetaNodes<T>()
{
var type = typeof(T);

FasterList<INode> nodes;
ITypeSafeList nodes;

if (_metaNodesDB.TryGetValue(type, out nodes) == false)
return RetrieveEmptyNodeList<T>();

return new FasterReadOnlyListCast<INode, T>(nodes);
return new FasterReadOnlyList<T>((FasterList<T>)nodes);
}

public bool TryQueryNode<T>(int ID, out T node) where T:INode
public bool TryQueryNode<T>(int ID, out T node)
{
var type = typeof(T);

INode internalNode;
T internalNode;

Dictionary<int, INode> nodes;
ITypeSafeDictionary nodes;

if (_nodesDBdic.TryGetValue(type, out nodes) &&
nodes.TryGetValue(ID, out internalNode))
(nodes as Dictionary<int, T>).TryGetValue(ID, out internalNode))
{
node = (T)internalNode;
node = internalNode;

return true;
}
node = default(T);

return false;
}

public T QueryNode<T>(int ID) where T:INode
public T QueryNode<T>(int ID)
{
var type = typeof(T);

INode internalNode; Dictionary<int, INode> nodes;
T internalNode; ITypeSafeDictionary nodes;

if (_nodesDBdic.TryGetValue(type, out nodes) &&
nodes.TryGetValue(ID, out internalNode))
(nodes as Dictionary<int, T>).TryGetValue(ID, out internalNode))
return (T)internalNode;

throw new Exception("Node Not Found");
}

static FasterReadOnlyListCast<INode, T> RetrieveEmptyNodeList<T>() where T : INode
static FasterReadOnlyList<T> RetrieveEmptyNodeList<T>()
{
return FasterReadOnlyListCast<INode, T>.DefaultList;
return FasterReadOnlyList<T>.DefaultList;
}

readonly Dictionary<Type, FasterList<INode>> _nodesDB;
readonly Dictionary<Type, Dictionary<int, INode>> _nodesDBdic;
readonly Dictionary<Type, FasterList<INode>> _metaNodesDB;

readonly ReadOnlyDictionary<int, INode> _defaultEmptyNodeDict = new ReadOnlyDictionary<int, INode>(new Dictionary<int, INode>());
readonly Dictionary<Type, ITypeSafeList> _nodesDB;
readonly Dictionary<Type, ITypeSafeDictionary> _nodesDBdic;
readonly Dictionary<Type, ITypeSafeList> _metaNodesDB;
}
}

+ 264
- 328
ECS/EnginesRoot.cs View File

@@ -3,14 +3,24 @@ using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using Svelto.ECS.NodeSchedulers;
using WeakReference = Svelto.DataStructures.WeakReference<Svelto.ECS.EnginesRoot>;
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<ITypeSafeList> _internalRemove;
public Action<ITypeSafeList> _internalEnable;
public Action<ITypeSafeList> _internalDisable;
}
}

namespace Svelto.ECS
{
@@ -22,22 +32,22 @@ namespace Svelto.ECS
_activableEngines = new Dictionary<Type, FasterList<IEngine>>();
_otherEngines = new FasterList<IEngine>();

_engineRootWeakReference = new WeakReference(this);
//_engineRootWeakReference = new DataStructures.WeakReference<EnginesRoot>(this);

_nodesDB = new Dictionary<Type, FasterList<INode>>();
_nodesDBdic = new Dictionary<Type, Dictionary<int, INode>>();
_nodesDB = new Dictionary<Type, ITypeSafeList>();
_metaNodesDB = new Dictionary<Type, ITypeSafeList>();
_nodesDBdic = new Dictionary<Type, ITypeSafeDictionary>();

_nodesToAdd = new FasterList<INode>();
_metaNodesToAdd = new FasterList<INode>();

_metaNodesDB = new Dictionary<Type, FasterList<INode>>();
_sharedStructNodeLists = new SharedStructNodeLists();
_sharedGroupedStructNodeLists = new SharedGroupedStructNodesLists();
_nodesToAdd = new Dictionary<Type, ITypeSafeList>();
_metaNodesToAdd = new Dictionary<Type, ITypeSafeList>();
_groupedNodesToAdd = new Dictionary<Type, Dictionary<int, ITypeSafeList>>();
_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);
/// <summary>
/// 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.
/// </summary>
/// <param name="metaEntityID"></param>
/// <param name="ed"></param>
public void BuildMetaEntity(int metaEntityID, EntityDescriptor ed)
{
ed.BuildNodes(metaEntityID, _metaNodesToAdd, ref _callBackStruct);
}

_nodesToAdd.Clear();
_metaNodesToAdd.Clear();
/// <summary>
/// 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.
/// </summary>
/// <param name="entityID"></param>
/// <param name="groupID"></param>
/// <param name="ed"></param>
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);
}

/// <summary>
/// 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.
/// </summary>
/// <param name="metaEntityID"></param>
/// <param name="ed"></param>
public void BuildMetaEntity(int metaEntityID, EntityDescriptor ed)
{
var entityNodes = ed.BuildNodes(metaEntityID,
_internalMetaRemove,
_internalEnable,
_internalDisable
);

_metaNodesToAdd.AddRange(entityNodes);
}

/// <summary>
/// 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.
/// </summary>
/// <param name="entityID"></param>
/// <param name="groupID"></param>
/// <param name="ed"></param>
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<Type, FasterList<IEngine>> engines)
Dictionary<Type, FasterList<IEngine>> 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<Type, ITypeSafeList> nodesToAdd,
Dictionary<Type, FasterList<IEngine>> nodeEngines,
Dictionary<Type, ITypeSafeDictionary> nodesDBdic,
Dictionary<Type, ITypeSafeList> nodesDB)
{
FasterList<INode> nodes;
if (_metaNodesDB.TryGetValue(nodeType, out nodes) == false)
nodes = _metaNodesDB[nodeType] = new FasterList<INode>();
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>(T node, Type nodeType) where T : INode
{
FasterList<INode> nodes;
if (_nodesDB.TryGetValue(nodeType, out nodes) == false)
nodes = _nodesDB[nodeType] = new FasterList<INode>();
dbList.AddRange(nodeList.Value);

nodes.Add(node);
AddNodeToNodesDictionary(nodesDBdic, nodeList.Value, nodeList.Key);
AddNodesToTheSuitableEngines(nodeEngines, nodeList.Value, nodeList.Key);
}
}

void AddNodeToNodesDictionary<T>(T node, Type nodeType) where T : INodeWithID
static void AddNodeToNodesDictionary(Dictionary<Type, ITypeSafeDictionary> nodesDBdic, ITypeSafeList nodes, Type nodeType)
{
Dictionary<int, INode> nodesDic;
ITypeSafeDictionary nodesDic;

if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false)
nodesDic = _nodesDBdic[nodeType] = new Dictionary<int, INode>();
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<Type, FasterList<IEngine>> nodeEngines, ITypeSafeList nodes, Type nodeType)
{
FasterList<IEngine> 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>(T node, Type nodeType) where T : INode
{
FasterList<INode> 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>(T node, Type nodeType) where T : INode
{
FasterList<INode> 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>(T node, Type nodeType) where T : INode
{
FasterList<INode> 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>(T node, Type nodeType) where T : INode
{
FasterList<INode> 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>(T node, Type nodeType) where T : INodeWithID
{
Dictionary<int, INode> nodesDic;

void RemoveNodeFromNodesDictionary<T>(T node, Type nodeType) where T : INodeWithID
{
Dictionary<int, INode> nodesDic;
if (_nodesDBdic.TryGetValue(nodeType, out nodesDic))
nodesDic.Remove(node.ID);
}

if (_nodesDBdic.TryGetValue(nodeType, out nodesDic))
nodesDic.Remove(node.ID);
}
void RemoveNodeFromEngines<T>(T node, Type nodeType) where T : INode
{
FasterList<IEngine> 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>(T node, Type nodeType) where T : INode
{
FasterList<IEngine> enginesForNode;
void DisableNodeFromEngines(INode node, Type nodeType)
{
FasterList<IEngine> 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<IEngine> 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<IEngine> enginesForNode;
void RemoveNodeFromEngine(IEngine engine, INode node)
{
(engine as INodeEngine).Remove(node);
}
#endif
/*
void InternalDisable(FasterList<INode> 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<INode> 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<IEngine> 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<INode> 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<EnginesRoot> _engineRootWeakReference;

*/

void InternalDisable(FasterList<INode> 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<INode> 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<INode> 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<INode> 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<Type, FasterList<IEngine>> _nodeEngines;
@@ -526,30 +465,27 @@ namespace Svelto.ECS

readonly FasterList<IEngine> _otherEngines;

readonly Dictionary<Type, FasterList<INode>> _nodesDB;
readonly Dictionary<Type, FasterList<INode>> _metaNodesDB;

readonly Dictionary<Type, Dictionary<int, INode>> _nodesDBdic;

readonly FasterList<INode> _nodesToAdd;
readonly FasterList<INode> _metaNodesToAdd;

readonly WeakReference _engineRootWeakReference;
readonly SharedStructNodeLists _sharedStructNodeLists;
readonly SharedGroupedStructNodesLists _sharedGroupedStructNodeLists;
readonly Dictionary<Type, ITypeSafeList> _nodesDB;
readonly Dictionary<Type, ITypeSafeList> _metaNodesDB;
readonly Dictionary<Type, Dictionary<int, ITypeSafeList>> _groupNodesDB;
readonly Dictionary<Type, ITypeSafeDictionary> _nodesDBdic;

/// <summary>
/// Need to think about how to make BuildEntity thread safe as well
/// </summary>
readonly Dictionary<Type, ITypeSafeList> _nodesToAdd;
readonly Dictionary<Type, ITypeSafeList> _metaNodesToAdd;
readonly Dictionary<Type, Dictionary<int, ITypeSafeList>> _groupedNodesToAdd;
readonly NodeSubmissionScheduler _scheduler;

readonly Action<FasterList<INode>> _internalRemove;
readonly Action<FasterList<INode>> _internalEnable;
readonly Action<FasterList<INode>> _internalDisable;
readonly Action<FasterList<INode>> _internalMetaRemove;

readonly Type _structNodeEngineType;
readonly Type _groupedStructNodesEngineType;
readonly Type _activableNodeEngineType;

readonly Dictionary<Type, Type[]> _implementedInterfaceTypes;
BuildNodeCallbackStruct _callBackStruct;
}
}

}

+ 152
- 122
ECS/EntityDescriptor.cs View File

@@ -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()
{
}
{}

/// <summary>
/// if you want to avoid allocation in run-time, you can prebuild
/// EntityDescriptors and use them to build entities at different
/// times
/// </summary>
protected EntityDescriptor(INodeBuilder[] nodesToBuild)
{
_nodesToBuild = new FasterList<INodeBuilder>(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<Type, Dictionary<int, ITypeSafeList>> 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<Monobehaviour> to fetch it? "
.FastConcat(ToString()));
}
else
var nodeBuilder = _nodesToBuild[index];
var nodeType = nodeBuilder.GetNodeType();
Dictionary<int, ITypeSafeList> 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<int, ITypeSafeList>();
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<INode> BuildNodes(int ID)
internal void BuildNodes(int entityID,
Dictionary<Type, ITypeSafeList> nodesToAdd,
ref BuildNodeCallbackStruct callBackstruct)
{
var nodes = new FasterList<INode>();

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<INode> BuildNodes(int ID,
Action<FasterList<INode>> removeEntity,
Action<FasterList<INode>> enableEntity,
Action<FasterList<INode>> 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<IRemoveEntityComponent>(implementor as IRemoveEntityComponent));
if (implementor is IDisableEntityComponent)
_disablingImplementors.Add(new DataStructures.WeakReference<IDisableEntityComponent>(implementor as IDisableEntityComponent));
if (implementor is IEnableEntityComponent)
_enablingImplementors.Add(new DataStructures.WeakReference<IEnableEntityComponent>(implementor as IEnableEntityComponent));

return nodes;
var interfaces = implementor.GetType().GetInterfaces();
var weakReference = new DataStructures.WeakReference<object>(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<FasterList<INode>> removeEntity,
Action<FasterList<INode>> enableEntity,
Action<FasterList<INode>> disableEntity,
FasterList<INode> 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>(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<object> 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<IDisableEntityComponent> _disablingImplementors = new FasterList<IDisableEntityComponent>();
readonly FasterList<IRemoveEntityComponent> _removingImplementors = new FasterList<IRemoveEntityComponent>();
readonly FasterList<IEnableEntityComponent> _enablingImplementors = new FasterList<IEnableEntityComponent>();
readonly Dictionary<Type, object> _implementorsByType = new Dictionary<Type, object>();

readonly FasterList<INodeBuilder> _nodesToBuild;
}

public interface INodeBuilder
{
INode Build(int ID);

FillNodeMode reflects { get; }
}

public class NodeBuilder<NodeType> : INodeBuilder where NodeType : NodeWithID, new()
{
public INode Build(int ID)
{
NodeWithID node = NodeWithID.BuildNode<NodeType>(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<NodeType> : 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<DataStructures.WeakReference<IDisableEntityComponent>> _disablingImplementors = new FasterList<DataStructures.WeakReference<IDisableEntityComponent>>();
readonly FasterList<DataStructures.WeakReference<IRemoveEntityComponent>> _removingImplementors = new FasterList<DataStructures.WeakReference<IRemoveEntityComponent>>();
readonly FasterList<DataStructures.WeakReference<IEnableEntityComponent>> _enablingImplementors = new FasterList<DataStructures.WeakReference<IEnableEntityComponent>>();

public class FastStructNodeBuilder<NodeType> : StructNodeBuilder<NodeType>
where NodeType : struct, IStructNodeWithID
{
public override FillNodeMode reflects { get { return FillNodeMode.None; } }
}

public enum FillNodeMode
{
Strict,
Relaxed,
readonly Dictionary<Type, DataStructures.WeakReference<object>> _implementorsByType = new Dictionary<Type, DataStructures.WeakReference<object>>();
#if DEBUG && !PROFILER
readonly Dictionary<Type, int> _implementorCounterByType = new Dictionary<Type, int>();
#endif
readonly FasterList<INodeBuilder> _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<Monobehaviour> to fetch it? ";
const string NOT_FOUND_EXCEPTION = "Svelto.ECS: Implementor not found for a Node. Implementor Type: ";
}
}

+ 8
- 0
ECS/Extensions/Unity/UnitySumbmissionNodeScheduler.cs View File

@@ -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()


+ 19
- 0
ECS/GenericEntityDescriptor.cs View File

@@ -140,6 +140,25 @@ namespace Svelto.ECS

static readonly INodeBuilder[] _nodesToBuild;
}
public class GenericMixedEntityDescriptor<T, U> : 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<T, U, V> : EntityDescriptor
where T : INodeBuilder, new()


+ 2
- 1
ECS/GenericEntityDescriptorHolder.cs View File

@@ -3,7 +3,8 @@ using System;

namespace Svelto.ECS
{
public class GenericEntityDescriptorHolder<T, I>: UnityEngine.MonoBehaviour, IEntityDescriptorHolder where T:EntityDescriptor
public class GenericEntityDescriptorHolder<T, I>:
UnityEngine.MonoBehaviour, IEntityDescriptorHolder where T:EntityDescriptor
{
public EntityDescriptor BuildDescriptorType(object[] externalImplentors)
{


+ 6
- 5
ECS/IEngine.cs View File

@@ -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
/// </summary>
public interface IGroupedStructNodesEngine<T> : IGroupedStructNodesEngine where T:struct, IGroupedStructNodeWithID
public interface IGroupedStructNodesEngine<T> : IGroupedStructNodesEngine where T:struct, IGroupedNode
{ }
}


+ 7
- 8
ECS/IEngineNodeDB.cs View File

@@ -4,16 +4,15 @@ namespace Svelto.ECS
{
public interface IEngineNodeDB
{
ReadOnlyDictionary<int, INode> QueryIndexableNodes<T>() where T:INode;
ReadOnlyDictionary<int, T> QueryIndexableNodes<T>();
bool TryQueryNode<T>(int ID, out T node) where T:INode;
T QueryNode<T>(int ID) where T:INode;
FasterReadOnlyListCast<INode, T> QueryNodes<T>() where T:INode;
bool TryQueryNode<T>(int ID, out T node);
T QueryNode<T>(int ID);
FasterReadOnlyList<T> QueryNodes<T>();

bool TryQueryMetaNode<T>(int metaEntityID, out T node) where T : INode;
T QueryMetaNode<T>(int metaEntityID) where T : INode;
FasterReadOnlyListCast<INode, T> QueryMetaNodes<T>() where T : INode;
bool TryQueryMetaNode<T>(int metaEntityID, out T node);
T QueryMetaNode<T>(int metaEntityID);
FasterReadOnlyList<T> QueryMetaNodes<T>();
}
}


+ 2
- 7
ECS/INode.cs View File

@@ -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<TNodeType>(int ID) where TNodeType: NodeWithID, new()
{


+ 72
- 15
ECS/MultiNodesEngine.cs View File

@@ -1,9 +1,9 @@
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS.Internal
{
public abstract class MultiNodesEngine<T>
where T : INode
public abstract class MultiNodesEngine<T> 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<T, U> : MultiNodesEngine<T>,
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<U>)
{
var strongTypeNodes = (FasterList<U>)nodes;

for (int i = 0; i < strongTypeNodes.Count; i++)
{
AddNode(strongTypeNodes[i]);
}
}
else
AddNode((U)node);
if (nodes is FasterList<T>)
{
var strongTypeNodes = (FasterList<T>)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>)
{
T node;
nodeWrapper.GetNode<T>(out node);

RemoveNode(ref node);
}
else
{
U node;
nodeWrapper.GetNode<U>(out node);

RemoveNode(ref node);
}*/
}
}

public abstract class MultiNodesEngine<T, U, V> : MultiNodesEngine<T, U> 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<V>)
{
var strongTypeNodes = (FasterList<V>)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<V>(out node);

RemoveNode(ref node);
}
else
RemoveNode((U)node);
base.Remove(nodeWrapper);*/
}
}
}

+ 77
- 0
ECS/NodeBuilder.cs View File

@@ -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<NodeType> : INodeBuilder where NodeType : NodeWithID, new()
{
public INode BuildAndAddToList(ref ITypeSafeList list, int entityID)
{
if (list == null)
list = new TypeSafeFasterList<NodeType>();

var castedList = list as FasterList<NodeType>;

var node = NodeWithID.BuildNode<NodeType>(entityID);

castedList.Add(node);

return node;
}

public FillNodeMode reflects
{
get { return FillNodeMode.Strict; }
}

public Type GetNodeType()
{
return typeof(NodeType);
}
}

public class StructNodeBuilder<NodeType> : 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<NodeType>();

var castedList = list as FasterList<NodeType>;

castedList.Add(node);

return null;
}

public Type GetNodeType()
{
return typeof(NodeType);
}

public virtual FillNodeMode reflects
{
get { return FillNodeMode.None; }
}
}
public enum FillNodeMode
{
Strict,
None
}
}

+ 1
- 1
ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs View File

@@ -1,4 +1,4 @@
#if UNITY_EDITOR
#if asdUNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;


+ 10
- 8
ECS/Profiler/EngineProfiler.cs View File

@@ -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<INodeEngine, INode> addingFunc, INodeEngine engine, INode node)
public static void MonitorAddDuration(Action<INodeEngine, INodeWrapper> 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<INodeEngine, INode> removeFunc, INodeEngine engine, INode node)
public static void MonitorRemoveDuration(Action<INodeEngine, INodeWrapper> 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<Type, EngineInfo> engineInfos = new Dictionary<Type, EngineInfo>();
}
}
#endif

+ 1
- 1
ECS/Profiler/EngineProfilerBehaviour.cs View File

@@ -1,4 +1,4 @@
#if UNITY_EDITOR
#if asdUNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEngine;


+ 18
- 8
ECS/SingleNodeEngine.cs View File

@@ -1,21 +1,31 @@
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public abstract class SingleNodeEngine<TNodeType> : INodeEngine
where TNodeType : INode
public abstract class SingleNodeEngine<T> : INodeEngine where T:class
{
public void Add(INode obj)
public void Add(ITypeSafeList nodes)
{
Add((TNodeType) obj);
var strongTypeNodes = (FasterList<T>)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);
}
}

+ 49
- 42
ECS/StructNodes.cs View File

@@ -5,7 +5,7 @@ using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public class StructNodes<T> where T:struct, IStructNodeWithID
public sealed class StructNodes<T> where T:struct, IStructNodeWithID
{
public T[] GetList(out int numberOfItems)
{
@@ -15,7 +15,7 @@ namespace Svelto.ECS

public StructNodes(SharedStructNodeLists container)
{
_internalList = container.GetList<T>();
_internalList = SharedStructNodeLists.NoVirt.GetList<T>(container);
}

public void Add(T node)
@@ -28,19 +28,20 @@ namespace Svelto.ECS
readonly FasterList<T> _internalList;
}

public class StructGroupNodes<T>
where T : struct, IGroupedStructNodeWithID
public struct StructGroupNodes<T>
where T : struct, IStructNodeWithID
{
public StructGroupNodes(SharedGroupedStructNodesLists container)
{
_container = container;
indices = new Dictionary<int, int>();
}

public void Add(int groupID, T node)
{
T convert = (T)node;

var fasterList = (_container.GetList<T>(groupID) as FasterList<T>);
var fasterList = (SharedGroupedStructNodesLists.NoVirt.GetList<T>(_container, groupID) as FasterList<T>);
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<T>(groupID) as FasterList<T>);
var fasterList = (SharedGroupedStructNodesLists.NoVirt.GetList<T>(_container, groupID) as FasterList<T>);
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<T>(groupID) as FasterList<T>);
numberOfItems = fasterList.Count;
return fasterList.ToArrayFast();
var fasterList = (SharedGroupedStructNodesLists.NoVirt.GetList<T>(_container, groupID) as FasterList<T>);
numberOfItems = FasterList<T>.NoVirt.Count(fasterList);
return FasterList<T>.NoVirt.ToArrayFast(fasterList);
}

readonly SharedGroupedStructNodesLists _container;
readonly Dictionary<int, int> indices = new Dictionary<int, int>();
readonly Dictionary<int, int> indices;
}

public class SharedStructNodeLists
{
readonly Dictionary<Type, IFasterList> _collection;

internal SharedStructNodeLists()
{
_collection = new Dictionary<Type, IFasterList>();
_collection = new Dictionary<Type, ITypeSafeList>();
}

internal FasterList<T> GetList<T>() where T:struct
internal static class NoVirt
{
IFasterList list;
if (_collection.TryGetValue(typeof (T), out list))
internal static FasterList<T> GetList<T>(SharedStructNodeLists obj) where T : struct
{
return list as FasterList<T>;
}
ITypeSafeList list;
if (obj._collection.TryGetValue(typeof(T), out list))
{
return list as FasterList<T>;
}

list = new FasterList<T>();
list = new TypeSafeFasterList<T>();

_collection.Add(typeof (T), list);
obj._collection.Add(typeof(T), list);

return (FasterList<T>) list;
return (FasterList<T>)list;
}
}

readonly Dictionary<Type, ITypeSafeList> _collection;
}

public class SharedGroupedStructNodesLists
{
internal SharedGroupedStructNodesLists()
{
_collection = new Dictionary<Type, Dictionary<int, IFasterList>>();
_collection = new Dictionary<Type, Dictionary<int, ITypeSafeList>>();
}

internal IFasterList GetList<T>(int groupID) where T : struct
internal static class NoVirt
{
Dictionary<int, IFasterList> dic = GetGroup<T>();
IFasterList localList;

if (dic.TryGetValue(groupID, out localList))
return localList;
internal static ITypeSafeList GetList<T>(SharedGroupedStructNodesLists list, int groupID) where T : struct
{
Dictionary<int, ITypeSafeList> dic = GetGroup<T>(list);
ITypeSafeList localList;

localList = new FasterList<T>();
dic.Add(groupID, localList);
if (dic.TryGetValue(groupID, out localList))
return localList;

return localList;
}
localList = new TypeSafeFasterList<T>();
dic.Add(groupID, localList);

internal Dictionary<int, IFasterList> GetGroup<T>() where T : struct
{
Dictionary<int, IFasterList> dic;
return localList;
}

if (_collection.TryGetValue(typeof(T), out dic))
internal static Dictionary<int, ITypeSafeList> GetGroup<T>(SharedGroupedStructNodesLists list) where T : struct
{
return dic;
}
Dictionary<int, ITypeSafeList> dic;

if (list._collection.TryGetValue(typeof(T), out dic))
{
return dic;
}

dic = new Dictionary<int, IFasterList>();
dic = new Dictionary<int, ITypeSafeList>();

_collection.Add(typeof(T), dic);
list._collection.Add(typeof(T), dic);

return dic;
return dic;
}
}

readonly Dictionary<Type, Dictionary<int, IFasterList>> _collection;
readonly Dictionary<Type, Dictionary<int, ITypeSafeList>> _collection;
}
}

+ 2
- 0
Factories/IGameObjectFactory.cs View File

@@ -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

+ 3
- 0
Factories/IMonoBehaviourFactory.cs View File

@@ -1,3 +1,5 @@
#if UNITY_5_3_OR_NEWER || UNITY_5

using System;
using UnityEngine;

@@ -9,3 +11,4 @@ namespace Svelto.Factories
}
}

#endif

+ 42
- 0
Observer/Observable.cs View File

@@ -0,0 +1,42 @@
using System;

namespace Svelto.Observer
{
public delegate void ObserverAction<DispatchType>(ref DispatchType parameter);

public interface IObservable
{
event Action Notify;

void Dispatch();
}

public interface IObservable<DispatchType>
{
event ObserverAction<DispatchType> Notify;

void Dispatch(ref DispatchType parameter);
}

public class Observable<DispatchType>:IObservable<DispatchType>
{
public event ObserverAction<DispatchType> 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();
}
}
}

+ 111
- 0
Observer/Observer.cs View File

@@ -0,0 +1,111 @@
using System;

namespace Svelto.Observer.InterNamespace
{
public abstract class Observer<DispatchType, ActionType> : IObserver<ActionType>
{
protected Observer(Observable<DispatchType> observable)
{
observable.Notify += OnObservableDispatched;

_unsubscribe = () => observable.Notify -= OnObservableDispatched;
}

public void AddAction(ObserverAction<ActionType> action)
{
_actions += action;
}

public void RemoveAction(ObserverAction<ActionType> 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<ActionType> _actions;
Action _unsubscribe;
}
}

namespace Svelto.Observer.IntraNamespace
{
public class Observer<DispatchType> : InterNamespace.Observer<DispatchType, DispatchType>
{
public Observer(Observable<DispatchType> 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<WatchingType>
{
void AddAction(ObserverAction<WatchingType> action);
void RemoveAction(ObserverAction<WatchingType> action);

void Unsubscribe();
}

public interface IObserver
{
void AddAction(Action action);
void RemoveAction(Action action);

void Unsubscribe();
}
}

+ 20
- 17
Utilities/Console.cs View File

@@ -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<Exception, object, string, string> onException;
public static Action<Exception, string, string> 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


+ 125
- 0
Utilities/NetFXCoreWrappers.cs View File

@@ -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<Attribute> 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<MemberInfo> propertyList = new FasterList<MemberInfo>(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);
}
}

+ 16
- 0
Utilities/ThreadUtility.cs View File

@@ -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
}
}
}

+ 2
- 7
WeakEvents/WeakAction.cs View File

@@ -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)


+ 2
- 8
WeakEvents/WeakActionStruct.cs View File

@@ -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
}



+ 39
- 8
WeakEvents/WeakEvent.cs View File

@@ -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<T1> operator+(WeakEvent<T1> c1, Action<T1> x)
{
if (c1 == null) c1 = new WeakEvent<T1>();
c1._subscribers.Add(new WeakActionStruct<T1>(x));
c1.Add(x);

return c1;
}
@@ -66,11 +77,21 @@ namespace Svelto.WeakEvents
public static WeakEvent<T1> operator-(WeakEvent<T1> c1, Action<T1> x)
{
DesignByContract.Check.Require(x != null);
c1.Remove(x.Target, x.GetMethodInfoEx());
c1.Remove(x);

return c1;
}

public void Add(Action<T1> x)
{
_subscribers.Add(new WeakActionStruct<T1>(x));
}

public void Remove(Action<T1> 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<T1, T2> operator-(WeakEvent<T1, T2> c1, Action<T1, T2> x)
{
DesignByContract.Check.Require(x != null);
c1.Remove(x.Target, x.GetMethodInfoEx());
c1.Remove(x);

return c1;
}

public void Add(Action<T1, T2> x)
{
_subscribers.Add(new WeakActionStruct<T1, T2>(x));
}

public void Remove(Action<T1, T2> 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)
{


Loading…
Cancel
Save