Browse Source

Svelto-ECS 2.0 first draft

tags/Rel2b
sebas77 6 years ago
parent
commit
52019c75f4
48 changed files with 1984 additions and 1825 deletions
  1. +0
    -1
      Context/Factories/MonoBehaviourFactory.cs
  2. +11
    -0
      Context/IUnityCompositionRoot.cs
  3. +0
    -2
      Context/IWaitForFrameworkDestruction.cs
  4. +0
    -2
      Context/IWaitForFrameworkInitialization.cs
  5. +5
    -0
      Context/UnityContext.cs
  6. +11
    -5
      DataStructures/FasterList.cs
  7. +1
    -1
      DataStructures/IGraphNode.cs
  8. +200
    -200
      DataStructures/LeftLeaningKeyedRedBlackTree.cs
  9. +220
    -220
      DataStructures/LeftLeaningRedBlackTree.cs
  10. +32
    -33
      DataStructures/LockFreeQueue.cs
  11. +99
    -101
      DataStructures/PriorityQueue/HeapPriorityQueue.cs
  12. +5
    -5
      DataStructures/PriorityQueue/IPriorityQueue.cs
  13. +3
    -3
      DataStructures/PriorityQueue/PriorityQueueNode.cs
  14. +13
    -12
      ECS/DataStructures/TypeSafeDictionary.cs
  15. +32
    -14
      ECS/DataStructures/TypeSafeFasterListForECS.cs
  16. +93
    -66
      ECS/EngineNodeDB.cs
  17. +404
    -339
      ECS/EnginesRoot.cs
  18. +0
    -220
      ECS/EntityDescriptor.cs
  19. +221
    -0
      ECS/EntityDescriptorTemplate.cs
  20. +178
    -0
      ECS/Experimental/StructNodeCollections.cs
  21. +14
    -0
      ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs
  22. +15
    -11
      ECS/Extensions/Unity/UnitySumbmissionNodeScheduler.cs
  23. +57
    -205
      ECS/GenericEntityDescriptor.cs
  24. +0
    -31
      ECS/GenericEntityDescriptorHolder.cs
  25. +0
    -7
      ECS/ICallBackOnAddEngine.cs
  26. +12
    -54
      ECS/IEngine.cs
  27. +11
    -11
      ECS/IEngineNodeDB.cs
  28. +31
    -0
      ECS/IEnginesInterfaces.cs
  29. +0
    -16
      ECS/IEnginesRoot.cs
  30. +1
    -5
      ECS/IEntityDescriptorHolder.cs
  31. +53
    -12
      ECS/INode.cs
  32. +0
    -19
      ECS/IRemoveEntityComponent.cs
  33. +107
    -0
      ECS/MixedEntityDescriptor.cs
  34. +37
    -35
      ECS/MultiNodesEngine.cs
  35. +26
    -23
      ECS/NodeBuilder.cs
  36. +4
    -4
      ECS/NodeSubmissionScheduler.cs
  37. +8
    -8
      ECS/Profiler/EngineInfo.cs
  38. +5
    -5
      ECS/Profiler/EngineProfiler.cs
  39. +42
    -0
      ECS/RemoveEntityImplementor.cs
  40. +7
    -7
      ECS/SingleNodeEngine.cs
  41. +0
    -141
      ECS/StructNodes.cs
  42. +2
    -2
      ECS/note.txt
  43. +24
    -0
      Utilities/FastInvoke.cs
  44. +0
    -1
      Utilities/Murmur3.cs
  45. +0
    -1
      Utilities/NetFXCoreWrappers.cs
  46. +0
    -1
      WeakEvents/WeakAction.cs
  47. +0
    -1
      WeakEvents/WeakActionStruct.cs
  48. +0
    -1
      WeakEvents/WeakEvent.cs

+ 0
- 1
Context/Factories/MonoBehaviourFactory.cs View File

@@ -2,7 +2,6 @@
#region

using System;
using Svelto.DataStructures;
using UnityEngine;

#endregion


+ 11
- 0
Context/IUnityCompositionRoot.cs View File

@@ -0,0 +1,11 @@
#if UNITY_5 || UNITY_5_3_OR_NEWER
namespace Svelto.Context
{
public interface IUnityCompositionRoot
{
void OnContextCreated(UnityContext contextHolder);
void OnContextInitialized();
void OnContextDestroyed();
}
}
#endif

+ 0
- 2
Context/IWaitForFrameworkDestruction.cs View File

@@ -1,5 +1,3 @@
using System;

namespace Svelto.Context
{
public interface IWaitForFrameworkDestruction


+ 0
- 2
Context/IWaitForFrameworkInitialization.cs View File

@@ -1,5 +1,3 @@
using System;

namespace Svelto.Context
{
public interface IWaitForFrameworkInitialization


+ 5
- 0
Context/UnityContext.cs View File

@@ -13,6 +13,11 @@ public abstract class UnityContext:MonoBehaviour
}
}

//a Unity context is a platform specific context wrapper.
//Unity will drive the ICompositionRoot interface.
//OnContextCreated is called during the Awake of this MB
//OnContextInitialized is called one frame after the MB started
//OnContextDestroyed is called when the MB is destroyed
public class UnityContext<T>: UnityContext where T:class, ICompositionRoot, new()
{
protected override void OnAwake()


+ 11
- 5
DataStructures/FasterList.cs View File

@@ -120,17 +120,20 @@ namespace Svelto.DataStructures

public struct FasterReadOnlyList<T> : IList<T>
{
public static FasterReadOnlyList<T> DefaultList = new FasterReadOnlyList<T>(new FasterList<T>());
public static FasterReadOnlyList<T> DefaultList = new FasterReadOnlyList<T>(FasterList<T>.DefaultList);
public int Count { get { return _list.Count; } }
public bool IsReadOnly { get { return true; } }

public FasterReadOnlyList(FasterList<T> list)
{
_list = list;

int count;
_buffer = FasterList<T>.NoVirt.ToArrayFast(list, out count);
}

public T this[int index] { get { return _list[index]; } set { throw new NotImplementedException(); } }
public T this[int index] { get { return _buffer[index]; } set { throw new NotImplementedException(); } }

public FasterListEnumerator<T> GetEnumerator()
{
@@ -188,6 +191,7 @@ namespace Svelto.DataStructures
}

readonly FasterList<T> _list;
private readonly T[] _buffer;
}

public struct FasterListThreadSafe<T> : IList<T>
@@ -396,7 +400,7 @@ namespace Svelto.DataStructures

public struct FasterReadOnlyListCast<T, U> : IList<U> where U:T
{
public static FasterReadOnlyListCast<T, U> DefaultList = new FasterReadOnlyListCast<T, U>(new FasterList<T>());
public static readonly FasterReadOnlyListCast<T, U> DefaultList = new FasterReadOnlyListCast<T, U>(new FasterList<T>());

public int Count { get { return _list.Count; } }
public bool IsReadOnly { get { return true; } }
@@ -471,6 +475,8 @@ namespace Svelto.DataStructures

public class FasterList<T> : IList<T>, IFasterList
{
public static readonly FasterList<T> DefaultList = new FasterList<T>();
const int MIN_SIZE = 4;

public int Count
@@ -631,7 +637,7 @@ namespace Svelto.DataStructures
{
var comp = EqualityComparer<T>.Default;

for (var index = _count - 1; index >= 0; --index)
for (var index = 0; index < _count; index++)
if (comp.Equals(_buffer[index], item))
return index;



+ 1
- 1
DataStructures/IGraphNode.cs View File

@@ -9,7 +9,7 @@
//------------------------------------------------------------------------------
using System;

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


+ 200
- 200
DataStructures/LeftLeaningKeyedRedBlackTree.cs View File

@@ -1,6 +1,6 @@
// Uncomment this to enable the following debugging aids:
// LeftLeaningRedBlackTree.HtmlFragment
// LeftLeaningRedBlackTree.Node.HtmlFragment
// LeftLeaningRedBlackTree.EntityView.HtmlFragment
// LeftLeaningRedBlackTree.AssertInvariants
// #define DEBUGGING

@@ -21,42 +21,42 @@ using System.Diagnostics;
public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
{
/// <summary>
/// Stores the root node of the tree.
/// Stores the root entityView of the tree.
/// </summary>
private Node _rootNode;
private EntityView _rootEntityView;

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

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

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

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

#if DEBUGGING
/// <summary>
/// Gets an HTML fragment representing the node and its children.
/// Gets an HTML fragment representing the entityView and its children.
/// </summary>
public string HtmlFragment
{
@@ -83,8 +83,8 @@ public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
/// <param name="key">Key to add.</param>
public void Add(TKey key)
{
_rootNode = Add(_rootNode, key);
_rootNode.IsBlack = true;
_rootEntityView = Add(_rootEntityView, key);
_rootEntityView.IsBlack = true;
#if DEBUGGING
AssertInvariants();
#endif
@@ -98,12 +98,12 @@ public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
public bool Remove(TKey key)
{
int initialCount = Count;
if (null != _rootNode)
if (null != _rootEntityView)
{
_rootNode = Remove(_rootNode, key);
if (null != _rootNode)
_rootEntityView = Remove(_rootEntityView, key);
if (null != _rootEntityView)
{
_rootNode.IsBlack = true;
_rootEntityView.IsBlack = true;
}
}
#if DEBUGGING
@@ -113,11 +113,11 @@ public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
}

/// <summary>
/// Removes all nodes in the tree.
/// Removes all entityViews in the tree.
/// </summary>
public void Clear()
{
_rootNode = null;
_rootEntityView = null;
Count = 0;
#if DEBUGGING
AssertInvariants();
@@ -133,7 +133,7 @@ public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
TKey lastKey = default(TKey);
bool lastKeyValid = false;
return Traverse(
_rootNode,
_rootEntityView,
n => !lastKeyValid || !object.Equals(lastKey, n.Key),
n =>
{
@@ -153,7 +153,7 @@ public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
/// </summary>
public TKey MinimumKey
{
get { return GetExtreme(_rootNode, n => n.Left, n => n.Key); }
get { return GetExtreme(_rootEntityView, n => n.Left, n => n.Key); }
}

/// <summary>
@@ -161,310 +161,310 @@ public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
/// </summary>
public TKey MaximumKey
{
get { return GetExtreme(_rootNode, n => n.Right, n => n.Key); }
get { return GetExtreme(_rootEntityView, n => n.Right, n => n.Key); }
}

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

/// <summary>
/// Adds the specified key/value pair below the specified root node.
/// Adds the specified key/value pair below the specified root entityView.
/// </summary>
/// <param name="node">Specified node.</param>
/// <param name="entityView">Specified entityView.</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)
/// <returns>New root entityView.</returns>
private EntityView Add(EntityView entityView, TKey key)
{
if (null == node)
if (null == entityView)
{
// Insert new node
// Insert new entityView
Count++;
return new Node { Key = key };
return new EntityView { Key = key };
}

if (IsRed(node.Left) && IsRed(node.Right))
if (IsRed(entityView.Left) && IsRed(entityView.Right))
{
// Split node with two red children
FlipColor(node);
// Split entityView with two red children
FlipColor(entityView);
}

// Find right place for new node
int comparisonResult = KeyComparison(key, node.Key);
// Find right place for new entityView
int comparisonResult = KeyComparison(key, entityView.Key);
if (comparisonResult < 0)
{
node.Left = Add(node.Left, key);
entityView.Left = Add(entityView.Left, key);
}
else if (0 < comparisonResult)
{
node.Right = Add(node.Right, key);
entityView.Right = Add(entityView.Right, key);
}

if (IsRed(node.Right))
if (IsRed(entityView.Right))
{
// Rotate to prevent red node on right
node = RotateLeft(node);
// Rotate to prevent red entityView on right
entityView = RotateLeft(entityView);
}

if (IsRed(node.Left) && IsRed(node.Left.Left))
if (IsRed(entityView.Left) && IsRed(entityView.Left.Left))
{
// Rotate to prevent consecutive red nodes
node = RotateRight(node);
// Rotate to prevent consecutive red entityViews
entityView = RotateRight(entityView);
}

return node;
return entityView;
}

/// <summary>
/// Removes the specified key/value pair from below the specified node.
/// Removes the specified key/value pair from below the specified entityView.
/// </summary>
/// <param name="node">Specified node.</param>
/// <param name="entityView">Specified entityView.</param>
/// <param name="key">Key to remove.</param>
/// <returns>True if key/value present and removed.</returns>
private Node Remove(Node node, TKey key)
private EntityView Remove(EntityView entityView, TKey key)
{
int comparisonResult = KeyComparison(key, node.Key);
int comparisonResult = KeyComparison(key, entityView.Key);
if (comparisonResult < 0)
{
// * Continue search if left is present
if (null != node.Left)
if (null != entityView.Left)
{
if (!IsRed(node.Left) && !IsRed(node.Left.Left))
if (!IsRed(entityView.Left) && !IsRed(entityView.Left.Left))
{
// Move a red node over
node = MoveRedLeft(node);
// Move a red entityView over
entityView = MoveRedLeft(entityView);
}

// Remove from left
node.Left = Remove(node.Left, key);
entityView.Left = Remove(entityView.Left, key);
}
}
else
{
if (IsRed(node.Left))
if (IsRed(entityView.Left))
{
// Flip a 3 node or unbalance a 4 node
node = RotateRight(node);
// Flip a 3 entityView or unbalance a 4 entityView
entityView = RotateRight(entityView);
}
if ((0 == KeyComparison(key, node.Key)) && (null == node.Right))
if ((0 == KeyComparison(key, entityView.Key)) && (null == entityView.Right))
{
// Remove leaf node
Debug.Assert(null == node.Left, "About to remove an extra node.");
// Remove leaf entityView
Debug.Assert(null == entityView.Left, "About to remove an extra entityView.");
Count--;
// Leaf node is gone
// Leaf entityView is gone
return null;
}
// * Continue search if right is present
if (null != node.Right)
if (null != entityView.Right)
{
if (!IsRed(node.Right) && !IsRed(node.Right.Left))
if (!IsRed(entityView.Right) && !IsRed(entityView.Right.Left))
{
// Move a red node over
node = MoveRedRight(node);
// Move a red entityView over
entityView = MoveRedRight(entityView);
}
if (0 == KeyComparison(key, node.Key))
if (0 == KeyComparison(key, entityView.Key))
{
// Remove leaf node
// Remove leaf entityView
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);
// Find the smallest entityView on the right, swap, and remove it
EntityView m = GetExtreme(entityView.Right, n => n.Left, n => n);
entityView.Key = m.Key;
entityView.Right = DeleteMinimum(entityView.Right);
}
else
{
// Remove from right
node.Right = Remove(node.Right, key);
entityView.Right = Remove(entityView.Right, key);
}
}
}

// Maintain invariants
return FixUp(node);
return FixUp(entityView);
}

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

/// <summary>
/// Rotate the specified node "left".
/// Rotate the specified entityView "left".
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
private static Node RotateLeft(Node node)
/// <param name="entityView">Specified entityView.</param>
/// <returns>New root entityView.</returns>
private static EntityView RotateLeft(EntityView entityView)
{
Node x = node.Right;
node.Right = x.Left;
x.Left = node;
x.IsBlack = node.IsBlack;
node.IsBlack = false;
EntityView x = entityView.Right;
entityView.Right = x.Left;
x.Left = entityView;
x.IsBlack = entityView.IsBlack;
entityView.IsBlack = false;
return x;
}

/// <summary>
/// Rotate the specified node "right".
/// Rotate the specified entityView "right".
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
private static Node RotateRight(Node node)
/// <param name="entityView">Specified entityView.</param>
/// <returns>New root entityView.</returns>
private static EntityView RotateRight(EntityView entityView)
{
Node x = node.Left;
node.Left = x.Right;
x.Right = node;
x.IsBlack = node.IsBlack;
node.IsBlack = false;
EntityView x = entityView.Left;
entityView.Left = x.Right;
x.Right = entityView;
x.IsBlack = entityView.IsBlack;
entityView.IsBlack = false;
return x;
}

/// <summary>
/// Moves a red node from the right child to the left child.
/// Moves a red entityView 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)
/// <param name="entityView">Parent entityView.</param>
/// <returns>New root entityView.</returns>
private static EntityView MoveRedLeft(EntityView entityView)
{
FlipColor(node);
if (IsRed(node.Right.Left))
FlipColor(entityView);
if (IsRed(entityView.Right.Left))
{
node.Right = RotateRight(node.Right);
node = RotateLeft(node);
FlipColor(node);
entityView.Right = RotateRight(entityView.Right);
entityView = RotateLeft(entityView);
FlipColor(entityView);

// * Avoid creating right-leaning nodes
if (IsRed(node.Right.Right))
// * Avoid creating right-leaning entityViews
if (IsRed(entityView.Right.Right))
{
node.Right = RotateLeft(node.Right);
entityView.Right = RotateLeft(entityView.Right);
}
}
return node;
return entityView;
}

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

/// <summary>
/// Deletes the minimum node under the specified node.
/// Deletes the minimum entityView under the specified entityView.
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
private Node DeleteMinimum(Node node)
/// <param name="entityView">Specified entityView.</param>
/// <returns>New root entityView.</returns>
private EntityView DeleteMinimum(EntityView entityView)
{
if (null == node.Left)
if (null == entityView.Left)
{
// Nothing to do
return null;
}

if (!IsRed(node.Left) && !IsRed(node.Left.Left))
if (!IsRed(entityView.Left) && !IsRed(entityView.Left.Left))
{
// Move red node left
node = MoveRedLeft(node);
// Move red entityView left
entityView = MoveRedLeft(entityView);
}

// Recursively delete
node.Left = DeleteMinimum(node.Left);
entityView.Left = DeleteMinimum(entityView.Left);

// Maintain invariants
return FixUp(node);
return FixUp(entityView);
}

/// <summary>
/// Maintains invariants by adjusting the specified nodes children.
/// Maintains invariants by adjusting the specified entityViews children.
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
private static Node FixUp(Node node)
/// <param name="entityView">Specified entityView.</param>
/// <returns>New root entityView.</returns>
private static EntityView FixUp(EntityView entityView)
{
if (IsRed(node.Right))
if (IsRed(entityView.Right))
{
// Avoid right-leaning node
node = RotateLeft(node);
// Avoid right-leaning entityView
entityView = RotateLeft(entityView);
}

if (IsRed(node.Left) && IsRed(node.Left.Left))
if (IsRed(entityView.Left) && IsRed(entityView.Left.Left))
{
// Balance 4-node
node = RotateRight(node);
// Balance 4-entityView
entityView = RotateRight(entityView);
}

if (IsRed(node.Left) && IsRed(node.Right))
if (IsRed(entityView.Left) && IsRed(entityView.Right))
{
// Push red up
FlipColor(node);
FlipColor(entityView);
}

// * Avoid leaving behind right-leaning nodes
if ((null != node.Left) && IsRed(node.Left.Right) && !IsRed(node.Left.Left))
// * Avoid leaving behind right-leaning entityViews
if ((null != entityView.Left) && IsRed(entityView.Left.Right) && !IsRed(entityView.Left.Left))
{
node.Left = RotateLeft(node.Left);
if (IsRed(node.Left))
entityView.Left = RotateLeft(entityView.Left);
if (IsRed(entityView.Left))
{
// Balance 4-node
node = RotateRight(node);
// Balance 4-entityView
entityView = RotateRight(entityView);
}
}

return node;
return entityView;
}

/// <summary>
/// Gets the (first) node corresponding to the specified key.
/// Gets the (first) entityView 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)
/// <returns>Corresponding entityView or null if none found.</returns>
private EntityView GetEntityViewForKey(TKey key)
{
// Initialize
Node node = _rootNode;
while (null != node)
EntityView entityView = _rootEntityView;
while (null != entityView)
{
// Compare keys and go left/right
int comparisonResult = key.CompareTo(node.Key);
int comparisonResult = key.CompareTo(entityView.Key);
if (comparisonResult < 0)
{
node = node.Left;
entityView = entityView.Left;
}
else if (0 < comparisonResult)
{
node = node.Right;
entityView = entityView.Right;
}
else
{
// Match; return node
return node;
// Match; return entityView
return entityView;
}
}

@@ -476,15 +476,15 @@ public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
/// 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="entityView">EntityView 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)
private static T GetExtreme<T>(EntityView entityView, Func<EntityView, EntityView> successor, Func<EntityView, T> selector)
{
// Initialize
T extreme = default(T);
Node current = node;
EntityView current = entityView;
while (null != current)
{
// Go to extreme
@@ -495,18 +495,18 @@ public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
}

/// <summary>
/// Traverses a subset of the sequence of nodes in order and selects the specified nodes.
/// Traverses a subset of the sequence of entityViews in order and selects the specified entityViews.
/// </summary>
/// <typeparam name="T">Type of elements.</typeparam>
/// <param name="node">Starting node.</param>
/// <param name="entityView">Starting entityView.</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)
/// <returns>Sequence of selected entityViews.</returns>
private IEnumerable<T> Traverse<T>(EntityView entityView, Func<EntityView, bool> condition, Func<EntityView, T> selector)
{
// Create a stack to avoid recursion
Stack<Node> stack = new Stack<Node>();
Node current = node;
Stack<EntityView> stack = new Stack<EntityView>();
EntityView current = entityView;
while (null != current)
{
if (null != current.Left)
@@ -519,7 +519,7 @@ public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
{
do
{
// Select current node if relevant
// Select current entityView if relevant
if (condition(current))
{
yield return selector(current);
@@ -552,29 +552,29 @@ public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
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))
Debug.Assert((null == _rootEntityView) || _rootEntityView.IsBlack, "Root is not black");
// Every path contains the same number of black entityViews
Dictionary<EntityView, EntityView> parents = new Dictionary<LeftLeaningRedBlackTree<TKey, TValue>.EntityView, LeftLeaningRedBlackTree<TKey, TValue>.EntityView>();
foreach (EntityView entityView in Traverse(_rootEntityView, n => true, n => n))
{
if (null != node.Left)
if (null != entityView.Left)
{
parents[node.Left] = node;
parents[entityView.Left] = entityView;
}
if (null != node.Right)
if (null != entityView.Right)
{
parents[node.Right] = node;
parents[entityView.Right] = entityView;
}
}
if (null != _rootNode)
if (null != _rootEntityView)
{
parents[_rootNode] = null;
parents[_rootEntityView] = null;
}
int treeCount = -1;
foreach (Node node in Traverse(_rootNode, n => (null == n.Left) || (null == n.Right), n => n))
foreach (EntityView entityView in Traverse(_rootEntityView, n => (null == n.Left) || (null == n.Right), n => n))
{
int pathCount = 0;
Node current = node;
EntityView current = entityView;
while (null != current)
{
if (current.IsBlack)
@@ -583,28 +583,28 @@ public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
}
current = parents[current];
}
Debug.Assert((-1 == treeCount) || (pathCount == treeCount), "Not all paths have the same number of black nodes.");
Debug.Assert((-1 == treeCount) || (pathCount == treeCount), "Not all paths have the same number of black entityViews.");
treeCount = pathCount;
}
// Verify node properties...
foreach (Node node in Traverse(_rootNode, n => true, n => n))
// Verify entityView properties...
foreach (EntityView entityView in Traverse(_rootEntityView, n => true, n => n))
{
// Left node is less
if (null != node.Left)
// Left entityView is less
if (null != entityView.Left)
{
Debug.Assert(0 > KeyAndValueComparison(node.Left.Key, node.Left.Value, node.Key, node.Value), "Left node is greater than its parent.");
Debug.Assert(0 > KeyAndValueComparison(entityView.Left.Key, entityView.Left.Value, entityView.Key, entityView.Value), "Left entityView is greater than its parent.");
}
// Right node is greater
if (null != node.Right)
// Right entityView is greater
if (null != entityView.Right)
{
Debug.Assert(0 < KeyAndValueComparison(node.Right.Key, node.Right.Value, node.Key, node.Value), "Right node is less than its parent.");
Debug.Assert(0 < KeyAndValueComparison(entityView.Right.Key, entityView.Right.Value, entityView.Key, entityView.Value), "Right entityView 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.");
// Both children of a red entityView are black
Debug.Assert(!IsRed(entityView) || (!IsRed(entityView.Left) && !IsRed(entityView.Right)), "Red entityView has a red child.");
// Always left-leaning
Debug.Assert(!IsRed(node.Right) || IsRed(node.Left), "Node is not left-leaning.");
Debug.Assert(!IsRed(entityView.Right) || IsRed(entityView.Left), "EntityView is not left-leaning.");
// No consecutive reds (subset of previous rule)
//Debug.Assert(!(IsRed(node) && IsRed(node.Left)));
//Debug.Assert(!(IsRed(entityView) && IsRed(entityView.Left)));
}
}

@@ -618,7 +618,7 @@ public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey>
return
"<html>" +
"<body>" +
(null != _rootNode ? _rootNode.HtmlFragment : "[null]") +
(null != _rootEntityView ? _rootEntityView.HtmlFragment : "[null]") +
"</body>" +
"</html>";
}


+ 220
- 220
DataStructures/LeftLeaningRedBlackTree.cs View File

@@ -1,6 +1,6 @@
// Uncomment this to enable the following debugging aids:
// LeftLeaningRedBlackTree.HtmlFragment
// LeftLeaningRedBlackTree.Node.HtmlFragment
// LeftLeaningRedBlackTree.EntityView.HtmlFragment
// LeftLeaningRedBlackTree.AssertInvariants
// #define DEBUGGING

@@ -32,52 +32,52 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
private Comparison<TValue> _valueComparison;

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

/// <summary>
/// Represents a node of the tree.
/// Represents a entityView 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
private class EntityView
{
/// <summary>
/// Gets or sets the node's key.
/// Gets or sets the entityView's key.
/// </summary>
public TKey Key;

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

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

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

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

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

#if DEBUGGING
/// <summary>
/// Gets an HTML fragment representing the node and its children.
/// Gets an HTML fragment representing the entityView and its children.
/// </summary>
public string HtmlFragment
{
@@ -141,8 +141,8 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
/// <param name="value">Value to add.</param>
public void Add(TKey key, TValue value)
{
_rootNode = Add(_rootNode, key, value);
_rootNode.IsBlack = true;
_rootEntityView = Add(_rootEntityView, key, value);
_rootEntityView.IsBlack = true;
#if DEBUGGING
AssertInvariants();
#endif
@@ -171,12 +171,12 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
public bool Remove(TKey key, TValue value)
{
int initialCount = Count;
if (null != _rootNode)
if (null != _rootEntityView)
{
_rootNode = Remove(_rootNode, key, value);
if (null != _rootNode)
_rootEntityView = Remove(_rootEntityView, key, value);
if (null != _rootEntityView)
{
_rootNode.IsBlack = true;
_rootEntityView.IsBlack = true;
}
}
#if DEBUGGING
@@ -186,11 +186,11 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
}

/// <summary>
/// Removes all nodes in the tree.
/// Removes all entityViews in the tree.
/// </summary>
public void Clear()
{
_rootNode = null;
_rootEntityView = null;
Count = 0;
#if DEBUGGING
AssertInvariants();
@@ -206,7 +206,7 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
TKey lastKey = default(TKey);
bool lastKeyValid = false;
return Traverse(
_rootNode,
_rootEntityView,
n => !lastKeyValid || !object.Equals(lastKey, n.Key),
n =>
{
@@ -227,10 +227,10 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
{
throw new InvalidOperationException("GetValueForKey is only supported when acting as a normal (non-multi) dictionary.");
}
Node node = GetNodeForKey(key);
if (null != node)
EntityView entityView = GetEntityViewForKey(key);
if (null != entityView)
{
return node.Value;
return entityView.Value;
}
else
{
@@ -245,7 +245,7 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
/// <returns>Sequence of values.</returns>
public IEnumerable<TValue> GetValuesForKey(TKey key)
{
return Traverse(GetNodeForKey(key), n => 0 == _keyComparison(n.Key, key), n => n.Value);
return Traverse(GetEntityViewForKey(key), n => 0 == _keyComparison(n.Key, key), n => n.Value);
}

/// <summary>
@@ -254,7 +254,7 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
/// <returns>Sequence of all values.</returns>
public IEnumerable<TValue> GetValuesForAllKeys()
{
return Traverse(_rootNode, n => true, n => n.Value);
return Traverse(_rootEntityView, n => true, n => n.Value);
}

/// <summary>
@@ -267,7 +267,7 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
/// </summary>
public TKey MinimumKey
{
get { return GetExtreme(_rootNode, n => n.Left, n => n.Key); }
get { return GetExtreme(_rootEntityView, n => n.Left, n => n.Key); }
}

/// <summary>
@@ -275,346 +275,346 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
/// </summary>
public TKey MaximumKey
{
get { return GetExtreme(_rootNode, n => n.Right, n => n.Key); }
get { return GetExtreme(_rootEntityView, n => n.Right, n => n.Key); }
}

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

/// <summary>
/// Adds the specified key/value pair below the specified root node.
/// Adds the specified key/value pair below the specified root entityView.
/// </summary>
/// <param name="node">Specified node.</param>
/// <param name="entityView">Specified entityView.</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)
/// <returns>New root entityView.</returns>
private EntityView Add(EntityView entityView, TKey key, TValue value)
{
if (null == node)
if (null == entityView)
{
// Insert new node
// Insert new entityView
Count++;
return new Node { Key = key, Value = value };
return new EntityView { Key = key, Value = value };
}

if (IsRed(node.Left) && IsRed(node.Right))
if (IsRed(entityView.Left) && IsRed(entityView.Right))
{
// Split node with two red children
FlipColor(node);
// Split entityView with two red children
FlipColor(entityView);
}

// Find right place for new node
int comparisonResult = KeyAndValueComparison(key, value, node.Key, node.Value);
// Find right place for new entityView
int comparisonResult = KeyAndValueComparison(key, value, entityView.Key, entityView.Value);
if (comparisonResult < 0)
{
node.Left = Add(node.Left, key, value);
entityView.Left = Add(entityView.Left, key, value);
}
else if (0 < comparisonResult)
{
node.Right = Add(node.Right, key, value);
entityView.Right = Add(entityView.Right, key, value);
}
else
{
if (IsMultiDictionary)
{
// Store the presence of a "duplicate" node
node.Siblings++;
// Store the presence of a "duplicate" entityView
entityView.Siblings++;
Count++;
}
else
{
// Replace the value of the existing node
node.Value = value;
// Replace the value of the existing entityView
entityView.Value = value;
}
}

if (IsRed(node.Right))
if (IsRed(entityView.Right))
{
// Rotate to prevent red node on right
node = RotateLeft(node);
// Rotate to prevent red entityView on right
entityView = RotateLeft(entityView);
}

if (IsRed(node.Left) && IsRed(node.Left.Left))
if (IsRed(entityView.Left) && IsRed(entityView.Left.Left))
{
// Rotate to prevent consecutive red nodes
node = RotateRight(node);
// Rotate to prevent consecutive red entityViews
entityView = RotateRight(entityView);
}

return node;
return entityView;
}

/// <summary>
/// Removes the specified key/value pair from below the specified node.
/// Removes the specified key/value pair from below the specified entityView.
/// </summary>
/// <param name="node">Specified node.</param>
/// <param name="entityView">Specified entityView.</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)
private EntityView Remove(EntityView entityView, TKey key, TValue value)
{
int comparisonResult = KeyAndValueComparison(key, value, node.Key, node.Value);
int comparisonResult = KeyAndValueComparison(key, value, entityView.Key, entityView.Value);
if (comparisonResult < 0)
{
// * Continue search if left is present
if (null != node.Left)
if (null != entityView.Left)
{
if (!IsRed(node.Left) && !IsRed(node.Left.Left))
if (!IsRed(entityView.Left) && !IsRed(entityView.Left.Left))
{
// Move a red node over
node = MoveRedLeft(node);
// Move a red entityView over
entityView = MoveRedLeft(entityView);
}

// Remove from left
node.Left = Remove(node.Left, key, value);
entityView.Left = Remove(entityView.Left, key, value);
}
}
else
{
if (IsRed(node.Left))
if (IsRed(entityView.Left))
{
// Flip a 3 node or unbalance a 4 node
node = RotateRight(node);
// Flip a 3 entityView or unbalance a 4 entityView
entityView = RotateRight(entityView);
}
if ((0 == KeyAndValueComparison(key, value, node.Key, node.Value)) && (null == node.Right))
if ((0 == KeyAndValueComparison(key, value, entityView.Key, entityView.Value)) && (null == entityView.Right))
{
// Remove leaf node
Debug.Assert(null == node.Left, "About to remove an extra node.");
// Remove leaf entityView
Debug.Assert(null == entityView.Left, "About to remove an extra entityView.");
Count--;
if (0 < node.Siblings)
if (0 < entityView.Siblings)
{
// Record the removal of the "duplicate" node
// Record the removal of the "duplicate" entityView
Debug.Assert(IsMultiDictionary, "Should not have siblings if tree is not a multi-dictionary.");
node.Siblings--;
return node;
entityView.Siblings--;
return entityView;
}
else
{
// Leaf node is gone
// Leaf entityView is gone
return null;
}
}
// * Continue search if right is present
if (null != node.Right)
if (null != entityView.Right)
{
if (!IsRed(node.Right) && !IsRed(node.Right.Left))
if (!IsRed(entityView.Right) && !IsRed(entityView.Right.Left))
{
// Move a red node over
node = MoveRedRight(node);
// Move a red entityView over
entityView = MoveRedRight(entityView);
}
if (0 == KeyAndValueComparison(key, value, node.Key, node.Value))
if (0 == KeyAndValueComparison(key, value, entityView.Key, entityView.Value))
{
// Remove leaf node
// Remove leaf entityView
Count--;
if (0 < node.Siblings)
if (0 < entityView.Siblings)
{
// Record the removal of the "duplicate" node
// Record the removal of the "duplicate" entityView
Debug.Assert(IsMultiDictionary, "Should not have siblings if tree is not a multi-dictionary.");
node.Siblings--;
entityView.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);
// Find the smallest entityView on the right, swap, and remove it
EntityView m = GetExtreme(entityView.Right, n => n.Left, n => n);
entityView.Key = m.Key;
entityView.Value = m.Value;
entityView.Siblings = m.Siblings;
entityView.Right = DeleteMinimum(entityView.Right);
}
}
else
{
// Remove from right
node.Right = Remove(node.Right, key, value);
entityView.Right = Remove(entityView.Right, key, value);
}
}
}

// Maintain invariants
return FixUp(node);
return FixUp(entityView);
}

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

/// <summary>
/// Rotate the specified node "left".
/// Rotate the specified entityView "left".
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
private static Node RotateLeft(Node node)
/// <param name="entityView">Specified entityView.</param>
/// <returns>New root entityView.</returns>
private static EntityView RotateLeft(EntityView entityView)
{
Node x = node.Right;
node.Right = x.Left;
x.Left = node;
x.IsBlack = node.IsBlack;
node.IsBlack = false;
EntityView x = entityView.Right;
entityView.Right = x.Left;
x.Left = entityView;
x.IsBlack = entityView.IsBlack;
entityView.IsBlack = false;
return x;
}

/// <summary>
/// Rotate the specified node "right".
/// Rotate the specified entityView "right".
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
private static Node RotateRight(Node node)
/// <param name="entityView">Specified entityView.</param>
/// <returns>New root entityView.</returns>
private static EntityView RotateRight(EntityView entityView)
{
Node x = node.Left;
node.Left = x.Right;
x.Right = node;
x.IsBlack = node.IsBlack;
node.IsBlack = false;
EntityView x = entityView.Left;
entityView.Left = x.Right;
x.Right = entityView;
x.IsBlack = entityView.IsBlack;
entityView.IsBlack = false;
return x;
}

/// <summary>
/// Moves a red node from the right child to the left child.
/// Moves a red entityView 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)
/// <param name="entityView">Parent entityView.</param>
/// <returns>New root entityView.</returns>
private static EntityView MoveRedLeft(EntityView entityView)
{
FlipColor(node);
if (IsRed(node.Right.Left))
FlipColor(entityView);
if (IsRed(entityView.Right.Left))
{
node.Right = RotateRight(node.Right);
node = RotateLeft(node);
FlipColor(node);
entityView.Right = RotateRight(entityView.Right);
entityView = RotateLeft(entityView);
FlipColor(entityView);

// * Avoid creating right-leaning nodes
if (IsRed(node.Right.Right))
// * Avoid creating right-leaning entityViews
if (IsRed(entityView.Right.Right))
{
node.Right = RotateLeft(node.Right);
entityView.Right = RotateLeft(entityView.Right);
}
}
return node;
return entityView;
}

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

/// <summary>
/// Deletes the minimum node under the specified node.
/// Deletes the minimum entityView under the specified entityView.
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
private Node DeleteMinimum(Node node)
/// <param name="entityView">Specified entityView.</param>
/// <returns>New root entityView.</returns>
private EntityView DeleteMinimum(EntityView entityView)
{
if (null == node.Left)
if (null == entityView.Left)
{
// Nothing to do
return null;
}

if (!IsRed(node.Left) && !IsRed(node.Left.Left))
if (!IsRed(entityView.Left) && !IsRed(entityView.Left.Left))
{
// Move red node left
node = MoveRedLeft(node);
// Move red entityView left
entityView = MoveRedLeft(entityView);
}

// Recursively delete
node.Left = DeleteMinimum(node.Left);
entityView.Left = DeleteMinimum(entityView.Left);

// Maintain invariants
return FixUp(node);
return FixUp(entityView);
}

/// <summary>
/// Maintains invariants by adjusting the specified nodes children.
/// Maintains invariants by adjusting the specified entityViews children.
/// </summary>
/// <param name="node">Specified node.</param>
/// <returns>New root node.</returns>
private static Node FixUp(Node node)
/// <param name="entityView">Specified entityView.</param>
/// <returns>New root entityView.</returns>
private static EntityView FixUp(EntityView entityView)
{
if (IsRed(node.Right))
if (IsRed(entityView.Right))
{
// Avoid right-leaning node
node = RotateLeft(node);
// Avoid right-leaning entityView
entityView = RotateLeft(entityView);
}

if (IsRed(node.Left) && IsRed(node.Left.Left))
if (IsRed(entityView.Left) && IsRed(entityView.Left.Left))
{
// Balance 4-node
node = RotateRight(node);
// Balance 4-entityView
entityView = RotateRight(entityView);
}

if (IsRed(node.Left) && IsRed(node.Right))
if (IsRed(entityView.Left) && IsRed(entityView.Right))
{
// Push red up
FlipColor(node);
FlipColor(entityView);
}

// * Avoid leaving behind right-leaning nodes
if ((null != node.Left) && IsRed(node.Left.Right) && !IsRed(node.Left.Left))
// * Avoid leaving behind right-leaning entityViews
if ((null != entityView.Left) && IsRed(entityView.Left.Right) && !IsRed(entityView.Left.Left))
{
node.Left = RotateLeft(node.Left);
if (IsRed(node.Left))
entityView.Left = RotateLeft(entityView.Left);
if (IsRed(entityView.Left))
{
// Balance 4-node
node = RotateRight(node);
// Balance 4-entityView
entityView = RotateRight(entityView);
}
}

return node;
return entityView;
}

/// <summary>
/// Gets the (first) node corresponding to the specified key.
/// Gets the (first) entityView 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)
/// <returns>Corresponding entityView or null if none found.</returns>
private EntityView GetEntityViewForKey(TKey key)
{
// Initialize
Node node = _rootNode;
while (null != node)
EntityView entityView = _rootEntityView;
while (null != entityView)
{
// Compare keys and go left/right
int comparisonResult = _keyComparison(key, node.Key);
int comparisonResult = _keyComparison(key, entityView.Key);
if (comparisonResult < 0)
{
node = node.Left;
entityView = entityView.Left;
}
else if (0 < comparisonResult)
{
node = node.Right;
entityView = entityView.Right;
}
else
{
// Match; return node
return node;
// Match; return entityView
return entityView;
}
}

@@ -626,15 +626,15 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
/// 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="entityView">EntityView 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)
private static T GetExtreme<T>(EntityView entityView, Func<EntityView, EntityView> successor, Func<EntityView, T> selector)
{
// Initialize
T extreme = default(T);
Node current = node;
EntityView current = entityView;
while (null != current)
{
// Go to extreme
@@ -645,18 +645,18 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
}

/// <summary>
/// Traverses a subset of the sequence of nodes in order and selects the specified nodes.
/// Traverses a subset of the sequence of entityViews in order and selects the specified entityViews.
/// </summary>
/// <typeparam name="T">Type of elements.</typeparam>
/// <param name="node">Starting node.</param>
/// <param name="entityView">Starting entityView.</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)
/// <returns>Sequence of selected entityViews.</returns>
private IEnumerable<T> Traverse<T>(EntityView entityView, Func<EntityView, bool> condition, Func<EntityView, T> selector)
{
// Create a stack to avoid recursion
Stack<Node> stack = new Stack<Node>();
Node current = node;
Stack<EntityView> stack = new Stack<EntityView>();
EntityView current = entityView;
while (null != current)
{
if (null != current.Left)
@@ -671,7 +671,7 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
{
for (int i = 0; i <= current.Siblings; i++)
{
// Select current node if relevant
// Select current entityView if relevant
if (condition(current))
{
yield return selector(current);
@@ -714,29 +714,29 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
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))
Debug.Assert((null == _rootEntityView) || _rootEntityView.IsBlack, "Root is not black");
// Every path contains the same number of black entityViews
Dictionary<EntityView, EntityView> parents = new Dictionary<LeftLeaningRedBlackTree<TKey, TValue>.EntityView, LeftLeaningRedBlackTree<TKey, TValue>.EntityView>();
foreach (EntityView entityView in Traverse(_rootEntityView, n => true, n => n))
{
if (null != node.Left)
if (null != entityView.Left)
{
parents[node.Left] = node;
parents[entityView.Left] = entityView;
}
if (null != node.Right)
if (null != entityView.Right)
{
parents[node.Right] = node;
parents[entityView.Right] = entityView;
}
}
if (null != _rootNode)
if (null != _rootEntityView)
{
parents[_rootNode] = null;
parents[_rootEntityView] = null;
}
int treeCount = -1;
foreach (Node node in Traverse(_rootNode, n => (null == n.Left) || (null == n.Right), n => n))
foreach (EntityView entityView in Traverse(_rootEntityView, n => (null == n.Left) || (null == n.Right), n => n))
{
int pathCount = 0;
Node current = node;
EntityView current = entityView;
while (null != current)
{
if (current.IsBlack)
@@ -745,28 +745,28 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
}
current = parents[current];
}
Debug.Assert((-1 == treeCount) || (pathCount == treeCount), "Not all paths have the same number of black nodes.");
Debug.Assert((-1 == treeCount) || (pathCount == treeCount), "Not all paths have the same number of black entityViews.");
treeCount = pathCount;
}
// Verify node properties...
foreach (Node node in Traverse(_rootNode, n => true, n => n))
// Verify entityView properties...
foreach (EntityView entityView in Traverse(_rootEntityView, n => true, n => n))
{
// Left node is less
if (null != node.Left)
// Left entityView is less
if (null != entityView.Left)
{
Debug.Assert(0 > KeyAndValueComparison(node.Left.Key, node.Left.Value, node.Key, node.Value), "Left node is greater than its parent.");
Debug.Assert(0 > KeyAndValueComparison(entityView.Left.Key, entityView.Left.Value, entityView.Key, entityView.Value), "Left entityView is greater than its parent.");
}
// Right node is greater
if (null != node.Right)
// Right entityView is greater
if (null != entityView.Right)
{
Debug.Assert(0 < KeyAndValueComparison(node.Right.Key, node.Right.Value, node.Key, node.Value), "Right node is less than its parent.");
Debug.Assert(0 < KeyAndValueComparison(entityView.Right.Key, entityView.Right.Value, entityView.Key, entityView.Value), "Right entityView 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.");
// Both children of a red entityView are black
Debug.Assert(!IsRed(entityView) || (!IsRed(entityView.Left) && !IsRed(entityView.Right)), "Red entityView has a red child.");
// Always left-leaning
Debug.Assert(!IsRed(node.Right) || IsRed(node.Left), "Node is not left-leaning.");
Debug.Assert(!IsRed(entityView.Right) || IsRed(entityView.Left), "EntityView is not left-leaning.");
// No consecutive reds (subset of previous rule)
//Debug.Assert(!(IsRed(node) && IsRed(node.Left)));
//Debug.Assert(!(IsRed(entityView) && IsRed(entityView.Left)));
}
}

@@ -780,7 +780,7 @@ public class LeftLeaningRedBlackTree<TKey, TValue>
return
"<html>" +
"<body>" +
(null != _rootNode ? _rootNode.HtmlFragment : "[null]") +
(null != _rootEntityView ? _rootEntityView.HtmlFragment : "[null]") +
"</body>" +
"</html>";
}


+ 32
- 33
DataStructures/LockFreeQueue.cs View File

@@ -1,14 +1,13 @@
using System.Collections.Generic;
using System.Threading;

//from unify wiki
namespace Svelto.DataStructures
{
public class SingleLinkNode<T>
public class SingleLinkEntityView<T>
{
// Note; the Next member cannot be a property since
// it participates in many CAS operations
public SingleLinkNode<T> Next;
public SingleLinkEntityView<T> Next;
public T Item;
}

@@ -24,33 +23,33 @@ namespace Svelto.DataStructures

public class LockFreeLinkPool<T>
{
private SingleLinkNode<T> head;
private SingleLinkEntityView<T> head;

public LockFreeLinkPool()
{
head = new SingleLinkNode<T>();
head = new SingleLinkEntityView<T>();
}

public void Push(SingleLinkNode<T> newNode)
public void Push(SingleLinkEntityView<T> newEntityView)
{
newNode.Item = default(T);
newEntityView.Item = default(T);
do
{
newNode.Next = head.Next;
} while (!SyncMethods.CAS<SingleLinkNode<T>>(ref head.Next, newNode.Next, newNode));
newEntityView.Next = head.Next;
} while (!SyncMethods.CAS<SingleLinkEntityView<T>>(ref head.Next, newEntityView.Next, newEntityView));
return;
}

public bool Pop(out SingleLinkNode<T> node)
public bool Pop(out SingleLinkEntityView<T> entityView)
{
do
{
node = head.Next;
if (node == null)
entityView = head.Next;
if (entityView == null)
{
return false;
}
} while (!SyncMethods.CAS<SingleLinkNode<T>>(ref head.Next, node, node.Next));
} while (!SyncMethods.CAS<SingleLinkEntityView<T>>(ref head.Next, entityView, entityView.Next));
return true;
}
}
@@ -58,35 +57,35 @@ namespace Svelto.DataStructures
public class LockFreeQueue<T>
{

SingleLinkNode<T> head;
SingleLinkNode<T> tail;
SingleLinkEntityView<T> head;
SingleLinkEntityView<T> tail;
LockFreeLinkPool<T> trash;

public LockFreeQueue()
{
head = new SingleLinkNode<T>();
head = new SingleLinkEntityView<T>();
tail = head;
trash = new LockFreeLinkPool<T>();
}

public void Enqueue(T item)
{
SingleLinkNode<T> oldTail = null;
SingleLinkNode<T> oldTailNext;
SingleLinkEntityView<T> oldTail = null;
SingleLinkEntityView<T> oldTailNext;

SingleLinkNode<T> newNode;
if (!trash.Pop(out newNode))
SingleLinkEntityView<T> newEntityView;
if (!trash.Pop(out newEntityView))
{
newNode = new SingleLinkNode<T>();
newEntityView = new SingleLinkEntityView<T>();
}
else
{
newNode.Next = null;
newEntityView.Next = null;
}
newNode.Item = item;
newEntityView.Item = item;

bool newNodeWasAdded = false;
while (!newNodeWasAdded)
bool newEntityViewWasAdded = false;
while (!newEntityViewWasAdded)
{
oldTail = tail;
oldTailNext = oldTail.Next;
@@ -94,26 +93,26 @@ namespace Svelto.DataStructures
if (tail == oldTail)
{
if (oldTailNext == null)
newNodeWasAdded = SyncMethods.CAS<SingleLinkNode<T>>(ref tail.Next, null, newNode);
newEntityViewWasAdded = SyncMethods.CAS<SingleLinkEntityView<T>>(ref tail.Next, null, newEntityView);
else
SyncMethods.CAS<SingleLinkNode<T>>(ref tail, oldTail, oldTailNext);
SyncMethods.CAS<SingleLinkEntityView<T>>(ref tail, oldTail, oldTailNext);
}
}
SyncMethods.CAS<SingleLinkNode<T>>(ref tail, oldTail, newNode);
SyncMethods.CAS<SingleLinkEntityView<T>>(ref tail, oldTail, newEntityView);
}

public bool Dequeue(out T item)
{
item = default(T);
SingleLinkNode<T> oldHead = null;
SingleLinkEntityView<T> oldHead = null;

bool haveAdvancedHead = false;
while (!haveAdvancedHead)
{

oldHead = head;
SingleLinkNode<T> oldTail = tail;
SingleLinkNode<T> oldHeadNext = oldHead.Next;
SingleLinkEntityView<T> oldTail = tail;
SingleLinkEntityView<T> oldHeadNext = oldHead.Next;

if (oldHead == head)
{
@@ -123,12 +122,12 @@ namespace Svelto.DataStructures
{
return false;
}
SyncMethods.CAS<SingleLinkNode<T>>(ref tail, oldTail, oldHeadNext);
SyncMethods.CAS<SingleLinkEntityView<T>>(ref tail, oldTail, oldHeadNext);
}
else
{
item = oldHeadNext.Item;
haveAdvancedHead = SyncMethods.CAS<SingleLinkNode<T>>(ref head, oldHead, oldHeadNext);
haveAdvancedHead = SyncMethods.CAS<SingleLinkEntityView<T>>(ref head, oldHead, oldHeadNext);
if (haveAdvancedHead)
{
trash.Push(oldHead);


+ 99
- 101
DataStructures/PriorityQueue/HeapPriorityQueue.cs View File

@@ -1,7 +1,5 @@
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Svelto.DataStructures;

namespace Svelto.DataStructures
{
@@ -9,40 +7,40 @@ namespace Svelto.DataStructures
/// An implementation of a min-Priority Queue using a heap. Has O(1) .Contains()!
/// See https://bitbucket.org/BlueRaja/high-speed-priority-queue-for-c/wiki/Getting%20Started for more information
/// </summary>
/// <typeparam name="T">The values in the queue. Must implement the PriorityQueueNode interface</typeparam>
/// <typeparam name="T">The values in the queue. Must implement the PriorityQueueEntityView interface</typeparam>
public sealed class HeapPriorityQueue<T> : IPriorityQueue<T>
where T : PriorityQueueNode
where T : PriorityQueueEntityView
{
private int _numNodes;
private readonly FasterList<T> _nodes;
private long _numNodesEverEnqueued;
private int _numEntityViews;
private readonly FasterList<T> _entityViews;
private long _numEntityViewsEverEnqueued;

/// <summary>
/// Instantiate a new Priority Queue
/// </summary>
/// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause an exception)</param>
/// <param name="maxEntityViews">The max entityViews ever allowed to be enqueued (going over this will cause an exception)</param>
public HeapPriorityQueue()
{
_numNodes = 0;
_nodes = new FasterList<T>();
_numNodesEverEnqueued = 0;
_numEntityViews = 0;
_entityViews = new FasterList<T>();
_numEntityViewsEverEnqueued = 0;
}

public HeapPriorityQueue(int initialSize)
{
_numNodes = 0;
_nodes = new FasterList<T>(initialSize);
_numNodesEverEnqueued = 0;
_numEntityViews = 0;
_entityViews = new FasterList<T>(initialSize);
_numEntityViewsEverEnqueued = 0;
}
/// <summary>
/// Returns the number of nodes in the queue. O(1)
/// Returns the number of entityViews in the queue. O(1)
/// </summary>
public int Count
{
get
{
return _numNodes;
return _numEntityViews;
}
}

@@ -54,119 +52,119 @@ namespace Svelto.DataStructures
{
get
{
return _nodes.Count - 1;
return _entityViews.Count - 1;
}
}

/// <summary>
/// Removes every node from the queue. O(n) (So, don't do this often!)
/// Removes every entityView from the queue. O(n) (So, don't do this often!)
/// </summary>
#if NET_VERSION_4_5
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public void Clear()
{
_nodes.FastClear();
_entityViews.FastClear();

_numNodes = 0;
_numEntityViews = 0;
}

/// <summary>
/// Returns (in O(1)!) whether the given node is in the queue. O(1)
/// Returns (in O(1)!) whether the given entityView is in the queue. O(1)
/// </summary>
#if NET_VERSION_4_5
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public bool Contains(T node)
public bool Contains(T entityView)
{
return (_nodes[node.QueueIndex] == node);
return (_entityViews[entityView.QueueIndex] == entityView);
}

/// <summary>
/// Enqueue a node - .Priority must be set beforehand! O(log n)
/// Enqueue a entityView - .Priority must be set beforehand! O(log n)
/// </summary>
#if NET_VERSION_4_5
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public void Enqueue(T node, double priority)
public void Enqueue(T entityView, double priority)
{
node.Priority = priority;
_numNodes++;
if (_nodes.Count < _numNodes)
_nodes.Resize(_numNodes + 1);
_nodes[_numNodes] = node;
node.QueueIndex = _numNodes;
node.InsertionIndex = _numNodesEverEnqueued++;
CascadeUp(_nodes[_numNodes]);
entityView.Priority = priority;
_numEntityViews++;
if (_entityViews.Count < _numEntityViews)
_entityViews.Resize(_numEntityViews + 1);
_entityViews[_numEntityViews] = entityView;
entityView.QueueIndex = _numEntityViews;
entityView.InsertionIndex = _numEntityViewsEverEnqueued++;
CascadeUp(_entityViews[_numEntityViews]);
}

#if NET_VERSION_4_5
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private void Swap(T node1, T node2)
private void Swap(T entityView1, T entityView2)
{
//Swap the nodes
_nodes[node1.QueueIndex] = node2;
_nodes[node2.QueueIndex] = node1;
//Swap the entityViews
_entityViews[entityView1.QueueIndex] = entityView2;
_entityViews[entityView2.QueueIndex] = entityView1;

//Swap their indicies
int temp = node1.QueueIndex;
node1.QueueIndex = node2.QueueIndex;
node2.QueueIndex = temp;
int temp = entityView1.QueueIndex;
entityView1.QueueIndex = entityView2.QueueIndex;
entityView2.QueueIndex = temp;
}

//Performance appears to be slightly better when this is NOT inlined o_O
private void CascadeUp(T node)
private void CascadeUp(T entityView)
{
//aka Heapify-up
int parent = node.QueueIndex / 2;
int parent = entityView.QueueIndex / 2;
while(parent >= 1)
{
T parentNode = _nodes[parent];
if(HasHigherPriority(parentNode, node))
T parentEntityView = _entityViews[parent];
if(HasHigherPriority(parentEntityView, entityView))
break;

//Node has lower priority value, so move it up the heap
Swap(node, parentNode); //For some reason, this is faster with Swap() rather than (less..?) individual operations, like in CascadeDown()
//EntityView has lower priority value, so move it up the heap
Swap(entityView, parentEntityView); //For some reason, this is faster with Swap() rather than (less..?) individual operations, like in CascadeDown()

parent = node.QueueIndex / 2;
parent = entityView.QueueIndex / 2;
}
}

#if NET_VERSION_4_5
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private void CascadeDown(T node)
private void CascadeDown(T entityView)
{
//aka Heapify-down
T newParent;
int finalQueueIndex = node.QueueIndex;
int finalQueueIndex = entityView.QueueIndex;
while(true)
{
newParent = node;
newParent = entityView;
int childLeftIndex = 2 * finalQueueIndex;

//Check if the left-child is higher-priority than the current node
if(childLeftIndex > _numNodes)
//Check if the left-child is higher-priority than the current entityView
if(childLeftIndex > _numEntityViews)
{
//This could be placed outside the loop, but then we'd have to check newParent != node twice
node.QueueIndex = finalQueueIndex;
_nodes[finalQueueIndex] = node;
//This could be placed outside the loop, but then we'd have to check newParent != entityView twice
entityView.QueueIndex = finalQueueIndex;
_entityViews[finalQueueIndex] = entityView;
break;
}

T childLeft = _nodes[childLeftIndex];
T childLeft = _entityViews[childLeftIndex];
if(HasHigherPriority(childLeft, newParent))
{
newParent = childLeft;
}

//Check if the right-child is higher-priority than either the current node or the left child
//Check if the right-child is higher-priority than either the current entityView or the left child
int childRightIndex = childLeftIndex + 1;
if(childRightIndex <= _numNodes)
if(childRightIndex <= _numEntityViews)
{
T childRight = _nodes[childRightIndex];
T childRight = _entityViews[childRightIndex];
if(HasHigherPriority(childRight, newParent))
{
newParent = childRight;
@@ -174,11 +172,11 @@ namespace Svelto.DataStructures
}

//If either of the children has higher (smaller) priority, swap and continue cascading
if(newParent != node)
if(newParent != entityView)
{
//Move new parent to its new index. node will be moved once, at the end
//Move new parent to its new index. entityView will be moved once, at the end
//Doing it this way is one less assignment operation than calling Swap()
_nodes[finalQueueIndex] = newParent;
_entityViews[finalQueueIndex] = newParent;

int temp = newParent.QueueIndex;
newParent.QueueIndex = finalQueueIndex;
@@ -187,8 +185,8 @@ namespace Svelto.DataStructures
else
{
//See note above
node.QueueIndex = finalQueueIndex;
_nodes[finalQueueIndex] = node;
entityView.QueueIndex = finalQueueIndex;
_entityViews[finalQueueIndex] = entityView;
break;
}
}
@@ -196,7 +194,7 @@ namespace Svelto.DataStructures

/// <summary>
/// Returns true if 'higher' has higher priority than 'lower', false otherwise.
/// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false
/// Note that calling HasHigherPriority(entityView, entityView) (ie. both arguments the same entityView) will return false
/// </summary>
#if NET_VERSION_4_5
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -208,11 +206,11 @@ namespace Svelto.DataStructures
}

/// <summary>
/// Removes the head of the queue (node with highest priority; ties are broken by order of insertion), and returns it. O(log n)
/// Removes the head of the queue (entityView with highest priority; ties are broken by order of insertion), and returns it. O(log n)
/// </summary>
public T Dequeue()
{
T returnMe = _nodes[1];
T returnMe = _entityViews[1];
Remove(returnMe);
return returnMe;
}
@@ -224,77 +222,77 @@ namespace Svelto.DataStructures
{
get
{
return _nodes[1];
return _entityViews[1];
}
}

/// <summary>
/// This method must be called on a node every time its priority changes while it is in the queue.
/// This method must be called on a entityView every time its priority changes while it is in the queue.
/// <b>Forgetting to call this method will result in a corrupted queue!</b>
/// O(log n)
/// </summary>
#if NET_VERSION_4_5
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public void UpdatePriority(T node, double priority)
public void UpdatePriority(T entityView, double priority)
{
node.Priority = priority;
OnNodeUpdated(node);
entityView.Priority = priority;
OnEntityViewUpdated(entityView);
}

private void OnNodeUpdated(T node)
private void OnEntityViewUpdated(T entityView)
{
//Bubble the updated node up or down as appropriate
int parentIndex = node.QueueIndex / 2;
T parentNode = _nodes[parentIndex];
//Bubble the updated entityView up or down as appropriate
int parentIndex = entityView.QueueIndex / 2;
T parentEntityView = _entityViews[parentIndex];

if(parentIndex > 0 && HasHigherPriority(node, parentNode))
if(parentIndex > 0 && HasHigherPriority(entityView, parentEntityView))
{
CascadeUp(node);
CascadeUp(entityView);
}
else
{
//Note that CascadeDown will be called if parentNode == node (that is, node is the root)
CascadeDown(node);
//Note that CascadeDown will be called if parentEntityView == entityView (that is, entityView is the root)
CascadeDown(entityView);
}
}

/// <summary>
/// Removes a node from the queue. Note that the node does not need to be the head of the queue. O(log n)
/// Removes a entityView from the queue. Note that the entityView does not need to be the head of the queue. O(log n)
/// </summary>
public void Remove(T node)
public void Remove(T entityView)
{
if(_numNodes <= 1)
if(_numEntityViews <= 1)
{
_nodes[1] = null;
_numNodes = 0;
_entityViews[1] = null;
_numEntityViews = 0;
return;
}

//Make sure the node is the last node in the queue
//Make sure the entityView is the last entityView in the queue
bool wasSwapped = false;
T formerLastNode = _nodes[_numNodes];
if(node.QueueIndex != _numNodes)
T formerLastEntityView = _entityViews[_numEntityViews];
if(entityView.QueueIndex != _numEntityViews)
{
//Swap the node with the last node
Swap(node, formerLastNode);
//Swap the entityView with the last entityView
Swap(entityView, formerLastEntityView);
wasSwapped = true;
}

_numNodes--;
_nodes[node.QueueIndex] = null;
_numEntityViews--;
_entityViews[entityView.QueueIndex] = null;

if(wasSwapped)
{
//Now bubble formerLastNode (which is no longer the last node) up or down as appropriate
OnNodeUpdated(formerLastNode);
//Now bubble formerLastEntityView (which is no longer the last entityView) up or down as appropriate
OnEntityViewUpdated(formerLastEntityView);
}
}

public IEnumerator<T> GetEnumerator()
{
for(int i = 1; i <= _numNodes; i++)
yield return _nodes[i];
for(int i = 1; i <= _numEntityViews; i++)
yield return _entityViews[i];
}

IEnumerator IEnumerable.GetEnumerator()
@@ -308,16 +306,16 @@ namespace Svelto.DataStructures
/// </summary>
public bool IsValidQueue()
{
for(int i = 1; i < _nodes.Count; i++)
for(int i = 1; i < _entityViews.Count; i++)
{
if(_nodes[i] != null)
if(_entityViews[i] != null)
{
int childLeftIndex = 2 * i;
if(childLeftIndex < _nodes.Count && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i]))
if(childLeftIndex < _entityViews.Count && _entityViews[childLeftIndex] != null && HasHigherPriority(_entityViews[childLeftIndex], _entityViews[i]))
return false;

int childRightIndex = childLeftIndex + 1;
if(childRightIndex < _nodes.Count && _nodes[childRightIndex] != null && HasHigherPriority(_nodes[childRightIndex], _nodes[i]))
if(childRightIndex < _entityViews.Count && _entityViews[childRightIndex] != null && HasHigherPriority(_entityViews[childRightIndex], _entityViews[i]))
return false;
}
}


+ 5
- 5
DataStructures/PriorityQueue/IPriorityQueue.cs View File

@@ -8,16 +8,16 @@ namespace Svelto.DataStructures
/// (theoretically?) optimize method calls from concrete-types slightly better.
/// </summary>
public interface IPriorityQueue<T> : IEnumerable<T>
where T : PriorityQueueNode
where T : PriorityQueueEntityView
{
void Remove(T node);
void UpdatePriority(T node, double priority);
void Enqueue(T node, double priority);
void Remove(T entityView);
void UpdatePriority(T entityView, double priority);
void Enqueue(T entityView, double priority);
T Dequeue();
T First { get; }
int Count { get; }
int MaxSize { get; }
void Clear();
bool Contains(T node);
bool Contains(T entityView);
}
}

+ 3
- 3
DataStructures/PriorityQueue/PriorityQueueNode.cs View File

@@ -1,9 +1,9 @@
namespace Svelto.DataStructures
{
public class PriorityQueueNode
public class PriorityQueueEntityView
{
/// <summary>
/// The Priority to insert this node at. Must be set BEFORE adding a node to the queue
/// The Priority to insert this entityView at. Must be set BEFORE adding a entityView to the queue
/// </summary>
public double Priority { get;
set;
@@ -11,7 +11,7 @@

/// <summary>
/// <b>Used by the priority queue - do not edit this value.</b>
/// Represents the order the node was inserted in
/// Represents the order the entityView was inserted in
/// </summary>
public long InsertionIndex { get; set; }



+ 13
- 12
ECS/DataStructures/TypeSafeDictionary.cs View File

@@ -1,6 +1,5 @@
using Svelto.DataStructures;
using System.Collections.Generic;
using Svelto.ECS.Internal;

namespace Svelto.ECS.Internal
{
@@ -14,35 +13,37 @@ namespace Svelto.ECS.Internal

public interface ITypeSafeDictionary
{
void FillWithIndexedNodes(ITypeSafeList nodes);
void Remove(int entityId);
NodeWithID GetIndexedNode(int entityID);
void FillWithIndexedEntityViews(ITypeSafeList entityViews);
bool Remove(int entityId);
IEntityView GetIndexedEntityView(int entityID);
}

class TypeSafeDictionary<TValue> : Dictionary<int, TValue>, ITypeSafeDictionary where TValue:NodeWithID
class TypeSafeDictionary<TValue> : Dictionary<int, TValue>, ITypeSafeDictionary where TValue:IEntityView
{
internal static readonly ReadOnlyDictionary<int, TValue> Default =
new ReadOnlyDictionary<int, TValue>(new Dictionary<int, TValue>());
public void FillWithIndexedNodes(ITypeSafeList nodes)
public void FillWithIndexedEntityViews(ITypeSafeList entityViews)
{
int count;
var buffer = FasterList<TValue>.NoVirt.ToArrayFast((FasterList<TValue>) nodes, out count);
var buffer = FasterList<TValue>.NoVirt.ToArrayFast((FasterList<TValue>) entityViews, out count);

for (int i = 0; i < count; i++)
{
var node = buffer[i];
var entityView = buffer[i];

Add(node.ID, node);
Add(entityView.ID, entityView);
}
}

public void Remove(int entityId)
new public bool Remove(int entityId)
{
throw new System.NotImplementedException();
base.Remove(entityId);

return this.Count > 0;
}

public NodeWithID GetIndexedNode(int entityID)
public IEntityView GetIndexedEntityView(int entityID)
{
return this[entityID];
}


+ 32
- 14
ECS/DataStructures/TypeSafeFasterListForECS.cs View File

@@ -7,51 +7,57 @@ namespace Svelto.ECS.Internal
{
public interface ITypeSafeList: IEnumerable
{
void AddRange(ITypeSafeList nodeListValue);
void AddRange(ITypeSafeList entityViewListValue);

ITypeSafeList Create();
bool isQueryiableNode { get; }
void UnorderedRemove(int index);
bool isQueryiableEntityView { get; }
bool UnorderedRemove(int entityID);
ITypeSafeDictionary CreateIndexedDictionary();
IEntityView[] ToArrayFast(out int count);
}

class TypeSafeFasterListForECS<T>: FasterList<T> where T:INode
class TypeSafeFasterListForECS<T>: FasterList<T> where T:IEntityView
{
protected TypeSafeFasterListForECS()
{
_mappedIndices = new Dictionary<int, int>();
}
public void UnorderedRemove(int mappedIndex)
public bool UnorderedRemove(int entityID)
{
var index = _mappedIndices[mappedIndex];
_mappedIndices.Remove(mappedIndex);
var index = _mappedIndices[entityID];

DesignByContract.Check.Assert(entityID == this[index].ID, "Something went wrong with the Svelto.ECS code, please contact the author");

_mappedIndices.Remove(entityID);

if (UnorderedRemoveAt(index))
_mappedIndices[this[index].ID] = index;

return this.Count > 0;
}
public void AddRange(ITypeSafeList nodeListValue)
public void AddRange(ITypeSafeList entityViewListValue)
{
var index = this.Count;
AddRange(nodeListValue as FasterList<T>);
AddRange(entityViewListValue as FasterList<T>);
for (int i = index; i < Count; ++i)
_mappedIndices[this[i].ID] = this.Count;
_mappedIndices[this[i].ID] = i;
}

readonly Dictionary<int, int> _mappedIndices;
}

class TypeSafeFasterListForECSForStructs<T> : TypeSafeFasterListForECS<T>, ITypeSafeList where T:struct, INode
class TypeSafeFasterListForECSForStructs<T> : TypeSafeFasterListForECS<T>, ITypeSafeList where T:struct, IEntityStruct
{
public ITypeSafeList Create()
{
return new TypeSafeFasterListForECSForStructs<T>();
}

public bool isQueryiableNode
public bool isQueryiableEntityView
{
get { return false; }
}
@@ -60,16 +66,21 @@ namespace Svelto.ECS.Internal
{
throw new Exception("Not Allowed");
}

public IEntityView[] ToArrayFast(out int count)
{
throw new Exception("Not Allowed");
}
}
class TypeSafeFasterListForECSForClasses<T> : TypeSafeFasterListForECS<T>, ITypeSafeList where T:NodeWithID
class TypeSafeFasterListForECSForClasses<T> : TypeSafeFasterListForECS<T>, ITypeSafeList where T:EntityView, new()
{
public ITypeSafeList Create()
{
return new TypeSafeFasterListForECSForClasses<T>();
}

public bool isQueryiableNode
public bool isQueryiableEntityView
{
get { return true; }
}
@@ -78,5 +89,12 @@ namespace Svelto.ECS.Internal
{
return new TypeSafeDictionary<T>();
}

public IEntityView[] ToArrayFast(out int count)
{
count = this.Count;
return this.ToArrayFast();
}
}
}

+ 93
- 66
ECS/EngineNodeDB.cs View File

@@ -5,137 +5,164 @@ using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public class EngineNodeDB : IEngineNodeDB
public class EngineEntityViewDB : IEngineEntityViewDB
{
internal EngineNodeDB( Dictionary<Type, ITypeSafeList> nodesDB,
Dictionary<Type, ITypeSafeDictionary> nodesDBdic,
Dictionary<Type, ITypeSafeList> metaNodesDB,
Dictionary<int, Dictionary<Type, ITypeSafeList>> groupNodesDB)
internal EngineEntityViewDB( Dictionary<Type, ITypeSafeList> entityViewsDB,
Dictionary<Type, ITypeSafeDictionary> entityViewsDBdic,
Dictionary<Type, ITypeSafeList> metaEntityViewsDB,
Dictionary<int, Dictionary<Type, ITypeSafeList>> groupEntityViewsDB)
{
_nodesDB = nodesDB;
_nodesDBdic = nodesDBdic;
_metaNodesDB = metaNodesDB;
_groupNodesDB = groupNodesDB;
_entityViewsDB = entityViewsDB;
_entityViewsDBdic = entityViewsDBdic;
_metaEntityViewsDB = metaEntityViewsDB;
_groupEntityViewsDB = groupEntityViewsDB;
}

public FasterReadOnlyList<T> QueryNodes<T>()
public FasterReadOnlyList<T> QueryEntityViews<T>() where T:EntityView<T>, new()
{
var type = typeof(T);

ITypeSafeList nodes;
ITypeSafeList entityViews;

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

return new FasterReadOnlyList<T>((FasterList<T>)nodes);
return new FasterReadOnlyList<T>((FasterList<T>)entityViews);
}

public FasterReadOnlyList<T> QueryGroupedNodes<T>(int @group)
public FasterReadOnlyList<T> QueryGroupedEntityViews<T>(int @group) where T:EntityView<T>, new()
{
return new FasterReadOnlyList<T>(_groupNodesDB[group] as FasterList<T>);
Dictionary<Type, ITypeSafeList> entityViews;

if (_groupEntityViewsDB.TryGetValue(group, out entityViews) == false)
return RetrieveEmptyEntityViewList<T>();
return new FasterReadOnlyList<T>(entityViews as FasterList<T>);
}

public T[] QueryNodesAsArray<T>(out int count) where T : struct
public T[] QueryEntityViewsAsArray<T>(out int count) where T : IEntityView
{
var type = typeof(T);
count = 0;
ITypeSafeList nodes;
ITypeSafeList entityViews;

if (_nodesDB.TryGetValue(type, out nodes) == false)
return null;
if (_entityViewsDB.TryGetValue(type, out entityViews) == false)
return RetrieveEmptyEntityViewArray<T>();
var castedNodes = (FasterList<T>)nodes;
var castedEntityViews = (FasterList<T>)entityViews;

count = castedNodes.Count;
count = castedEntityViews.Count;

return castedNodes.ToArrayFast();
return castedEntityViews.ToArrayFast();
}
public ReadOnlyDictionary<int, T> QueryIndexableNodes<T>() where T:NodeWithID
public T[] QueryGroupedEntityViewsAsArray<T>(int @group, out int count) where T : IEntityView
{
var type = typeof(T);
count = 0;
Dictionary<Type, ITypeSafeList> entityViews;
if (_groupEntityViewsDB.TryGetValue(group, out entityViews) == false)
return RetrieveEmptyEntityViewArray<T>();
var castedEntityViews = (FasterList<T>)entityViews[type];

ITypeSafeDictionary nodes;

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

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

public T QueryMetaNode<T>(int metaEntityID) where T:NodeWithID
{
return QueryNode<T>(metaEntityID);
}
count = castedEntityViews.Count;

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

public FasterReadOnlyList<T> QueryMetaNodes<T>()
public ReadOnlyDictionary<int, T> QueryIndexableEntityViews<T>() where T:IEntityView
{
var type = typeof(T);

ITypeSafeList nodes;
ITypeSafeDictionary entityViews;

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

return new FasterReadOnlyList<T>((FasterList<T>)nodes);
return new ReadOnlyDictionary<int, T>(entityViews as Dictionary<int, T>);
}

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

T internalNode;
T internalEntityView;

ITypeSafeDictionary nodes;
ITypeSafeDictionary entityViews;
TypeSafeDictionary<T> casted;

_nodesDBdic.TryGetValue(type, out nodes);
casted = nodes as TypeSafeDictionary<T>;
_entityViewsDBdic.TryGetValue(type, out entityViews);
casted = entityViews as TypeSafeDictionary<T>;

if (casted != null &&
casted.TryGetValue(ID, out internalNode))
casted.TryGetValue(ID, out internalEntityView))
{
node = (T) internalNode;
entityView = (T)internalEntityView;

return true;
}
node = default(T);
entityView = default(T);

return false;
}

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

T internalNode; ITypeSafeDictionary nodes;
T internalEntityView; ITypeSafeDictionary entityViews;
TypeSafeDictionary<T> casted;

_nodesDBdic.TryGetValue(type, out nodes);
casted = nodes as TypeSafeDictionary<T>;
_entityViewsDBdic.TryGetValue(type, out entityViews);
casted = entityViews as TypeSafeDictionary<T>;

if (casted != null &&
casted.TryGetValue(ID, out internalNode))
return (T)internalNode;
casted.TryGetValue(ID, out internalEntityView))
return (T)internalEntityView;

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

static FasterReadOnlyList<T> RetrieveEmptyNodeList<T>()
public T QueryMetaEntityView<T>(int metaEntityID) where T:EntityView<T>, new()
{
return QueryEntityView<T>(metaEntityID);
}

public bool TryQueryMetaEntityView<T>(int metaEntityID, out T entityView) where T:EntityView<T>, new()
{
return TryQueryEntityView(metaEntityID, out entityView);
}

public FasterReadOnlyList<T> QueryMetaEntityViews<T>() where T:EntityView<T>, new()
{
var type = typeof(T);

ITypeSafeList entityViews;

if (_metaEntityViewsDB.TryGetValue(type, out entityViews) == false)
return RetrieveEmptyEntityViewList<T>();

return new FasterReadOnlyList<T>((FasterList<T>)entityViews);
}

static FasterReadOnlyList<T> RetrieveEmptyEntityViewList<T>()
{
return FasterReadOnlyList<T>.DefaultList;
}

readonly Dictionary<Type, ITypeSafeList> _nodesDB;
readonly Dictionary<Type, ITypeSafeDictionary> _nodesDBdic;
readonly Dictionary<Type, ITypeSafeList> _metaNodesDB;
readonly Dictionary<int, Dictionary<Type, ITypeSafeList>> _groupNodesDB;
static T[] RetrieveEmptyEntityViewArray<T>()
{
return FasterList<T>.DefaultList.ToArrayFast();
}

readonly Dictionary<Type, ITypeSafeList> _entityViewsDB;
readonly Dictionary<Type, ITypeSafeDictionary> _entityViewsDBdic;
readonly Dictionary<Type, ITypeSafeList> _metaEntityViewsDB;
readonly Dictionary<int, Dictionary<Type, ITypeSafeList>> _groupEntityViewsDB;
}
}

+ 404
- 339
ECS/EnginesRoot.cs View File

@@ -1,167 +1,200 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using Svelto.ECS.Legacy;
using Svelto.ECS.NodeSchedulers;
using Svelto.ECS.Profiler;
using Svelto.ECS.Schedulers;
using Svelto.Utilities;
using Svelto.WeakEvents;

#if EXPERIMENTAL
using Svelto.ECS.Experimental;
using Svelto.ECS.Experimental.Internal;
#endif

#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
using Svelto.ECS.Profiler;
#endif

namespace Svelto.ECS.Internal
{
struct BuildNodeCallbackStruct
{
public Action<FasterList<INodeBuilder>, int> internalRemove;
public Action<FasterList<INodeBuilder>, int> internalEnable;
public Action<FasterList<INodeBuilder>, int> internalDisable;
}
}

namespace Svelto.ECS
{
public sealed class EnginesRoot : IEnginesRoot, IEntityFactory
public sealed class EnginesRoot : IEntityFunctions, IEntityFactory, IDisposable
{
public EnginesRoot(NodeSubmissionScheduler nodeScheduler)
public EnginesRoot(EntityViewSubmissionScheduler entityViewScheduler)
{
_nodeEngines = new Dictionary<Type, FasterList<IEngine>>();
_activableEngines = new Dictionary<Type, FasterList<IEngine>>();
_entityViewEngines = new Dictionary<Type, FasterList<IHandleEntityViewEngine>>();
_otherEngines = new FasterList<IEngine>();

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

_nodesDB = new Dictionary<Type, ITypeSafeList>();
_metaNodesDB = new Dictionary<Type, ITypeSafeList>();
_groupNodesDB = new Dictionary<int, Dictionary<Type, ITypeSafeList>>();
_nodesDBdic = new Dictionary<Type, ITypeSafeDictionary>();
_sharedStructNodeLists = new SharedStructNodeLists();
_sharedGroupedStructNodeLists = new SharedGroupedStructNodesLists();

_nodesToAdd = new DoubleBufferedNodes<Dictionary<Type, ITypeSafeList>>();
_metaNodesToAdd = new DoubleBufferedNodes<Dictionary<Type, ITypeSafeList>>();
_groupedNodesToAdd = new DoubleBufferedNodes<Dictionary<int, Dictionary<Type, ITypeSafeList>>>();
_callBackStructForBuiltGroupedNodes = new BuildNodeCallbackStruct();

_callBackStructForBuiltGroupedNodes.internalRemove = InternalRemove;
_callBackStructForBuiltGroupedNodes.internalDisable = InternalDisable;
_callBackStructForBuiltGroupedNodes.internalEnable = InternalEnable;
_entityViewsDB = new Dictionary<Type, ITypeSafeList>();
_metaEntityViewsDB = new Dictionary<Type, ITypeSafeList>();
_groupEntityViewsDB = new Dictionary<int, Dictionary<Type, ITypeSafeList>>();
_entityViewsDBdic = new Dictionary<Type, ITypeSafeDictionary>();
_callBackStructForBuiltNodes = new BuildNodeCallbackStruct();

_callBackStructForBuiltNodes.internalRemove = InternalGroupedRemove;
_callBackStructForBuiltNodes.internalDisable = InternalDisable;
_callBackStructForBuiltNodes.internalEnable = InternalEnable;
_callBackStructForBuiltMetaNodes = new BuildNodeCallbackStruct();

_callBackStructForBuiltMetaNodes.internalRemove = InternalMetaRemove;
_callBackStructForBuiltMetaNodes.internalDisable = InternalDisable;
_callBackStructForBuiltMetaNodes.internalEnable = InternalEnable;

_scheduler = nodeScheduler;
_scheduler.Schedule(SubmitNodes);

_structNodeEngineType = typeof(IStructNodeEngine<>);
_groupedStructNodesEngineType = typeof(IGroupedStructNodesEngine<>);
_activableNodeEngineType = typeof(IActivableNodeEngine<>);
_entityViewsToAdd = new DoubleBufferedEntityViews<Dictionary<Type, ITypeSafeList>>();
_metaEntityViewsToAdd = new DoubleBufferedEntityViews<Dictionary<Type, ITypeSafeList>>();
_groupedEntityViewsToAdd = new DoubleBufferedEntityViews<Dictionary<int, Dictionary<Type, ITypeSafeList>>>();
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
_addEntityViewToEngine = AddEntityViewToEngine;
_removeEntityViewFromEngine = RemoveEntityViewFromEngine;
#endif
_engineEntityViewDB = new EngineEntityViewDB(_entityViewsDB, _entityViewsDBdic, _metaEntityViewsDB, _groupEntityViewsDB);

_scheduler = entityViewScheduler;
_scheduler.Schedule(new WeakAction(SubmitEntityViews));
#if EXPERIMENTAL
_sharedStructEntityViewLists = new SharedStructEntityViewLists();
_sharedGroupedStructEntityViewLists = new SharedGroupedStructEntityViewsLists();

_structEntityViewEngineType = typeof(IStructEntityViewEngine<>);
_groupedStructEntityViewsEngineType = typeof(IGroupedStructEntityViewsEngine<>);
_implementedInterfaceTypes = new Dictionary<Type, Type[]>();
#if UNITY_EDITOR
#endif
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
UnityEngine.GameObject debugEngineObject = new UnityEngine.GameObject("Engine Debugger");
debugEngineObject.gameObject.AddComponent<EngineProfilerBehaviour>();
#endif
}

public void BuildEntity(int ID, EntityDescriptor ed)
public IEntityFactory GenerateEntityFactory()
{
return new GenericEntityFactory(new DataStructures.WeakReference<EnginesRoot>(this));
}

public IEntityFunctions GenerateEntityFunctions()
{
ed.BuildNodes(ID, _nodesToAdd.other, ref _callBackStructForBuiltNodes);
return new GenericEntityFunctions(new DataStructures.WeakReference<EnginesRoot>(this));
}
public void BuildEntity<T>(int entityID, object[] implementors = null) where T:IEntityDescriptor, new()
{
EntityFactory.BuildEntityViews
(entityID, _entityViewsToAdd.current, EntityDescriptorTemplate<T>.Default, implementors);
}

public void BuildEntity(int entityID, EntityDescriptorInfo entityDescriptor, object[] implementors = null)
{
EntityFactory.BuildEntityViews
(entityID, _entityViewsToAdd.current, entityDescriptor, implementors);
}

/// <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
/// by size and type and then use the meta entity entityView 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
/// Since the entities are managed through the shared entityView, the same
/// shared entityView must be found on the single entities of the same type and size.
/// The shared entityView of the meta entity is then used by engines that are meant
/// to manage a group of entities through a single entityView.
/// The same engine can manage several meta entities entityViews too.
/// The Engine manages the logic of the Meta EntityView data and other engines
/// can read back this data through the normal entity as the shared entityView
/// 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
/// It's a way to control a group of Entities through a entityView only.
/// This set of entities can share exactly the same entityView 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
/// on a group of entities, instead to inject N entityViews and iterate over
/// them to set the same value, you can inject just one entityView, 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)
/// <param name="implementors"></param>
public void BuildMetaEntity<T>(int metaEntityID, object[] implementors) where T:IEntityDescriptor, new()
{
ed.BuildNodes(metaEntityID, _metaNodesToAdd.other, ref _callBackStructForBuiltMetaNodes);
EntityFactory.BuildEntityViews(metaEntityID, _entityViewsToAdd.current,
EntityDescriptorTemplate<T>.Default, implementors);
}

/// <summary>
/// Using this function is like building a normal entity, but the nodes
/// Using this function is like building a normal entity, but the entityViews
/// 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.
/// improve cache locality. Only IGroupStructEntityViewWithID entityViews are grouped
/// other entityViews 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)
/// <param name="implementors"></param>
public void BuildEntityInGroup<T>(int entityID, int groupID, object[] implementors = null) where T:IEntityDescriptor, new()
{
ed.BuildGroupedNodes(entityID, groupID, _groupedNodesToAdd.other, ref _callBackStructForBuiltGroupedNodes);
EntityFactory.BuildGroupedEntityViews(entityID, groupID,
_groupedEntityViewsToAdd.current,
EntityDescriptorTemplate<T>.Default,
implementors);
}

public void AddEngine(IEngine engine)
public void BuildEntityInGroup(int entityID, int groupID, EntityDescriptorInfo entityDescriptor, object[] implementors = null)
{
#if UNITY_EDITOR
EngineProfiler.AddEngine(engine);
#endif
var queryableNodeEngine = engine as IQueryableNodeEngine;
if (queryableNodeEngine != null)
queryableNodeEngine.nodesDB =
new EngineNodeDB(_nodesDB, _nodesDBdic, _metaNodesDB, _groupNodesDB);
EntityFactory.BuildGroupedEntityViews(entityID, groupID,
_groupedEntityViewsToAdd.current,
entityDescriptor, implementors);
}

var engineType = engine.GetType();
var implementedInterfaces = engineType.GetInterfaces();
public void RemoveEntity(int entityID, IRemoveEntityComponent removeInfo)
{
var removeEntityImplementor = removeInfo as RemoveEntityImplementor;

CollectImplementedInterfaces(implementedInterfaces);
if (removeEntityImplementor.removeEntityInfo.isInAGroup)
InternalRemove(removeEntityImplementor.removeEntityInfo.descriptor.entityViewsToBuild, entityID, removeEntityImplementor.removeEntityInfo.groupID, _entityViewsDB);
else
InternalRemove(removeEntityImplementor.removeEntityInfo.descriptor.entityViewsToBuild, entityID, _entityViewsDB);
}

var engineAdded = CheckGenericEngines(engine);
public void RemoveEntity<T>(int entityID) where T : IEntityDescriptor, new()
{
InternalRemove(EntityDescriptorTemplate<T>.Default.descriptor.entityViewsToBuild, entityID, _entityViewsDB);
}

if (CheckLegacyNodesEngine(engine, ref engineAdded) == false)
CheckNodesEngine(engine, engineType, ref engineAdded);
public void RemoveMetaEntity<T>(int metaEntityID) where T : IEntityDescriptor, new()
{
InternalRemove(EntityDescriptorTemplate<T>.Default.descriptor.entityViewsToBuild, metaEntityID, _metaEntityViewsDB);
}

if (engineAdded == false)
public void RemoveEntityFromGroup<T>(int entityID, int groupID) where T : IEntityDescriptor, new()
{
InternalRemove(EntityDescriptorTemplate<T>.Default.descriptor.entityViewsToBuild, entityID, _groupEntityViewsDB[groupID]);
}
public void AddEngine(IEngine engine)
{
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
Profiler.EngineProfiler.AddEngine(engine);
#endif
var engineType = engine.GetType();
#if EXPERIMENTAL
bool engineAdded;
var implementedInterfaces = engineType.GetInterfaces();
CollectImplementedInterfaces(implementedInterfaces);
engineAdded = CheckSpecialEngine(engine);
#endif
var viewEngine = engine as IHandleEntityViewEngine;
if (viewEngine != null)
CheckEntityViewsEngine(viewEngine, engineType);
else
_otherEngines.Add(engine);

var callBackOnAddEngine = engine as ICallBackOnAddEngine;
if (callBackOnAddEngine != null)
callBackOnAddEngine.Ready();
var queryableEntityViewEngine = engine as IQueryingEntityViewEngine;
if (queryableEntityViewEngine != null)
{
queryableEntityViewEngine.entityViewsDB = _engineEntityViewDB;
queryableEntityViewEngine.Ready();
}
}

void CollectImplementedInterfaces(Type[] implementedInterfaces)
#if EXPERIMENTAL
void CollectImplementedInterfaces(Type[] implementedInterfaces)
{
_implementedInterfaceTypes.Clear();

var type = typeof(IEngine);
var type = typeof(IHandleEntityViewEngine);

for (int index = 0; index < implementedInterfaces.Length; index++)
{
@@ -180,81 +213,60 @@ namespace Svelto.ECS
_implementedInterfaceTypes.Add(genericTypeDefinition, interfaceType.GetGenericArguments());
}
}
bool CheckGenericEngines(IEngine engine)
bool CheckSpecialEngine(IEngine engine)
{
if (_implementedInterfaceTypes.Count == 0) return false;

bool engineAdded = false;

if (_implementedInterfaceTypes.ContainsKey(_structNodeEngineType))
{
((IStructNodeEngine)engine).CreateStructNodes
(_sharedStructNodeLists);
}

if (_implementedInterfaceTypes.ContainsKey(_groupedStructNodesEngineType))
if (_implementedInterfaceTypes.ContainsKey(_structEntityViewEngineType))
{
((IGroupedStructNodesEngine)engine).CreateStructNodes
(_sharedGroupedStructNodeLists);
((IStructEntityViewEngine)engine).CreateStructEntityViews
(_sharedStructEntityViewLists);
}

Type[] arguments;
if (_implementedInterfaceTypes.TryGetValue(_activableNodeEngineType,
out arguments))
if (_implementedInterfaceTypes.ContainsKey(_groupedStructEntityViewsEngineType))
{
AddEngine(engine, arguments, _activableEngines);

engineAdded = true;
((IGroupedStructEntityViewsEngine)engine).CreateStructEntityViews
(_sharedGroupedStructEntityViewLists);
}

return engineAdded;
}

bool CheckLegacyNodesEngine(IEngine engine, ref bool engineAdded)
{
var nodesEngine = engine as INodesEngine;
if (nodesEngine != null)
{
AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines);

engineAdded = true;

return true;
}

return false;
}

bool CheckNodesEngine(IEngine engine, Type engineType, ref bool engineAdded)
#endif
void CheckEntityViewsEngine(IEngine engine, Type engineType)
{
var baseType = engineType.GetBaseType();
if (baseType.IsGenericTypeEx()
&& engine is INodeEngine)
{
AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines);

engineAdded = true;
if (baseType.IsGenericTypeEx())
{
var genericArguments = baseType.GetGenericArguments();
AddEngine(engine as IHandleEntityViewEngine, genericArguments, _entityViewEngines);
#if EXPERIMENTAL
var activableEngine = engine as IHandleActivableEntityEngine;
if (activableEngine != null)
AddEngine(activableEngine, genericArguments, _activableViewEntitiesEngines);
#endif

return true;
return;
}

return false;
throw new Exception("Not Supported Engine");
}

static void AddEngine(IEngine engine, Type[] types,
Dictionary<Type, FasterList<IEngine>> engines)
static void AddEngine<T>(T engine, Type[] types,
Dictionary<Type, FasterList<T>> engines)
{
for (int i = 0; i < types.Length; i++)
{
FasterList<IEngine> list;
FasterList<T> list;

var type = types[i];

if (engines.TryGetValue(type, out list) == false)
{
list = new FasterList<IEngine>();
list = new FasterList<T>();

engines.Add(type, list);
}
@@ -263,309 +275,362 @@ namespace Svelto.ECS
}
}

static void AddNodesToTheDBAndSuitableEngines(Dictionary<Type, ITypeSafeList> nodesToAdd,
Dictionary<Type, FasterList<IEngine>> nodeEngines,
Dictionary<Type, ITypeSafeDictionary> nodesDBdic,
Dictionary<Type, ITypeSafeList> nodesDB)
void AddEntityViewsToTheDBAndSuitableEngines(Dictionary<Type, ITypeSafeList> entityViewsToAdd,
Dictionary<Type, ITypeSafeList> entityViewsDB)
{
foreach (var nodeList in nodesToAdd)
foreach (var entityViewList in entityViewsToAdd)
{
AddNodeToDB(nodesDB, nodeList);
AddEntityViewToDB(entityViewsDB, entityViewList);

if (entityViewList.Value.isQueryiableEntityView)
{
AddEntityViewToEntityViewsDictionary(_entityViewsDBdic, entityViewList.Value, entityViewList.Key);
}
}

if (nodeList.Value.isQueryiableNode)
foreach (var entityViewList in entityViewsToAdd)
{
if (entityViewList.Value.isQueryiableEntityView)
{
AddNodeToNodesDictionary(nodesDBdic, nodeList.Value, nodeList.Key);
foreach (var node in nodeList.Value)
AddNodeToTheSuitableEngines(nodeEngines, node as NodeWithID, nodeList.Key);
AddEntityViewToTheSuitableEngines(_entityViewEngines, entityViewList.Value,
entityViewList.Key);
}
}
}

static void AddGroupNodesToTheDBAndSuitableEngines(Dictionary<int, Dictionary<Type, ITypeSafeList>> groupedNodesToAdd,
Dictionary<Type, FasterList<IEngine>> nodeEngines,
Dictionary<Type, ITypeSafeDictionary> nodesDBdic,
Dictionary<int, Dictionary<Type, ITypeSafeList>> groupNodesDB,
Dictionary<Type, ITypeSafeList> nodesDB)
void AddGroupEntityViewsToTheDBAndSuitableEngines(Dictionary<int, Dictionary<Type, ITypeSafeList>> groupedEntityViewsToAdd,
Dictionary<int, Dictionary<Type, ITypeSafeList>> groupEntityViewsDB,
Dictionary<Type, ITypeSafeList> entityViewsDB)
{
foreach (var group in groupedNodesToAdd)
foreach (var group in groupedEntityViewsToAdd)
{
AddNodesToTheDBAndSuitableEngines(group.Value, nodeEngines, nodesDBdic, nodesDB);
AddEntityViewsToTheDBAndSuitableEngines(group.Value, entityViewsDB);

AddNodesToGroupDB(groupNodesDB, @group);
AddEntityViewsToGroupDB(groupEntityViewsDB, @group);
}
}

static void AddNodesToGroupDB(Dictionary<int, Dictionary<Type, ITypeSafeList>> groupNodesDB,
static void AddEntityViewsToGroupDB(Dictionary<int, Dictionary<Type, ITypeSafeList>> groupEntityViewsDB,
KeyValuePair<int, Dictionary<Type, ITypeSafeList>> @group)
{
Dictionary<Type, ITypeSafeList> groupedNodesByType;
Dictionary<Type, ITypeSafeList> groupedEntityViewsByType;

if (groupNodesDB.TryGetValue(@group.Key, out groupedNodesByType) == false)
groupedNodesByType = groupNodesDB[@group.Key] = new Dictionary<Type, ITypeSafeList>();
if (groupEntityViewsDB.TryGetValue(@group.Key, out groupedEntityViewsByType) == false)
groupedEntityViewsByType = groupEntityViewsDB[@group.Key] = new Dictionary<Type, ITypeSafeList>();

foreach (var node in @group.Value)
foreach (var entityView in @group.Value)
{
groupedNodesByType.Add(node.Key, node.Value);
groupedEntityViewsByType.Add(entityView.Key, entityView.Value);
}
}

static void AddNodeToDB(Dictionary<Type, ITypeSafeList> nodesDB, KeyValuePair<Type, ITypeSafeList> nodeList)
static void AddEntityViewToDB(Dictionary<Type, ITypeSafeList> entityViewsDB, KeyValuePair<Type, ITypeSafeList> entityViewList)
{
ITypeSafeList dbList;

if (nodesDB.TryGetValue(nodeList.Key, out dbList) == false)
dbList = nodesDB[nodeList.Key] = nodeList.Value.Create();
if (entityViewsDB.TryGetValue(entityViewList.Key, out dbList) == false)
dbList = entityViewsDB[entityViewList.Key] = entityViewList.Value.Create();

dbList.AddRange(nodeList.Value);
dbList.AddRange(entityViewList.Value);
}
static void AddNodeToNodesDictionary(Dictionary<Type, ITypeSafeDictionary> nodesDBdic,
ITypeSafeList nodes, Type nodeType)
static void AddEntityViewToEntityViewsDictionary(Dictionary<Type, ITypeSafeDictionary> entityViewsDBdic,
ITypeSafeList entityViews, Type entityViewType)
{
ITypeSafeDictionary nodesDic;
ITypeSafeDictionary entityViewsDic;
if (nodesDBdic.TryGetValue(nodeType, out nodesDic) == false)
nodesDic = nodesDBdic[nodeType] = nodes.CreateIndexedDictionary();
if (entityViewsDBdic.TryGetValue(entityViewType, out entityViewsDic) == false)
entityViewsDic = entityViewsDBdic[entityViewType] = entityViews.CreateIndexedDictionary();

nodesDic.FillWithIndexedNodes(nodes);
entityViewsDic.FillWithIndexedEntityViews(entityViews);
}

static void AddNodeToTheSuitableEngines(Dictionary<Type, FasterList<IEngine>> nodeEngines, NodeWithID node, Type nodeType)
static void AddEntityViewToTheSuitableEngines(Dictionary<Type, FasterList<IHandleEntityViewEngine>> entityViewEngines, ITypeSafeList entityViewsList, Type entityViewType)
{
FasterList<IEngine> enginesForNode;
FasterList<IHandleEntityViewEngine> enginesForEntityView;

if (nodeEngines.TryGetValue(nodeType, out enginesForNode))
if (entityViewEngines.TryGetValue(entityViewType, out enginesForEntityView))
{
for (int j = 0; j < enginesForNode.Count; j++)
int viewsCount;

var entityViews = entityViewsList.ToArrayFast(out viewsCount);

for (int i = 0; i < viewsCount; i++)
{
int count;
var fastList = FasterList<IHandleEntityViewEngine>.NoVirt.ToArrayFast(enginesForEntityView, out count);
IEntityView entityView = entityViews[i];
for (int j = 0; j < count; j++)
{
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, (enginesForNode[j] as INodeEngine), node);
EngineProfiler.MonitorAddDuration(_addEntityViewToEngine, fastList[j], entityView);
#else
(enginesForNode[j] as INodeEngine).Add(node);
fastList[j].Add(entityView);
#endif
}
}
}
}
/*
void DisableNodeFromEngines(INode node, Type nodeType)
{
ITypeSafeList enginesForNode;

if (_activableEngines.TryGetValue(nodeType, out enginesForNode))
void InternalRemove(IEntityViewBuilder[] entityViewBuilders, int entityID,
Dictionary<Type, ITypeSafeList> entityViewsDB)
{
int entityViewBuildersCount = entityViewBuilders.Length;
for (int i = 0; i < entityViewBuildersCount; i++)
{
for (int j = 0; j < enginesForNode.Count; j++)
{
(enginesForNode[j] as IActivableNodeEngine).Disable(node);
}
}
}
Type entityViewType = entityViewBuilders[i].GetEntityViewType();

void EnableNodeFromEngines(INode node, Type nodeType)
{
ITypeSafeList enginesForNode;
ITypeSafeList entityViews = entityViewsDB[entityViewType];
if (entityViews.UnorderedRemove(entityID) == false)
entityViewsDB.Remove(entityViewType);

if (_activableEngines.TryGetValue(nodeType, out enginesForNode))
{
for (int j = 0; j < enginesForNode.Count; j++)
if (entityViews.isQueryiableEntityView)
{
(enginesForNode[j] as IActivableNodeEngine).Enable(node);
var typeSafeDictionary = _entityViewsDBdic[entityViewType];
var entityView = typeSafeDictionary.GetIndexedEntityView(entityID);

if (typeSafeDictionary.Remove(entityID) == false)
_entityViewsDBdic.Remove(entityViewType);

RemoveEntityViewFromEngines(_entityViewEngines, entityView, entityViewType);
}
}
}*/
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
void AddNodeToEngine(IEngine engine, INode node)
{
(engine as INodeEngine).Add(node);
}

void RemoveNodeFromEngine(IEngine engine, INode node)
{
(engine as INodeEngine).Remove(node);
}
#endif

void InternalDisable(FasterList<INodeBuilder> nodeBuilders, int entityID)
void InternalRemove(IEntityViewBuilder[] entityViewBuilders, int entityID, int groupID,
Dictionary<Type, ITypeSafeList> entityViewsDB)
{
/* if (_engineRootWeakReference.IsValid == false)
return;
int entityViewBuildersCount = entityViewBuilders.Length;

for (int i = 0; i < nodes.Count; i++)
for (int i = 0; i < entityViewBuildersCount; i++)
{
var node = nodes[i];
Type nodeType = node.GetType();
DisableNodeFromEngines(node, nodeType);
}*/
}
Type entityViewType = entityViewBuilders[i].GetEntityViewType();
Dictionary<Type, ITypeSafeList> dictionary = _groupEntityViewsDB[groupID];

void InternalEnable(FasterList<INodeBuilder> nodeBuilders, int entityID)
{/*
if (_engineRootWeakReference.IsValid == false)
return;
if (dictionary[entityViewType].UnorderedRemove(entityID) == false)
dictionary.Remove(entityViewType);

for (int i = 0; i < nodes.Count; i++)
{
var node = nodes[i];
Type nodeType = node.GetType();
EnableNodeFromEngines(node, nodeType);
}*/
if (dictionary.Count == 0) _groupEntityViewsDB.Remove(groupID);
}

InternalRemove(entityViewBuilders, entityID, entityViewsDB);
}

void InternalRemove(FasterList<INodeBuilder> nodeBuilders, int entityID)
static void RemoveEntityViewFromEngines(Dictionary<Type, FasterList<IHandleEntityViewEngine>> entityViewEngines,
IEntityView entityView, Type entityViewType)
{
if (_engineRootWeakReference.IsValid == false)
return;
FasterList<IHandleEntityViewEngine> enginesForEntityView;

int nodeBuildersCount = nodeBuilders.Count;
for (int i = 0; i < nodeBuildersCount; i++)
if (entityViewEngines.TryGetValue(entityViewType, out enginesForEntityView))
{
Type nodeType = nodeBuilders[i].GetType();

ITypeSafeList nodes;
if (_nodesDB.TryGetValue(nodeType, out nodes) == true)
nodes.UnorderedRemove(entityID);

if (nodes.isQueryiableNode)
int count;
var fastList = FasterList<IHandleEntityViewEngine>.NoVirt.ToArrayFast(enginesForEntityView, out count);
for (int j = 0; j < count; j++)
{
var node = _nodesDBdic[nodeType].GetIndexedNode(entityID);
_nodesDBdic[nodeType].Remove(entityID);
RemoveNodeFromEngines(_nodeEngines, node, nodeType);
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
EngineProfiler.MonitorRemoveDuration(_removeEntityViewFromEngine, fastList[j], entityView);
#else
fastList[j].Remove(entityView);
#endif
}
}
}
void InternalGroupedRemove(FasterList<INodeBuilder> nodeBuilders, int entityID)

#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
static void AddEntityViewToEngine(IHandleEntityViewEngine engine, IEntityView entityView)
{
engine.Add(entityView);
}

void InternalMetaRemove(FasterList<INodeBuilder> nodeBuilders, int entityID)
static void RemoveEntityViewFromEngine(IHandleEntityViewEngine engine, IEntityView entityView)
{
engine.Remove(entityView);
}
static void RemoveNodeFromEngines(Dictionary<Type, FasterList<IEngine>> nodeEngines, NodeWithID node, Type nodeType)
{
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 SubmitNodes()
void SubmitEntityViews()
{
_nodesToAdd.Swap();
_metaNodesToAdd.Swap();
_groupedNodesToAdd.Swap();
bool newNodesHaveBeenAddedWhileIterating =
_metaNodesToAdd.Count > 0
|| _nodesToAdd.Count > 0
|| _groupedNodesToAdd.Count > 0;
bool newEntityViewsHaveBeenAddedWhileIterating =
_metaEntityViewsToAdd.current.Count > 0
|| _entityViewsToAdd.current.Count > 0
|| _groupedEntityViewsToAdd.current.Count > 0;

int numberOfReenteringLoops = 0;

while (newNodesHaveBeenAddedWhileIterating)
while (newEntityViewsHaveBeenAddedWhileIterating)
{
if ( _nodesToAdd.Count > 0)
AddNodesToTheDBAndSuitableEngines(_nodesToAdd.current, _nodeEngines, _nodesDBdic, _nodesDB);
//use other as source from now on
//current will be use to write new entityViews
_entityViewsToAdd.Swap();
_metaEntityViewsToAdd.Swap();
_groupedEntityViewsToAdd.Swap();

if ( _entityViewsToAdd.other.Count > 0)
AddEntityViewsToTheDBAndSuitableEngines(_entityViewsToAdd.other, _entityViewsDB);
if ( _metaNodesToAdd.Count > 0)
AddNodesToTheDBAndSuitableEngines(_metaNodesToAdd.current, _nodeEngines, _nodesDBdic, _metaNodesDB);
if ( _metaEntityViewsToAdd.other.Count > 0)
AddEntityViewsToTheDBAndSuitableEngines(_metaEntityViewsToAdd.other, _metaEntityViewsDB);
if (_groupedNodesToAdd.Count > 0)
AddGroupNodesToTheDBAndSuitableEngines(_groupedNodesToAdd.current, _nodeEngines, _nodesDBdic, _groupNodesDB, _nodesDB);
if (_groupedEntityViewsToAdd.other.Count > 0)
AddGroupEntityViewsToTheDBAndSuitableEngines(_groupedEntityViewsToAdd.other, _groupEntityViewsDB, _entityViewsDB);
_nodesToAdd.Clear();
_metaNodesToAdd.Clear();
_groupedNodesToAdd.Clear();
//other can be cleared now
_entityViewsToAdd.other.Clear();
_metaEntityViewsToAdd.other.Clear();
_groupedEntityViewsToAdd.other.Clear();

_nodesToAdd.Swap();
_metaNodesToAdd.Swap();
_groupedNodesToAdd.Swap();
newNodesHaveBeenAddedWhileIterating =
_metaNodesToAdd.Count > 0
|| _nodesToAdd.Count > 0
|| _groupedNodesToAdd.Count > 0;
//has current new entityViews?
newEntityViewsHaveBeenAddedWhileIterating =
_metaEntityViewsToAdd.current.Count > 0
|| _entityViewsToAdd.current.Count > 0
|| _groupedEntityViewsToAdd.current.Count > 0;

if (numberOfReenteringLoops > 5)
throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method");
throw new Exception("possible infinite loop found creating Entities inside IEntityViewsEngine Add method, please consider building entities outside IEntityViewsEngine Add method");

numberOfReenteringLoops++;
}
}

readonly Dictionary<Type, FasterList<IEngine>> _nodeEngines;
readonly Dictionary<Type, FasterList<IEngine>> _activableEngines;
readonly Dictionary<Type, FasterList<IHandleEntityViewEngine>> _entityViewEngines;

readonly FasterList<IEngine> _otherEngines;

readonly Dictionary<Type, ITypeSafeList> _nodesDB;
readonly Dictionary<Type, ITypeSafeList> _metaNodesDB;
readonly Dictionary<int, Dictionary<Type, ITypeSafeList>> _groupNodesDB;
readonly Dictionary<Type, ITypeSafeList> _entityViewsDB;
readonly Dictionary<Type, ITypeSafeList> _metaEntityViewsDB;
readonly Dictionary<int, Dictionary<Type, ITypeSafeList>> _groupEntityViewsDB;
readonly Dictionary<Type, ITypeSafeDictionary> _nodesDBdic;
readonly Dictionary<Type, ITypeSafeDictionary> _entityViewsDBdic;

readonly DoubleBufferedNodes<Dictionary<Type, ITypeSafeList>> _nodesToAdd;
readonly DoubleBufferedNodes<Dictionary<Type, ITypeSafeList>> _metaNodesToAdd;
readonly DoubleBufferedNodes<Dictionary<int, Dictionary<Type, ITypeSafeList>>> _groupedNodesToAdd;
readonly DoubleBufferedEntityViews<Dictionary<Type, ITypeSafeList>> _entityViewsToAdd;
readonly DoubleBufferedEntityViews<Dictionary<Type, ITypeSafeList>> _metaEntityViewsToAdd;
readonly DoubleBufferedEntityViews<Dictionary<int, Dictionary<Type, ITypeSafeList>>> _groupedEntityViewsToAdd;
readonly NodeSubmissionScheduler _scheduler;

readonly Type _structNodeEngineType;
readonly Type _groupedStructNodesEngineType;
readonly Type _activableNodeEngineType;
readonly EntityViewSubmissionScheduler _scheduler;
#if EXPERIMENTAL
readonly Type _structEntityViewEngineType;
readonly Type _groupedStructEntityViewsEngineType;
readonly SharedStructNodeLists _sharedStructNodeLists;
readonly SharedGroupedStructNodesLists _sharedGroupedStructNodeLists;

readonly Dictionary<Type, Type[]> _implementedInterfaceTypes;
readonly DataStructures.WeakReference<EnginesRoot> _engineRootWeakReference;
BuildNodeCallbackStruct _callBackStructForBuiltNodes;
BuildNodeCallbackStruct _callBackStructForBuiltGroupedNodes;
BuildNodeCallbackStruct _callBackStructForBuiltMetaNodes;
readonly SharedStructEntityViewLists _sharedStructEntityViewLists;
readonly SharedGroupedStructEntityViewsLists _sharedGroupedStructEntityViewLists;

readonly Dictionary<Type, Type[]> _implementedInterfaceTypes;
#endif
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
static Action<IHandleEntityViewEngine, IEntityView> _addEntityViewToEngine;
static Action<IHandleEntityViewEngine, IEntityView> _removeEntityViewFromEngine;
#endif
readonly EngineEntityViewDB _engineEntityViewDB;

class DoubleBufferedNodes<T> where T : class, IDictionary, new()
class DoubleBufferedEntityViews<T> where T : class, IDictionary, new()
{
readonly T _nodesToAddBufferA = new T();
readonly T _nodesToAddBufferB = new T();
readonly T _entityViewsToAddBufferA = new T();
readonly T _entityViewsToAddBufferB = new T();

public DoubleBufferedNodes()
public DoubleBufferedEntityViews()
{
this.other = _nodesToAddBufferA;
this.current = _nodesToAddBufferB;
this.other = _entityViewsToAddBufferA;
this.current = _entityViewsToAddBufferB;
}

public T other { get; private set; }
public T current { get; private set; }
public void Swap()
{
var toSwap = other;
other = current;
current = toSwap;
}
}
class GenericEntityFactory : IEntityFactory
{
DataStructures.WeakReference<EnginesRoot> _weakEngine;

public int Count
public GenericEntityFactory(DataStructures.WeakReference<EnginesRoot> weakReference)
{
get { return current.Count; }
_weakEngine = weakReference;
}
public void Clear()
public void BuildEntity<T>(int entityID, object[] implementors = null) where T : IEntityDescriptor, new()
{
current.Clear();
_weakEngine.Target.BuildEntity<T>(entityID, implementors);
}

public void Swap()
public void BuildEntity(int entityID, EntityDescriptorInfo entityDescriptor, object[] implementors = null)
{
var toSwap = other;
other = current;
current = toSwap;
_weakEngine.Target.BuildEntity(entityID, entityDescriptor, implementors);
}

public void BuildMetaEntity<T>(int metaEntityID, object[] implementors = null) where T : IEntityDescriptor, new()
{
_weakEngine.Target.BuildMetaEntity<T>(metaEntityID, implementors);
}

public void BuildEntityInGroup<T>(int entityID, int groupID, object[] implementors = null) where T : IEntityDescriptor, new()
{
_weakEngine.Target.BuildEntityInGroup<T>(entityID, groupID, implementors);
}

public void BuildEntityInGroup(int entityID, int groupID, EntityDescriptorInfo entityDescriptor, object[] implementors = null)
{
_weakEngine.Target.BuildEntityInGroup(entityID, groupID, entityDescriptor, implementors);
}
}
class GenericEntityFunctions : IEntityFunctions
{
public GenericEntityFunctions(DataStructures.WeakReference<EnginesRoot> weakReference)
{
_weakReference = weakReference;
}

public void RemoveEntity(int entityID, IRemoveEntityComponent entityTemplate)
{
_weakReference.Target.RemoveEntity(entityID, entityTemplate);
}

public void RemoveEntity<T>(int entityID) where T : IEntityDescriptor, new()
{
_weakReference.Target.RemoveEntity<T>(entityID);
}

public void RemoveMetaEntity<T>(int metaEntityID) where T : IEntityDescriptor, new()
{
_weakReference.Target.RemoveEntity<T>(metaEntityID);
}

public void RemoveEntityFromGroup<T>(int entityID, int groupID) where T : IEntityDescriptor, new()
{
_weakReference.Target.RemoveEntity<T>(entityID);
}
readonly DataStructures.WeakReference<EnginesRoot> _weakReference;
}

public void Dispose()
{
foreach (var entity in _entityViewsDB)
{
if (entity.Value.isQueryiableEntityView == true)
{
foreach (var entityView in entity.Value)
{
RemoveEntityViewFromEngines(_entityViewEngines, entityView as EntityView, entity.Key);
}
}
}

foreach (var entity in _metaEntityViewsDB)
{
foreach (var entityView in entity.Value)
{
RemoveEntityViewFromEngines(_entityViewEngines, entityView as EntityView, entity.Key);
}
}
}
}

+ 0
- 220
ECS/EntityDescriptor.cs View File

@@ -1,220 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public class EntityDescriptor
{
public void AddImplementors(params object[] componentsImplementor)
{
ProcessImplementors(componentsImplementor);
}
public void AddNodes(params INodeBuilder[] nodesWithID)
{
_nodesToBuild.AddRange(nodesWithID);
}

internal void BuildGroupedNodes
(int entityID, int groupID,
Dictionary<int, Dictionary<Type, ITypeSafeList>> groupNodesByType,
ref BuildNodeCallbackStruct callBackstruct)
{
for (int index = 0; index < _nodesToBuild.Count; index++)
{
var nodeBuilder = _nodesToBuild[index];
var nodeType = nodeBuilder.GetNodeType();
Dictionary<Type, ITypeSafeList> groupedNodesTyped;
if (groupNodesByType.TryGetValue(groupID, out groupedNodesTyped) == false)
{
groupedNodesTyped = new Dictionary<Type, ITypeSafeList>();
groupNodesByType.Add(groupID, groupedNodesTyped);
};
BuildAndFillNode(entityID, groupedNodesTyped, nodeType, nodeBuilder);
}
SetupImplementors(ref callBackstruct, entityID);
}

internal void BuildNodes(int entityID,
Dictionary<Type, ITypeSafeList> nodesByType,
ref BuildNodeCallbackStruct callBackstruct)
{
int count = _nodesToBuild.Count;
for (int index = 0; index < count; index++)
{
var nodeBuilder = _nodesToBuild[index];
var nodeType = nodeBuilder.GetNodeType();
BuildAndFillNode(entityID, nodesByType, nodeType, nodeBuilder);
}
SetupImplementors(ref callBackstruct, entityID);
}
void BuildAndFillNode(int entityID, Dictionary<Type, ITypeSafeList> groupedNodesTyped, Type nodeType, INodeBuilder nodeBuilder)
{
ITypeSafeList nodes;

var nodesPoolWillBeCreated = groupedNodesTyped.TryGetValue(nodeType, out nodes) == false;
var nodeObjectToFill = nodeBuilder.BuildNodeAndAddToList(ref nodes, entityID);

if (nodesPoolWillBeCreated)
groupedNodesTyped.Add(nodeType, nodes);

//the semantic of this code must still be improved
//but only classes can be filled, so I am aware
//it's a NodeWithID
if (nodeObjectToFill != null)
FillNode(nodeObjectToFill as NodeWithID);
}
/// <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)
{
ProcessImplementors(componentsImplementor);
}
void ProcessImplementors(object[] implementors)
{
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));

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(
ref BuildNodeCallbackStruct callBackstruct,
int entityID)
{
var RemoveEntity = callBackstruct.internalRemove;
var DisableEntity = callBackstruct.internalDisable;
var EnableEntity = callBackstruct.internalEnable;
int removingImplementorsCount = _removingImplementors.Count;
if (removingImplementorsCount > 0)
{
Action removeEntityAction = () => RemoveEntity(_nodesToBuild, entityID);
for (int index = 0; index < removingImplementorsCount; index++)
_removingImplementors[index].Target.removeEntity = removeEntityAction;
}

int disablingImplementorsCount = _disablingImplementors.Count;
if (disablingImplementorsCount > 0)
{
Action disableEntityAction = () => DisableEntity(_nodesToBuild, entityID);
for (int index = 0; index < disablingImplementorsCount; index++)
_disablingImplementors[index].Target.disableEntity = disableEntityAction;
}

int enablingImplementorsCount = _enablingImplementors.Count;
if (enablingImplementorsCount > 0)
{
Action enableEntityAction = () => EnableEntity(_nodesToBuild, entityID);
for (int index = 0; index < enablingImplementorsCount; index++)
_enablingImplementors[index].Target.enableEntity = enableEntityAction;
}
}

void FillNode<TNode>(TNode node) where TNode : NodeWithID
{
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;
DataStructures.WeakReference<object> component;
if (_implementorsByType.TryGetValue(fieldType, out component) == false)
{
Exception e =
new Exception(NOT_FOUND_EXCEPTION +
field.FieldType.Name + " - Node: " + node.GetType().Name +
" - EntityDescriptor " + this);

throw e;
}
else
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

}
}
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>>();

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;
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: ";
}
}

+ 221
- 0
ECS/EntityDescriptorTemplate.cs View File

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

namespace Svelto.ECS
{
public interface IEntityDescriptor
{
IEntityViewBuilder[] entityViewsToBuild { get; }
}
static class EntityDescriptorTemplate<TType> where TType : IEntityDescriptor, new()
{
public static readonly EntityDescriptorInfo Default = new EntityDescriptorInfo(new TType());
}

public class EntityDescriptorInfo
{
public readonly IEntityDescriptor descriptor;
public readonly string name;

public EntityDescriptorInfo(IEntityDescriptor entityDescriptor)
{
descriptor = entityDescriptor;
name = descriptor.ToString();
}
}
}

namespace Svelto.ECS.Internal
{
static class EntityFactory
{
internal static void BuildGroupedEntityViews(int entityID, int groupID,
Dictionary<int, Dictionary<Type, ITypeSafeList>> groupEntityViewsByType,
EntityDescriptorInfo entityViewsToBuildDescriptor,
object[] implementors)
{
var entityViewsToBuild = entityViewsToBuildDescriptor.descriptor.entityViewsToBuild;
int count = entityViewsToBuild.Length;

RemoveEntityImplementor removeEntityImplementor = null;

for (int index = 0; index < count; index++)
{
var entityViewBuilder = entityViewsToBuild[index];
var entityViewType = entityViewBuilder.GetEntityViewType();

Dictionary<Type, ITypeSafeList> groupedEntityViewsTyped;

if (groupEntityViewsByType.TryGetValue(groupID, out groupedEntityViewsTyped) == false)
{
groupedEntityViewsTyped = new Dictionary<Type, ITypeSafeList>();
groupEntityViewsByType.Add(groupID, groupedEntityViewsTyped);
}

var entityViewObjectToFill =
BuildEntityView(entityID, groupedEntityViewsTyped, entityViewType, entityViewBuilder);

//the semantic of this code must still be improved
//but only classes can be filled, so I am aware
//it's a EntityViewWithID
if (entityViewObjectToFill != null)
{
if (removeEntityImplementor == null)
removeEntityImplementor = new RemoveEntityImplementor(entityViewsToBuildDescriptor.descriptor, groupID);

FillEntityView(entityViewObjectToFill as EntityView, implementors, removeEntityImplementor,
entityViewsToBuildDescriptor.name);
}
}
}

internal static void BuildEntityViews(int entityID, Dictionary<Type, ITypeSafeList> entityViewsByType,
EntityDescriptorInfo entityViewsToBuildDescriptor,
object[] implementors)
{
var entityViewsToBuild = entityViewsToBuildDescriptor.descriptor.entityViewsToBuild;
int count = entityViewsToBuild.Length;
RemoveEntityImplementor removeEntityImplementor = null;

for (int index = 0; index < count; index++)
{
var entityViewBuilder = entityViewsToBuild[index];
var entityViewType = entityViewBuilder.GetEntityViewType();

var entityViewObjectToFill =
BuildEntityView(entityID, entityViewsByType, entityViewType, entityViewBuilder);

//the semantic of this code must still be improved
//but only classes can be filled, so I am aware
//it's a EntityView
if (entityViewObjectToFill != null)
{
if (removeEntityImplementor == null)
removeEntityImplementor = new RemoveEntityImplementor(entityViewsToBuildDescriptor.descriptor);
FillEntityView(entityViewObjectToFill as EntityView, implementors, removeEntityImplementor,
entityViewsToBuildDescriptor.name);
}
}
}

static IEntityView BuildEntityView(int entityID, Dictionary<Type, ITypeSafeList> groupedEntityViewsTyped,
Type entityViewType, IEntityViewBuilder entityViewBuilderId)
{
ITypeSafeList entityViews;

var entityViewsPoolWillBeCreated =
groupedEntityViewsTyped.TryGetValue(entityViewType, out entityViews) == false;
var entityViewObjectToFill = entityViewBuilderId.BuildEntityViewAndAddToList(ref entityViews, entityID);

if (entityViewsPoolWillBeCreated)
groupedEntityViewsTyped.Add(entityViewType, entityViews);

return entityViewObjectToFill as IEntityView;
}

static void FillEntityView(EntityView entityView, object[] implementors, RemoveEntityImplementor removeEntity,
string entityDescriptorName)
{
for (int index = 0; index < implementors.Length; index++)
{
var implementor = implementors[index];

if (implementor != null)
{
var type = implementor.GetType();

Type[] interfaces;
if (_cachedTypes.TryGetValue(type, out interfaces) == false)
interfaces = _cachedTypes[type] = type.GetInterfaces();

for (int iindex = 0; iindex < interfaces.Length; iindex++)
{
var componentType = interfaces[iindex];
#if DEBUG && !PROFILER
Tuple<object, int> implementorHolder;
if (implementorsByType.TryGetValue(componentType, out implementorHolder) == true)
implementorHolder.item2++;
else
#endif

implementorsByType[componentType] = new Tuple<object, int>(implementor, 0);
}
}
#if DEBUG && !PROFILER
else
Utility.Console.LogError(NULL_IMPLEMENTOR_ERROR.FastConcat(entityView.ToString()));
#endif
}

int count;

//Very efficent way to collect the fields of every EntityViewType
KeyValuePair<Type, Action<EntityView, object>>[] setters =
entityView.EntityViewBlazingFastReflection(out count);

var removeEntityComponentType = typeof(IRemoveEntityComponent);

for (int i = 0; i < count; i++)
{
var keyValuePair = setters[i];
Type fieldType = keyValuePair.Key;
if (fieldType == removeEntityComponentType)
{
keyValuePair.Value(entityView, removeEntity);
}
else
{
Tuple<object, int> component;

if (implementorsByType.TryGetValue(fieldType, out component) == false)
{
Exception e = new Exception(NOT_FOUND_EXCEPTION + " Component Type: " + fieldType.Name + " - EntityView: " +
entityView.GetType().Name + " - EntityDescriptor " + entityDescriptorName);

throw e;
}

if (component.item2 > 1)
Utility.Console.LogError(DUPLICATE_IMPLEMENTOR_ERROR.FastConcat(
"Component Type: ", fieldType.Name, " implementor: ",
component.item1.ToString()) + " - EntityView: " +
entityView.GetType().Name + " - EntityDescriptor " + entityDescriptorName);

keyValuePair.Value(entityView, component.item1);
}

}

implementorsByType.Clear();
}

struct Tuple<T1, T2>
{
public T1 item1;
public T2 item2;

public Tuple(T1 implementor, T2 v)
{
item1 = implementor;
item2 = v;
}
}

//this is used to avoid newing a dictionary every time, but it's used locally only
static readonly Dictionary<Type, Tuple<object, int>> implementorsByType = new Dictionary<Type, Tuple<object, int>>();
static Dictionary<Type, Type[]> _cachedTypes = new Dictionary<Type, Type[]>();

const string DUPLICATE_IMPLEMENTOR_ERROR =
"<color=orange>Svelto.ECS</color> the same component is implemented with more than one implementor. This is considered an error and MUST be fixed.";

const string NULL_IMPLEMENTOR_ERROR =
"<color=orange>Svelto.ECS</color> Null implementor, please be careful about the implementors passed to avoid performance loss";

const string NOT_FOUND_EXCEPTION = "<color=orange>Svelto.ECS</color> Implementor not found for an EntityView.";
}
}


+ 178
- 0
ECS/Experimental/StructNodeCollections.cs View File

@@ -0,0 +1,178 @@
using System;
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.ECS.Experimental.Internal;

namespace Svelto.ECS.Experimental.Internal
{
public interface IStructEntityViewEngine : IEngine
{
void CreateStructEntityViews(SharedStructEntityViewLists sharedStructEntityViewLists);
}

public interface IGroupedStructEntityViewsEngine : IEngine
{
void CreateStructEntityViews(SharedGroupedStructEntityViewsLists sharedStructEntityViewLists);
}
}

namespace Svelto.ECS.Experimental
{
public interface IGroupedEntityView
{
int groupID { get; set; }
}
/// <summary>
/// The engines can receive and store IEntityViews structs
/// Unboxing will happen during the Add, but the
/// data will then be stored and processed as stucts
/// </summary>
public interface IStructEntityViewEngine<T> : IStructEntityViewEngine where T:struct, IEntityStruct
{ }

/// <summary>
/// same as above, but the entityViews are grouped by ID
/// usually the ID is the owner of the entityViews of that
/// group
/// </summary>
public interface IGroupedStructEntityViewsEngine<T> : IGroupedStructEntityViewsEngine where T : struct, IGroupedEntityView
{
void Add(ref T entityView);
void Remove(ref T entityView);
}
public sealed class StructEntityViews<T> where T:struct, IEntityStruct
{
public T[] GetList(out int numberOfItems)
{
numberOfItems = _internalList.Count;
return _internalList.ToArrayFast();
}

public StructEntityViews(SharedStructEntityViewLists container)
{
_internalList = SharedStructEntityViewLists.NoVirt.GetList<T>(container);
}

public void Add(T entityView)
{
T convert = (T)entityView;

_internalList.Add(convert);
}

readonly FasterList<T> _internalList;
}

public struct StructGroupEntityViews<T>
where T : struct, IEntityView
{
public StructGroupEntityViews(SharedGroupedStructEntityViewsLists container)
{
_container = container;
indices = new Dictionary<int, int>();
}

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

var fasterList = (SharedGroupedStructEntityViewsLists.NoVirt.GetList<T>(_container, groupID) as FasterList<T>);
indices[entityView.ID] = fasterList.Count;

fasterList.Add(convert);
}

public void Remove(int groupID, T entityView)
{
var fasterList = (SharedGroupedStructEntityViewsLists.NoVirt.GetList<T>(_container, groupID) as FasterList<T>);
var index = indices[entityView.ID];
indices.Remove(entityView.ID);

if (fasterList.UnorderedRemoveAt(index))
indices[fasterList[index].ID] = index;
}

public T[] GetList(int groupID, out int numberOfItems)
{
var fasterList = (SharedGroupedStructEntityViewsLists.NoVirt.GetList<T>(_container, groupID) as FasterList<T>);
return FasterList<T>.NoVirt.ToArrayFast(fasterList, out numberOfItems);
}

readonly SharedGroupedStructEntityViewsLists _container;
readonly Dictionary<int, int> indices;
}

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

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

list = new FasterList<T>();

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

return (FasterList<T>)list;
}
}

readonly Dictionary<Type, IFasterList> _collection;
}

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

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

if (dic.TryGetValue(groupID, out localList))
return localList;

localList = new FasterList<T>();
dic.Add(groupID, localList);

return localList;
}

internal static Dictionary<int, IFasterList> GetGroup<T>(SharedGroupedStructEntityViewsLists list) where T : struct
{
Dictionary<int, IFasterList> dic;

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

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

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

return dic;
}
}

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

+ 14
- 0
ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs View File

@@ -0,0 +1,14 @@
#if UNITY_5 || UNITY_5_3_OR_NEWER
namespace Svelto.ECS
{
public class GenericEntityDescriptorHolder<T>:
UnityEngine.MonoBehaviour, IEntityDescriptorHolder
where T: class, IEntityDescriptor, new()
{
public EntityDescriptorInfo RetrieveDescriptor()
{
return EntityDescriptorTemplate<T>.Default;
}
}
}
#endif

+ 15
- 11
ECS/Extensions/Unity/UnitySumbmissionNodeScheduler.cs View File

@@ -1,30 +1,30 @@
#if UNITY_5 || UNITY_5_3_OR_NEWER
using System;
using System.Collections;
using Svelto.WeakEvents;
using UnityEngine;

namespace Svelto.ECS.NodeSchedulers
namespace Svelto.ECS.Schedulers
{
//The NodeSubmissionScheduler has been introduced to make
//the node submission logic platform indipendent.
//The EntityViewSubmissionScheduler has been introduced to make
//the entityView 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
//the entityViews immediatly just because convenient for your game
//logic. This is not how it works.
public class UnitySumbmissionNodeScheduler : NodeSubmissionScheduler
public class UnitySumbmissionEntityViewScheduler : EntityViewSubmissionScheduler
{
public UnitySumbmissionNodeScheduler()
public UnitySumbmissionEntityViewScheduler()
{
GameObject go = new GameObject("ECSScheduler");

_scheduler = go.AddComponent<Scheduler>();
}

public override void Schedule(Action submitNodes)
public override void Schedule(WeakAction submitEntityViews)
{
_scheduler.OnTick += submitNodes;
_scheduler.OnTick = submitEntityViews;
}

class Scheduler : MonoBehaviour
@@ -35,11 +35,15 @@ namespace Svelto.ECS.NodeSchedulers
{
yield return _wait;

OnTick();
if (OnTick.IsValid)
OnTick.Invoke();
else
yield break;
}
}

internal Action OnTick;
internal WeakAction OnTick;

WaitForEndOfFrame _wait = new WaitForEndOfFrame();
}



+ 57
- 205
ECS/GenericEntityDescriptor.cs View File

@@ -1,258 +1,110 @@
using System.Runtime.InteropServices;

namespace Svelto.ECS
{
public class GenericEntityDescriptor<T> : EntityDescriptor
where T : NodeWithID, new()
public class GenericEntityDescriptor<T>:IEntityDescriptor where T : EntityView<T>, new()
{
static GenericEntityDescriptor()
{
_nodesToBuild = new INodeBuilder[] { new NodeBuilder<T>() };
entityViewBuilders = new IEntityViewBuilder[] { new EntityViewBuilder<T>() };
}
public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor)
{}

static readonly INodeBuilder[] _nodesToBuild;
}

public class GenericEntityDescriptor<T, U> : EntityDescriptor
where T : NodeWithID, new()
where U : NodeWithID, new()
{
static GenericEntityDescriptor()
public IEntityViewBuilder[] entityViewsToBuild
{
_nodesToBuild = new INodeBuilder[]
{
new NodeBuilder<T>(),
new NodeBuilder<U>()
};
get { return entityViewBuilders; }
}

public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor)
{}

static readonly INodeBuilder[] _nodesToBuild;
public static readonly IEntityViewBuilder[] entityViewBuilders;
}

public class GenericEntityDescriptor<T, U, V> : EntityDescriptor
where T : NodeWithID, new()
where U : NodeWithID, new()
where V : NodeWithID, new()
public class GenericEntityDescriptor<T, U> : IEntityDescriptor where T : EntityView<T>, new()
where U : EntityView<U>, new()
{
static GenericEntityDescriptor()
{
_nodesToBuild = new INodeBuilder[]
{
new NodeBuilder<T>(),
new NodeBuilder<U>(),
new NodeBuilder<V>()
};
entityViewBuilders = new IEntityViewBuilder[] {new EntityViewBuilder<T>(), new EntityViewBuilder<U>()};
}
public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor)
{}

static readonly INodeBuilder[] _nodesToBuild;
}

public class GenericEntityDescriptor<T, U, V, W> : EntityDescriptor
where T : NodeWithID, new()
where U : NodeWithID, new()
where V : NodeWithID, new()
where W : NodeWithID, new()
{
static GenericEntityDescriptor()
public IEntityViewBuilder[] entityViewsToBuild
{
_nodesToBuild = new INodeBuilder[]
{
new NodeBuilder<T>(),
new NodeBuilder<U>(),
new NodeBuilder<V>(),
new NodeBuilder<W>()
};
get { return entityViewBuilders; }
}
public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor)
{}

static readonly INodeBuilder[] _nodesToBuild;
public static readonly IEntityViewBuilder[] entityViewBuilders;
}

public class GenericEntityDescriptor<T, U, V, W, X> : EntityDescriptor
where T : NodeWithID, new()
where U : NodeWithID, new()
where V : NodeWithID, new()
where W : NodeWithID, new()
where X : NodeWithID, new()
public class GenericEntityDescriptor<T, U, V> : IEntityDescriptor where T : EntityView<T>, new()
where U : EntityView<U>, new()
where V : EntityView<V>, new()
{
static GenericEntityDescriptor()
{
_nodesToBuild = new INodeBuilder[]
{
new NodeBuilder<T>(),
new NodeBuilder<U>(),
new NodeBuilder<V>(),
new NodeBuilder<W>(),
new NodeBuilder<X>()
};
entityViewBuilders = new IEntityViewBuilder[] {new EntityViewBuilder<T>(), new EntityViewBuilder<U>(), new EntityViewBuilder<V>()};
}
public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor)
{}

static readonly INodeBuilder[] _nodesToBuild;
}

public class GenericEntityDescriptor<T, U, V, W, X, Y> : EntityDescriptor
where T : NodeWithID, new()
where U : NodeWithID, new()
where V : NodeWithID, new()
where W : NodeWithID, new()
where X : NodeWithID, new()
where Y : NodeWithID, new()
{
static GenericEntityDescriptor()
public IEntityViewBuilder[] entityViewsToBuild
{
_nodesToBuild = new INodeBuilder[]
{
new NodeBuilder<T>(),
new NodeBuilder<U>(),
new NodeBuilder<V>(),
new NodeBuilder<W>(),
new NodeBuilder<X>(),
new NodeBuilder<Y>()
};
get { return entityViewBuilders; }
}
public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor)
{}

static readonly INodeBuilder[] _nodesToBuild;
public static readonly IEntityViewBuilder[] entityViewBuilders;
}

public class GenericMixedEntityDescriptor<T> : EntityDescriptor
where T : INodeBuilder, new()
public class GenericEntityDescriptor<T, U, V, W> : IEntityDescriptor where T : EntityView<T>, new()
where U : EntityView<U>, new()
where V : EntityView<V>, new()
where W : EntityView<W>, new()
{
static GenericMixedEntityDescriptor()
static GenericEntityDescriptor()
{
_nodesToBuild = new INodeBuilder[]
{
new T(),
};
entityViewBuilders = new IEntityViewBuilder[] {new EntityViewBuilder<T>(), new EntityViewBuilder<U>(), new EntityViewBuilder<V>(), new EntityViewBuilder<W>()};
}
public GenericMixedEntityDescriptor(params object[] componentsImplementor) :
base(_nodesToBuild, componentsImplementor)
{ }

static readonly INodeBuilder[] _nodesToBuild;
}
public class GenericMixedEntityDescriptor<T, U> : EntityDescriptor
where T : INodeBuilder, new()
where U : INodeBuilder, new()
{
static GenericMixedEntityDescriptor()
public IEntityViewBuilder[] entityViewsToBuild
{
_nodesToBuild = new INodeBuilder[]
{
new T(),
new U(),
};
get { return entityViewBuilders; }
}
public GenericMixedEntityDescriptor(params object[] componentsImplementor) :
base(_nodesToBuild, componentsImplementor)
{ }

static readonly INodeBuilder[] _nodesToBuild;
public static readonly IEntityViewBuilder[] entityViewBuilders;
}

public class GenericMixedEntityDescriptor<T, U, V> : EntityDescriptor
where T : INodeBuilder, new()
where U : INodeBuilder, new()
where V : INodeBuilder, new()
public class GenericEntityDescriptor<T, U, V, W, X> : IEntityDescriptor where T : EntityView<T>, new()
where U : EntityView<U>, new()
where V : EntityView<V>, new()
where W : EntityView<W>, new()
where X : EntityView<X>, new()
{
static GenericMixedEntityDescriptor()
static GenericEntityDescriptor()
{
_nodesToBuild = new INodeBuilder[]
{
new T(),
new U(),
new V()
};
entityViewBuilders = new IEntityViewBuilder[] {new EntityViewBuilder<T>(), new EntityViewBuilder<U>(), new EntityViewBuilder<V>(), new EntityViewBuilder<W>(), new EntityViewBuilder<X>()};
}
public GenericMixedEntityDescriptor(params object[] componentsImplementor) :
base(_nodesToBuild, componentsImplementor)
{ }

static readonly INodeBuilder[] _nodesToBuild;
}

public class GenericMixedEntityDescriptor<T, U, V, W> : EntityDescriptor
where T : INodeBuilder, new()
where U : INodeBuilder, new()
where V : INodeBuilder, new()
where W : INodeBuilder, new()
{
static GenericMixedEntityDescriptor()
public IEntityViewBuilder[] entityViewsToBuild
{
_nodesToBuild = new INodeBuilder[]
{
new T(),
new U(),
new V(),
new W()
};
get { return entityViewBuilders; }
}
public GenericMixedEntityDescriptor(params object[] componentsImplementor) :
base(_nodesToBuild, componentsImplementor)
{ }

static readonly INodeBuilder[] _nodesToBuild;
public static readonly IEntityViewBuilder[] entityViewBuilders;
}

public class GenericMixedEntityDescriptor<T, U, V, W, X> : EntityDescriptor
where T : INodeBuilder, new()
where U : INodeBuilder, new()
where V : INodeBuilder, new()
where W : INodeBuilder, new()
where X : INodeBuilder, new()
public class GenericEntityDescriptor<T, U, V, W, X, Y> : IEntityDescriptor where T : EntityView<T>, new()
where U : EntityView<U>, new()
where V : EntityView<V>, new()
where W : EntityView<W>, new()
where X : EntityView<X>, new()
where Y : EntityView<Y>, new()
{
static GenericMixedEntityDescriptor()
static GenericEntityDescriptor()
{
_nodesToBuild = new INodeBuilder[]
{
new T(),
new U(),
new V(),
new W(),
new X()
};
entityViewBuilders = new IEntityViewBuilder[] {new EntityViewBuilder<T>(), new EntityViewBuilder<U>(), new EntityViewBuilder<V>(), new EntityViewBuilder<W>(), new EntityViewBuilder<X>(), new EntityViewBuilder<Y>()};
}
public GenericMixedEntityDescriptor(params object[] componentsImplementor) :
base(_nodesToBuild, componentsImplementor)
{ }

static readonly INodeBuilder[] _nodesToBuild;
}

public class GenericMixedEntityDescriptor<T, U, V, W, X, Y> : EntityDescriptor
where T : INodeBuilder, new()
where U : INodeBuilder, new()
where V : INodeBuilder, new()
where W : INodeBuilder, new()
where X : INodeBuilder, new()
where Y : INodeBuilder, new()
{
static GenericMixedEntityDescriptor()
public IEntityViewBuilder[] entityViewsToBuild
{
_nodesToBuild = new INodeBuilder[]
{
new T(),
new U(),
new V(),
new W(),
new X(),
new Y()
};
get { return entityViewBuilders; }
}
public GenericMixedEntityDescriptor(params object[] componentsImplementor) :
base(_nodesToBuild, componentsImplementor)
{ }

static readonly INodeBuilder[] _nodesToBuild;
public static readonly IEntityViewBuilder[] entityViewBuilders;
}
}

+ 0
- 31
ECS/GenericEntityDescriptorHolder.cs View File

@@ -1,31 +0,0 @@
#if UNITY_5 || UNITY_5_3_OR_NEWER
using System;

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

if (externalImplentors != null)
{
I[] baseImplentors = gameObject.GetComponents<I>();

implementors = new I[externalImplentors.Length + baseImplentors.Length];

Array.Copy(baseImplentors, implementors, baseImplentors.Length);
Array.Copy(externalImplentors, 0, implementors, baseImplentors.Length, externalImplentors.Length);
}
else
{
implementors = gameObject.GetComponents<I>();
}

return (T)Activator.CreateInstance(typeof(T), implementors);
}
}
}
#endif

+ 0
- 7
ECS/ICallBackOnAddEngine.cs View File

@@ -1,7 +0,0 @@
namespace Svelto.ECS
{
public interface ICallBackOnAddEngine
{
void Ready();
}
}

+ 12
- 54
ECS/IEngine.cs View File

@@ -1,36 +1,9 @@
using Rewired.Utils;
using Svelto.ECS.Internal;

namespace Svelto.ECS.Internal
{
public interface IStructNodeEngine : IEngine
{
void CreateStructNodes(SharedStructNodeLists sharedStructNodeLists);
}

public interface IGroupedStructNodesEngine : IEngine
public interface IHandleEntityViewEngine : IEngine
{
void CreateStructNodes(SharedGroupedStructNodesLists sharedStructNodeLists);
}

public interface IActivableNodeEngine : IEngine
{
void Enable(NodeWithID node);
void Disable(NodeWithID node);
}

public interface INodeEngine : IEngine
{
void Add(NodeWithID node);
void Remove(NodeWithID node);
}
}

namespace Svelto.ECS.Legacy
{
public interface INodesEngine : INodeEngine
{
System.Type[] AcceptedNodes();
void Add(IEntityView entityView);
void Remove(IEntityView entityView);
}
}

@@ -38,32 +11,17 @@ namespace Svelto.ECS
{
public interface IEngine
{}

public interface IActivableNodeEngine<in TNodeType> : IActivableNodeEngine where TNodeType : INode
{ }

public interface IQueryableNodeEngine:IEngine
#if EXPERIMENTAL
public interface IHandleActivableEntityEngine : IEngine
{
IEngineNodeDB nodesDB { set; }
void Enable(EntityView entityView);
void Disable(EntityView entityView);
}

/// <summary>
/// The engines can receive and store INodes structs
/// Unboxing will happen during the Add, but the
/// data will then be stored and processed as stucts
/// </summary>
public interface IStructNodeEngine<T> : IStructNodeEngine where T:struct, IStructNodeWithID
{ }

/// <summary>
/// same as above, but the nodes are grouped by ID
/// usually the ID is the owner of the nodes of that
/// group
/// </summary>
public interface IGroupedStructNodesEngine<T> : IGroupedStructNodesEngine where T : struct, IGroupedNode
#endif
public interface IQueryingEntityViewEngine : IEngine
{
void Add(ref T node);
void Remove(ref T node);
IEngineEntityViewDB entityViewsDB { set; }

void Ready();
}
}


+ 11
- 11
ECS/IEngineNodeDB.cs View File

@@ -2,21 +2,21 @@ using Svelto.DataStructures;

namespace Svelto.ECS
{
public interface IEngineNodeDB
public interface IEngineEntityViewDB
{
FasterReadOnlyList<T> QueryNodes<T>();
FasterReadOnlyList<T> QueryMetaNodes<T>();
FasterReadOnlyList<T> QueryGroupedNodes<T>(int group);
FasterReadOnlyList<T> QueryEntityViews<T>() where T:EntityView<T>, new();
FasterReadOnlyList<T> QueryMetaEntityViews<T>() where T: EntityView<T>, new();
FasterReadOnlyList<T> QueryGroupedEntityViews<T>(int group) where T: EntityView<T>, new();
T[] QueryNodesAsArray<T>(out int count) where T:struct;
T[] QueryEntityViewsAsArray<T>(out int count) where T: IEntityView;
T[] QueryGroupedEntityViewsAsArray<T>(int @group, out int count) where T: IEntityView;
ReadOnlyDictionary<int, T> QueryIndexableNodes<T>() where T:NodeWithID;
bool TryQueryNode<T>(int ID, out T node) where T:NodeWithID;
T QueryNode<T>(int ID) where T:NodeWithID;
ReadOnlyDictionary<int, T> QueryIndexableEntityViews<T>() where T: IEntityView;
bool TryQueryEntityView<T>(int ID, out T entityView) where T : IEntityView;
T QueryEntityView<T>(int ID) where T: IEntityView;

bool TryQueryMetaNode<T>(int metaEntityID, out T node) where T:NodeWithID;
T QueryMetaNode<T>(int metaEntityID) where T:NodeWithID;
bool TryQueryMetaEntityView<T>(int metaEntityID, out T entityView) where T: EntityView<T>, new();
T QueryMetaEntityView<T>(int metaEntityID) where T: EntityView<T>, new();
}
}


+ 31
- 0
ECS/IEnginesInterfaces.cs View File

@@ -0,0 +1,31 @@
namespace Svelto.ECS
{
public interface IEntityFactory
{
void BuildEntity<T>(int entityID, object[] implementors = null) where T:IEntityDescriptor, new();
void BuildEntity(int entityID, EntityDescriptorInfo entityDescriptor, object[] implementors = null);

void BuildMetaEntity<T>(int metaEntityID, object[] implementors = null) where T:IEntityDescriptor, new();

void BuildEntityInGroup<T>(int entityID, int groupID, object[] implementors = null) where T:IEntityDescriptor, new();
void BuildEntityInGroup(int entityID, int groupID, EntityDescriptorInfo entityDescriptor, object[] implementors = null);
}
public interface IEntityFunctions
{
void RemoveEntity(int entityID, IRemoveEntityComponent template);

void RemoveEntity<T>(int entityID) where T:IEntityDescriptor, new();
void RemoveMetaEntity<T>(int metaEntityID) where T:IEntityDescriptor, new();

void RemoveEntityFromGroup<T>(int entityID, int groupID) where T:IEntityDescriptor, new();
#if EXPERIMENTAL
void SetEntityActiveState<T>(int entityID, bool state) where T:IEntityDescriptor, new();
void SetMetaEntityActiveState<T>(int metaEntityID, bool state) where T:IEntityDescriptor, new();
void SetEntityInGroupActiveState<T>(int entityID, int group, bool state) where T:IEntityDescriptor, new();
#endif
}
}

+ 0
- 16
ECS/IEnginesRoot.cs View File

@@ -1,16 +0,0 @@
namespace Svelto.ECS
{
public interface IEnginesRoot
{
void AddEngine(IEngine engine);
}

public interface IEntityFactory
{
void BuildEntity(int ID, EntityDescriptor ED);

void BuildMetaEntity(int metaEntityID, EntityDescriptor ED);

void BuildEntityInGroup(int entityID, int groupID, EntityDescriptor ED);
}
}

+ 1
- 5
ECS/IEntityDescriptorHolder.cs View File

@@ -1,11 +1,7 @@
namespace Svelto.ECS
{
/// <summary>
/// please use [DisallowMultipleComponent] in your monobehaviours that implement IEntityDescriptorHolder
/// </summary>
public interface IEntityDescriptorHolder
{
//I must find a nicer solution for the extraImplentors
EntityDescriptor BuildDescriptorType(object[] extraImplentors = null);
EntityDescriptorInfo RetrieveDescriptor();
}
}

+ 53
- 12
ECS/INode.cs View File

@@ -1,29 +1,70 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Svelto.DataStructures;
using Svelto.Utilities;

namespace Svelto.ECS
{
public interface INode
{
public interface IEntityView
{
int ID { get; }
}
public interface IGroupedNode
public interface IEntityStruct:IEntityView
{
int groupID { get; set; }
new int ID { set; }
}
public interface IStructNodeWithID : INode
public abstract class EntityView : IEntityView
{
new int ID { get; set; }
public int ID { get { return _ID; } }

abstract internal KeyValuePair<Type, Action<EntityView, object>>[]
EntityViewBlazingFastReflection(out int count);

protected int _ID;
}

public class NodeWithID: INode
public class EntityView<T>: EntityView where T: EntityView
{
public static TNodeType BuildNode<TNodeType>(int ID) where TNodeType: NodeWithID, new()
internal static TEntityViewType BuildEntityView<TEntityViewType>(int ID) where TEntityViewType: EntityView<T>, new()
{
return new TNodeType { _ID = ID };
if (FieldCache.list.Count == 0)
{
var type = typeof(TEntityViewType);

var fields = type.GetFields(BindingFlags.Public |
BindingFlags.Instance);

for (int i = fields.Length - 1; i >= 0; --i)
{
var field = fields[i];

Action<EntityView, object> setter = FastInvoke<EntityView>.MakeSetter(field);

FieldCache.Add(new KeyValuePair<Type, Action<EntityView, object>>(field.FieldType, setter));
}
}

return new TEntityViewType { _ID = ID };
}

public int ID { get { return _ID; } }
override internal KeyValuePair<Type, Action<EntityView, object>>[]
EntityViewBlazingFastReflection(out int count)
{
return FasterList<KeyValuePair<Type, Action<EntityView, object>>>.NoVirt.ToArrayFast(FieldCache.list, out count);
}

protected int _ID;
static class FieldCache
{
internal static void Add(KeyValuePair<Type, Action<EntityView, object>> setter)
{
list.Add(setter);
}
internal static readonly FasterList<KeyValuePair<Type, Action<EntityView, object>>> list = new FasterList<KeyValuePair<Type, Action<EntityView, object>>>();
}
}
}


+ 0
- 19
ECS/IRemoveEntityComponent.cs View File

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

namespace Svelto.ECS
{
public interface IRemoveEntityComponent
{
Action removeEntity { get; set; }
}

public interface IDisableEntityComponent
{
Action disableEntity { get; set; }
}

public interface IEnableEntityComponent
{
Action enableEntity { get; set; }
}
}

+ 107
- 0
ECS/MixedEntityDescriptor.cs View File

@@ -0,0 +1,107 @@
namespace Svelto.ECS
{
public class MixedEntityDescriptor<T>:IEntityDescriptor where T : class, IEntityViewBuilder, new()
{
static MixedEntityDescriptor()
{
_entityViewsToBuild = new IEntityViewBuilder[] {new T()};
}
public IEntityViewBuilder[] entityViewsToBuild
{
get { return _entityViewsToBuild; }
}
static readonly IEntityViewBuilder[] _entityViewsToBuild;
}

public class MixedEntityDescriptor<T, U> : IEntityDescriptor where T : class, IEntityViewBuilder, new()
where U : class, IEntityViewBuilder, new()
{
static MixedEntityDescriptor()
{
_entityViewsToBuild = new IEntityViewBuilder[] {new T(), new U()};
}

public IEntityViewBuilder[] entityViewsToBuild
{
get { return _entityViewsToBuild; }
}
static readonly IEntityViewBuilder[] _entityViewsToBuild;
}

public class MixedEntityDescriptor<T, U, V> : IEntityDescriptor where T : class, IEntityViewBuilder, new()
where U : class, IEntityViewBuilder, new()
where V : class, IEntityViewBuilder, new()
{
static MixedEntityDescriptor()
{
_entityViewsToBuild = new IEntityViewBuilder[] {new T(), new U(), new V()};
}

public IEntityViewBuilder[] entityViewsToBuild
{
get { return _entityViewsToBuild; }
}
static readonly IEntityViewBuilder[] _entityViewsToBuild;
}

public class MixedEntityDescriptor<T, U, V, W> : IEntityDescriptor where T : class, IEntityViewBuilder, new()
where U : class, IEntityViewBuilder, new()
where V : class, IEntityViewBuilder, new()
where W : class, IEntityViewBuilder, new()
{
static MixedEntityDescriptor()
{
_entityViewsToBuild = new IEntityViewBuilder[] {new T(), new U(), new V(), new W()};
}

public IEntityViewBuilder[] entityViewsToBuild
{
get { return _entityViewsToBuild; }
}
static readonly IEntityViewBuilder[] _entityViewsToBuild;
}

public class MixedEntityDescriptor<T, U, V, W, X> : IEntityDescriptor where T : class, IEntityViewBuilder, new()
where U : class, IEntityViewBuilder, new()
where V : class, IEntityViewBuilder, new()
where W : class, IEntityViewBuilder, new()
where X : class, IEntityViewBuilder, new()
{
static MixedEntityDescriptor()
{
_entityViewsToBuild = new IEntityViewBuilder[] {new T(), new U(), new V(), new W(), new X()};
}

public IEntityViewBuilder[] entityViewsToBuild
{
get { return _entityViewsToBuild; }
}
static readonly IEntityViewBuilder[] _entityViewsToBuild;
}

public class MixedEntityDescriptor<T, U, V, W, X, Y> : IEntityDescriptor where T : class, IEntityViewBuilder, new()
where U : class, IEntityViewBuilder, new()
where V : class, IEntityViewBuilder, new()
where W : class, IEntityViewBuilder, new()
where X : class, IEntityViewBuilder, new()
where Y : class, IEntityViewBuilder, new()
{
static MixedEntityDescriptor()
{
_entityViewsToBuild = new IEntityViewBuilder[] {new T(), new U(), new V(), new W(), new X(), new Y()};
}

public IEntityViewBuilder[] entityViewsToBuild
{
get { return _entityViewsToBuild; }
}
static readonly IEntityViewBuilder[] _entityViewsToBuild;
}
}

+ 37
- 35
ECS/MultiNodesEngine.cs View File

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

namespace Svelto.ECS.Internal
{
public abstract class MultiNodesEngine<T>:INodeEngine where T:NodeWithID
public abstract class MultiEntityViewsEngine<T>:IHandleEntityViewEngine where T:EntityView<T>, new()
{
protected abstract void Add(T node);
protected abstract void Remove(T node);
protected abstract void Add(T entityView);
protected abstract void Remove(T entityView);
public virtual void Add(NodeWithID node)
public virtual void Add(IEntityView entityView)
{
Add((T) node);
Add((T) entityView);
}

public virtual void Remove(NodeWithID node)
public virtual void Remove(IEntityView entityView)
{
Remove((T) node);
Remove((T) entityView);
}
}
}

namespace Svelto.ECS
{
public abstract class MultiNodesEngine<T, U> : MultiNodesEngine<T>
where T:NodeWithID where U:NodeWithID
public abstract class MultiEntityViewsEngine<T, U> : MultiEntityViewsEngine<T>
where T:EntityView<T>, new()
where U:EntityView<U>, new()
{
protected abstract void Add(U node);
protected abstract void Remove(U node);
protected abstract void Add(U entityView);
protected abstract void Remove(U entityView);

public override void Add(NodeWithID node)
public override void Add(IEntityView entityView)
{
var castedNode = node as U;
if (castedNode != null)
var castedEntityView = entityView as U;
if (castedEntityView != null)
{
Add(castedNode);
Add(castedEntityView);
}
else
{
base.Add(node);
base.Add(entityView);
}
}

public override void Remove(NodeWithID node)
public override void Remove(IEntityView entityView)
{
if (node is U)
if (entityView is U)
{
Remove((U) node);
Remove((U) entityView);
}
else
{
base.Remove(node);
base.Remove(entityView);
}
}
}

public abstract class MultiNodesEngine<T, U, V> : MultiNodesEngine<T, U>
where T: NodeWithID where U : NodeWithID where V:NodeWithID
public abstract class MultiEntityViewsEngine<T, U, V> : MultiEntityViewsEngine<T, U>
where T : EntityView<T>, new()
where U : EntityView<U>, new()
where V : EntityView<V>, new()
{
protected abstract void Add(V node);
protected abstract void Remove(V node);
protected abstract void Add(V entityView);
protected abstract void Remove(V entityView);

public override void Add(NodeWithID node)
public override void Add(IEntityView entityView)
{
var castedNode = node as V;
if (castedNode != null)
var castedEntityView = entityView as V;
if (castedEntityView != null)
{
Add(castedNode);
Add(castedEntityView);
}
else
base.Add(node);
base.Add(entityView);
}

public override void Remove(NodeWithID node)
public override void Remove(IEntityView entityView)
{
var castedNode = node as V;
if (castedNode != null)
var castedEntityView = entityView as V;
if (castedEntityView != null)
{
Remove(castedNode);
Remove(castedEntityView);
}
else
base.Remove(node);
base.Remove(entityView);
}
}
}

+ 26
- 23
ECS/NodeBuilder.cs View File

@@ -1,58 +1,61 @@
using System;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public interface INodeBuilder
public interface IEntityViewBuilder
{
INode BuildNodeAndAddToList(ref ITypeSafeList list, int entityID);
IEntityView BuildEntityViewAndAddToList(ref ITypeSafeList list, int entityID);

Type GetNodeType();
Type GetEntityViewType();
}

public class NodeBuilder<NodeType> : INodeBuilder where NodeType : NodeWithID, new()
public class EntityViewBuilder<EntityViewType> : IEntityViewBuilder where EntityViewType : EntityView<EntityViewType>, new()
{
public INode BuildNodeAndAddToList(ref ITypeSafeList list, int entityID)
public IEntityView BuildEntityViewAndAddToList(ref ITypeSafeList list, int entityID)
{
if (list == null)
list = new TypeSafeFasterListForECSForClasses<NodeType>();
list = new TypeSafeFasterListForECSForClasses<EntityViewType>();

var castedList = list as TypeSafeFasterListForECSForClasses<NodeType>;
var castedList = list as TypeSafeFasterListForECSForClasses<EntityViewType>;

var node = NodeWithID.BuildNode<NodeType>(entityID);
var entityView = EntityView<EntityViewType>.BuildEntityView<EntityViewType>(entityID);

castedList.Add(node);
castedList.Add(entityView);

return node;
return entityView;
}

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

readonly Type _entityViewType = typeof(EntityViewType);
}

public class StructNodeBuilder<NodeType> : INodeBuilder where NodeType : struct, IStructNodeWithID
public class EntityStructBuilder<EntityViewType> : IEntityViewBuilder where EntityViewType : struct, IEntityStruct
{
public INode BuildNodeAndAddToList(ref ITypeSafeList list, int entityID)
public IEntityView BuildEntityViewAndAddToList(ref ITypeSafeList list, int entityID)
{
var node = default(NodeType);
node.ID = entityID;
var entityView = default(EntityViewType);
entityView.ID = entityID;
if (list == null)
list = new TypeSafeFasterListForECSForStructs<NodeType>();
list = new TypeSafeFasterListForECSForStructs<EntityViewType>();

var castedList = list as TypeSafeFasterListForECSForStructs<NodeType>;
var castedList = list as TypeSafeFasterListForECSForStructs<EntityViewType>;

castedList.Add(node);
castedList.Add(entityView);

return null;
}

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

readonly Type _entityViewType = typeof(EntityViewType);
}
}

+ 4
- 4
ECS/NodeSubmissionScheduler.cs View File

@@ -1,9 +1,9 @@
using System;
using Svelto.WeakEvents;

namespace Svelto.ECS.NodeSchedulers
namespace Svelto.ECS.Schedulers
{
public abstract class NodeSubmissionScheduler
public abstract class EntityViewSubmissionScheduler
{
abstract public void Schedule(Action submitNodes);
abstract public void Schedule(WeakAction submitEntityViews);
}
}

+ 8
- 8
ECS/Profiler/EngineInfo.cs View File

@@ -33,12 +33,12 @@ namespace Svelto.ECS.Profiler
double _accumulatedAddDuration;
double _minAddDuration;
double _maxAddDuration;
int _nodesAddedCount;
int _entityViewsAddedCount;

double _accumulatedRemoveDuration;
double _minRemoveDuration;
double _maxRemoveDuration;
int _nodesRemovedCount;
int _entityViewsRemovedCount;

public IEngine engine { get { return _engine; } }
public string engineName { get { return _engineName; } }
@@ -56,8 +56,8 @@ namespace Svelto.ECS.Profiler
public double maxRemoveDuration { get { return _maxRemoveDuration; } }
public double maxUpdateDuration { get { return _maxUpdateDuration[(int)UpdateType.Update]; } }

public double averageAddDuration { get { return _nodesAddedCount == 0 ? 0 : _accumulatedAddDuration / _nodesAddedCount; } }
public double averageRemoveDuration { get { return _nodesRemovedCount == 0 ? 0 : _accumulatedRemoveDuration / _nodesRemovedCount; } }
public double averageAddDuration { get { return _entityViewsAddedCount == 0 ? 0 : _accumulatedAddDuration / _entityViewsAddedCount; } }
public double averageRemoveDuration { get { return _entityViewsRemovedCount == 0 ? 0 : _accumulatedRemoveDuration / _entityViewsRemovedCount; } }
public double averageUpdateDuration { get { return _updateFrameTimes[(int)UpdateType.Update].Count == 0 ? 0 : _accumulatedUpdateDuration[(int)UpdateType.Update] / _updateFrameTimes[(int)UpdateType.Update].Count; } }
public double averageLateUpdateDuration { get { return _updateFrameTimes[(int)UpdateType.LateUpdate].Count == 0 ? 0 : _accumulatedUpdateDuration[(int)UpdateType.LateUpdate] / _updateFrameTimes[(int)UpdateType.LateUpdate].Count; } }
public double averageFixedUpdateDuration { get { return _updateFrameTimes[(int)UpdateType.FixedUpdate].Count == 0 ? 0 : _accumulatedUpdateDuration[(int)UpdateType.FixedUpdate] / _updateFrameTimes[(int)UpdateType.FixedUpdate].Count; } }
@@ -126,7 +126,7 @@ namespace Svelto.ECS.Profiler
_maxAddDuration = duration;
}
_accumulatedAddDuration += duration;
_nodesAddedCount += 1;
_entityViewsAddedCount += 1;
}

public void AddRemoveDuration(double duration)
@@ -140,7 +140,7 @@ namespace Svelto.ECS.Profiler
_maxRemoveDuration = duration;
}
_accumulatedRemoveDuration += duration;
_nodesRemovedCount += 1;
_entityViewsRemovedCount += 1;
}

public void ResetDurations()
@@ -156,12 +156,12 @@ namespace Svelto.ECS.Profiler
_accumulatedAddDuration = 0;
_minAddDuration = 0;
_maxAddDuration = 0;
_nodesAddedCount = 0;
_entityViewsAddedCount = 0;

_accumulatedRemoveDuration = 0;
_minRemoveDuration = 0;
_maxRemoveDuration = 0;
_nodesRemovedCount = 0;
_entityViewsRemovedCount = 0;
}
}
}

+ 5
- 5
ECS/Profiler/EngineProfiler.cs View File

@@ -12,27 +12,27 @@ 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<IHandleEntityViewEngine, IEntityView> addingFunc, IHandleEntityViewEngine engine, IEntityView entityView)
{
EngineInfo info;
if (engineInfos.TryGetValue(engine.GetType(), out info))
{
_stopwatch.Start();
addingFunc(engine, node);
addingFunc(engine, entityView);
_stopwatch.Reset();

info.AddAddDuration(_stopwatch.Elapsed.TotalMilliseconds);
}
}

public static void MonitorRemoveDuration(Action<INodeEngine, INode> removeFunc, INodeEngine engine, NodeWithID node)
public static void MonitorRemoveDuration(Action<IHandleEntityViewEngine, IEntityView> removeFunc, IHandleEntityViewEngine engine, IEntityView entityView)
{
EngineInfo info;
if (engineInfos.TryGetValue(engine.GetType(), out info))
{
_stopwatch.Start();
removeFunc(engine, node);
engine.Remove(node);
removeFunc(engine, entityView);
engine.Remove(entityView);
_stopwatch.Reset();

info.AddRemoveDuration(_stopwatch.Elapsed.TotalMilliseconds);


+ 42
- 0
ECS/RemoveEntityImplementor.cs View File

@@ -0,0 +1,42 @@
namespace Svelto.ECS.Internal
{
sealed class RemoveEntityImplementor : IRemoveEntityComponent
{
public RemoveEntityImplementor(IEntityDescriptor descriptor, int groupID) : this(descriptor)
{
removeEntityInfo = new RemoveEntityInfo(descriptor, groupID);
}

internal RemoveEntityImplementor(IEntityDescriptor descriptor)
{
removeEntityInfo = new RemoveEntityInfo(descriptor);
}

internal RemoveEntityInfo removeEntityInfo;
}
}

namespace Svelto.ECS
{
public interface IRemoveEntityComponent
{}

public struct RemoveEntityInfo
{
readonly public IEntityDescriptor descriptor;
readonly public int groupID;
readonly public bool isInAGroup;

public RemoveEntityInfo(IEntityDescriptor descriptor) : this()
{
this.descriptor = descriptor;
}

public RemoveEntityInfo(IEntityDescriptor descriptor, int groupID)
{
this.descriptor = descriptor;
this.groupID = groupID;
isInAGroup = true;
}
}
}

+ 7
- 7
ECS/SingleNodeEngine.cs View File

@@ -2,19 +2,19 @@ using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public abstract class SingleNodeEngine<T> : INodeEngine where T:NodeWithID
public abstract class SingleEntityViewEngine<T> : IHandleEntityViewEngine where T:EntityView<T>, new()
{
public void Add(NodeWithID node)
public void Add(IEntityView entityView)
{
Add((T)node); //when byref returns will be vailable, this should be passed by reference, not copy!
Add((T)entityView); //when byref returns will be vailable, this should be passed by reference, not copy!
}

public void Remove(NodeWithID node)
public void Remove(IEntityView entityView)
{
Remove((T)node);
Remove((T)entityView);
}

protected abstract void Add(T node);
protected abstract void Remove(T node);
protected abstract void Add(T entityView);
protected abstract void Remove(T entityView);
}
}

+ 0
- 141
ECS/StructNodes.cs View File

@@ -1,141 +0,0 @@
using System;
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public sealed class StructNodes<T> where T:struct, IStructNodeWithID
{
public T[] GetList(out int numberOfItems)
{
numberOfItems = _internalList.Count;
return _internalList.ToArrayFast();
}

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

public void Add(T node)
{
T convert = (T)node;

_internalList.Add(convert);
}

readonly FasterList<T> _internalList;
}

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 = (SharedGroupedStructNodesLists.NoVirt.GetList<T>(_container, groupID) as FasterList<T>);
indices[node.ID] = fasterList.Count;

fasterList.Add(convert);
}

public void Remove(int groupID, T node)
{
var fasterList = (SharedGroupedStructNodesLists.NoVirt.GetList<T>(_container, groupID) as FasterList<T>);
var index = indices[node.ID];
indices.Remove(node.ID);

if (fasterList.UnorderedRemoveAt(index))
indices[fasterList[index].ID] = index;
}

public T[] GetList(int groupID, out int numberOfItems)
{
var fasterList = (SharedGroupedStructNodesLists.NoVirt.GetList<T>(_container, groupID) as FasterList<T>);
return FasterList<T>.NoVirt.ToArrayFast(fasterList, out numberOfItems);
}

readonly SharedGroupedStructNodesLists _container;
readonly Dictionary<int, int> indices;
}

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

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

list = new FasterList<T>();

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

return (FasterList<T>)list;
}
}

readonly Dictionary<Type, IFasterList> _collection;
}

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

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

if (dic.TryGetValue(groupID, out localList))
return localList;

localList = new FasterList<T>();
dic.Add(groupID, localList);

return localList;
}

internal static Dictionary<int, IFasterList> GetGroup<T>(SharedGroupedStructNodesLists list) where T : struct
{
Dictionary<int, IFasterList> dic;

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

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

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

return dic;
}
}

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

+ 2
- 2
ECS/note.txt View File

@@ -27,7 +27,7 @@ producer/consumer has the inconvienent to force check the number of jobs availab

Engine can't be removed, they can only be disabled, but the logic of disabling must be handled by the engine itself

Should components have just one element? Should engines use just nodes? Components are ditacted by the entities and Nodes by the engines
Should components have just one element? Should engines use just entityViews? Components are ditacted by the entities and EntityViews by the engines

http://thelinuxlich.github.io/artemis_CSharp/

@@ -42,7 +42,7 @@ http://entity-systems.wikidot.com/es-articles

http://www.ashframework.org/

it's very important to give a namespace to the engines. In this way it's impossible to create semantically wrong nodes (PlayerNode Vs TargetNode)
it's very important to give a namespace to the engines. In this way it's impossible to create semantically wrong entityViews (PlayerEntityView Vs TargetEntityView)

ToDo:



+ 24
- 0
Utilities/FastInvoke.cs View File

@@ -0,0 +1,24 @@
using System;
using System.Reflection;
using System.Reflection.Emit;

namespace Svelto.Utilities
{
//https://stackoverflow.com/questions/321650/how-do-i-set-a-field-value-in-an-c-sharp-expression-tree/321686#321686
public static class FastInvoke<T> where T : class
{
public static Action<T, object> MakeSetter(FieldInfo field)
{
DynamicMethod m = new DynamicMethod("setter", typeof(void), new Type[] {typeof(T), typeof(object)});
ILGenerator cg = m.GetILGenerator();

// arg0.<field> = arg1
cg.Emit(OpCodes.Ldarg_0);
cg.Emit(OpCodes.Ldarg_1);
cg.Emit(OpCodes.Stfld, field);
cg.Emit(OpCodes.Ret);

return (Action<T, object>) m.CreateDelegate(typeof(Action<T, object>));;
}
}
}

+ 0
- 1
Utilities/Murmur3.cs View File

@@ -1,5 +1,4 @@
using System;
using System.IO;

/// <summary>
/// Murmur hash.


+ 0
- 1
Utilities/NetFXCoreWrappers.cs View File

@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using Svelto.DataStructures;

namespace Svelto.Utilities


+ 0
- 1
WeakEvents/WeakAction.cs View File

@@ -1,6 +1,5 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using Svelto.Utilities;

namespace Svelto.WeakEvents


+ 0
- 1
WeakEvents/WeakActionStruct.cs View File

@@ -1,6 +1,5 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Svelto.Utilities;



+ 0
- 1
WeakEvents/WeakEvent.cs View File

@@ -1,7 +1,6 @@
using Svelto.DataStructures;
using System;
using System.Reflection;
using Svelto.Utilities;

namespace Svelto.WeakEvents
{


Loading…
Cancel
Save