Browse Source

Dispatcher.cs is now deprecated, a legacy copy will be found in the example

DispatcherOnChange and DispatcherOnSet now hold weakreferences of the actions
Ticker is now deprecated, a legacy copy will be found in the example
Added Engines Profiler Tool
Added the concept of Entity Group (more will follow on the subject in an article)
Handling the creation of entities inside the Add Methods
fixed some bugs
renamed namespace to Svelto.ECS
add the concept of ExtraImplementors for the EntityDescriptor
tags/Rel1
sebas77 7 years ago
parent
commit
393733853b
88 changed files with 3323 additions and 1067 deletions
  1. +8
    -12
      Context/ContextNotifier.cs
  2. +2
    -2
      Context/Factories/GameObjectFactory.cs
  3. +9
    -0
      Context/Factories/Legacy.meta
  4. +98
    -0
      Context/Factories/Legacy/GameObjectFactory.cs
  5. +3
    -1
      Context/Factories/Legacy/GameObjectFactory.cs.meta
  6. +36
    -0
      Context/Factories/Legacy/MonoBehaviourFactory.cs
  7. +3
    -1
      Context/Factories/Legacy/MonoBehaviourFactory.cs.meta
  8. +1
    -1
      Context/IContextNotifer.cs
  9. +11
    -0
      Context/IUnityContextHierarchyChangedListener.cs
  10. +8
    -0
      Context/IUnityContextHierarchyChangedListener.cs.meta
  11. +9
    -0
      Context/Unity.meta
  12. +30
    -0
      Context/Unity/NotifyComponentsRemoved.cs
  13. +8
    -0
      Context/Unity/NotifyComponentsRemoved.cs.meta
  14. +183
    -0
      DataStructures/CircularBufferIndexer.cs
  15. +8
    -0
      DataStructures/CircularBufferIndexer.cs.meta
  16. +146
    -112
      DataStructures/FasterList.cs
  17. +3
    -1
      DataStructures/FasterList.cs.meta
  18. +56
    -0
      DataStructures/HashableWeakReference.cs
  19. +3
    -1
      DataStructures/HashableWeakReference.cs.meta
  20. +1
    -1
      DataStructures/Priority Queue.meta
  21. +327
    -0
      DataStructures/Priority Queue/HeapPriorityQueue.cs
  22. +8
    -0
      DataStructures/Priority Queue/HeapPriorityQueue.cs.meta
  23. +23
    -0
      DataStructures/Priority Queue/IPriorityQueue.cs
  24. +8
    -0
      DataStructures/Priority Queue/IPriorityQueue.cs.meta
  25. +24
    -0
      DataStructures/Priority Queue/PriorityQueueNode.cs
  26. +8
    -0
      DataStructures/Priority Queue/PriorityQueueNode.cs.meta
  27. +154
    -265
      DataStructures/ReadOnlyDictionary.cs
  28. +183
    -0
      DataStructures/SerializableDictionary.cs
  29. +7
    -0
      DataStructures/SerializableDictionary.cs.meta
  30. +303
    -0
      DataStructures/ThreadSafeDictionary.cs
  31. +3
    -1
      DataStructures/ThreadSafeDictionary.cs.meta
  32. +50
    -3
      DataStructures/ThreadSafeQueue.cs
  33. +5
    -0
      DataStructures/WeakReference.cs
  34. +2
    -0
      ECS.meta
  35. +0
    -39
      ECS/Dispatcher/Dispatcher.cs
  36. +0
    -12
      ECS/Dispatcher/Dispatcher.cs.meta
  37. +13
    -23
      ECS/Dispatcher/DispatcherOnChange.cs
  38. +48
    -0
      ECS/Dispatcher/DispatcherOnSet.cs
  39. +12
    -0
      ECS/Dispatcher/DispatcherOnSet.cs.meta
  40. +36
    -7
      ECS/EngineNodeDB.cs
  41. +183
    -35
      ECS/EnginesRoot.cs
  42. +30
    -33
      ECS/EntityDescriptor.cs
  43. +0
    -6
      ECS/IComponent.cs
  44. +1
    -17
      ECS/IEngine.cs
  45. +8
    -1
      ECS/IEngineNodeDB.cs
  46. +3
    -1
      ECS/IEnginesRoot.cs
  47. +6
    -5
      ECS/IEntityDescriptorHolder.cs
  48. +1
    -1
      ECS/IEntityDescriptorHolder.cs.meta
  49. +6
    -0
      ECS/IImplementor.cs
  50. +0
    -0
      ECS/IImplementor.cs.meta
  51. +2
    -2
      ECS/INode.cs
  52. +1
    -1
      ECS/IRemoveEntityComponent.cs
  53. +9
    -0
      ECS/Profiler.meta
  54. +9
    -0
      ECS/Profiler/Editor.meta
  55. +9
    -0
      ECS/Profiler/Editor/EngineProfiler.meta
  56. +373
    -0
      ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs
  57. +12
    -0
      ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs.meta
  58. +22
    -0
      ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs
  59. +12
    -0
      ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs.meta
  60. +132
    -0
      ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs
  61. +12
    -0
      ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs.meta
  62. +65
    -0
      ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs
  63. +12
    -0
      ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs.meta
  64. +167
    -0
      ECS/Profiler/EngineInfo.cs
  65. +12
    -0
      ECS/Profiler/EngineInfo.cs.meta
  66. +126
    -0
      ECS/Profiler/EngineProfiler.cs
  67. +12
    -0
      ECS/Profiler/EngineProfiler.cs.meta
  68. +19
    -0
      ECS/Profiler/EngineProfilerBehaviour.cs
  69. +12
    -0
      ECS/Profiler/EngineProfilerBehaviour.cs.meta
  70. +18
    -0
      ECS/SingleNodeEngine.cs
  71. +12
    -0
      ECS/SingleNodeEngine.cs.meta
  72. +1
    -1
      ECS/note.txt
  73. +3
    -3
      Factories.meta
  74. +0
    -42
      Observer/Observable.cs
  75. +0
    -12
      Observer/Observable.cs.meta
  76. +0
    -111
      Observer/Observer.cs
  77. +0
    -12
      Observer/Observer.cs.meta
  78. +0
    -5
      Ticker.meta
  79. +0
    -31
      Ticker/ITickable.cs
  80. +0
    -8
      Ticker/ITicker.cs
  81. +0
    -15
      Ticker/IntervaledTickAttribute.cs
  82. +0
    -12
      Ticker/IntervaledTickAttribute.cs.meta
  83. +0
    -153
      Ticker/TickBehaviour.cs
  84. +0
    -57
      Ticker/UnityTicker.cs
  85. +3
    -3
      Utilities.meta
  86. +79
    -18
      Utilities/Print.cs
  87. +99
    -0
      Utilities/WeakAction.cs
  88. +12
    -0
      Utilities/WeakAction.cs.meta

+ 8
- 12
Context/ContextNotifier.cs View File

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

namespace Svelto.Context
{
@@ -33,19 +33,17 @@ namespace Svelto.Context
/// </summary>
public void NotifyFrameworkDeinitialized()
{
for (int i = _toDeinitialize.Count - 1; i >= 0; --i)
{
for (var i = _toDeinitialize.Count - 1; i >= 0; --i)
try
{
var obj = _toDeinitialize[i];
if (obj.IsAlive == true)
(obj.Target as IWaitForFrameworkDestruction).OnFrameworkDestroyed();
if (obj.IsAlive)
obj.Target.OnFrameworkDestroyed();
}
catch (Exception e)
{
Utility.Console.LogException(e);
}
}

_toDeinitialize = null;
}
@@ -55,24 +53,22 @@ namespace Svelto.Context
/// </summary>
public void NotifyFrameworkInitialized()
{
for (int i = _toInitialize.Count - 1; i >= 0; --i)
{
for (var i = _toInitialize.Count - 1; i >= 0; --i)
try
{
var obj = _toInitialize[i];
if (obj.IsAlive == true)
(obj.Target as IWaitForFrameworkInitialization).OnFrameworkInitialized();
if (obj.IsAlive)
obj.Target.OnFrameworkInitialized();
}
catch (Exception e)
{
Utility.Console.LogException(e);
}
}

_toInitialize = null;
}

List<WeakReference<IWaitForFrameworkDestruction>> _toDeinitialize;
List<WeakReference<IWaitForFrameworkDestruction>> _toDeinitialize;
List<WeakReference<IWaitForFrameworkInitialization>> _toInitialize;
}
}

+ 2
- 2
Context/Factories/GameObjectFactory.cs View File

@@ -16,7 +16,7 @@ namespace Svelto.Context

public GameObject Build(string prefabName)
{
DesignByContract.Check.Require(_prefabs.ContainsKey(prefabName), "Svelto.Factories.IGameObjectFactory - Invalid Prefab Type");
DesignByContract.Check.Require(_prefabs.ContainsKey(prefabName), "Svelto.Factories.IGameObjectFactory -prefab was not found:" + prefabName);

var go = Build(_prefabs[prefabName][0]);

@@ -48,7 +48,7 @@ namespace Svelto.Context
/// <param name="prefab">original prefab</param>
public GameObject Build(GameObject prefab)
{
Profiler.BeginSample("GameObject Factory Build");
UnityEngine.Profiling.Profiler.BeginSample("GameObject Factory Build");

var copy = Object.Instantiate(prefab) as GameObject;



+ 9
- 0
Context/Factories/Legacy.meta View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 8d34411ef4f6005489ca3bb91987c84a
folderAsset: yes
timeCreated: 1466179612
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

+ 98
- 0
Context/Factories/Legacy/GameObjectFactory.cs View File

@@ -0,0 +1,98 @@
#region

using System.Collections.Generic;
using Svelto.Context.Legacy;
using Svelto.DataStructures;
using UnityEngine;

#endregion

namespace Svelto.Context.Legacy
{
public class GameObjectFactory : Factories.IGameObjectFactory
{
public GameObjectFactory(IUnityContextHierarchyChangedListener root)
{
_unityContext = new WeakReference<IUnityContextHierarchyChangedListener>(root);

_prefabs = new Dictionary<string, GameObject[]>();
}

public GameObject Build(string prefabName)
{
DesignByContract.Check.Require(_prefabs.ContainsKey(prefabName), "Svelto.Factories.IGameObjectFactory - Invalid Prefab Type");

var go = Build(_prefabs[prefabName][0]);

GameObject parent = _prefabs[prefabName][1];

if (parent != null)
{
Transform transform = go.transform;

var scale = transform.localScale;
var rotation = transform.localRotation;
var position = transform.localPosition;

parent.SetActive(true);

transform.parent = parent.transform;

transform.localPosition = position;
transform.localRotation = rotation;
transform.localScale = scale;
}

return go;
}

/// <summary>
/// Register a prefab to be built later using a string ID.
/// </summary>
/// <param name="prefab">original prefab</param>
public GameObject Build(GameObject prefab)
{
DesignByContract.Check.Require(_unityContext.IsAlive == true, "Context is used, but not alive");

UnityEngine.Profiling.Profiler.BeginSample("GameObject Factory Build");

var copy = Object.Instantiate(prefab) as GameObject;
var components = copy.GetComponentsInChildren<MonoBehaviour>(true);

for (var i = 0; i < components.Length; ++i)
{
var monoBehaviour = components[i];
if (monoBehaviour != null)
{
var currentGo = monoBehaviour.gameObject;

_unityContext.Target.OnMonobehaviourAdded(monoBehaviour);
if (currentGo.GetComponent<NotifyComponentsRemoved>() == null)
currentGo.AddComponent<NotifyComponentsRemoved>().unityContext = _unityContext;
}
else
{
//Utility.Console.Log("delete me");
}
}

UnityEngine.Profiling.Profiler.EndSample();

return copy;
}

public void RegisterPrefab(GameObject prefab, string prefabName, GameObject parent = null)
{
var objects = new GameObject[2];

objects[0] = prefab; objects[1] = parent;

_prefabs.Add(prefabName, objects);
}

Dictionary<string, GameObject[]> _prefabs;
WeakReference<IUnityContextHierarchyChangedListener> _unityContext;
}
}

Ticker/ITickable.cs.meta → Context/Factories/Legacy/GameObjectFactory.cs.meta View File

@@ -1,5 +1,7 @@
fileFormatVersion: 2
guid: 8830537b810b3fa489b14e66327f0af2
guid: 110a36d7de251bc41a444815f58a61c5
timeCreated: 1466179612
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []

+ 36
- 0
Context/Factories/Legacy/MonoBehaviourFactory.cs View File

@@ -0,0 +1,36 @@
#region

using System;
using Svelto.DataStructures;
using UnityEngine;

#endregion

namespace Svelto.Context.Legacy
{
public class MonoBehaviourFactory : Factories.IMonoBehaviourFactory
{
public MonoBehaviourFactory(IUnityContextHierarchyChangedListener unityContext)
{
_unityContext = new WeakReference<IUnityContextHierarchyChangedListener>(unityContext);
}

public M Build<M>(Func<M> constructor) where M : MonoBehaviour
{
DesignByContract.Check.Require(_unityContext.IsAlive == true, "Context is used, but not alive");

var mb = constructor();

_unityContext.Target.OnMonobehaviourAdded(mb);

GameObject go = mb.gameObject;

if (go.GetComponent<NotifyComponentsRemoved>() == null)
go.AddComponent<NotifyComponentsRemoved>().unityContext = _unityContext;

return mb;
}

WeakReference<IUnityContextHierarchyChangedListener> _unityContext;
}
}

Ticker/ITicker.cs.meta → Context/Factories/Legacy/MonoBehaviourFactory.cs.meta View File

@@ -1,5 +1,7 @@
fileFormatVersion: 2
guid: 0bf92a08bb722064ab845dea9463933e
guid: 86181a20ee37df64c9332a1c32369d72
timeCreated: 1466179768
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []

+ 1
- 1
Context/IContextNotifer.cs View File

@@ -5,7 +5,7 @@ namespace Svelto.Context
void NotifyFrameworkInitialized();
void NotifyFrameworkDeinitialized();

void AddFrameworkInitializationListener(IWaitForFrameworkInitialization obj);
void AddFrameworkInitializationListener(IWaitForFrameworkInitialization obj);
void AddFrameworkDestructionListener(IWaitForFrameworkDestruction obj);
}
}

+ 11
- 0
Context/IUnityContextHierarchyChangedListener.cs View File

@@ -0,0 +1,11 @@
using UnityEngine;

namespace Svelto.Context.Legacy
{
public interface IUnityContextHierarchyChangedListener
{
void OnMonobehaviourAdded(MonoBehaviour component);

void OnMonobehaviourRemoved(MonoBehaviour component);
}
}

+ 8
- 0
Context/IUnityContextHierarchyChangedListener.cs.meta View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 905bfd795bede6d448694b23dd88e0be
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

+ 9
- 0
Context/Unity.meta View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 14144568b327bbe40876c12a777e5f05
folderAsset: yes
timeCreated: 1434752394
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

+ 30
- 0
Context/Unity/NotifyComponentsRemoved.cs View File

@@ -0,0 +1,30 @@
using Svelto.DataStructures;
using UnityEngine;

namespace Svelto.Context.Legacy
{
public class NotifyComponentsRemoved : MonoBehaviour
{
public WeakReference<IUnityContextHierarchyChangedListener> unityContext { private get; set; }

void Start()
{
if (unityContext == null)
{
Destroy(this);
}
}

void OnDestroy()
{
if (unityContext == null || unityContext.IsAlive == false)
return;

MonoBehaviour[] components = gameObject.GetComponents<MonoBehaviour>();

for (int i = 0; i < components.Length; ++i)
if (components[i] != null)
unityContext.Target.OnMonobehaviourRemoved(components[i]);
}
}
}

+ 8
- 0
Context/Unity/NotifyComponentsRemoved.cs.meta View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 13278f0cd187d8d43acc6a02479e459b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

+ 183
- 0
DataStructures/CircularBufferIndexer.cs View File

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

namespace Svelto.DataStructures
{
// Serves as simple circular buffer dictionary, first in, first out
// Main drawback: it is the oldest in the list that is removed and the fact that we might re access a key
// isn't taken into account (we would have to do a shift in both arrays)
// Could be added as an option?

class CircularBufferIndexer<TKey, TVal> : IDictionary<TKey, TVal>
{
public ICollection<TKey> Keys
{
get { return _keys; }
}

public ICollection<TVal> Values
{
get { return _values; }
}

public int Count
{
get { throw new NotImplementedException(); }
}

public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}

public CircularBufferIndexer(int size)
{
_keys = new TKey[size];
_values = new TVal[size];
_length = _startIndex = _nextIndex = 0;
}

public TVal this[TKey key]
{
get
{
int index = _startIndex;
for (int i = 0; i < _length; ++i)
{
if (_keys[index].Equals(key))
{
return _values[index];
}

index = NextPosition(index);
}
throw new KeyNotFoundException();
}
set
{
int index = _startIndex;
for (int i = 0; i < _length; ++i)
{
if (_keys[index].Equals(key))
{
_values[index] = value;
return;
}

index = NextPosition(index);
}
throw new KeyNotFoundException();
}
}

public void Add(TKey key, TVal value)
{
if (ContainsKey(key))
{
this[key] = value;
return;
}

_keys[_nextIndex] = key;
_values[_nextIndex] = value;
_nextIndex = NextPosition(_nextIndex);
if (IsFull())
{
_startIndex = NextPosition(_startIndex);
}
else
{
++_length;
}
}

public bool ContainsKey(TKey key)
{
int index = _startIndex;
for (int i = 0; i < _length; ++i)
{
if (_keys[index].Equals(key))
{
return true;
}

index = NextPosition(index);
}
return false;
}

public bool Remove(TKey key)
{
throw new NotImplementedException();
}

public bool TryGetValue(TKey key, out TVal value)
{
value = default(TVal);
int index = _startIndex;
for (int i = 0; i < _length; ++i)
{
if (_keys[index].Equals(key))
{
value = _values[index];
return true;
}

index = NextPosition(index);
}
return false;
}

public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}

public void Add(KeyValuePair<TKey, TVal> item)
{
Add(item.Key, item.Value);
}

public void Clear()
{
throw new NotImplementedException();
}

public bool Contains(KeyValuePair<TKey, TVal> item)
{
throw new NotImplementedException();
}

public void CopyTo(KeyValuePair<TKey, TVal>[] array, int arrayIndex)
{
throw new NotImplementedException();
}

public bool Remove(KeyValuePair<TKey, TVal> item)
{
throw new NotImplementedException();
}

IEnumerator<KeyValuePair<TKey, TVal>> IEnumerable<KeyValuePair<TKey, TVal>>.GetEnumerator()
{
throw new NotImplementedException();
}

int NextPosition(int position)
{
return (position + 1) % _keys.Length;
}

bool IsFull()
{
return _length == _values.Length;
}

TKey[] _keys;
TVal[] _values;
int _startIndex;
int _nextIndex;
int _length;
}
}

+ 8
- 0
DataStructures/CircularBufferIndexer.cs.meta View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0d87fb4479e8365499c162fe0fc94acc
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

+ 146
- 112
DataStructures/FasterList.cs View File

@@ -1,7 +1,3 @@
/**
* Custom modified version of Michael Lyashenko's BetterList
**/

using System;
using System.Collections;
using System.Collections.Generic;
@@ -16,16 +12,6 @@ namespace Svelto.DataStructures
get { return _current; }
}

object IEnumerator.Current
{
get { return _current; }
}

T IEnumerator<T>.Current
{
get { return _current; }
}

public FasterListEnumerator(T[] buffer, int size)
{
_size = size;
@@ -34,19 +20,19 @@ namespace Svelto.DataStructures
_current = default(T);
}

public void Dispose()
object IEnumerator.Current
{
_buffer = null;
get { return _current; }
}

bool IEnumerator.MoveNext()
T IEnumerator<T>.Current
{
return MoveNext();
get { return _current; }
}

void IEnumerator.Reset()
public void Dispose()
{
Reset();
_buffer = null;
}

public bool MoveNext()
@@ -68,6 +54,16 @@ namespace Svelto.DataStructures
_counter = 0;
}

bool IEnumerator.MoveNext()
{
return MoveNext();
}

void IEnumerator.Reset()
{
Reset();
}

T[] _buffer;
int _counter;
int _size;
@@ -81,42 +77,42 @@ namespace Svelto.DataStructures
get { return (T)_buffer.Current; }
}

object IEnumerator.Current
public FasterListEnumeratorCast(FasterListEnumerator<U> buffer)
{
get { return (T)_buffer.Current; }
_buffer = buffer;
}

T IEnumerator<T>.Current
object IEnumerator.Current
{
get { return (T)_buffer.Current; }
}

public FasterListEnumeratorCast(FasterListEnumerator<U> buffer)
T IEnumerator<T>.Current
{
_buffer = buffer;
get { return (T)_buffer.Current; }
}

public void Dispose()
{}

bool IEnumerator.MoveNext()
public bool MoveNext()
{
return MoveNext();
return _buffer.MoveNext();
}

void IEnumerator.Reset()
public void Reset()
{
Reset();
_buffer.Reset();
}

public bool MoveNext()
bool IEnumerator.MoveNext()
{
return _buffer.MoveNext();
return MoveNext();
}

public void Reset()
void IEnumerator.Reset()
{
_buffer.Reset();
Reset();
}

FasterListEnumerator<U> _buffer;
@@ -124,20 +120,15 @@ namespace Svelto.DataStructures

public struct FasterReadOnlyList<T> : IList<T>
{
public int Count { get { return _list.Count; } }
public bool IsReadOnly { get { return true; } }

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

IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public T this[int index] { get { return _list[index]; } set { throw new NotImplementedException(); } }

public FasterListEnumerator<T> GetEnumerator()
{
@@ -169,9 +160,6 @@ namespace Svelto.DataStructures
throw new NotImplementedException();
}

public int Count { get { return _list.Count; } }
public bool IsReadOnly { get { return true; } }

public int IndexOf(T item)
{
return _list.IndexOf(item);
@@ -187,27 +175,32 @@ namespace Svelto.DataStructures
throw new NotImplementedException();
}

public T this[int index] { get { return _list[index]; } set { throw new NotImplementedException(); } }
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

readonly FasterList<T> _list;
}

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

public int Count { get { return _list.Count; } }
public bool IsReadOnly { get { return true; } }

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

IEnumerator<U> IEnumerable<U>.GetEnumerator()
{
return GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public U this[int index] { get { return (U)_list[index]; } set { throw new NotImplementedException(); } }

public FasterListEnumeratorCast<U, T> GetEnumerator()
{
@@ -239,9 +232,6 @@ namespace Svelto.DataStructures
throw new NotImplementedException();
}

public int Count { get { return _list.Count; } }
public bool IsReadOnly { get { return true; } }

public int IndexOf(U item)
{
return _list.IndexOf(item);
@@ -257,14 +247,23 @@ namespace Svelto.DataStructures
throw new NotImplementedException();
}

public U this[int index] { get { return (U)_list[index]; } set { throw new NotImplementedException(); } }
IEnumerator<U> IEnumerable<U>.GetEnumerator()
{
return GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

readonly FasterList<T> _list;
static public FasterList<T> DefaultList = new FasterList<T>();
}

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

public int Count
{
get { return _count; }
@@ -307,6 +306,12 @@ namespace Svelto.DataStructures
_count = listCopy.Count;
}

public T this[int i]
{
get { DesignByContract.Check.Require(i < _count, "out of bound index"); return _buffer[i]; }
set { DesignByContract.Check.Require(i < _count, "out of bound index"); _buffer[i] = value; }
}

public void Add(T item)
{
if (_count == _buffer.Length)
@@ -315,44 +320,46 @@ namespace Svelto.DataStructures
_buffer[_count++] = item;
}

public static FasterList<T> PreFill<U>(int initialSize) where U:T, new()
{
var list = new FasterList<T>(initialSize);

for (int i = 0; i < initialSize; i++)
list.Add(new U());

list.Clear();

return list;
}

public void AddRange(IEnumerable<T> items, int count)
{
AddRange(items.GetEnumerator(), count);
}

public void AddRange(IEnumerator<T> items, int count)
{
if (_count + count >= _buffer.Length)
AllocateMore(_count + count);

for (var i = items.GetEnumerator(); i.MoveNext();)
{
var item = i.Current;
_buffer[_count++] = item;
}
while (items.MoveNext())
_buffer[_count++] = items.Current;
}

public void AddRange(ICollection<T> items)
{
var count = items.Count;
if (_count + count >= _buffer.Length)
AllocateMore(_count + count);

for (var i = items.GetEnumerator(); i.MoveNext();)
{
var item = i.Current;
_buffer[_count++] = item;
}
AddRange(items.GetEnumerator(), items.Count);
}

public void AddRange(FasterList<T> items)
{
var count = items.Count;
if (_count + count >= _buffer.Length)
AllocateMore(_count + count);

Array.Copy(items._buffer, 0, _buffer, _count, count);
_count += count;
AddRange(items.ToArrayFast(), items.Count);
}

public void AddRange(T[] items)
public void AddRange(T[] items, int count)
{
var count = items.Length;
if (count == 0) return;

if (_count + count >= _buffer.Length)
AllocateMore(_count + count);

@@ -365,11 +372,22 @@ namespace Svelto.DataStructures
return new FasterReadOnlyList<T>(this);
}

/// <summary>
/// Careful, you could keep on holding references you don't want to hold to anymore
/// Use DeepClear in case.
/// </summary>
public void Clear()
{
_count = 0;
}

public void DeepClear()
{
Array.Clear(_buffer, 0, _buffer.Length);

_count = 0;
}

public bool Contains(T item)
{
var index = IndexOf(item);
@@ -387,16 +405,6 @@ namespace Svelto.DataStructures
return new FasterListEnumerator<T>(_buffer, Count);
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}

public int IndexOf(T item)
{
var comp = EqualityComparer<T>.Default;
@@ -442,16 +450,19 @@ namespace Svelto.DataStructures
{
DesignByContract.Check.Require(index < _count, "out of bound index");

--_count;

if (index == _count)
if (index == --_count)
return;

Array.Copy(_buffer, index + 1, _buffer, index, _count - index);

_buffer[_count] = default(T);
}

public void Resize(int newSize)
{
if (newSize < MIN_SIZE)
newSize = MIN_SIZE;

Array.Resize(ref _buffer, newSize);

_count = newSize;
@@ -468,10 +479,9 @@ namespace Svelto.DataStructures
this[index] = value;
}

public void Sort(Comparison<T> comparer)
public void Sort(IComparer<T> comparer)
{
Trim();
Array.Sort(_buffer, comparer);
Array.Sort(_buffer, 0, _count, comparer);
}

public T[] ToArray()
@@ -505,46 +515,70 @@ namespace Svelto.DataStructures
return true;
}

public void UnorderredRemoveAt(int index)
public T UnorderredRemoveAt(int index)
{
DesignByContract.Check.Require(index < _count, "out of bound index");
DesignByContract.Check.Require(index < _count && _count > 0, "out of bound index");

_buffer[index] = _buffer[--_count];
T item = _buffer[index];

if (index == --_count)
return item;

T swap = _buffer[index];
_buffer[index] = _buffer[_count];
_buffer[_count] = swap;

return item;
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

private void AllocateMore()
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}

void AllocateMore()
{
var newList = new T[Mathf.Max(_buffer.Length << 1, MIN_SIZE)];
if (_count > 0) _buffer.CopyTo(newList, 0);
_buffer = newList;
}

private void AllocateMore(int newSize)
void AllocateMore(int newSize)
{
var oldLength = _buffer.Length;
var oldLength = Mathf.Max(_buffer.Length, MIN_SIZE);

while (oldLength < newSize)
oldLength <<= 1;

var newList = new T[oldLength];
if (_count > 0) _buffer.CopyTo(newList, 0);
if (_count > 0) Array.Copy(_buffer, newList, _count);
_buffer = newList;
}

private void Trim()
public void Trim()
{
if (_count < _buffer.Length)
Resize(_count);
}

public T this[int i]
public bool Reuse(int index, out T result)
{
get { DesignByContract.Check.Require(i < _count, "out of bound index"); return _buffer[i]; }
set { DesignByContract.Check.Require(i < _count, "out of bound index"); _buffer[i] = value; }
result = default(T);

if (index >= _buffer.Length)
return false;

result = _buffer[index];

return result != null;
}

private const int MIN_SIZE = 32;
private T[] _buffer;
private int _count;
T[] _buffer;
int _count;
}
}

+ 3
- 1
DataStructures/FasterList.cs.meta View File

@@ -1,5 +1,7 @@
fileFormatVersion: 2
guid: 0859454e6e6d968498cea54757578eda
guid: d19e59cbec974dd4d821a7dd21f87a88
timeCreated: 1472488070
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []


+ 56
- 0
DataStructures/HashableWeakReference.cs View File

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

namespace Svelto.DataStructures
{
class HashableWeakRef<T> : IEquatable<HashableWeakRef<T>> where T : class
{
public bool isAlive { get { return _weakRef.IsAlive; } }
public T Target { get { return (T)_weakRef.Target; } }

public HashableWeakRef(T target)
{
_weakRef = new WeakReference(target);
_hash = target.GetHashCode();
}

public static bool operator !=(HashableWeakRef<T> a, HashableWeakRef<T> b)
{
return !(a == b);
}

public static bool operator ==(HashableWeakRef<T> a, HashableWeakRef<T> b)
{
if (a._hash != b._hash)
return false;

var tmpTargetA = (T) a._weakRef.Target;
var tmpTargetB = (T) b._weakRef.Target;

if (tmpTargetA == null || tmpTargetB == null)
return false;

return tmpTargetA == tmpTargetB;
}

public override bool Equals(object other)
{
if (other is HashableWeakRef<T>)
return this.Equals((HashableWeakRef<T>)other);

return false;
}

public bool Equals(HashableWeakRef<T> other)
{
return (this == other);
}

public override int GetHashCode()
{
return _hash;
}

int _hash;
WeakReference _weakRef;
}
}

Ticker/TickBehaviour.cs.meta → DataStructures/HashableWeakReference.cs.meta View File

@@ -1,5 +1,7 @@
fileFormatVersion: 2
guid: 82d08d9d100803c47b036667fdc671cf
guid: 0cfa789b3147c2e4e80d067693a58103
timeCreated: 1455809369
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []

Observer.meta → DataStructures/Priority Queue.meta View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 48e872bc336b396409c9316cdbfc045a
guid: 8fffa27a4e8919a4db6fe1369e22e1b5
folderAsset: yes
DefaultImporter:
userData:

+ 327
- 0
DataStructures/Priority Queue/HeapPriorityQueue.cs View File

@@ -0,0 +1,327 @@
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Svelto.DataStructures;

namespace Svelto.DataStructures
{
/// <summary>
/// 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>
public sealed class HeapPriorityQueue<T> : IPriorityQueue<T>
where T : PriorityQueueNode
{
private int _numNodes;
private readonly FasterList<T> _nodes;
private long _numNodesEverEnqueued;

/// <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>
public HeapPriorityQueue()
{
_numNodes = 0;
_nodes = new FasterList<T>();
_numNodesEverEnqueued = 0;
}

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

/// <summary>
/// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize),
/// attempting to enqueue another item will throw an exception. O(1)
/// </summary>
public int MaxSize
{
get
{
return _nodes.Count - 1;
}
}

/// <summary>
/// Removes every node 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.Clear();

_numNodes = 0;
}

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

/// <summary>
/// Enqueue a node - .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)
{
node.Priority = priority;
_numNodes++;
if (_nodes.Count < _numNodes)
_nodes.Resize(_numNodes + 1);

_nodes[_numNodes] = node;
node.QueueIndex = _numNodes;
node.InsertionIndex = _numNodesEverEnqueued++;
CascadeUp(_nodes[_numNodes]);
}

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

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

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

parent = node.QueueIndex / 2;
}
}

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

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

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

//Check if the right-child is higher-priority than either the current node or the left child
int childRightIndex = childLeftIndex + 1;
if(childRightIndex <= _numNodes)
{
T childRight = _nodes[childRightIndex];
if(HasHigherPriority(childRight, newParent))
{
newParent = childRight;
}
}

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

int temp = newParent.QueueIndex;
newParent.QueueIndex = finalQueueIndex;
finalQueueIndex = temp;
}
else
{
//See note above
node.QueueIndex = finalQueueIndex;
_nodes[finalQueueIndex] = node;
break;
}
}
}

/// <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
/// </summary>
#if NET_VERSION_4_5
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private bool HasHigherPriority(T higher, T lower)
{
return (higher.Priority < lower.Priority ||
(higher.Priority == lower.Priority && higher.InsertionIndex < lower.InsertionIndex));
}

/// <summary>
/// Removes the head of the queue (node with highest priority; ties are broken by order of insertion), and returns it. O(log n)
/// </summary>
public T Dequeue()
{
T returnMe = _nodes[1];
Remove(returnMe);
return returnMe;
}

/// <summary>
/// Returns the head of the queue, without removing it (use Dequeue() for that). O(1)
/// </summary>
public T First
{
get
{
return _nodes[1];
}
}

/// <summary>
/// This method must be called on a node 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)
{
node.Priority = priority;
OnNodeUpdated(node);
}

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

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

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

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

_numNodes--;
_nodes[node.QueueIndex] = null;

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

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

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

/// <summary>
/// <b>Should not be called in production code.</b>
/// Checks to make sure the queue is still in a valid state. Used for testing/debugging the queue.
/// </summary>
public bool IsValidQueue()
{
for(int i = 1; i < _nodes.Count; i++)
{
if(_nodes[i] != null)
{
int childLeftIndex = 2 * i;
if(childLeftIndex < _nodes.Count && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i]))
return false;

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

+ 8
- 0
DataStructures/Priority Queue/HeapPriorityQueue.cs.meta View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ac754b29409379046935c0890bab6dc5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

+ 23
- 0
DataStructures/Priority Queue/IPriorityQueue.cs View File

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

namespace Svelto.DataStructures
{
/// <summary>
/// The IPriorityQueue interface. This is mainly here for purists, and in case I decide to add more implementations later.
/// For speed purposes, it is actually recommended that you *don't* access the priority queue through this interface, since the JIT can
/// (theoretically?) optimize method calls from concrete-types slightly better.
/// </summary>
public interface IPriorityQueue<T> : IEnumerable<T>
where T : PriorityQueueNode
{
void Remove(T node);
void UpdatePriority(T node, double priority);
void Enqueue(T node, double priority);
T Dequeue();
T First { get; }
int Count { get; }
int MaxSize { get; }
void Clear();
bool Contains(T node);
}
}

+ 8
- 0
DataStructures/Priority Queue/IPriorityQueue.cs.meta View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5d399e4c2c1fe1f47833d6b70bf16184
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

+ 24
- 0
DataStructures/Priority Queue/PriorityQueueNode.cs View File

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

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

/// <summary>
/// <b>Used by the priority queue - do not edit this value.</b>
/// Represents the current position in the queue
/// </summary>
public int QueueIndex { get; set; }
}
}

+ 8
- 0
DataStructures/Priority Queue/PriorityQueueNode.cs.meta View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2a6cc02a61a6ff549b1dcac74c71681f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

+ 154
- 265
DataStructures/ReadOnlyDictionary.cs View File

@@ -1,28 +1,11 @@
//note: ripped from openstacknetsdk

using System;
using System.Collections;
using System.Collections.Generic;

namespace Svelto.DataStructures
{
public struct ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary
public struct ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly IDictionary<TKey, TValue> _dictionary;

/// <summary>
/// Initializes a new instance of the <see cref="ReadOnlyDictionary{TKey, TValue}"/> class
/// that is a wrapper around the specified dictionary.
/// </summary>
/// <param name="dictionary">The dictionary to wrap.</param>
public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");

_dictionary = dictionary;
}

public bool isInitialized { get { return _dictionary != null; } }

/// <summary>
@@ -40,45 +23,6 @@ namespace Svelto.DataStructures
}
}

/// <inheritdoc/>
/// <summary>
/// Gets the element that has the specified key.
/// </summary>
/// <exception cref="NotSupportedException">If the property is set.</exception>
TValue IDictionary<TKey, TValue>.this[TKey key]
{
get
{
return this[key];
}

set
{
throw new NotSupportedException();
}
}

/// <inheritdoc/>
/// <summary>
/// Gets the element that has the specified key.
/// </summary>
/// <exception cref="NotSupportedException">If the property is set.</exception>
object IDictionary.this[object key]
{
get
{
if (!(key is TKey))
return null;

return this[(TKey)key];
}

set
{
throw new NotSupportedException();
}
}

/// <summary>
/// Gets the number of items in the dictionary.
/// </summary>
@@ -88,8 +32,8 @@ namespace Svelto.DataStructures
public int Count
{
get
{
return _dictionary.Count;
{
return _dictionary.Count;
}
}

@@ -107,24 +51,6 @@ namespace Svelto.DataStructures
}
}

/// <inheritdoc/>
ICollection<TKey> IDictionary<TKey, TValue>.Keys
{
get
{
return Keys;
}
}

/// <inheritdoc/>
ICollection IDictionary.Keys
{
get
{
return Keys;
}
}

/// <summary>
/// Gets a collection that contains the values in the dictionary.
/// </summary>
@@ -139,155 +65,82 @@ namespace Svelto.DataStructures
}
}

/// <inheritdoc/>
ICollection<TValue> IDictionary<TKey, TValue>.Values
/// <summary>
/// Initializes a new instance of the <see cref="ReadOnlyDictionary{TKey, TValue}"/> class
/// that is a wrapper around the specified dictionary.
/// </summary>
/// <param name="dictionary">The dictionary to wrap.</param>
public ReadOnlyDictionary(Dictionary<TKey, TValue> dictionary)
{
get
{
return Values;
}
}
if (dictionary == null)
throw new ArgumentNullException("dictionary");

/// <inheritdoc/>
ICollection IDictionary.Values
{
get
{
return Values;
}
_dictionary = dictionary;
}

/// <inheritdoc/>
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
/// <summary>
/// Gets the element that has the specified key.
/// </summary>
/// <exception cref="NotSupportedException">If the property is set.</exception>
TValue IDictionary<TKey, TValue>.this[TKey key]
{
get
{
return true;
return this[key];
}
}

/// <inheritdoc/>
bool IDictionary.IsFixedSize
{
get
set
{
return true;
throw new NotSupportedException();
}
}

/// <inheritdoc/>
bool IDictionary.IsReadOnly
ICollection<TKey> IDictionary<TKey, TValue>.Keys
{
get
{
return true;
return Keys;
}
}

/// <inheritdoc/>
bool ICollection.IsSynchronized
ICollection<TValue> IDictionary<TKey, TValue>.Values
{
get
{
return false;
return Values;
}
}

/// <inheritdoc/>
object ICollection.SyncRoot
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
{
get
{
ICollection collection = this as ICollection;
if (collection == null)
return collection.SyncRoot;

throw new NotSupportedException("The current object does not support the SyncRoot property.");
return true;
}
}

/// <summary>
/// Determines whether the dictionary contains an element that has the specified key.
/// </summary>
/// <param name="key">The key to locate in the dictionary.</param>
/// <returns><see langword="true"/> if the dictionary contains an element that has the specified key; otherwise, <see langword="false"/>.</returns>
public bool ContainsKey(TKey key)
{
return _dictionary.ContainsKey(key);
}

/// <inheritdoc/>
bool IDictionary.Contains(object key)
{
if (key == null)
throw new ArgumentNullException("key");

if (key is TKey)
return ContainsKey((TKey)key);

return false;
}

/// <summary>
/// Returns an enumerator that iterates through the <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
/// </summary>
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dictionary.GetEnumerator();
}

/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

/// <inheritdoc/>
IDictionaryEnumerator IDictionary.GetEnumerator()
{
IDictionary dictionary = _dictionary as IDictionary;
if (dictionary != null)
return dictionary.GetEnumerator();

return new DictionaryEnumerator(_dictionary);
}

/// <summary>
/// Retrieves the value that is associated with the specified key.
/// </summary>
/// <param name="key">The key whose value will be retrieved.</param>
/// <param name="value">When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.</param>
/// <returns><see langword="true"/> if the object that implements <see cref="ReadOnlyDictionary{TKey, TValue}"/> contains an element with the specified key; otherwise, <see langword="false"/>.</returns>
public bool TryGetValue(TKey key, out TValue value)
{
return _dictionary.TryGetValue(key, out value);
}

/// <inheritdoc/>
void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
{
throw new NotSupportedException();
}

/// <inheritdoc/>
void IDictionary.Add(object key, object value)
{
throw new NotSupportedException();
}

/// <inheritdoc/>
bool IDictionary<TKey, TValue>.Remove(TKey key)
{
throw new NotSupportedException();
}

/// <inheritdoc/>
void IDictionary.Remove(object key)
{
throw new NotSupportedException();
}

/// <inheritdoc/>
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
@@ -318,29 +171,60 @@ namespace Svelto.DataStructures
throw new NotSupportedException();
}

/// <inheritdoc/>
void IDictionary.Clear()
bool IDictionary<TKey, TValue>.ContainsKey(TKey key)
{
throw new NotSupportedException();
return _dictionary.ContainsKey(key);
}

/// <inheritdoc/>
void ICollection.CopyTo(Array array, int index)
bool IDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value)
{
throw new NotImplementedException();
return _dictionary.TryGetValue(key, out value);
}

/// <summary>
int ICollection<KeyValuePair<TKey, TValue>>.Count
{
get { return _dictionary.Count; }
}

IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return _dictionary.GetEnumerator();
}

/// <summary>
/// Determines whether the dictionary contains an element that has the specified key.
/// </summary>
/// <param name="key">The key to locate in the dictionary.</param>
/// <returns><see langword="true"/> if the dictionary contains an element that has the specified key; otherwise, <see langword="false"/>.</returns>
public bool ContainsKey(TKey key)
{
return _dictionary.ContainsKey(key);
}

public DictionaryEnumerator GetEnumerator()
{
return new DictionaryEnumerator(_dictionary);
}

/// <summary>
/// Retrieves the value that is associated with the specified key.
/// </summary>
/// <param name="key">The key whose value will be retrieved.</param>
/// <param name="value">When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.</param>
/// <returns><see langword="true"/> if the object that implements <see cref="ReadOnlyDictionary{TKey, TValue}"/> contains an element with the specified key; otherwise, <see langword="false"/>.</returns>
public bool TryGetValue(TKey key, out TValue value)
{
return _dictionary.TryGetValue(key, out value);
}

readonly Dictionary<TKey, TValue> _dictionary;

/// <summary>
/// Represents a read-only collection of the keys of a <see cref="ReadOnlyDictionary{TKey, TValue}"/> object.
/// </summary>
public struct KeyCollection : ICollection<TKey>, ICollection
{
/// <summary>
/// The wrapped collection of keys.
/// </summary>
private readonly ICollection<TKey> _keys;

/// <summary>
/// <summary>
/// Initializes a new instance of the <see cref="KeyCollection"/> class
/// as a wrapper around the specified collection of keys.
/// </summary>
@@ -354,48 +238,54 @@ namespace Svelto.DataStructures
_keys = keys;
}

/// <summary>
/// Gets the number of elements in the collection.
/// </summary>
/// <value>
/// The number of elements in the collection.
/// </value>
public int Count
/// <inheritdoc/>
bool ICollection.IsSynchronized
{
get
{
return _keys.Count;
return false;
}
}

/// <inheritdoc/>
bool ICollection<TKey>.IsReadOnly
/// <inheritdoc/>
object ICollection.SyncRoot
{
get
{
return true;
throw new NotImplementedException();
}
}

/// <inheritdoc/>
bool ICollection.IsSynchronized
/// <inheritdoc/>
void ICollection.CopyTo(Array array, int index)
{
throw new NotImplementedException();
}

/// <summary>
/// Gets the number of elements in the collection.
/// </summary>
/// <value>
/// The number of elements in the collection.
/// </value>
public int Count
{
get
{
return false;
return _keys.Count;
}
}

/// <inheritdoc/>
object ICollection.SyncRoot
/// <inheritdoc/>
bool ICollection<TKey>.IsReadOnly
{
get
{
throw new NotImplementedException();
return true;
}
}

/// <summary>
/// <summary>
/// Copies the elements of the collection to an array, starting at a specific array index.
/// </summary>
/// <param name="array">The one-dimensional array that is the destination of the elements copied from the collection. The array must have zero-based indexing.</param>
@@ -414,13 +304,7 @@ namespace Svelto.DataStructures
_keys.CopyTo(array, arrayIndex);
}

/// <inheritdoc/>
void ICollection.CopyTo(Array array, int index)
{
throw new NotImplementedException();
}

/// <summary>
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
@@ -429,35 +313,40 @@ namespace Svelto.DataStructures
return _keys.GetEnumerator();
}

/// <inheritdoc/>
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

/// <inheritdoc/>
/// <inheritdoc/>
bool ICollection<TKey>.Contains(TKey item)
{
return _keys.Contains(item);
}

/// <inheritdoc/>
/// <inheritdoc/>
void ICollection<TKey>.Add(TKey item)
{
throw new NotSupportedException();
}

/// <inheritdoc/>
/// <inheritdoc/>
bool ICollection<TKey>.Remove(TKey item)
{
throw new NotSupportedException();
}

/// <inheritdoc/>
/// <inheritdoc/>
void ICollection<TKey>.Clear()
{
throw new NotSupportedException();
}

/// <summary>
/// The wrapped collection of keys.
/// </summary>
readonly ICollection<TKey> _keys;
}

/// <summary>
@@ -465,11 +354,6 @@ namespace Svelto.DataStructures
/// </summary>
public struct ValueCollection : ICollection<TValue>, ICollection
{
/// <summary>
/// The wrapped collection of values.
/// </summary>
private readonly ICollection<TValue> _values;

/// <summary>
/// Initializes a new instance of the <see cref="ValueCollection"/> class
/// as a wrapper around the specified collection of values.
@@ -484,44 +368,50 @@ namespace Svelto.DataStructures
_values = values;
}

/// <summary>
/// Gets the number of elements in the collection.
/// </summary>
/// <value>
/// The number of elements in the collection.
/// </value>
public int Count
/// <inheritdoc/>
bool ICollection.IsSynchronized
{
get
{
return _values.Count;
return false;
}
}

/// <inheritdoc/>
bool ICollection<TValue>.IsReadOnly
object ICollection.SyncRoot
{
get
{
return true;
throw new NotImplementedException();
}
}

/// <inheritdoc/>
bool ICollection.IsSynchronized
void ICollection.CopyTo(Array array, int index)
{
throw new NotImplementedException();
}

/// <summary>
/// Gets the number of elements in the collection.
/// </summary>
/// <value>
/// The number of elements in the collection.
/// </value>
public int Count
{
get
{
return false;
return _values.Count;
}
}

/// <inheritdoc/>
object ICollection.SyncRoot
bool ICollection<TValue>.IsReadOnly
{
get
{
throw new NotImplementedException();
return true;
}
}

@@ -544,12 +434,6 @@ namespace Svelto.DataStructures
_values.CopyTo(array, arrayIndex);
}

/// <inheritdoc/>
void ICollection.CopyTo(Array array, int index)
{
throw new NotImplementedException();
}

/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
@@ -588,54 +472,47 @@ namespace Svelto.DataStructures
{
throw new NotSupportedException();
}

/// <summary>
/// The wrapped collection of values.
/// </summary>
readonly ICollection<TValue> _values;
}

struct DictionaryEnumerator : IDictionaryEnumerator
public struct DictionaryEnumerator:IEnumerator<KeyValuePair<TKey,TValue>>
{
private readonly IEnumerator<KeyValuePair<TKey, TValue>> _enumerator;

public DictionaryEnumerator(IDictionary<TKey, TValue> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");

_enumerator = dictionary.GetEnumerator();
}

/// <inheritdoc/>
public DictionaryEntry Entry
public TKey Key
{
get
{
KeyValuePair<TKey, TValue> current = _enumerator.Current;
return new DictionaryEntry(current.Key, current.Value);
return _enumerator.Current.Key;
}
}

/// <inheritdoc/>
public object Key
public TValue Value
{
get
{
return _enumerator.Current.Key;
return _enumerator.Current.Value;
}
}

/// <inheritdoc/>
public object Value
public DictionaryEnumerator(IDictionary<TKey, TValue> dictionary)
{
get
{
return _enumerator.Current.Value;
}
if (dictionary == null)
throw new ArgumentNullException("dictionary");
_enumerator = dictionary.GetEnumerator();
}

/// <inheritdoc/>
public object Current
public KeyValuePair<TKey, TValue> Current
{
get
{
return Entry;
return _enumerator.Current;
}
}

@@ -650,6 +527,18 @@ namespace Svelto.DataStructures
{
_enumerator.Reset();
}

object IEnumerator.Current
{
get { return _enumerator.Current; }
}

public void Dispose()
{
_enumerator.Dispose();
}

readonly IEnumerator<KeyValuePair<TKey, TValue>> _enumerator;
}
}
}

+ 183
- 0
DataStructures/SerializableDictionary.cs View File

@@ -0,0 +1,183 @@
#define XML_ENABLED

using System;
using System.Runtime.Serialization;
#if XML_ENABLED
using System.Xml;
using System.Xml.Serialization;
#endif
using System.Collections.Generic;

[Serializable()]
public struct KeyValueSerialization<TKey, TVal>
{
public TKey Key;
public TVal Value;
}

[Serializable()]
public class SerializableDictionary<TKey, TVal> : Dictionary<TKey, TVal>,
#if XML_ENABLED
IXmlSerializable,
#endif
ISerializable
{
#region Constants
private const string DictionaryNodeName = "Dictionary";
private const string ItemNodeName = "Item";
private const string KeyNodeName = "Key";
private const string ValueNodeName = "Value";
#endregion
#region Constructors
public SerializableDictionary()
{
}
public SerializableDictionary(IDictionary<TKey, TVal> dictionary)
: base(dictionary)
{
}
public SerializableDictionary(IEqualityComparer<TKey> comparer)
: base(comparer)
{
}
public SerializableDictionary(int capacity)
: base(capacity)
{
}
public SerializableDictionary(IDictionary<TKey, TVal> dictionary, IEqualityComparer<TKey> comparer)
: base(dictionary, comparer)
{
}
public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer)
: base(capacity, comparer)
{
}
#endregion
#region ISerializable Members
public SerializableDictionary(SerializationInfo info, StreamingContext context)
{
int itemCount = info.GetInt32("count");
for (int i = 0; i < itemCount; i++)
{
KeyValueSerialization<TKey, TVal> kvp = (KeyValueSerialization<TKey, TVal>)info.GetValue(String.Format("Im{0}", i), typeof(KeyValueSerialization<TKey, TVal>));
this.Add(kvp.Key, kvp.Value);
}
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("count", this.Count);
int itemIdx = 0;
foreach (KeyValuePair<TKey, TVal> kvp in this)
{
KeyValueSerialization<TKey, TVal> kvs = new KeyValueSerialization<TKey, TVal>();
kvs.Key = kvp.Key;
kvs.Value = kvp.Value;
info.AddValue(String.Format("Im{0}", itemIdx), kvs, typeof(KeyValueSerialization<TKey, TVal>));
itemIdx++;
}
}
#endregion
#if XML_ENABLED
#region IXmlSerializable Members
void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
{
//writer.WriteStartElement(DictionaryNodeName);
foreach (KeyValuePair<TKey, TVal> kvp in this)
{
writer.WriteStartElement(ItemNodeName);
writer.WriteStartElement(KeyNodeName);
KeySerializer.Serialize(writer, kvp.Key);
writer.WriteEndElement();
writer.WriteStartElement(ValueNodeName);
ValueSerializer.Serialize(writer, kvp.Value);
writer.WriteEndElement();
writer.WriteEndElement();
}
//writer.WriteEndElement();
}
void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
{
if (reader.IsEmptyElement)
{
return;
}
// Move past container
if (!reader.Read())
{
throw new XmlException("Error in Deserialization of Dictionary");
}
//reader.ReadStartElement(DictionaryNodeName);
while (reader.NodeType != XmlNodeType.EndElement)
{
reader.ReadStartElement(ItemNodeName);
reader.ReadStartElement(KeyNodeName);
TKey key = (TKey)KeySerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadStartElement(ValueNodeName);
TVal value = (TVal)ValueSerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadEndElement();
this.Add(key, value);
reader.MoveToContent();
}
//reader.ReadEndElement();
reader.ReadEndElement(); // Read End Element to close Read of containing node
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
{
return null;
}

#endregion
#region Private Properties

protected XmlSerializer ValueSerializer
{
get
{
if (valueSerializer == null)
{
valueSerializer = new XmlSerializer(typeof(TVal));
}
return valueSerializer;
}
}

private XmlSerializer KeySerializer
{
get
{
if (keySerializer == null)
{
keySerializer = new XmlSerializer(typeof(TKey));
}
return keySerializer;
}
}

#endregion
#region Private Members
private XmlSerializer keySerializer = null;
private XmlSerializer valueSerializer = null;
#endregion
#endif
}


+ 7
- 0
DataStructures/SerializableDictionary.cs.meta View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b4a9cb2903cd07946a8650aeefb8d853
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}

+ 303
- 0
DataStructures/ThreadSafeDictionary.cs View File

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

namespace Svelto.DataStructures
{
/// <summary>
/// original code: http://devplanet.com/blogs/brianr/archive/2008/09/29/thread-safe-dictionary-update.aspx
/// simplified (not an IDictionary) and apdated (uses FasterList)
/// </summary>
/// <typeparam name = "TKey"></typeparam>
/// <typeparam name = "TValue"></typeparam>
[Serializable]
public class ThreadSafeDictionary<TKey, TValue>
{
// setup the lock;
public virtual int Count
{
get
{
using (new ReadOnlyLock(dictionaryLock))
{
return dict.Count;
}
}
}

public virtual bool IsReadOnly
{
get
{
using (new ReadOnlyLock(dictionaryLock))
{
return dict.IsReadOnly;
}
}
}

public virtual FasterList<TKey> Keys
{
get
{
using (new ReadOnlyLock(dictionaryLock))
{
return new FasterList<TKey>(dict.Keys);
}
}
}

public virtual FasterList<TValue> Values
{
get
{
using (new ReadOnlyLock(dictionaryLock))
{
return new FasterList<TValue>(dict.Values);
}
}
}

public virtual TValue this[TKey key]
{
get
{
using (new ReadOnlyLock(dictionaryLock))
{
return dict[key];
}
}

set
{
using (new WriteLock(dictionaryLock))
{
dict[key] = value;
}
}
}

public virtual void Add(KeyValuePair<TKey, TValue> item)
{
using (new WriteLock(dictionaryLock))
{
dict.Add(item);
}
}

public virtual void Clear()
{
using (new WriteLock(dictionaryLock))
{
dict.Clear();
}
}

public virtual bool Contains(KeyValuePair<TKey, TValue> item)
{
using (new ReadOnlyLock(dictionaryLock))
{
return dict.Contains(item);
}
}

public virtual void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
using (new ReadOnlyLock(dictionaryLock))
{
dict.CopyTo(array, arrayIndex);
}
}

public virtual bool Remove(KeyValuePair<TKey, TValue> item)
{
using (new WriteLock(dictionaryLock))
{
return dict.Remove(item);
}
}

public virtual void Add(TKey key, TValue value)
{
using (new WriteLock(dictionaryLock))
{
dict.Add(key, value);
}
}

public virtual bool ContainsKey(TKey key)
{
using (new ReadOnlyLock(dictionaryLock))
{
return dict.ContainsKey(key);
}
}

public virtual bool Remove(TKey key)
{
using (new WriteLock(dictionaryLock))
{
return dict.Remove(key);
}
}

public virtual bool TryGetValue(TKey key, out TValue value)
{
using (new ReadOnlyLock(dictionaryLock))
{
return dict.TryGetValue(key, out value);
}
}

/// <summary>
/// Merge does a blind remove, and then add. Basically a blind Upsert.
/// </summary>
/// <param name = "key">Key to lookup</param>
/// <param name = "newValue">New Value</param>
public void MergeSafe(TKey key, TValue newValue)
{
using (new WriteLock(dictionaryLock))
{
// take a writelock immediately since we will always be writing
if (dict.ContainsKey(key))
dict.Remove(key);

dict.Add(key, newValue);
}
}

/// <summary>
/// This is a blind remove. Prevents the need to check for existence first.
/// </summary>
/// <param name = "key">Key to remove</param>
public void RemoveSafe(TKey key)
{
using (new ReadLock(dictionaryLock))
{
if (dict.ContainsKey(key))
using (new WriteLock(dictionaryLock))
{
dict.Remove(key);
}
}
}

// This is the internal dictionary that we are wrapping
readonly IDictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>();

[NonSerialized] readonly ReaderWriterLockSlim dictionaryLock = Locks.GetLockInstance(LockRecursionPolicy.NoRecursion);
}

public static class Locks
{
public static ReaderWriterLockSlim GetLockInstance()
{
return GetLockInstance(LockRecursionPolicy.SupportsRecursion);
}

public static ReaderWriterLockSlim GetLockInstance(LockRecursionPolicy recursionPolicy)
{
return new ReaderWriterLockSlim(recursionPolicy);
}

public static void GetReadLock(ReaderWriterLockSlim locks)
{
var lockAcquired = false;
while (!lockAcquired)
lockAcquired = locks.TryEnterUpgradeableReadLock(1);
}

public static void GetReadOnlyLock(ReaderWriterLockSlim locks)
{
var lockAcquired = false;
while (!lockAcquired)
lockAcquired = locks.TryEnterReadLock(1);
}

public static void GetWriteLock(ReaderWriterLockSlim locks)
{
var lockAcquired = false;
while (!lockAcquired)
lockAcquired = locks.TryEnterWriteLock(1);
}

public static void ReleaseLock(ReaderWriterLockSlim locks)
{
ReleaseWriteLock(locks);
ReleaseReadLock(locks);
ReleaseReadOnlyLock(locks);
}

public static void ReleaseReadLock(ReaderWriterLockSlim locks)
{
if (locks.IsUpgradeableReadLockHeld)
locks.ExitUpgradeableReadLock();
}

public static void ReleaseReadOnlyLock(ReaderWriterLockSlim locks)
{
if (locks.IsReadLockHeld)
locks.ExitReadLock();
}

public static void ReleaseWriteLock(ReaderWriterLockSlim locks)
{
if (locks.IsWriteLockHeld)
locks.ExitWriteLock();
}
}

public abstract class BaseLock : IDisposable
{
protected ReaderWriterLockSlim _Locks;

public BaseLock(ReaderWriterLockSlim locks)
{
_Locks = locks;
}

public abstract void Dispose();
}

public class ReadLock : BaseLock
{
public ReadLock(ReaderWriterLockSlim locks)
: base(locks)
{
Locks.GetReadLock(_Locks);
}

public override void Dispose()
{
Locks.ReleaseReadLock(_Locks);
}
}

public class ReadOnlyLock : BaseLock
{
public ReadOnlyLock(ReaderWriterLockSlim locks)
: base(locks)
{
Locks.GetReadOnlyLock(_Locks);
}

public override void Dispose()
{
Locks.ReleaseReadOnlyLock(_Locks);
}
}

public class WriteLock : BaseLock
{
public WriteLock(ReaderWriterLockSlim locks)
: base(locks)
{
Locks.GetWriteLock(_Locks);
}

public override void Dispose()
{
Locks.ReleaseWriteLock(_Locks);
}
}
}

Ticker/UnityTicker.cs.meta → DataStructures/ThreadSafeDictionary.cs.meta View File

@@ -1,5 +1,7 @@
fileFormatVersion: 2
guid: b62a08472017f914d9fc399eeb95108e
guid: db7c720ce4e437f48b1380223ba08192
timeCreated: 1470829214
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []

+ 50
- 3
DataStructures/ThreadSafeQueue.cs View File

@@ -1,4 +1,3 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
@@ -88,12 +87,12 @@ namespace Svelto.DataStructures
}
}

public List<T> DequeueAll()
public FasterList<T> DequeueAll()
{
LockQ.EnterWriteLock();
try
{
List<T> returnList = new List<T>();
FasterList<T> returnList = new FasterList<T>();

while (m_Queue.Count > 0)
returnList.Add(m_Queue.Dequeue());
@@ -107,6 +106,40 @@ namespace Svelto.DataStructures
}
}

public void DequeueAllInto(FasterList<T> list)
{
LockQ.EnterWriteLock();
try
{
while (m_Queue.Count > 0)
list.Add(m_Queue.Dequeue());
}

finally
{
LockQ.ExitWriteLock();
}
}

public FasterList<U> DequeueAllAs<U>() where U:class
{
LockQ.EnterWriteLock();
try
{
FasterList<U> returnList = new FasterList<U>();

while (m_Queue.Count > 0)
returnList.Add(m_Queue.Dequeue() as U);

return returnList;
}

finally
{
LockQ.ExitWriteLock();
}
}

public T Peek()
{
LockQ.EnterWriteLock();
@@ -126,6 +159,20 @@ namespace Svelto.DataStructures
}
}

public void Clear()
{
LockQ.EnterWriteLock();
try
{
m_Queue.Clear();
}

finally
{
LockQ.ExitWriteLock();
}
}

public bool TryDequeue(out T item)
{
LockQ.EnterWriteLock();


+ 5
- 0
DataStructures/WeakReference.cs View File

@@ -55,4 +55,9 @@ namespace Svelto.DataStructures
: base(info, context)
{ }
}

public static class WeakReferenceUtility
{
public static bool IsValid(this WeakReference obj) { return obj != null && obj.IsAlive == true && obj.Target != null; }
}
}

+ 2
- 0
ECS.meta View File

@@ -3,3 +3,5 @@ guid: 5015ea56c030d6542bd4daa46f59e549
folderAsset: yes
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

+ 0
- 39
ECS/Dispatcher/Dispatcher.cs View File

@@ -1,39 +0,0 @@
public class Dispatcher<S, T>
{
public event System.Action<S, T> subscribers;

private Dispatcher() { }

public Dispatcher(S sender)
{
_sender = sender;
}

public void Dispatch(ref T value)
{
if (subscribers != null)
subscribers(_sender, value);
}

readonly S _sender;
}

public class Dispatcher<S>
{
public event System.Action<S> subscribers;

private Dispatcher() { }

public Dispatcher(S sender)
{
_sender = sender;
}

public void Dispatch()
{
if (subscribers != null)
subscribers(_sender);
}

readonly S _sender;
}

+ 0
- 12
ECS/Dispatcher/Dispatcher.cs.meta View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: d8886d5d5f5929c479998a1316264c23
timeCreated: 1436121137
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 13
- 23
ECS/Dispatcher/DispatcherOnChange.cs View File

@@ -1,34 +1,24 @@
using System.Collections.Generic;

public class DispatcherOnChange<S, T>: Dispatcher<S, T>
namespace Svelto.ECS
{
public DispatcherOnChange(S sender) : base(sender) { }

public T value
public class DispatcherOnChange<T> : DispatcherOnSet<T>
{
set
public DispatcherOnChange(int senderID) : base(senderID)
{ }

public new T value
{
if (EqualityComparer<T>.Default.Equals(value, _value) == false)
set
{
_value = value;
if (EqualityComparer<T>.Default.Equals(value, _value) == false)
base.value = _value;
}

Dispatch(ref value);
get
{
return _value;
}
}
}

T _value;
}

public class DispatcherOnSet<S, T>: Dispatcher<S, T>
{
public DispatcherOnSet(S sender) : base(sender) { }

public T value
{
set
{
Dispatch(ref value);
}
}
}

+ 48
- 0
ECS/Dispatcher/DispatcherOnSet.cs View File

@@ -0,0 +1,48 @@
namespace Svelto.ECS
{
public class DispatcherOnSet<T>
{
public DispatcherOnSet(int senderID)
{
_senderID = senderID;
_subscribers = new System.Collections.Generic.HashSet<WeakAction<int, T>>();
}

public T value
{
set
{
_value = value;

if (_subscribers != null)
{
using (var enumerator = _subscribers.GetEnumerator())
{
while (enumerator.MoveNext() == true)
enumerator.Current.Invoke(_senderID, _value);
}
}
}

get
{
return _value;
}
}

public void NotifyOnDataChange(System.Action<int, T> action)
{
_subscribers.Add(new WeakAction<int, T>(action));
}

public void StopNotifyOnDataChange(System.Action<int, T> action)
{
_subscribers.Remove(new WeakAction<int, T>(action));
}

protected T _value;
protected int _senderID;

protected System.Collections.Generic.HashSet<WeakAction<int, T>> _subscribers;
}
}

+ 12
- 0
ECS/Dispatcher/DispatcherOnSet.cs.meta View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cbb1d54b291794b4686526222ee2bbb6
timeCreated: 1471250507
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 36
- 7
ECS/EngineNodeDB.cs View File

@@ -2,14 +2,17 @@ using System;
using System.Collections.Generic;
using Svelto.DataStructures;

namespace Svelto.ES
namespace Svelto.ECS
{
class EngineNodeDB : IEngineNodeDB
{
internal EngineNodeDB(Dictionary<Type, FasterList<INode>> nodesDB, Dictionary<Type, Dictionary<int, INode>> nodesDBdic)
internal EngineNodeDB( Dictionary<Type, FasterList<INode>> nodesDB,
Dictionary<Type, Dictionary<int, INode>> nodesDBdic,
Dictionary<Type, FasterList<INode>> nodesDBgroups)
{
this._nodesDB = nodesDB;
this._nodesDBdic = nodesDBdic;
_nodesDB = nodesDB;
_nodesDBdic = nodesDBdic;
_nodesDBgroups = nodesDBgroups;
}

public FasterReadOnlyListCast<INode, T> QueryNodes<T>() where T:INode
@@ -17,7 +20,7 @@ namespace Svelto.ES
var type = typeof(T);

if (_nodesDB.ContainsKey(type) == false)
return new FasterReadOnlyListCast<INode, T>(FasterReadOnlyListCast<INode, T>.DefaultList);
return RetrieveEmptyNodeList<T>();

return new FasterReadOnlyListCast<INode, T>(_nodesDB[type]);
}
@@ -32,6 +35,26 @@ namespace Svelto.ES
return new ReadOnlyDictionary<int, INode>(_nodesDBdic[type]);
}

public T QueryNodeFromGroup<T>(int groupID) where T : INode
{
return QueryNode<T>(groupID);
}

public bool QueryNodeFromGroup<T>(int groupID, out T node) where T : INode
{
return QueryNode<T>(groupID, out node);
}

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

if (_nodesDBgroups.ContainsKey(type) == false)
return RetrieveEmptyNodeList<T>();

return new FasterReadOnlyListCast<INode, T>(_nodesDBgroups[type]);
}

public bool QueryNode<T>(int ID, out T node) where T:INode
{
var type = typeof(T);
@@ -62,9 +85,15 @@ namespace Svelto.ES
throw new Exception("Node Not Found");
}

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

Dictionary<Type, FasterList<INode>> _nodesDB;
Dictionary<Type, Dictionary<int, INode>> _nodesDBdic;
Dictionary<Type, Dictionary<int, INode>> _nodesDBdic;
Dictionary<Type, FasterList<INode>> _nodesDBgroups;

ReadOnlyDictionary<int, INode> _defaultEmptyNodeDict = new ReadOnlyDictionary<int, INode>(new Dictionary<int, INode>());
ReadOnlyDictionary<int, INode> _defaultEmptyNodeDict = new ReadOnlyDictionary<int, INode>(new Dictionary<int, INode>());
}
}

+ 183
- 35
ECS/EnginesRoot.cs View File

@@ -1,38 +1,117 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.Ticker;
using Nodes.Player;
using UnityEngine;

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

namespace Svelto.ECS
{
public sealed class EnginesRoot: IEnginesRoot, IEndOfFrameTickable, IEntityFactory
class Scheduler : MonoBehaviour
{
public EnginesRoot(ITicker ticker)
IEnumerator Start()
{
ticker.Add(this);
while (true)
{
yield return new WaitForEndOfFrame();

OnTick();
}
}

internal Action OnTick;
}

public sealed class EnginesRoot : IEnginesRoot, IEntityFactory
{
public EnginesRoot()
{
_nodeEngines = new Dictionary<Type, FasterList<INodeEngine<INode>>>();
_engineRootWeakReference = new WeakReference<EnginesRoot>(this);
_otherEnginesReferences = new FasterList<IEngine>();
_nodesDB = new Dictionary<Type, FasterList<INode>>();
_nodesDBdic = new Dictionary<Type, Dictionary<int, INode>>();

_nodesToAdd = new Queue<INode>();
_nodesToRemove = new Queue<INode>();
_nodesToAdd = new FasterList<INode>();
_groupNodesToAdd = new FasterList<INode>();

_nodesDBgroups = new Dictionary<Type, FasterList<INode>>();

GameObject go = new GameObject("ECSScheduler");

go.AddComponent<Scheduler>().OnTick += SubmitNodes;

#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
GameObject debugEngineObject = new GameObject("Engine Debugger");
debugEngineObject.gameObject.AddComponent<EngineProfilerBehaviour>();
#endif
}

public void EndOfFrameTick(float deltaSec)
void SubmitNodes()
{
while (_nodesToAdd.Count > 0) InternalAdd(_nodesToAdd.Dequeue());
while (_nodesToRemove.Count > 0) InternalRemove(_nodesToRemove.Dequeue());
int groupNodesCount;
int nodesCount;
bool newNodesHaveBeenAddedWhileIterating;
int startNodes = 0;
int startGroupNodes = 0;
int numberOfReenteringLoops = 0;

do
{
groupNodesCount = _groupNodesToAdd.Count;
nodesCount = _nodesToAdd.Count;

for (int i = startNodes; i < nodesCount; i++)
{
var node = _nodesToAdd[i];
AddNodeToTheDB(node, node.GetType());
}

for (int i = startGroupNodes; i < groupNodesCount; i++)
{
var node = _groupNodesToAdd[i];
AddNodeToGroupDB(node, node.GetType());
}

for (int i = startNodes; i < nodesCount; i++)
{
var node = _nodesToAdd[i];
AddNodeToTheSuitableEngines(node, node.GetType());
}

for (int i = startGroupNodes; i < groupNodesCount; i++)
{
var node = _groupNodesToAdd[i];
AddNodeToTheSuitableEngines(node, node.GetType());
}

newNodesHaveBeenAddedWhileIterating = _groupNodesToAdd.Count > groupNodesCount || _nodesToAdd.Count > nodesCount;

startNodes = nodesCount;
startGroupNodes = groupNodesCount;

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

numberOfReenteringLoops++;

} while (newNodesHaveBeenAddedWhileIterating);

_nodesToAdd.Clear();
_groupNodesToAdd.Clear();
}

public void AddEngine(IEngine engine)
{
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
EngineProfiler.AddEngine(engine);
#endif
if (engine is IQueryableNodeEngine)
(engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic);
(engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _nodesDBgroups);

if (engine is INodesEngine)
{
@@ -47,7 +126,7 @@ namespace Svelto.ES
if (baseType.IsGenericType)
{
var genericType = baseType.GetGenericTypeDefinition();
if (genericType == typeof(SingleNodeEngine<>))
{
AddEngine(engine as INodeEngine<INode>, baseType.GetGenericArguments(), _nodeEngines);
@@ -56,7 +135,7 @@ namespace Svelto.ES
}
}

_otherEnginesReferences.Add(engine);
_otherEnginesReferences.Add(engine);
}

public void BuildEntity(int ID, EntityDescriptor ed)
@@ -64,14 +143,24 @@ namespace Svelto.ES
var entityNodes = ed.BuildNodes(ID, (node) =>
{
if (_engineRootWeakReference.IsValid == true)
_engineRootWeakReference.Target._nodesToRemove.Enqueue(node);
InternalRemove(node);
});
for (int i = 0; i < entityNodes.Count; i++)
_nodesToAdd.Enqueue(entityNodes[i]);
_nodesToAdd.AddRange(entityNodes);
}

static void AddEngine<T>(T engine, Type[] types, Dictionary<Type, FasterList<INodeEngine<INode>>> engines) where T:INodeEngine<INode>
public void BuildEntityGroup(int groupID, EntityDescriptor ed)
{
var entityNodes = ed.BuildNodes(groupID, (node) =>
{
if (_engineRootWeakReference.IsValid == true)
InternalGroupRemove(node);
});

_groupNodesToAdd.AddRange(entityNodes);
}

static void AddEngine<T>(T engine, Type[] types, Dictionary<Type, FasterList<INodeEngine<INode>>> engines) where T : INodeEngine<INode>
{
for (int i = 0; i < types.Length; i++)
{
@@ -90,20 +179,15 @@ namespace Svelto.ES
}
}

void InternalAdd<T>(T node) where T:INode
void AddNodeToGroupDB(INode node, Type nodeType)
{
Type nodeType = node.GetType();

AddNodeToTheSuitableEngines(node, nodeType);
AddNodeToTheDB(node, nodeType);
}
FasterList<INode> nodes;
if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == false)
nodes = _nodesDBgroups[nodeType] = new FasterList<INode>();

void InternalRemove<T>(T node) where T:INode
{
Type nodeType = node.GetType();
nodes.Add(node);

RemoveNodeFromEngines(node, nodeType);
RemoveNodeFromTheDB(node, nodeType);
AddNodeToNodesDictionary(node, nodeType);
}

void AddNodeToTheDB<T>(T node, Type nodeType) where T : INode
@@ -114,12 +198,17 @@ namespace Svelto.ES

nodes.Add(node);

AddNodeToNodesDictionary(node, nodeType);
}

void AddNodeToNodesDictionary<T>(T node, Type nodeType) where T : INode
{
if (node is NodeWithID)
{
Dictionary<int, INode> nodesDic;
if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false)
nodesDic = _nodesDBdic[nodeType] = new Dictionary<int, INode>();
nodesDic[(node as NodeWithID).ID] = node;
}
}
@@ -129,16 +218,29 @@ namespace Svelto.ES
FasterList<INodeEngine<INode>> enginesForNode;

if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
{
for (int j = 0; j < enginesForNode.Count; j++)
{
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
EngineProfiler.MonitorAddDuration(AddNodeToEngine, enginesForNode[j], node);
#else
enginesForNode[j].Add(node);
#endif
}
}
}

void RemoveNodeFromTheDB<T>(T node, Type nodeType) where T : INode
{
FasterList<INode> nodes;
if (_nodesDB.TryGetValue(nodeType, out nodes) == true)
nodes.Remove(node); //should I remove it from the dictionary if length is zero?
nodes.UnorderredRemove(node); //should I remove it from the dictionary if length is zero?

RemoveNodeFromNodesDictionary(node, nodeType);
}

void RemoveNodeFromNodesDictionary<T>(T node, Type nodeType) where T : INode
{
if (node is NodeWithID)
{
Dictionary<int, INode> nodesDic;
@@ -148,13 +250,57 @@ namespace Svelto.ES
}
}

void RemoveNodeFromGroupDB<T>(T node, Type nodeType) where T : INode
{
FasterList<INode> nodes;
if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == true)
nodes.UnorderredRemove(node); //should I remove it from the dictionary if length is zero?

RemoveNodeFromNodesDictionary(node, nodeType);
}

void RemoveNodeFromEngines<T>(T node, Type nodeType) where T : INode
{
FasterList<INodeEngine<INode>> 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], node);
#else
enginesForNode[j].Remove(node);
#endif
}
}
}
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
void AddNodeToEngine(INodeEngine<INode> engine, INode node)
{
engine.Add(node);
}

void RemoveNodeFromEngine(INodeEngine<INode> engine, INode node)
{
engine.Remove(node);
}
#endif
void InternalRemove<T>(T node) where T : INode
{
Type nodeType = node.GetType();

RemoveNodeFromEngines(node, nodeType);
RemoveNodeFromTheDB(node, node.GetType());
}

void InternalGroupRemove<T>(T node) where T : INode
{
Type nodeType = node.GetType();

RemoveNodeFromEngines(node, nodeType);
RemoveNodeFromGroupDB(node, node.GetType());
}

Dictionary<Type, FasterList<INodeEngine<INode>>> _nodeEngines;
@@ -163,11 +309,13 @@ namespace Svelto.ES
Dictionary<Type, FasterList<INode>> _nodesDB;
Dictionary<Type, Dictionary<int, INode>> _nodesDBdic;

Queue<INode> _nodesToAdd;
Queue<INode> _nodesToRemove;
Dictionary<Type, FasterList<INode>> _nodesDBgroups;

FasterList<INode> _nodesToAdd;
FasterList<INode> _groupNodesToAdd;

WeakReference<EnginesRoot> _engineRootWeakReference;
//integrated pooling system
//add debug panel like Entitas has
//GCHandle should be used to reduce the number of strong references


+ 30
- 33
ECS/EntityDescriptor.cs View File

@@ -2,37 +2,37 @@
using System.Reflection;
using Svelto.DataStructures;

namespace Svelto.ES
namespace Svelto.ECS
{
public class EntityDescriptor
{
EntityDescriptor()
{}

protected EntityDescriptor(INodeBuilder[] nodesToBuild, params object[] componentsImplementor)
protected EntityDescriptor(INodeBuilder[] nodesToBuild, params object[] componentsImplementor)
{
_implementors = componentsImplementor; _nodesToBuild = nodesToBuild;
_implementors = componentsImplementor;
_nodesToBuild = nodesToBuild;
}

virtual public FasterList<INode> BuildNodes(int ID, Action<INode> removeAction)
{
var nodes = new FasterList<INode>();
public virtual FasterList<INode> BuildNodes(int ID, Action<INode> removeAction)
{
var nodes = new FasterList<INode>();

for (int index = 0; index < _nodesToBuild.Length; index++)
{
var nodeBuilder = _nodesToBuild[index];
var node = FillNode(nodeBuilder.Build(ID), () =>
{
for (int i = 0; i < nodes.Count; i++)
removeAction(nodes[i]);

for (int index = 0; index < _nodesToBuild.Length; index++)
{
var nodeBuilder = _nodesToBuild[index];
var node = FillNode(nodeBuilder.Build(ID), () =>
{
for (int i = 0; i < nodes.Count; i++)
removeAction(nodes[i]);
}
);
nodes.Clear();
}
);

nodes.Add (node);
}
nodes.Add (node);
}

return nodes;
}
return nodes;
}

TNode FillNode<TNode>(TNode node, Action removeAction) where TNode: INode
{
@@ -40,15 +40,15 @@ namespace Svelto.ES

for (int i = fields.Length - 1; i >=0 ; --i)
{
var field = fields[i];
Type fieldType = field.FieldType;
object component = null;
var field = fields[i];
Type fieldType = field.FieldType;
object component = null;

for (int j = 0; j < _implementors.Length; j++)
{
var implementor = _implementors[j];

if (fieldType.IsAssignableFrom(implementor.GetType()))
if (implementor != null && fieldType.IsAssignableFrom(implementor.GetType()))
{
component = implementor;

@@ -61,10 +61,8 @@ namespace Svelto.ES

if (component == null)
{
Exception e = new Exception("Svelto.ES: An Entity must hold all the components needed for a Node. " +
"Type: " + field.FieldType.Name + " Node: " + node.GetType().Name);

Utility.Console.LogException(e);
Exception e = new Exception("Svelto.ECS: Implementor not found for a Node. " +
"Implementor Type: " + field.FieldType.Name + " - Node: " + node.GetType().Name + " - EntityDescriptor " + this);

throw e;
}
@@ -75,9 +73,8 @@ namespace Svelto.ES
return node;
}

readonly object[] _implementors;

INodeBuilder[] _nodesToBuild;
readonly object[] _implementors;
INodeBuilder[] _nodesToBuild;
}

public interface INodeBuilder


+ 0
- 6
ECS/IComponent.cs View File

@@ -1,6 +0,0 @@
namespace Svelto.ES
{
public interface IComponent
{
}
}

+ 1
- 17
ECS/IEngine.cs View File

@@ -1,4 +1,4 @@
namespace Svelto.ES
namespace Svelto.ECS
{
public interface IEngine
{}
@@ -18,20 +18,4 @@ namespace Svelto.ES
{
IEngineNodeDB nodesDB { set; }
}

public abstract class SingleNodeEngine<TNodeType> : INodeEngine<INode> where TNodeType:class, INode
{
void INodeEngine<INode>.Add(INode obj)
{
Add(obj as TNodeType);
}

void INodeEngine<INode>.Remove(INode obj)
{
Remove(obj as TNodeType);
}

protected abstract void Add(TNodeType node);
protected abstract void Remove(TNodeType node);
}
}

+ 8
- 1
ECS/IEngineNodeDB.cs View File

@@ -1,12 +1,19 @@
using Svelto.DataStructures;

namespace Svelto.ES
namespace Svelto.ECS
{
public interface IEngineNodeDB
{
ReadOnlyDictionary<int, INode> QueryIndexableNodes<T>() where T:INode;
bool QueryNode<T>(int ID, out T node) where T:INode;
T QueryNode<T>(int ID) where T:INode;
FasterReadOnlyListCast<INode, T> QueryNodes<T>() where T:INode;

bool QueryNodeFromGroup<T>(int ID, out T node) where T : INode;
T QueryNodeFromGroup<T>(int ID) where T : INode;
FasterReadOnlyListCast<INode, T> QueryNodesFromGroups<T>() where T : INode;
}
}


+ 3
- 1
ECS/IEnginesRoot.cs View File

@@ -1,4 +1,4 @@
namespace Svelto.ES
namespace Svelto.ECS
{
public interface IEnginesRoot
{
@@ -8,5 +8,7 @@ namespace Svelto.ES
public interface IEntityFactory
{
void BuildEntity(int ID, EntityDescriptor ED);

void BuildEntityGroup(int ID, EntityDescriptor ED);
}
}

+ 6
- 5
ECS/IEntityDescriptorHolder.cs View File

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

+ 1
- 1
ECS/IEntityDescriptorHolder.cs.meta View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 5a8db83410a8aaa40b824fdc3d968f67
guid: 0badb413a22f42a4b9f68e356e88b07f
timeCreated: 1463438461
licenseType: Free
MonoImporter:


+ 6
- 0
ECS/IImplementor.cs View File

@@ -0,0 +1,6 @@
namespace Svelto.ECS
{
public interface IImplementor
{
}
}

ECS/IComponent.cs.meta → ECS/IImplementor.cs.meta View File


+ 2
- 2
ECS/INode.cs View File

@@ -1,11 +1,11 @@
namespace Svelto.ES
namespace Svelto.ECS
{
public interface INode
{}

public class NodeWithID: INode
{
public static TNodeType BuildNode<TNodeType>(int ID) where TNodeType: NodeWithID, new()
public static TNodeType BuildNode<TNodeType>(int ID) where TNodeType: NodeWithID, new()
{
return new TNodeType { _ID = ID };
}


+ 1
- 1
ECS/IRemoveEntityComponent.cs View File

@@ -1,6 +1,6 @@
using System;

namespace Svelto.ES
namespace Svelto.ECS
{
public interface IRemoveEntityComponent
{


+ 9
- 0
ECS/Profiler.meta View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 8d07544bb8b417f4a9eaefe0d4771d89
folderAsset: yes
timeCreated: 1462355668
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

+ 9
- 0
ECS/Profiler/Editor.meta View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 71a4fc836cd9bde4bbb936a073804ec5
folderAsset: yes
timeCreated: 1480683133
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

+ 9
- 0
ECS/Profiler/Editor/EngineProfiler.meta View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 777a9420fd80746428f9d2c5b718fd2f
folderAsset: yes
timeCreated: 1462351213
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

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

@@ -0,0 +1,373 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

//This profiler is based on the Entitas Visual Debugging tool
//https://github.com/sschmid/Entitas-CSharp

namespace Svelto.ECS.Profiler
{
[CustomEditor(typeof (EngineProfilerBehaviour))]
public class EngineProfilerInspector : Editor
{
enum SORTING_OPTIONS
{
AVERAGE,
MIN,
MAX,
NAME,
NONE
}

static bool _hideEmptyEngines = true;
static bool _showTickEngines;
static bool _showAddEngines;
static bool _showRemoveEngines;

static string _systemNameSearchTerm = string.Empty;

float _axisUpperBounds = 2f;

string updateTitle = "Update".PadRight(15, ' ');
string lateUpdateTitle = "Late".PadRight(13, ' ');
string fixedupdateTitle = "Fixed".PadRight(15, ' ');
string minTitle = "Min".PadRight(15, ' ');
string maxTitle = "Max".PadRight(15, ' ');
string avgTitle = "Avg".PadRight(15, ' ');

EnginesMonitor _enginesMonitor;
Queue<float> _engineMonitorData;
const int SYSTEM_MONITOR_DATA_LENGTH = 300;
SORTING_OPTIONS _sortingOption = SORTING_OPTIONS.AVERAGE;

public override void OnInspectorGUI()
{
var engineProfilerBehaviour = (EngineProfilerBehaviour) target;
EngineInfo[] engines = new EngineInfo[engineProfilerBehaviour.engines.Count];
engineProfilerBehaviour.engines.CopyTo(engines, 0);

DrawEnginesMonitor(engines);
DrawEngineList(engineProfilerBehaviour, engines);
EditorUtility.SetDirty(target);
}

void DrawEngineList(EngineProfilerBehaviour engineProfilerBehaviour, EngineInfo[] engines)
{
ProfilerEditorLayout.BeginVerticalBox();
{
ProfilerEditorLayout.BeginHorizontal();
{
if (GUILayout.Button("Reset Durations", GUILayout.Width(120), GUILayout.Height(14)))
{
engineProfilerBehaviour.ResetDurations();
}
}
ProfilerEditorLayout.EndHorizontal();

_sortingOption = (SORTING_OPTIONS) EditorGUILayout.EnumPopup("Sort By:", _sortingOption);

_hideEmptyEngines = EditorGUILayout.Toggle("Hide empty systems", _hideEmptyEngines);
EditorGUILayout.Space();

ProfilerEditorLayout.BeginHorizontal();
{
_systemNameSearchTerm = EditorGUILayout.TextField("Search", _systemNameSearchTerm);

const string clearButtonControlName = "Clear Button";
GUI.SetNextControlName(clearButtonControlName);
if (GUILayout.Button("x", GUILayout.Width(19), GUILayout.Height(14)))
{
_systemNameSearchTerm = string.Empty;
GUI.FocusControl(clearButtonControlName);
}
}
ProfilerEditorLayout.EndHorizontal();

_showTickEngines = EditorGUILayout.Foldout(_showTickEngines, "Engines Ticks");
if (_showTickEngines && ShouldShowSystems(engines))
{
ProfilerEditorLayout.BeginVerticalBox();
{
var systemsDrawn = DrawUpdateEngineInfos(engines);
if (systemsDrawn == 0)
{
EditorGUILayout.LabelField(string.Empty);
}
}
ProfilerEditorLayout.EndVertical();
}

_showAddEngines = EditorGUILayout.Foldout(_showAddEngines, "Engines Add");
if (_showAddEngines && ShouldShowSystems(engines))
{
ProfilerEditorLayout.BeginVerticalBox();
{
var systemsDrawn = DrawAddEngineInfos(engines);
if (systemsDrawn == 0)
{
EditorGUILayout.LabelField(string.Empty);
}
}
ProfilerEditorLayout.EndVertical();
}

_showRemoveEngines = EditorGUILayout.Foldout(_showRemoveEngines, "Engines Remove");
if (_showRemoveEngines && ShouldShowSystems(engines))
{
ProfilerEditorLayout.BeginVerticalBox();
{
var systemsDrawn = DrawRemoveEngineInfos(engines);
if (systemsDrawn == 0)
{
EditorGUILayout.LabelField(string.Empty);
}
}
ProfilerEditorLayout.EndVertical();
}
}
ProfilerEditorLayout.EndVertical();
}

void DrawEnginesMonitor(EngineInfo[] engines)
{
if (_enginesMonitor == null)
{
_enginesMonitor = new EnginesMonitor(SYSTEM_MONITOR_DATA_LENGTH);
_engineMonitorData = new Queue<float>(new float[SYSTEM_MONITOR_DATA_LENGTH]);
if (EditorApplication.update != Repaint)
{
EditorApplication.update += Repaint;
}
}
double totalDuration = 0;
for (int i = 0; i < engines.Length; i++)
{
totalDuration += engines[i].lastUpdateDuration;
}

ProfilerEditorLayout.BeginVerticalBox();
{
EditorGUILayout.LabelField("Execution duration", EditorStyles.boldLabel);

ProfilerEditorLayout.BeginHorizontal();
{
EditorGUILayout.LabelField("Total", totalDuration.ToString());
}
ProfilerEditorLayout.EndHorizontal();

ProfilerEditorLayout.BeginHorizontal();
{
_axisUpperBounds = EditorGUILayout.FloatField("Axis Upper Bounds", _axisUpperBounds);
}
ProfilerEditorLayout.EndHorizontal();

if (!EditorApplication.isPaused)
{
if (_engineMonitorData.Count >= SYSTEM_MONITOR_DATA_LENGTH)
{
_engineMonitorData.Dequeue();
}

_engineMonitorData.Enqueue((float) totalDuration);
}
_enginesMonitor.Draw(_engineMonitorData.ToArray(), 80f, _axisUpperBounds);
}
ProfilerEditorLayout.EndVertical();
}

int DrawUpdateEngineInfos(EngineInfo[] engines)
{
if (_sortingOption != SORTING_OPTIONS.NONE)
{
SortUpdateEngines(engines);
}

string title =
updateTitle.FastConcat(lateUpdateTitle)
.FastConcat(fixedupdateTitle)
.FastConcat(minTitle)
.FastConcat(maxTitle);
EditorGUILayout.LabelField("Engine Name", title, EditorStyles.boldLabel);

int enginesDrawn = 0;
for (int i = 0; i < engines.Length; i++)
{
EngineInfo engineInfo = engines[i];

if (engineInfo.engineName.ToLower().Contains(_systemNameSearchTerm.ToLower()))
{
ProfilerEditorLayout.BeginHorizontal();
{
var avg = string.Format("{0:0.000}", engineInfo.averageUpdateDuration).PadRight(15);
var avgLate = string.Format("{0:0.000}", engineInfo.averageLateUpdateDuration).PadRight(15);
var avgFixed = string.Format("{0:0.000}", engineInfo.averageFixedUpdateDuration).PadRight(15);
var min = string.Format("{0:0.000}", engineInfo.minUpdateDuration).PadRight(15);
var max = string.Format("{0:0.000}", engineInfo.maxUpdateDuration);

string output = avg.FastConcat(avgLate).FastConcat(avgFixed).FastConcat(min).FastConcat(max);

EditorGUILayout.LabelField(engineInfo.engineName, output, GetEngineStyle());
}
ProfilerEditorLayout.EndHorizontal();

enginesDrawn += 1;
}
}
return enginesDrawn;
}

int DrawAddEngineInfos(EngineInfo[] engines)
{
if (_sortingOption != SORTING_OPTIONS.NONE)
{
SortAddEngines(engines);
}

string title = avgTitle.FastConcat(minTitle).FastConcat(maxTitle);
EditorGUILayout.LabelField("Engine Name", title, EditorStyles.boldLabel);

int enginesDrawn = 0;
for (int i = 0; i < engines.Length; i++)
{
EngineInfo engineInfo = engines[i];
if (engineInfo.engineName.ToLower().Contains(_systemNameSearchTerm.ToLower()) &&
!engineInfo.minAddDuration.Equals(0) && !engineInfo.maxAddDuration.Equals(0))
{
ProfilerEditorLayout.BeginHorizontal();
{
var avg = string.Format("{0:0.000}", engineInfo.averageAddDuration).PadRight(15);
var min = string.Format("{0:0.000}", engineInfo.minAddDuration).PadRight(15);
var max = string.Format("{0:0.000}", engineInfo.maxAddDuration);

string output = avg.FastConcat(min).FastConcat(max);

EditorGUILayout.LabelField(engineInfo.engineName, output, GetEngineStyle());
}
ProfilerEditorLayout.EndHorizontal();

enginesDrawn += 1;
}
}
return enginesDrawn;
}

int DrawRemoveEngineInfos(EngineInfo[] engines)
{
if (_sortingOption != SORTING_OPTIONS.NONE)
{
SortRemoveEngines(engines);
}

string title = avgTitle.FastConcat(minTitle).FastConcat(maxTitle);
EditorGUILayout.LabelField("Engine Name", title, EditorStyles.boldLabel);

int enginesDrawn = 0;
for (int i = 0; i < engines.Length; i++)
{
EngineInfo engineInfo = engines[i];
if (engineInfo.engineName.ToLower().Contains(_systemNameSearchTerm.ToLower()) &&
!engineInfo.minRemoveDuration.Equals(0) && !engineInfo.maxRemoveDuration.Equals(0))
{
ProfilerEditorLayout.BeginHorizontal();
{
var avg = string.Format("{0:0.000}", engineInfo.averageRemoveDuration).PadRight(15);
var min = string.Format("{0:0.000}", engineInfo.minRemoveDuration).PadRight(15);
var max = string.Format("{0:0.000}", engineInfo.maxRemoveDuration);

string output = avg.FastConcat(min).FastConcat(max);

EditorGUILayout.LabelField(engineInfo.engineName, output, GetEngineStyle());
}
ProfilerEditorLayout.EndHorizontal();

enginesDrawn += 1;
}
}
return enginesDrawn;
}

static GUIStyle GetEngineStyle()
{
var style = new GUIStyle(GUI.skin.label);
var color = EditorGUIUtility.isProSkin ? Color.white : style.normal.textColor;

style.normal.textColor = color;

return style;
}

static bool ShouldShowSystems(EngineInfo[] engines)
{
return engines.Length > 0;
}

#region Sorting Engines
void SortUpdateEngines(EngineInfo[] engines)
{
switch (_sortingOption)
{
case SORTING_OPTIONS.AVERAGE:
Array.Sort(engines,
(engine1, engine2) => engine2.averageUpdateDuration.CompareTo(engine1.averageUpdateDuration));
break;
case SORTING_OPTIONS.MIN:
Array.Sort(engines,
(engine1, engine2) => engine2.minUpdateDuration.CompareTo(engine1.minUpdateDuration));
break;
case SORTING_OPTIONS.MAX:
Array.Sort(engines,
(engine1, engine2) => engine2.maxUpdateDuration.CompareTo(engine1.maxUpdateDuration));
break;
case SORTING_OPTIONS.NAME:
Array.Sort(engines, StringComparer.InvariantCulture);
break;
}
}

void SortAddEngines(EngineInfo[] engines)
{
switch (_sortingOption)
{
case SORTING_OPTIONS.AVERAGE:
Array.Sort(engines,
(engine1, engine2) => engine2.averageAddDuration.CompareTo(engine1.averageAddDuration));
break;
case SORTING_OPTIONS.MIN:
Array.Sort(engines,
(engine1, engine2) => engine2.minAddDuration.CompareTo(engine1.minAddDuration));
break;
case SORTING_OPTIONS.MAX:
Array.Sort(engines,
(engine1, engine2) => engine2.maxAddDuration.CompareTo(engine1.maxAddDuration));
break;
case SORTING_OPTIONS.NAME:
Array.Sort(engines, StringComparer.InvariantCulture);
break;
}
}

void SortRemoveEngines(EngineInfo[] engines)
{
switch (_sortingOption)
{
case SORTING_OPTIONS.AVERAGE:
Array.Sort(engines,
(engine1, engine2) => engine2.averageRemoveDuration.CompareTo(engine1.averageRemoveDuration));
break;
case SORTING_OPTIONS.MIN:
Array.Sort(engines,
(engine1, engine2) => engine2.minRemoveDuration.CompareTo(engine1.minRemoveDuration));
break;
case SORTING_OPTIONS.MAX:
Array.Sort(engines,
(engine1, engine2) => engine2.maxRemoveDuration.CompareTo(engine1.maxRemoveDuration));
break;
case SORTING_OPTIONS.NAME:
Array.Sort(engines, StringComparer.InvariantCulture);
break;
}
}
}
#endregion
}

+ 12
- 0
ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs.meta View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a2202d1747b86dc428310091a9c1a7ef
timeCreated: 1462469401
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 22
- 0
ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs View File

@@ -0,0 +1,22 @@
using UnityEditor;

//This profiler is based on the Entitas Visual Debugging tool
//https://github.com/sschmid/Entitas-CSharp

namespace Svelto.ECS.Profiler
{
internal class EngineProfilerMenuItem
{
[MenuItem("Engines/Enable Profiler")]
public static void EnableProfiler()
{
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, "ENGINE_PROFILER_ENABLED");
}

[MenuItem("Engines/Disable Profiler")]
public static void DisableProfiler()
{
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, "");
}
}
}

+ 12
- 0
ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs.meta View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e7b6bbeaaa16ab84aaa99c3311199efa
timeCreated: 1462351229
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 132
- 0
ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs View File

@@ -0,0 +1,132 @@
using System.Linq;
using UnityEditor;
using UnityEngine;

//This profiler is based on the Entitas Visual Debugging tool
//https://github.com/sschmid/Entitas-CSharp

namespace Svelto.ECS.Profiler
{
public class EnginesMonitor
{
public float xBorder = 48;
public float yBorder = 20;
public int rightLinePadding = -15;
public string labelFormat = "{0:0.0}";
public string axisFormat = "{0:0.0}";
public int gridLines = 1;
public float axisRounding = 5f;
public float anchorRadius = 1f;
public Color lineColor = Color.magenta;

readonly GUIStyle _labelTextStyle;
readonly GUIStyle _centeredStyle;
readonly Vector3[] _cachedLinePointVerticies;
readonly Vector3[] _linePoints;

public EnginesMonitor(int dataLength)
{
_labelTextStyle = new GUIStyle(GUI.skin.label);
_labelTextStyle.alignment = TextAnchor.UpperRight;
_centeredStyle = new GUIStyle();
_centeredStyle.alignment = TextAnchor.UpperCenter;
_centeredStyle.normal.textColor = Color.white;
_linePoints = new Vector3[dataLength];
_cachedLinePointVerticies = new[]
{
new Vector3(-1, 1, 0)*anchorRadius,
new Vector3(1, 1, 0)*anchorRadius,
new Vector3(1, -1, 0)*anchorRadius,
new Vector3(-1, -1, 0)*anchorRadius,
};
}

public void Draw(float[] data, float height, float axisUpperBounds)
{
axisRounding = axisUpperBounds;
var rect = GUILayoutUtility.GetRect(EditorGUILayout.GetControlRect().width, height);
var top = rect.y + yBorder;
var floor = rect.y + rect.height - yBorder;
var availableHeight = floor - top;
var max = data.Length != 0 ? data.Max() : 0f;
if (max%axisRounding != 0)
{
max = max + axisRounding - (max%axisRounding);
}

drawGridLines(top, rect.width, availableHeight, max);
drawLine(data, floor, rect.width, availableHeight, max);
}

void drawGridLines(float top, float width, float availableHeight, float max)
{
var handleColor = Handles.color;
Handles.color = Color.grey;
var n = gridLines + 1;
var lineSpacing = availableHeight/n;
for (int i = 0; i <= n; i++)
{
var lineY = top + (lineSpacing*i);
Handles.DrawLine(
new Vector2(xBorder, lineY),
new Vector2(width - rightLinePadding, lineY)
);
GUI.Label(
new Rect(0, lineY - 8, xBorder - 2, 50),
string.Format(axisFormat, max*(1f - ((float) i/(float) n))),
_labelTextStyle
);
}
Handles.color = handleColor;
}

void drawLine(float[] data, float floor, float width, float availableHeight, float max)
{
var lineWidth = (float) (width - xBorder - rightLinePadding)/data.Length;
var handleColor = Handles.color;
var labelRect = new Rect();
Vector2 newLine;
bool mousePositionDiscovered = false;
float mouseHoverDataValue = 0;
float linePointScale;
Handles.color = lineColor;
Handles.matrix = Matrix4x4.identity;
HandleUtility.handleMaterial.SetPass(0);
for (int i = 0; i < data.Length; i++)
{
var value = data[i];
var lineTop = floor - (availableHeight*(value/max));
newLine = new Vector2(xBorder + (lineWidth*i), lineTop);
_linePoints[i] = new Vector3(newLine.x, newLine.y, 0);
linePointScale = 1f;
if (!mousePositionDiscovered)
{
var anchorPosRadius3 = anchorRadius*3;
var anchorPosRadius6 = anchorRadius*6;
var anchorPos = newLine - (Vector2.up*0.5f);
labelRect = new Rect(anchorPos.x - anchorPosRadius3, anchorPos.y - anchorPosRadius3,
anchorPosRadius6, anchorPosRadius6);
if (labelRect.Contains(Event.current.mousePosition))
{
mousePositionDiscovered = true;
mouseHoverDataValue = value;
linePointScale = 3f;
}
}
Handles.matrix = Matrix4x4.TRS(_linePoints[i], Quaternion.identity, Vector3.one*linePointScale);
Handles.DrawAAConvexPolygon(_cachedLinePointVerticies);
}
Handles.matrix = Matrix4x4.identity;
Handles.DrawAAPolyLine(2f, data.Length, _linePoints);

if (mousePositionDiscovered)
{
labelRect.y -= 16;
labelRect.width += 50;
labelRect.x -= 25;
GUI.Label(labelRect, string.Format(labelFormat, mouseHoverDataValue), _centeredStyle);
}
Handles.color = handleColor;
}
}
}

+ 12
- 0
ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs.meta View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 30f9f7f6468a96d4da2525c65ecfe637
timeCreated: 1467633311
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 65
- 0
ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs View File

@@ -0,0 +1,65 @@
using UnityEditor;
using UnityEngine;

//This profiler is based on the Entitas Visual Debugging tool
//https://github.com/sschmid/Entitas-CSharp

namespace Svelto.ECS.Profiler
{
public static class ProfilerEditorLayout
{
public static void ShowWindow<T>(string title) where T : EditorWindow
{
var window = EditorWindow.GetWindow<T>(true, title);
window.minSize = window.maxSize = new Vector2(415f, 520f);
window.Show();
}

public static Texture2D LoadTexture(string label)
{
var guid = AssetDatabase.FindAssets(label)[0];
if (guid != null)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
return AssetDatabase.LoadAssetAtPath<Texture2D>(path);
}
return null;
}

public static float DrawHeaderTexture(EditorWindow window, Texture2D texture)
{
const int scollBarWidth = 15;
var ratio = texture.width/texture.height;
var width = window.position.width - 8 - scollBarWidth;
var height = width/ratio;
GUI.DrawTexture(new Rect(4, 2, width, height), texture, ScaleMode.ScaleToFit);

return height;
}

public static Rect BeginVertical()
{
return EditorGUILayout.BeginVertical();
}

public static Rect BeginVerticalBox(GUIStyle style = null)
{
return EditorGUILayout.BeginVertical(style ?? GUI.skin.box);
}

public static void EndVertical()
{
EditorGUILayout.EndVertical();
}

public static Rect BeginHorizontal()
{
return EditorGUILayout.BeginHorizontal();
}

public static void EndHorizontal()
{
EditorGUILayout.EndHorizontal();
}
}
}

+ 12
- 0
ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs.meta View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1c9cf391ddfdd92429ce90eeb73a936a
timeCreated: 1462527509
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 167
- 0
ECS/Profiler/EngineInfo.cs View File

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

//This profiler is based on the Entitas Visual Debugging tool
//https://github.com/sschmid/Entitas-CSharp

namespace Svelto.ECS.Profiler
{
public sealed class EngineInfo
{
enum UpdateType
{
Update = 0,
LateUpdate = 1,
FixedUpdate = 2,
}

readonly IEngine _engine;
readonly string _engineName;
readonly Type _engineType;

const int NUM_UPDATE_TYPES = 3;
const int NUM_FRAMES_TO_AVERAGE = 10;

//use a queue to averave out the last 30 frames
Queue<double>[] _updateFrameTimes = new Queue<double>[NUM_UPDATE_TYPES];
readonly double[] _accumulatedUpdateDuration = new double[NUM_UPDATE_TYPES];
readonly double[] _lastUpdateDuration = new double[NUM_UPDATE_TYPES];

readonly double[] _minUpdateDuration = new double[NUM_UPDATE_TYPES];
readonly double[] _maxUpdateDuration = new double[NUM_UPDATE_TYPES];

double _accumulatedAddDuration;
double _minAddDuration;
double _maxAddDuration;
int _nodesAddedCount;

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

public IEngine engine { get { return _engine; } }
public string engineName { get { return _engineName; } }
public Type engineType { get { return _engineType; } }

public double lastUpdateDuration { get { return _lastUpdateDuration[(int) UpdateType.Update]; } }
public double lastFixedUpdateDuration { get { return _lastUpdateDuration[(int)UpdateType.LateUpdate]; } }
public double lastLateUpdateDuration { get { return _lastUpdateDuration[(int)UpdateType.FixedUpdate]; } }

public double minAddDuration { get { return _minAddDuration; } }
public double minRemoveDuration { get { return _minRemoveDuration; } }
public double minUpdateDuration { get { return _minUpdateDuration[(int)UpdateType.Update]; } }

public double maxAddDuration { get { return _maxAddDuration; } }
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 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; } }

public EngineInfo(IEngine engine)
{
_engine = engine;
_engineName = _engine.ToString();

int foundNamespace = _engineName.LastIndexOf(".");
_engineName = _engineName.Remove(0, foundNamespace + 1);

_engineType = engine.GetType();

for (int i = 0; i < NUM_UPDATE_TYPES; i++)
{
_updateFrameTimes[i] = new Queue<double>();
}
ResetDurations();
}

public void AddUpdateDuration(double updateDuration)
{
AddUpdateDurationForType(updateDuration, (int)UpdateType.Update);
}

public void AddLateUpdateDuration(double updateDuration)
{
AddUpdateDurationForType(updateDuration, (int)UpdateType.LateUpdate);
}

public void AddFixedUpdateDuration(double updateDuration)
{
AddUpdateDurationForType(updateDuration, (int)UpdateType.FixedUpdate);
}

void AddUpdateDurationForType(double updateDuration, int updateType)
{
if (updateDuration < _minUpdateDuration[updateType] || _minUpdateDuration[updateType] == 0)
{
_minUpdateDuration[updateType] = updateDuration;
}
if (updateDuration > _maxUpdateDuration[updateType])
{
_maxUpdateDuration[updateType] = updateDuration;
}

if (_updateFrameTimes[updateType].Count == NUM_FRAMES_TO_AVERAGE)
{
_accumulatedUpdateDuration[updateType] -= _updateFrameTimes[updateType].Dequeue();
}

_accumulatedUpdateDuration[updateType] += updateDuration;
_updateFrameTimes[updateType].Enqueue(updateDuration);
_lastUpdateDuration[updateType] = updateDuration;
}

public void AddAddDuration(double duration)
{
if (duration < _minAddDuration || _minAddDuration == 0)
{
_minAddDuration = duration;
}
if (duration > _maxAddDuration)
{
_maxAddDuration = duration;
}
_accumulatedAddDuration += duration;
_nodesAddedCount += 1;
}

public void AddRemoveDuration(double duration)
{
if (duration < _minRemoveDuration || _minRemoveDuration == 0)
{
_minRemoveDuration = duration;
}
if (duration > _maxRemoveDuration)
{
_maxRemoveDuration = duration;
}
_accumulatedRemoveDuration += duration;
_nodesRemovedCount += 1;
}

public void ResetDurations()
{
for (int i = 0; i < NUM_UPDATE_TYPES; i++)
{
_accumulatedUpdateDuration[i] = 0;
_minUpdateDuration[i] = 0;
_maxUpdateDuration[i] = 0;
_updateFrameTimes[i].Clear();
}

_accumulatedAddDuration = 0;
_minAddDuration = 0;
_maxAddDuration = 0;
_nodesAddedCount = 0;

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

+ 12
- 0
ECS/Profiler/EngineInfo.cs.meta View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 93c5ba48b51186e44b7094eef7028c90
timeCreated: 1462357591
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 126
- 0
ECS/Profiler/EngineProfiler.cs View File

@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
using Svelto.Ticker.Legacy;

//This profiler is based on the Entitas Visual Debugging tool
//https://github.com/sschmid/Entitas-CSharp

namespace Svelto.ECS.Profiler
{
public sealed class EngineProfiler
{
static readonly Stopwatch _stopwatch = new Stopwatch();

public static void MonitorAddDuration(Action<INodeEngine<INode>, INode> addingFunc, INodeEngine<INode> engine, INode node)
{
EngineInfo info;
if (engineInfos.TryGetValue(engine.GetType(), out info))
{
_stopwatch.Reset();
_stopwatch.Start();
addingFunc(engine, node);
_stopwatch.Stop();

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

public static void MonitorRemoveDuration(Action<INodeEngine<INode>, INode> removeFunc, INodeEngine<INode> engine, INode node)
{
EngineInfo info;
if (engineInfos.TryGetValue(engine.GetType(), out info))
{
_stopwatch.Reset();
_stopwatch.Start();
removeFunc(engine, node);
engine.Remove(node);
_stopwatch.Stop();
info.AddRemoveDuration(_stopwatch.Elapsed.TotalMilliseconds);
}
}

public static void MonitorUpdateDuration(ITickable tickable)
{
if (tickable is INodeEngine<INode>)
{
EngineInfo info;
if (engineInfos.TryGetValue((tickable as INodeEngine<INode>).GetType(), out info))
{
_stopwatch.Reset();
_stopwatch.Start();
tickable.Tick(Time.deltaTime);
_stopwatch.Stop();

info.AddUpdateDuration(_stopwatch.Elapsed.TotalMilliseconds);
}
}
else
{
tickable.Tick(Time.deltaTime);
}
}

public static void MonitorUpdateDuration(IPhysicallyTickable tickable)
{
if (tickable is INodeEngine<INode>)
{
EngineInfo info;
if (engineInfos.TryGetValue((tickable as INodeEngine<INode>).GetType(), out info))
{
_stopwatch.Reset();
_stopwatch.Start();
tickable.PhysicsTick(Time.fixedDeltaTime);
_stopwatch.Stop();

info.AddFixedUpdateDuration(_stopwatch.Elapsed.TotalMilliseconds);
}
}
else
{
tickable.PhysicsTick(Time.fixedDeltaTime);
}
}

public static void MonitorUpdateDuration(ILateTickable tickable)
{
if (tickable is INodeEngine<INode>)
{
EngineInfo info;
if (engineInfos.TryGetValue((tickable as INodeEngine<INode>).GetType(), out info))
{
_stopwatch.Reset();
_stopwatch.Start();
tickable.LateTick(Time.deltaTime);
_stopwatch.Stop();

info.AddLateUpdateDuration(_stopwatch.Elapsed.TotalMilliseconds);
}
}
else
{
tickable.LateTick(Time.deltaTime);
}
}

public static void AddEngine(IEngine engine)
{
if (engineInfos.ContainsKey(engine.GetType()) == false)
{
engineInfos.Add(engine.GetType(), new EngineInfo(engine));
}
}

public static void ResetDurations()
{
foreach (var engine in engineInfos)
{
engine.Value.ResetDurations();
}
}

public static readonly Dictionary<Type, EngineInfo> engineInfos = new Dictionary<Type, EngineInfo>();
}
}

+ 12
- 0
ECS/Profiler/EngineProfiler.cs.meta View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 74b59045fe5033b4b98facadd4d6b114
timeCreated: 1462355668
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 19
- 0
ECS/Profiler/EngineProfilerBehaviour.cs View File

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

//This profiler is based on the Entitas Visual Debugging tool
//https://github.com/sschmid/Entitas-CSharp

namespace Svelto.ECS.Profiler
{
public class EngineProfilerBehaviour : MonoBehaviour
{
public Dictionary<Type, EngineInfo>.ValueCollection engines { get { return EngineProfiler.engineInfos.Values; } }

public void ResetDurations()
{
EngineProfiler.ResetDurations();
}
}
}

+ 12
- 0
ECS/Profiler/EngineProfilerBehaviour.cs.meta View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 688f5cf68b171ef4fa0f6aab49644c48
timeCreated: 1462469401
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 18
- 0
ECS/SingleNodeEngine.cs View File

@@ -0,0 +1,18 @@
namespace Svelto.ECS
{
public abstract class SingleNodeEngine<TNodeType> : INodeEngine<INode> where TNodeType : class, INode
{
void INodeEngine<INode>.Add(INode obj)
{
Add(obj as TNodeType);
}

void INodeEngine<INode>.Remove(INode obj)
{
Remove(obj as TNodeType);
}

protected abstract void Add(TNodeType node);
protected abstract void Remove(TNodeType node);
}
}

+ 12
- 0
ECS/SingleNodeEngine.cs.meta View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 406a5e818b179b743b582c8fec087ac1
timeCreated: 1469806920
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 1
- 1
ECS/note.txt View File

@@ -1,7 +1,7 @@
systems do not hold component data, but only system states
systems cannot be injected
systems are SRP and OCP
systems communicates between component, mediators, producer/consumer, observers. producer/consumer and observers must be defined in the layer of the engine.
systems communicates between component, mediators, producer/consumer, Svelto.ECS.Example.Observers. producer/consumer and observers must be defined in the layer of the engine.
systems can have injected dependencies

components don't have logic


+ 3
- 3
Factories.meta View File

@@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 354b7801c1d782b47b5d9ff9aba85e83
guid: 7f7896118ef3c4949898b5c30aea0c47
folderAsset: yes
timeCreated: 1434752394
licenseType: Free
timeCreated: 1484044925
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:


+ 0
- 42
Observer/Observable.cs View File

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

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

public interface IObservable
{
event Action Notify;

void Dispatch();
}

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

void Dispatch(ref DispatchType parameter);
}

public class Observable<DispatchType>:IObservable<DispatchType>
{
public event ObserverAction<DispatchType> Notify;

public void Dispatch(ref DispatchType parameter)
{
if (Notify != null)
Notify(ref parameter);
}
}

public class Observable:IObservable
{
public event Action Notify;

public void Dispatch()
{
if (Notify != null)
Notify();
}
}
}

+ 0
- 12
Observer/Observable.cs.meta View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: e9d378efdf4e5dd439400d001b6775ec
timeCreated: 1440356670
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 0
- 111
Observer/Observer.cs View File

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

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

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

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

public void RemoveAction(ObserverAction<ActionType> action)
{
_actions -= action;
}

public void Unsubscribe()
{
_unsubscribe();
}

void OnObservableDispatched(ref DispatchType dispatchNotification)
{
if (_actions != null)
{
var actionType = TypeMap(ref dispatchNotification);

_actions(ref actionType);
}
}

protected abstract ActionType TypeMap(ref DispatchType dispatchNotification);

ObserverAction<ActionType> _actions;
Action _unsubscribe;
}
}

namespace Svelto.Observer.IntraNamespace
{
public class Observer<DispatchType> : InterNamespace.Observer<DispatchType, DispatchType>
{
public Observer(Observable<DispatchType> observable) : base(observable)
{ }

protected override DispatchType TypeMap(ref DispatchType dispatchNotification)
{
return dispatchNotification;
}
}
}

namespace Svelto.Observer
{
public class Observer: IObserver
{
public Observer(Observable observable)
{
observable.Notify += OnObservableDispatched;

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

public void AddAction(Action action)
{
_actions += action;
}

public void RemoveAction(Action action)
{
_actions -= action;
}

public void Unsubscribe()
{
_unsubscribe();
}

void OnObservableDispatched()
{
if (_actions != null)
_actions();
}

Action _actions;
readonly Action _unsubscribe;
}

public interface IObserver<WatchingType>
{
void AddAction(ObserverAction<WatchingType> action);
void RemoveAction(ObserverAction<WatchingType> action);

void Unsubscribe();
}

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

void Unsubscribe();
}
}

+ 0
- 12
Observer/Observer.cs.meta View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 7b4eff431179a5047a4b9110fe27ecf6
timeCreated: 1440352946
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 0
- 5
Ticker.meta View File

@@ -1,5 +0,0 @@
fileFormatVersion: 2
guid: 86d00787627f3e643bfb37812067fbc5
folderAsset: yes
DefaultImporter:
userData:

+ 0
- 31
Ticker/ITickable.cs View File

@@ -1,31 +0,0 @@
namespace Svelto.Ticker
{
public interface ILateTickable : ITickableBase
{
void LateTick(float deltaSec);
}

public interface IPhysicallyTickable : ITickableBase
{
void PhysicsTick(float deltaSec);
}

public interface ITickable : ITickableBase
{
void Tick(float deltaSec);
}

public interface IEndOfFrameTickable : ITickableBase
{
void EndOfFrameTick(float deltaSec);
}

public interface IIntervaledTickable : ITickableBase
{
void IntervaledTick();
}

public interface ITickableBase
{
}
}

+ 0
- 8
Ticker/ITicker.cs View File

@@ -1,8 +0,0 @@
namespace Svelto.Ticker
{
public interface ITicker
{
void Add(ITickableBase tickable);
void Remove(ITickableBase tickable);
}
}

+ 0
- 15
Ticker/IntervaledTickAttribute.cs View File

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

namespace Svelto.Ticker
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
internal class IntervaledTickAttribute : Attribute
{
public float interval;

public IntervaledTickAttribute(float intervalTime)
{
interval = intervalTime;
}
}
}

+ 0
- 12
Ticker/IntervaledTickAttribute.cs.meta View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: fe6689309725deb4f975fb62e1e061e4
timeCreated: 1435431616
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

+ 0
- 153
Ticker/TickBehaviour.cs View File

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

namespace Svelto.Ticker
{
public class TickBehaviour : MonoBehaviour
{
IEnumerator Start ()
{
var waitForEndFrame = new WaitForEndOfFrame();

while (true)
{
yield return waitForEndFrame;

for (int i = _endOfFrameTicked.Count - 1; i >= 0; --i)
{
try
{
_endOfFrameTicked[i].EndOfFrameTick(Time.deltaTime);
}
catch (Exception e)
{
Utility.Console.LogException(e);
}
}
}
}
internal void Add(ITickable tickable)
{
_ticked.Add(tickable);
}

internal void AddLate(ILateTickable tickable)
{
_lateTicked.Add(tickable);
}

internal void AddPhysic(IPhysicallyTickable tickable)
{
_physicallyTicked.Add(tickable);
}

internal void AddEndOfFrame(IEndOfFrameTickable tickable)
{
_endOfFrameTicked.Add(tickable);
}

internal void Remove(ITickable tickable)
{
_ticked.Remove(tickable);
}

internal void RemoveLate(ILateTickable tickable)
{
_lateTicked.Remove(tickable);
}

internal void RemovePhysic(IPhysicallyTickable tickable)
{
_physicallyTicked.Remove(tickable);
}

internal void RemoveEndOfFrame(IEndOfFrameTickable tickable)
{
_endOfFrameTicked.Remove(tickable);
}

void FixedUpdate()
{
for (int i = _physicallyTicked.Count - 1; i >= 0; --i)
{
try
{
_physicallyTicked[i].PhysicsTick(Time.fixedDeltaTime);
}
catch (Exception e)
{
Utility.Console.LogException(e);
}
}
}

void LateUpdate()
{
for (int i = _lateTicked.Count - 1; i >= 0; --i)
{
try
{
_lateTicked[i].LateTick(Time.deltaTime);
}
catch (Exception e)
{
Utility.Console.LogException(e);
}
}
}

void Update()
{
for (int i = _ticked.Count - 1; i >= 0; --i)
{
try
{
_ticked[i].Tick(Time.deltaTime);
}
catch (Exception e)
{
Utility.Console.LogException(e);
}
}
}

internal void AddIntervaled(IIntervaledTickable tickable)
{
var methodInfo = ((Action)tickable.IntervaledTick).Method;
object[] attrs = methodInfo.GetCustomAttributes(typeof(IntervaledTickAttribute), true);

IEnumerator intervaledTick = IntervaledUpdate(tickable, (attrs[0] as IntervaledTickAttribute).interval);

_intervalledTicked[tickable] = intervaledTick;

StartCoroutine(intervaledTick);
}

internal void RemoveIntervaled(IIntervaledTickable tickable)
{
IEnumerator enumerator;

if (_intervalledTicked.TryGetValue(tickable, out enumerator))
{
StopCoroutine(enumerator);

_intervalledTicked.Remove(tickable);
}
}

IEnumerator IntervaledUpdate(IIntervaledTickable tickable, float seconds)
{
while (true) { DateTime next = DateTime.UtcNow.AddSeconds(seconds); while (DateTime.UtcNow < next) yield return null; tickable.IntervaledTick(); }
}

List<ILateTickable> _lateTicked = new List<ILateTickable>();
List<IPhysicallyTickable> _physicallyTicked = new List<IPhysicallyTickable>();
List<ITickable> _ticked = new List<ITickable>();
List<IEndOfFrameTickable> _endOfFrameTicked = new List<IEndOfFrameTickable>();

Dictionary<IIntervaledTickable, IEnumerator> _intervalledTicked = new Dictionary<IIntervaledTickable, IEnumerator>();
}
}

+ 0
- 57
Ticker/UnityTicker.cs View File

@@ -1,57 +0,0 @@
using UnityEngine;

namespace Svelto.Ticker
{
class UnityTicker : ITicker
{
public UnityTicker()
{
_ticker = Object.FindObjectOfType<TickBehaviour>();

if (_ticker == null)
{
var go = new GameObject("SveltoTicker");

_ticker = go.AddComponent<TickBehaviour>();
}
}

public void Add(ITickableBase tickable)
{
if (tickable is ITickable)
_ticker.Add(tickable as ITickable);

if (tickable is IPhysicallyTickable)
_ticker.AddPhysic(tickable as IPhysicallyTickable);

if (tickable is ILateTickable)
_ticker.AddLate(tickable as ILateTickable);

if (tickable is IEndOfFrameTickable)
_ticker.AddEndOfFrame(tickable as IEndOfFrameTickable);

if (tickable is IIntervaledTickable)
_ticker.AddIntervaled(tickable as IIntervaledTickable);
}

public void Remove(ITickableBase tickable)
{
if (tickable is ITickable)
_ticker.Remove(tickable as ITickable);

if (tickable is IPhysicallyTickable)
_ticker.RemovePhysic(tickable as IPhysicallyTickable);

if (tickable is ILateTickable)
_ticker.RemoveLate(tickable as ILateTickable);

if (tickable is IEndOfFrameTickable)
_ticker.RemoveEndOfFrame(tickable as IEndOfFrameTickable);

if (tickable is IIntervaledTickable)
_ticker.RemoveIntervaled(tickable as IIntervaledTickable);
}

readonly TickBehaviour _ticker;
}
}

+ 3
- 3
Utilities.meta View File

@@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 9581aacbfc73076489f471e3e83ba71d
guid: b7374ca2eb247d44c8783d7d23f9b89e
folderAsset: yes
timeCreated: 1440947158
licenseType: Free
timeCreated: 1484394896
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:


+ 79
- 18
Utilities/Print.cs View File

@@ -1,7 +1,7 @@
using System;
using System.Diagnostics;
using System.Text;
using Debug = UnityEngine.Debug;
using UnityEngine;

public static class FastConcatUtility
{
@@ -65,20 +65,78 @@ public static class FastConcatUtility
return _stringBuilder.ToString();
}
}

public static string FastJoin(this string[] str)
{
lock (_stringBuilder)
{
_stringBuilder.Length = 0;

for (int i = 0; i < str.Length; i++)
_stringBuilder.Append(str[i]);

return _stringBuilder.ToString();
}
}

public static string FastJoin(this string[] str, string str1)
{
lock (_stringBuilder)
{
_stringBuilder.Length = 0;

for (int i = 0; i < str.Length; i++)
_stringBuilder.Append(str[i]);

_stringBuilder.Append(str1);

return _stringBuilder.ToString();
}
}
}

namespace Utility
{
public interface ILogger
{
void Log (string txt, string stack = null, LogType type = LogType.Log);
}

public class SlowLogger : ILogger
{
public void Log(string txt, string stack = null, LogType type = LogType.Log)
{
switch (type)
{
case LogType.Log:
UnityEngine.Debug.Log(stack != null ? txt.FastConcat(stack) : txt);
break;
case LogType.Exception:
UnityEngine.Debug.LogError("Log of exceptions not supported");
break;
case LogType.Warning:
UnityEngine.Debug.LogWarning(stack != null ? txt.FastConcat(stack) : txt);
break;
case LogType.Error:
UnityEngine.Debug.LogError(stack != null ? txt.FastConcat(stack) : txt);
break;
}
}
}

public static class Console
{
static StringBuilder _stringBuilder = new StringBuilder(256);

public static ILogger logger = new SlowLogger();
public static volatile bool BatchLog = false;

public static void Log(string txt)
{
Debug.Log(txt);
logger.Log(txt);
}

public static void LogError(string txt)
public static void LogError(string txt, bool showCurrentStack = true)
{
string toPrint;
@@ -90,31 +148,34 @@ namespace Utility

toPrint = _stringBuilder.ToString();
}
Debug.LogError(toPrint);
}

public static void LogException(Exception e)
{
LogException(e, null);
logger.Log(toPrint, showCurrentStack == true ? new StackTrace().ToString() : null, LogType.Error);
}

public static void LogException(Exception e, UnityEngine.Object obj)
public static void LogError(string txt, string stack)
{
string toPrint;

lock (_stringBuilder)
{
_stringBuilder.Length = 0;
_stringBuilder.Append("-!!!!!!-> ").Append(e);
_stringBuilder.Append("-!!!!!!-> ");
_stringBuilder.Append(txt);

toPrint = _stringBuilder.ToString();
}

Exception ex = new Exception(e.ToString());
logger.Log(toPrint, stack, LogType.Error);
}

public static void LogException(Exception e)
{
LogException(e, null);
}

Debug.Log(toPrint);
Debug.LogException(ex, obj);
public static void LogException(Exception e, UnityEngine.Object obj)
{
UnityEngine.Debug.LogException(e, obj);
}

public static void LogWarning(string txt)
@@ -130,11 +191,11 @@ namespace Utility
toPrint = _stringBuilder.ToString();
}

Debug.LogWarning(toPrint);
logger.Log(toPrint, null, LogType.Warning);
}

/// <summary>
/// This function should never be used explicitly
/// Use this function if you don't want the message to be batched
/// </summary>
/// <param name="txt"></param>
public static void SystemLog(string txt)
@@ -144,7 +205,7 @@ namespace Utility
lock (_stringBuilder)
{
string currentTimeString = DateTime.UtcNow.ToLongTimeString(); //ensure includes seconds
string processTimeString = (DateTime.Now - Process.GetCurrentProcess().StartTime).ToString();
string processTimeString = (DateTime.UtcNow - Process.GetCurrentProcess().StartTime).ToString();

_stringBuilder.Length = 0;
_stringBuilder.Append("[").Append(currentTimeString);
@@ -158,7 +219,7 @@ namespace Utility
#if !UNITY_EDITOR
System.Console.WriteLine(toPrint);
#else
Debug.Log(toPrint);
UnityEngine.Debug.Log(toPrint);
#endif
}
}


+ 99
- 0
Utilities/WeakAction.cs View File

@@ -0,0 +1,99 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

public struct WeakAction<T1, T2>:IEquatable<WeakAction<T1, T2>>
{
public WeakAction(Action<T1, T2> listener)
{
ObjectRef = GCHandle.Alloc(listener.Target, GCHandleType.Weak);
Method = listener.Method;

if (Method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0)
throw new ArgumentException("Cannot create weak event to anonymous method with closure.");
}

public bool Invoke(T1 data1, T2 data2)
{
if (ObjectRef.IsAllocated && ObjectRef.Target != null)
{
Method.Invoke(ObjectRef.Target, new object[] { data1, data2 });
return true;
}

return false;
}

public bool Equals(WeakAction<T1, T2> other)
{
return (Method.Equals(other.Method));
}

readonly GCHandle ObjectRef;
readonly MethodInfo Method;
}

public struct WeakAction<T>:IEquatable<WeakAction<T>>
{
public WeakAction(Action<T> listener)
{
ObjectRef = GCHandle.Alloc(listener.Target, GCHandleType.Weak);
Method = listener.Method;

if (Method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0)
throw new ArgumentException("Cannot create weak event to anonymous method with closure.");
}

public bool Invoke(T data)
{
if (ObjectRef.IsAllocated && ObjectRef.Target != null)
{
Method.Invoke(ObjectRef.Target, new object[] { data });
return true;
}

return false;
}

public bool Equals(WeakAction<T> other)
{
return (Method.Equals(other.Method));
}

readonly GCHandle ObjectRef;
readonly MethodInfo Method;
}

public struct WeakAction:IEquatable<WeakAction>
{
public WeakAction(Action listener)
{
ObjectRef = GCHandle.Alloc(listener.Target, GCHandleType.Weak);
Method = listener.Method;

if (Method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0)
throw new ArgumentException("Cannot create weak event to anonymous method with closure.");
}

public bool Invoke()
{
if (ObjectRef.IsAllocated && ObjectRef.Target != null)
{
Method.Invoke(ObjectRef.Target, null);
return true;
}

return false;
}

public bool Equals(WeakAction other)
{
return (Method.Equals(other.Method));
}

readonly GCHandle ObjectRef;
readonly MethodInfo Method;
}



+ 12
- 0
Utilities/WeakAction.cs.meta View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: eaffccbdbcacfa449b997dc1f7181bb4
timeCreated: 1461942385
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Loading…
Cancel
Save