@@ -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; | |||
} | |||
} |
@@ -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; | |||
@@ -0,0 +1,9 @@ | |||
fileFormatVersion: 2 | |||
guid: 8d34411ef4f6005489ca3bb91987c84a | |||
folderAsset: yes | |||
timeCreated: 1466179612 | |||
licenseType: Pro | |||
DefaultImporter: | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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; | |||
} | |||
} |
@@ -1,5 +1,7 @@ | |||
fileFormatVersion: 2 | |||
guid: 8830537b810b3fa489b14e66327f0af2 | |||
guid: 110a36d7de251bc41a444815f58a61c5 | |||
timeCreated: 1466179612 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] |
@@ -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; | |||
} | |||
} |
@@ -1,5 +1,7 @@ | |||
fileFormatVersion: 2 | |||
guid: 0bf92a08bb722064ab845dea9463933e | |||
guid: 86181a20ee37df64c9332a1c32369d72 | |||
timeCreated: 1466179768 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] |
@@ -5,7 +5,7 @@ namespace Svelto.Context | |||
void NotifyFrameworkInitialized(); | |||
void NotifyFrameworkDeinitialized(); | |||
void AddFrameworkInitializationListener(IWaitForFrameworkInitialization obj); | |||
void AddFrameworkInitializationListener(IWaitForFrameworkInitialization obj); | |||
void AddFrameworkDestructionListener(IWaitForFrameworkDestruction obj); | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
using UnityEngine; | |||
namespace Svelto.Context.Legacy | |||
{ | |||
public interface IUnityContextHierarchyChangedListener | |||
{ | |||
void OnMonobehaviourAdded(MonoBehaviour component); | |||
void OnMonobehaviourRemoved(MonoBehaviour component); | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
fileFormatVersion: 2 | |||
guid: 905bfd795bede6d448694b23dd88e0be | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: |
@@ -0,0 +1,9 @@ | |||
fileFormatVersion: 2 | |||
guid: 14144568b327bbe40876c12a777e5f05 | |||
folderAsset: yes | |||
timeCreated: 1434752394 | |||
licenseType: Free | |||
DefaultImporter: | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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]); | |||
} | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
fileFormatVersion: 2 | |||
guid: 13278f0cd187d8d43acc6a02479e459b | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: |
@@ -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; | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
fileFormatVersion: 2 | |||
guid: 0d87fb4479e8365499c162fe0fc94acc | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: |
@@ -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; | |||
} | |||
} |
@@ -1,5 +1,7 @@ | |||
fileFormatVersion: 2 | |||
guid: 0859454e6e6d968498cea54757578eda | |||
guid: d19e59cbec974dd4d821a7dd21f87a88 | |||
timeCreated: 1472488070 | |||
licenseType: Free | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
@@ -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; | |||
} | |||
} |
@@ -1,5 +1,7 @@ | |||
fileFormatVersion: 2 | |||
guid: 82d08d9d100803c47b036667fdc671cf | |||
guid: 0cfa789b3147c2e4e80d067693a58103 | |||
timeCreated: 1455809369 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] |
@@ -1,5 +1,5 @@ | |||
fileFormatVersion: 2 | |||
guid: 48e872bc336b396409c9316cdbfc045a | |||
guid: 8fffa27a4e8919a4db6fe1369e22e1b5 | |||
folderAsset: yes | |||
DefaultImporter: | |||
userData: |
@@ -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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
fileFormatVersion: 2 | |||
guid: ac754b29409379046935c0890bab6dc5 | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: |
@@ -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); | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
fileFormatVersion: 2 | |||
guid: 5d399e4c2c1fe1f47833d6b70bf16184 | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: |
@@ -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; } | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
fileFormatVersion: 2 | |||
guid: 2a6cc02a61a6ff549b1dcac74c71681f | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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 | |||
} | |||
@@ -0,0 +1,7 @@ | |||
fileFormatVersion: 2 | |||
guid: b4a9cb2903cd07946a8650aeefb8d853 | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -1,5 +1,7 @@ | |||
fileFormatVersion: 2 | |||
guid: b62a08472017f914d9fc399eeb95108e | |||
guid: db7c720ce4e437f48b1380223ba08192 | |||
timeCreated: 1470829214 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] |
@@ -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(); | |||
@@ -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; } | |||
} | |||
} |
@@ -3,3 +3,5 @@ guid: 5015ea56c030d6542bd4daa46f59e549 | |||
folderAsset: yes | |||
DefaultImporter: | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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; | |||
} |
@@ -1,12 +0,0 @@ | |||
fileFormatVersion: 2 | |||
guid: d8886d5d5f5929c479998a1316264c23 | |||
timeCreated: 1436121137 | |||
licenseType: Free | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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 DispatchOnChange<T> : DispatchOnSet<T> | |||
{ | |||
set | |||
public DispatchOnChange(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); | |||
} | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
using BetterWeakEvents; | |||
namespace Svelto.ECS | |||
{ | |||
public class DispatchOnSet<T> | |||
{ | |||
public DispatchOnSet(int senderID) | |||
{ | |||
_senderID = senderID; | |||
_subscribers = new WeakEvent<int, T>(); | |||
} | |||
public T value | |||
{ | |||
set | |||
{ | |||
_value = value; | |||
_subscribers.Invoke(_senderID, value); | |||
} | |||
get | |||
{ | |||
return _value; | |||
} | |||
} | |||
public void NotifyOnValueSet(System.Action<int, T> action) | |||
{ | |||
_subscribers += action; | |||
} | |||
public void StopNotify(System.Action<int, T> action) | |||
{ | |||
_subscribers -= action; | |||
} | |||
protected T _value; | |||
protected int _senderID; | |||
protected WeakEvent<int, T> _subscribers; | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
fileFormatVersion: 2 | |||
guid: cbb1d54b291794b4686526222ee2bbb6 | |||
timeCreated: 1471250507 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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>()); | |||
} | |||
} |
@@ -1,37 +1,117 @@ | |||
using System; | |||
using System.Collections; | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
using Svelto.Ticker; | |||
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) | |||
{ | |||
@@ -46,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); | |||
@@ -55,7 +135,7 @@ namespace Svelto.ES | |||
} | |||
} | |||
_otherEnginesReferences.Add(engine); | |||
_otherEnginesReferences.Add(engine); | |||
} | |||
public void BuildEntity(int ID, EntityDescriptor ed) | |||
@@ -63,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++) | |||
{ | |||
@@ -89,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 | |||
@@ -113,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; | |||
} | |||
} | |||
@@ -128,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; | |||
@@ -147,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; | |||
@@ -162,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 | |||
@@ -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,0 +1,127 @@ | |||
namespace Svelto.ECS | |||
{ | |||
class GenericEntityDescriptor<T> : EntityDescriptor | |||
where T : NodeWithID, new() | |||
{ | |||
static GenericEntityDescriptor() | |||
{ | |||
_nodesToBuild = new INodeBuilder[] | |||
{ | |||
new NodeBuilder<T>() | |||
}; | |||
} | |||
public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) | |||
{ | |||
} | |||
static INodeBuilder[] _nodesToBuild; | |||
} | |||
class GenericEntityDescriptor<T, U> : EntityDescriptor | |||
where T : NodeWithID, new() | |||
where U : NodeWithID, new() | |||
{ | |||
static GenericEntityDescriptor() | |||
{ | |||
_nodesToBuild = new INodeBuilder[] | |||
{ | |||
new NodeBuilder<T>(), | |||
new NodeBuilder<U>() | |||
}; | |||
} | |||
public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) | |||
{ | |||
} | |||
static INodeBuilder[] _nodesToBuild; | |||
} | |||
class GenericEntityDescriptor<T, U, V> : EntityDescriptor | |||
where T : NodeWithID, new() | |||
where U : NodeWithID, new() | |||
where V : NodeWithID, new() | |||
{ | |||
static GenericEntityDescriptor() | |||
{ | |||
_nodesToBuild = new INodeBuilder[] | |||
{ | |||
new NodeBuilder<T>(), | |||
new NodeBuilder<U>(), | |||
new NodeBuilder<V>() | |||
}; | |||
} | |||
public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) | |||
{ | |||
} | |||
static INodeBuilder[] _nodesToBuild; | |||
} | |||
class GenericEntityDescriptor<T, U, V, W> : EntityDescriptor | |||
where T : NodeWithID, new() | |||
where U : NodeWithID, new() | |||
where V : NodeWithID, new() | |||
where W : NodeWithID, new() | |||
{ | |||
static GenericEntityDescriptor() | |||
{ | |||
_nodesToBuild = new INodeBuilder[] | |||
{ | |||
new NodeBuilder<T>(), | |||
new NodeBuilder<U>(), | |||
new NodeBuilder<V>(), | |||
new NodeBuilder<W>() | |||
}; | |||
} | |||
public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) | |||
{ | |||
} | |||
static INodeBuilder[] _nodesToBuild; | |||
} | |||
class GenericEntityDescriptor<T, U, V, W, X> : EntityDescriptor | |||
where T : NodeWithID, new() | |||
where U : NodeWithID, new() | |||
where V : NodeWithID, new() | |||
where W : NodeWithID, new() | |||
where X : NodeWithID, new() | |||
{ | |||
static GenericEntityDescriptor() | |||
{ | |||
_nodesToBuild = new INodeBuilder[] | |||
{ | |||
new NodeBuilder<T>(), | |||
new NodeBuilder<U>(), | |||
new NodeBuilder<V>(), | |||
new NodeBuilder<W>(), | |||
new NodeBuilder<X>() | |||
}; | |||
} | |||
public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) | |||
{ | |||
} | |||
static INodeBuilder[] _nodesToBuild; | |||
} | |||
class GenericEntityDescriptor<T, U, V, W, X, Y> : EntityDescriptor | |||
where T : NodeWithID, new() | |||
where U : NodeWithID, new() | |||
where V : NodeWithID, new() | |||
where W : NodeWithID, new() | |||
where X : NodeWithID, new() | |||
where Y : NodeWithID, new() | |||
{ | |||
static GenericEntityDescriptor() | |||
{ | |||
_nodesToBuild = new INodeBuilder[] | |||
{ | |||
new NodeBuilder<T>(), | |||
new NodeBuilder<U>(), | |||
new NodeBuilder<V>(), | |||
new NodeBuilder<W>(), | |||
new NodeBuilder<X>(), | |||
new NodeBuilder<Y>() | |||
}; | |||
} | |||
public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) | |||
{ | |||
} | |||
static INodeBuilder[] _nodesToBuild; | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
fileFormatVersion: 2 | |||
guid: fd70d1b3107162f4790b1b71a129818f | |||
timeCreated: 1484485852 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -1,6 +0,0 @@ | |||
namespace Svelto.ES | |||
{ | |||
public interface IComponent | |||
{ | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
@@ -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); | |||
} | |||
} |
@@ -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,5 +1,5 @@ | |||
fileFormatVersion: 2 | |||
guid: 5a8db83410a8aaa40b824fdc3d968f67 | |||
guid: 0badb413a22f42a4b9f68e356e88b07f | |||
timeCreated: 1463438461 | |||
licenseType: Free | |||
MonoImporter: | |||
@@ -0,0 +1,6 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public interface IImplementor | |||
{ | |||
} | |||
} |
@@ -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,6 +1,6 @@ | |||
using System; | |||
namespace Svelto.ES | |||
namespace Svelto.ECS | |||
{ | |||
public interface IRemoveEntityComponent | |||
{ | |||
@@ -0,0 +1,9 @@ | |||
fileFormatVersion: 2 | |||
guid: 8d07544bb8b417f4a9eaefe0d4771d89 | |||
folderAsset: yes | |||
timeCreated: 1462355668 | |||
licenseType: Pro | |||
DefaultImporter: | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -0,0 +1,9 @@ | |||
fileFormatVersion: 2 | |||
guid: 71a4fc836cd9bde4bbb936a073804ec5 | |||
folderAsset: yes | |||
timeCreated: 1480683133 | |||
licenseType: Pro | |||
DefaultImporter: | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -0,0 +1,9 @@ | |||
fileFormatVersion: 2 | |||
guid: 777a9420fd80746428f9d2c5b718fd2f | |||
folderAsset: yes | |||
timeCreated: 1462351213 | |||
licenseType: Pro | |||
DefaultImporter: | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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 | |||
} |
@@ -0,0 +1,12 @@ | |||
fileFormatVersion: 2 | |||
guid: a2202d1747b86dc428310091a9c1a7ef | |||
timeCreated: 1462469401 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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, ""); | |||
} | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
fileFormatVersion: 2 | |||
guid: e7b6bbeaaa16ab84aaa99c3311199efa | |||
timeCreated: 1462351229 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
fileFormatVersion: 2 | |||
guid: 30f9f7f6468a96d4da2525c65ecfe637 | |||
timeCreated: 1467633311 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
fileFormatVersion: 2 | |||
guid: 1c9cf391ddfdd92429ce90eeb73a936a | |||
timeCreated: 1462527509 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
fileFormatVersion: 2 | |||
guid: 93c5ba48b51186e44b7094eef7028c90 | |||
timeCreated: 1462357591 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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>(); | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
fileFormatVersion: 2 | |||
guid: 74b59045fe5033b4b98facadd4d6b114 | |||
timeCreated: 1462355668 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
fileFormatVersion: 2 | |||
guid: 688f5cf68b171ef4fa0f6aab49644c48 | |||
timeCreated: 1462469401 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -0,0 +1,52 @@ | |||
using System; | |||
using Steps = System.Collections.Generic.Dictionary<Svelto.ECS.IEngine, System.Collections.Generic.Dictionary<System.Enum, Svelto.ECS.IStep[]>>; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IStep | |||
{ } | |||
public interface IStep<T>:IStep | |||
{ | |||
void Step(ref T token, Enum condition); | |||
} | |||
public class Sequencer | |||
{ | |||
public Sequencer() | |||
{} | |||
public void SetSequence(Steps steps) | |||
{ | |||
_steps = steps; | |||
} | |||
public void Next<T>(IEngine engine, ref T param) | |||
{ | |||
Next(engine, ref param, Condition.always); | |||
} | |||
public void Next<T>(IEngine engine, ref T param, Enum condition) | |||
{ | |||
var tos = _steps[engine]; | |||
var steps = tos[condition]; | |||
if (steps != null) | |||
for (int i = 0; i < steps.Length; i++) | |||
((IStep<T>)steps[i]).Step(ref param, condition); | |||
} | |||
Steps _steps; | |||
} | |||
public enum Condition | |||
{ | |||
always | |||
} | |||
public enum DamageCondition | |||
{ | |||
damage, | |||
dead | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
fileFormatVersion: 2 | |||
guid: 4e1f40937ca5027428ed7193729be1a5 | |||
timeCreated: 1484487023 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -0,0 +1,20 @@ | |||
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 virtual void Add(TNodeType node) | |||
{} | |||
protected virtual void Remove(TNodeType node) | |||
{} | |||
} | |||
} |
@@ -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,8 +1,8 @@ | |||
fileFormatVersion: 2 | |||
guid: 354b7801c1d782b47b5d9ff9aba85e83 | |||
guid: 7f7896118ef3c4949898b5c30aea0c47 | |||
folderAsset: yes | |||
timeCreated: 1434752394 | |||
licenseType: Free | |||
timeCreated: 1484044925 | |||
licenseType: Pro | |||
DefaultImporter: | |||
userData: | |||
assetBundleName: | |||
@@ -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(); | |||
} | |||
} | |||
} |
@@ -1,12 +0,0 @@ | |||
fileFormatVersion: 2 | |||
guid: e9d378efdf4e5dd439400d001b6775ec | |||
timeCreated: 1440356670 | |||
licenseType: Free | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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(); | |||
} | |||
} |
@@ -1,12 +0,0 @@ | |||
fileFormatVersion: 2 | |||
guid: 7b4eff431179a5047a4b9110fe27ecf6 | |||
timeCreated: 1440352946 | |||
licenseType: Free | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -1,5 +0,0 @@ | |||
fileFormatVersion: 2 | |||
guid: 86d00787627f3e643bfb37812067fbc5 | |||
folderAsset: yes | |||
DefaultImporter: | |||
userData: |
@@ -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 | |||
{ | |||
} | |||
} |
@@ -1,8 +0,0 @@ | |||
namespace Svelto.Ticker | |||
{ | |||
public interface ITicker | |||
{ | |||
void Add(ITickableBase tickable); | |||
void Remove(ITickableBase tickable); | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -1,12 +0,0 @@ | |||
fileFormatVersion: 2 | |||
guid: fe6689309725deb4f975fb62e1e061e4 | |||
timeCreated: 1435431616 | |||
licenseType: Free | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -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>(); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -1,8 +1,8 @@ | |||
fileFormatVersion: 2 | |||
guid: 9581aacbfc73076489f471e3e83ba71d | |||
guid: b7374ca2eb247d44c8783d7d23f9b89e | |||
folderAsset: yes | |||
timeCreated: 1440947158 | |||
licenseType: Free | |||
timeCreated: 1484394896 | |||
licenseType: Pro | |||
DefaultImporter: | |||
userData: | |||
assetBundleName: | |||
@@ -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 | |||
} | |||
} | |||
@@ -0,0 +1,122 @@ | |||
using System; | |||
using System.Reflection; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
//careful, you must handle the destruction of the GCHandles! | |||
namespace BetterWeakEvents | |||
{ | |||
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; | |||
} | |||
Release(); | |||
return false; | |||
} | |||
public bool Equals(WeakAction<T1, T2> other) | |||
{ | |||
return (Method.Equals(other.Method)); | |||
} | |||
public void Release() | |||
{ | |||
ObjectRef.Free(); | |||
} | |||
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; | |||
} | |||
Release(); | |||
return false; | |||
} | |||
public bool Equals(WeakAction<T> other) | |||
{ | |||
return (Method.Equals(other.Method)); | |||
} | |||
public void Release() | |||
{ | |||
ObjectRef.Free(); | |||
} | |||
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; | |||
} | |||
Release(); | |||
return false; | |||
} | |||
public bool Equals(WeakAction other) | |||
{ | |||
return (Method.Equals(other.Method)); | |||
} | |||
public void Release() | |||
{ | |||
ObjectRef.Free(); | |||
} | |||
readonly GCHandle ObjectRef; | |||
readonly MethodInfo Method; | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
fileFormatVersion: 2 | |||
guid: 1662357721652524593d7b0f731aef45 | |||
timeCreated: 1484483913 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |
@@ -0,0 +1,35 @@ | |||
using Svelto.DataStructures; | |||
using System; | |||
namespace BetterWeakEvents | |||
{ | |||
public class WeakEvent<T1, T2> | |||
{ | |||
public static WeakEvent<T1, T2> operator+(WeakEvent<T1, T2> c1, Action<T1, T2> x) | |||
{ | |||
c1._subscribers.Add(new WeakAction<T1, T2>(x)); | |||
return c1; | |||
} | |||
public static WeakEvent<T1, T2> operator-(WeakEvent<T1, T2> c1, Action<T1, T2> x) | |||
{ | |||
c1._subscribers.UnorderredRemove(new WeakAction<T1, T2>(x)); | |||
return c1; | |||
} | |||
public void Invoke(T1 arg1, T2 arg2) | |||
{ | |||
for (int i = 0; i < _subscribers.Count; i++) | |||
if (_subscribers[i].Invoke(arg1, arg2) == false) | |||
_subscribers.UnorderredRemoveAt(i--); | |||
} | |||
~WeakEvent() | |||
{ | |||
for (int i = 0; i < _subscribers.Count; i++) | |||
_subscribers[i].Release(); | |||
} | |||
protected FasterList<WeakAction<T1, T2>> _subscribers = new FasterList<WeakAction<T1, T2>>(); | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
fileFormatVersion: 2 | |||
guid: e2ee3763e7dffd4459c588eda6867ddd | |||
timeCreated: 1484480295 | |||
licenseType: Pro | |||
MonoImporter: | |||
serializedVersion: 2 | |||
defaultReferences: [] | |||
executionOrder: 0 | |||
icon: {instanceID: 0} | |||
userData: | |||
assetBundleName: | |||
assetBundleVariant: |