@@ -2,7 +2,6 @@ | |||
#region | |||
using System; | |||
using Svelto.DataStructures; | |||
using UnityEngine; | |||
#endregion | |||
@@ -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 |
@@ -1,5 +1,3 @@ | |||
using System; | |||
namespace Svelto.Context | |||
{ | |||
public interface IWaitForFrameworkDestruction | |||
@@ -1,5 +1,3 @@ | |||
using System; | |||
namespace Svelto.Context | |||
{ | |||
public interface IWaitForFrameworkInitialization | |||
@@ -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() | |||
@@ -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; | |||
@@ -9,7 +9,7 @@ | |||
//------------------------------------------------------------------------------ | |||
using System; | |||
public interface IGraphNode<T> | |||
public interface IGraphEntityView<T> | |||
{ | |||
void VisitNeighbours(System.Action<T> onVisiting); | |||
} | |||
@@ -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>"; | |||
} | |||
@@ -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>"; | |||
} | |||
@@ -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); | |||
@@ -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; | |||
} | |||
} | |||
@@ -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); | |||
} | |||
} |
@@ -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; } | |||
@@ -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]; | |||
} | |||
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} | |||
} |
@@ -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: "; | |||
} | |||
} |
@@ -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."; | |||
} | |||
} | |||
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -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(); | |||
} | |||
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -1,7 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public interface ICallBackOnAddEngine | |||
{ | |||
void Ready(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
@@ -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(); | |||
} | |||
} | |||
@@ -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 | |||
} | |||
} |
@@ -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,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(); | |||
} | |||
} |
@@ -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>>>(); | |||
} | |||
} | |||
} | |||
@@ -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; } | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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: | |||
@@ -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>));; | |||
} | |||
} | |||
} |
@@ -1,5 +1,4 @@ | |||
using System; | |||
using System.IO; | |||
/// <summary> | |||
/// Murmur hash. | |||
@@ -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 | |||
@@ -1,6 +1,5 @@ | |||
using System; | |||
using System.Reflection; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Utilities; | |||
namespace Svelto.WeakEvents | |||
@@ -1,6 +1,5 @@ | |||
using System; | |||
using System.Reflection; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
using Svelto.Utilities; | |||
@@ -1,7 +1,6 @@ | |||
using Svelto.DataStructures; | |||
using System; | |||
using System.Reflection; | |||
using Svelto.Utilities; | |||
namespace Svelto.WeakEvents | |||
{ | |||