From 393733853ba51ae7de6646f23440e4415d73220c Mon Sep 17 00:00:00 2001 From: sebas77 Date: Sun, 15 Jan 2017 11:25:47 +0000 Subject: [PATCH 1/3] Dispatcher.cs is now deprecated, a legacy copy will be found in the example DispatcherOnChange and DispatcherOnSet now hold weakreferences of the actions Ticker is now deprecated, a legacy copy will be found in the example Added Engines Profiler Tool Added the concept of Entity Group (more will follow on the subject in an article) Handling the creation of entities inside the Add Methods fixed some bugs renamed namespace to Svelto.ECS add the concept of ExtraImplementors for the EntityDescriptor --- Context/ContextNotifier.cs | 20 +- Context/Factories/GameObjectFactory.cs | 4 +- Context/Factories/Legacy.meta | 9 + Context/Factories/Legacy/GameObjectFactory.cs | 98 ++++ .../Legacy/GameObjectFactory.cs.meta | 4 +- .../Factories/Legacy/MonoBehaviourFactory.cs | 36 ++ .../Legacy/MonoBehaviourFactory.cs.meta | 4 +- Context/IContextNotifer.cs | 2 +- .../IUnityContextHierarchyChangedListener.cs | 11 + ...ityContextHierarchyChangedListener.cs.meta | 8 + Context/Unity.meta | 9 + Context/Unity/NotifyComponentsRemoved.cs | 30 ++ Context/Unity/NotifyComponentsRemoved.cs.meta | 8 + DataStructures/CircularBufferIndexer.cs | 183 ++++++++ DataStructures/CircularBufferIndexer.cs.meta | 8 + DataStructures/FasterList.cs | 258 ++++++----- DataStructures/FasterList.cs.meta | 4 +- DataStructures/HashableWeakReference.cs | 56 +++ .../HashableWeakReference.cs.meta | 4 +- .../Priority Queue.meta | 2 +- .../Priority Queue/HeapPriorityQueue.cs | 327 ++++++++++++++ .../Priority Queue/HeapPriorityQueue.cs.meta | 8 + .../Priority Queue/IPriorityQueue.cs | 23 + .../Priority Queue/IPriorityQueue.cs.meta | 8 + .../Priority Queue/PriorityQueueNode.cs | 24 + .../Priority Queue/PriorityQueueNode.cs.meta | 8 + DataStructures/ReadOnlyDictionary.cs | 419 +++++++----------- DataStructures/SerializableDictionary.cs | 183 ++++++++ DataStructures/SerializableDictionary.cs.meta | 7 + DataStructures/ThreadSafeDictionary.cs | 303 +++++++++++++ .../ThreadSafeDictionary.cs.meta | 4 +- DataStructures/ThreadSafeQueue.cs | 53 ++- DataStructures/WeakReference.cs | 5 + ECS.meta | 2 + ECS/Dispatcher/Dispatcher.cs | 39 -- ECS/Dispatcher/Dispatcher.cs.meta | 12 - ECS/Dispatcher/DispatcherOnChange.cs | 36 +- ECS/Dispatcher/DispatcherOnSet.cs | 48 ++ ECS/Dispatcher/DispatcherOnSet.cs.meta | 12 + ECS/EngineNodeDB.cs | 43 +- ECS/EnginesRoot.cs | 218 +++++++-- ECS/EntityDescriptor.cs | 63 ++- ECS/IComponent.cs | 6 - ECS/IEngine.cs | 18 +- ECS/IEngineNodeDB.cs | 9 +- ECS/IEnginesRoot.cs | 4 +- ECS/IEntityDescriptorHolder.cs | 11 +- ECS/IEntityDescriptorHolder.cs.meta | 2 +- ECS/IImplementor.cs | 6 + ...Component.cs.meta => IImplementor.cs.meta} | 0 ECS/INode.cs | 4 +- ECS/IRemoveEntityComponent.cs | 2 +- ECS/Profiler.meta | 9 + ECS/Profiler/Editor.meta | 9 + ECS/Profiler/Editor/EngineProfiler.meta | 9 + .../EngineProfiler/EngineProfilerInspector.cs | 373 ++++++++++++++++ .../EngineProfilerInspector.cs.meta | 12 + .../EngineProfiler/EngineProfilerMenuItem.cs | 22 + .../EngineProfilerMenuItem.cs.meta | 12 + .../Editor/EngineProfiler/EnginesMonitor.cs | 132 ++++++ .../EngineProfiler/EnginesMonitor.cs.meta | 12 + .../EngineProfiler/ProfilerEditorLayout.cs | 65 +++ .../ProfilerEditorLayout.cs.meta | 12 + ECS/Profiler/EngineInfo.cs | 167 +++++++ ECS/Profiler/EngineInfo.cs.meta | 12 + ECS/Profiler/EngineProfiler.cs | 126 ++++++ ECS/Profiler/EngineProfiler.cs.meta | 12 + ECS/Profiler/EngineProfilerBehaviour.cs | 19 + ECS/Profiler/EngineProfilerBehaviour.cs.meta | 12 + ECS/SingleNodeEngine.cs | 18 + ECS/SingleNodeEngine.cs.meta | 12 + ECS/note.txt | 2 +- Factories.meta | 6 +- Observer/Observable.cs | 42 -- Observer/Observable.cs.meta | 12 - Observer/Observer.cs | 111 ----- Observer/Observer.cs.meta | 12 - Ticker.meta | 5 - Ticker/ITickable.cs | 31 -- Ticker/ITicker.cs | 8 - Ticker/IntervaledTickAttribute.cs | 15 - Ticker/IntervaledTickAttribute.cs.meta | 12 - Ticker/TickBehaviour.cs | 153 ------- Ticker/UnityTicker.cs | 57 --- Utilities.meta | 6 +- Utilities/Print.cs | 97 +++- Utilities/WeakAction.cs | 99 +++++ Utilities/WeakAction.cs.meta | 12 + 88 files changed, 3323 insertions(+), 1067 deletions(-) create mode 100644 Context/Factories/Legacy.meta create mode 100644 Context/Factories/Legacy/GameObjectFactory.cs rename Ticker/ITickable.cs.meta => Context/Factories/Legacy/GameObjectFactory.cs.meta (69%) create mode 100644 Context/Factories/Legacy/MonoBehaviourFactory.cs rename Ticker/ITicker.cs.meta => Context/Factories/Legacy/MonoBehaviourFactory.cs.meta (69%) create mode 100644 Context/IUnityContextHierarchyChangedListener.cs create mode 100644 Context/IUnityContextHierarchyChangedListener.cs.meta create mode 100644 Context/Unity.meta create mode 100644 Context/Unity/NotifyComponentsRemoved.cs create mode 100644 Context/Unity/NotifyComponentsRemoved.cs.meta create mode 100644 DataStructures/CircularBufferIndexer.cs create mode 100644 DataStructures/CircularBufferIndexer.cs.meta create mode 100644 DataStructures/HashableWeakReference.cs rename Ticker/TickBehaviour.cs.meta => DataStructures/HashableWeakReference.cs.meta (69%) rename Observer.meta => DataStructures/Priority Queue.meta (63%) create mode 100644 DataStructures/Priority Queue/HeapPriorityQueue.cs create mode 100644 DataStructures/Priority Queue/HeapPriorityQueue.cs.meta create mode 100644 DataStructures/Priority Queue/IPriorityQueue.cs create mode 100644 DataStructures/Priority Queue/IPriorityQueue.cs.meta create mode 100644 DataStructures/Priority Queue/PriorityQueueNode.cs create mode 100644 DataStructures/Priority Queue/PriorityQueueNode.cs.meta create mode 100644 DataStructures/SerializableDictionary.cs create mode 100644 DataStructures/SerializableDictionary.cs.meta create mode 100644 DataStructures/ThreadSafeDictionary.cs rename Ticker/UnityTicker.cs.meta => DataStructures/ThreadSafeDictionary.cs.meta (69%) delete mode 100644 ECS/Dispatcher/Dispatcher.cs delete mode 100644 ECS/Dispatcher/Dispatcher.cs.meta create mode 100644 ECS/Dispatcher/DispatcherOnSet.cs create mode 100644 ECS/Dispatcher/DispatcherOnSet.cs.meta delete mode 100644 ECS/IComponent.cs create mode 100644 ECS/IImplementor.cs rename ECS/{IComponent.cs.meta => IImplementor.cs.meta} (100%) create mode 100644 ECS/Profiler.meta create mode 100644 ECS/Profiler/Editor.meta create mode 100644 ECS/Profiler/Editor/EngineProfiler.meta create mode 100644 ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs create mode 100644 ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs.meta create mode 100644 ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs create mode 100644 ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs.meta create mode 100644 ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs create mode 100644 ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs.meta create mode 100644 ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs create mode 100644 ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs.meta create mode 100644 ECS/Profiler/EngineInfo.cs create mode 100644 ECS/Profiler/EngineInfo.cs.meta create mode 100644 ECS/Profiler/EngineProfiler.cs create mode 100644 ECS/Profiler/EngineProfiler.cs.meta create mode 100644 ECS/Profiler/EngineProfilerBehaviour.cs create mode 100644 ECS/Profiler/EngineProfilerBehaviour.cs.meta create mode 100644 ECS/SingleNodeEngine.cs create mode 100644 ECS/SingleNodeEngine.cs.meta delete mode 100644 Observer/Observable.cs delete mode 100644 Observer/Observable.cs.meta delete mode 100644 Observer/Observer.cs delete mode 100644 Observer/Observer.cs.meta delete mode 100644 Ticker.meta delete mode 100644 Ticker/ITickable.cs delete mode 100644 Ticker/ITicker.cs delete mode 100644 Ticker/IntervaledTickAttribute.cs delete mode 100644 Ticker/IntervaledTickAttribute.cs.meta delete mode 100644 Ticker/TickBehaviour.cs delete mode 100644 Ticker/UnityTicker.cs create mode 100644 Utilities/WeakAction.cs create mode 100644 Utilities/WeakAction.cs.meta diff --git a/Context/ContextNotifier.cs b/Context/ContextNotifier.cs index dfe4aa9..88d56f6 100644 --- a/Context/ContextNotifier.cs +++ b/Context/ContextNotifier.cs @@ -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 /// 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 /// 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> _toDeinitialize; + List> _toDeinitialize; List> _toInitialize; } } diff --git a/Context/Factories/GameObjectFactory.cs b/Context/Factories/GameObjectFactory.cs index 3f9c806..9519e6d 100644 --- a/Context/Factories/GameObjectFactory.cs +++ b/Context/Factories/GameObjectFactory.cs @@ -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 /// original prefab public GameObject Build(GameObject prefab) { - Profiler.BeginSample("GameObject Factory Build"); + UnityEngine.Profiling.Profiler.BeginSample("GameObject Factory Build"); var copy = Object.Instantiate(prefab) as GameObject; diff --git a/Context/Factories/Legacy.meta b/Context/Factories/Legacy.meta new file mode 100644 index 0000000..1f220ba --- /dev/null +++ b/Context/Factories/Legacy.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 8d34411ef4f6005489ca3bb91987c84a +folderAsset: yes +timeCreated: 1466179612 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Context/Factories/Legacy/GameObjectFactory.cs b/Context/Factories/Legacy/GameObjectFactory.cs new file mode 100644 index 0000000..49c1008 --- /dev/null +++ b/Context/Factories/Legacy/GameObjectFactory.cs @@ -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(root); + + _prefabs = new Dictionary(); + } + + 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; + } + + /// + /// Register a prefab to be built later using a string ID. + /// + /// original prefab + 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(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() == null) + currentGo.AddComponent().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 _prefabs; + WeakReference _unityContext; + } +} \ No newline at end of file diff --git a/Ticker/ITickable.cs.meta b/Context/Factories/Legacy/GameObjectFactory.cs.meta similarity index 69% rename from Ticker/ITickable.cs.meta rename to Context/Factories/Legacy/GameObjectFactory.cs.meta index 0f9fefa..b07702c 100644 --- a/Ticker/ITickable.cs.meta +++ b/Context/Factories/Legacy/GameObjectFactory.cs.meta @@ -1,5 +1,7 @@ fileFormatVersion: 2 -guid: 8830537b810b3fa489b14e66327f0af2 +guid: 110a36d7de251bc41a444815f58a61c5 +timeCreated: 1466179612 +licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/Context/Factories/Legacy/MonoBehaviourFactory.cs b/Context/Factories/Legacy/MonoBehaviourFactory.cs new file mode 100644 index 0000000..5669802 --- /dev/null +++ b/Context/Factories/Legacy/MonoBehaviourFactory.cs @@ -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(unityContext); + } + + public M Build(Func 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() == null) + go.AddComponent().unityContext = _unityContext; + + return mb; + } + + WeakReference _unityContext; + } +} diff --git a/Ticker/ITicker.cs.meta b/Context/Factories/Legacy/MonoBehaviourFactory.cs.meta similarity index 69% rename from Ticker/ITicker.cs.meta rename to Context/Factories/Legacy/MonoBehaviourFactory.cs.meta index c872bc0..8922e2e 100644 --- a/Ticker/ITicker.cs.meta +++ b/Context/Factories/Legacy/MonoBehaviourFactory.cs.meta @@ -1,5 +1,7 @@ fileFormatVersion: 2 -guid: 0bf92a08bb722064ab845dea9463933e +guid: 86181a20ee37df64c9332a1c32369d72 +timeCreated: 1466179768 +licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/Context/IContextNotifer.cs b/Context/IContextNotifer.cs index 13f744f..6ac2c14 100644 --- a/Context/IContextNotifer.cs +++ b/Context/IContextNotifer.cs @@ -5,7 +5,7 @@ namespace Svelto.Context void NotifyFrameworkInitialized(); void NotifyFrameworkDeinitialized(); - void AddFrameworkInitializationListener(IWaitForFrameworkInitialization obj); + void AddFrameworkInitializationListener(IWaitForFrameworkInitialization obj); void AddFrameworkDestructionListener(IWaitForFrameworkDestruction obj); } } diff --git a/Context/IUnityContextHierarchyChangedListener.cs b/Context/IUnityContextHierarchyChangedListener.cs new file mode 100644 index 0000000..5289957 --- /dev/null +++ b/Context/IUnityContextHierarchyChangedListener.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +namespace Svelto.Context.Legacy +{ + public interface IUnityContextHierarchyChangedListener + { + void OnMonobehaviourAdded(MonoBehaviour component); + + void OnMonobehaviourRemoved(MonoBehaviour component); + } +} diff --git a/Context/IUnityContextHierarchyChangedListener.cs.meta b/Context/IUnityContextHierarchyChangedListener.cs.meta new file mode 100644 index 0000000..f26268b --- /dev/null +++ b/Context/IUnityContextHierarchyChangedListener.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 905bfd795bede6d448694b23dd88e0be +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Context/Unity.meta b/Context/Unity.meta new file mode 100644 index 0000000..75e174f --- /dev/null +++ b/Context/Unity.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 14144568b327bbe40876c12a777e5f05 +folderAsset: yes +timeCreated: 1434752394 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Context/Unity/NotifyComponentsRemoved.cs b/Context/Unity/NotifyComponentsRemoved.cs new file mode 100644 index 0000000..429c162 --- /dev/null +++ b/Context/Unity/NotifyComponentsRemoved.cs @@ -0,0 +1,30 @@ +using Svelto.DataStructures; +using UnityEngine; + +namespace Svelto.Context.Legacy +{ + public class NotifyComponentsRemoved : MonoBehaviour + { + public WeakReference unityContext { private get; set; } + + void Start() + { + if (unityContext == null) + { + Destroy(this); + } + } + + void OnDestroy() + { + if (unityContext == null || unityContext.IsAlive == false) + return; + + MonoBehaviour[] components = gameObject.GetComponents(); + + for (int i = 0; i < components.Length; ++i) + if (components[i] != null) + unityContext.Target.OnMonobehaviourRemoved(components[i]); + } + } +} diff --git a/Context/Unity/NotifyComponentsRemoved.cs.meta b/Context/Unity/NotifyComponentsRemoved.cs.meta new file mode 100644 index 0000000..4418602 --- /dev/null +++ b/Context/Unity/NotifyComponentsRemoved.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 13278f0cd187d8d43acc6a02479e459b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/DataStructures/CircularBufferIndexer.cs b/DataStructures/CircularBufferIndexer.cs new file mode 100644 index 0000000..cc9d397 --- /dev/null +++ b/DataStructures/CircularBufferIndexer.cs @@ -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 : IDictionary + { + public ICollection Keys + { + get { return _keys; } + } + + public ICollection 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 item) + { + Add(item.Key, item.Value); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public bool Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + IEnumerator> IEnumerable>.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; + } +} diff --git a/DataStructures/CircularBufferIndexer.cs.meta b/DataStructures/CircularBufferIndexer.cs.meta new file mode 100644 index 0000000..a7e75ad --- /dev/null +++ b/DataStructures/CircularBufferIndexer.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0d87fb4479e8365499c162fe0fc94acc +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/DataStructures/FasterList.cs b/DataStructures/FasterList.cs index 15dc5b1..78cbaa2 100644 --- a/DataStructures/FasterList.cs +++ b/DataStructures/FasterList.cs @@ -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.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.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 buffer) { - get { return (T)_buffer.Current; } + _buffer = buffer; } - T IEnumerator.Current + object IEnumerator.Current { get { return (T)_buffer.Current; } } - public FasterListEnumeratorCast(FasterListEnumerator buffer) + T IEnumerator.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 _buffer; @@ -124,20 +120,15 @@ namespace Svelto.DataStructures public struct FasterReadOnlyList : IList { + public int Count { get { return _list.Count; } } + public bool IsReadOnly { get { return true; } } + public FasterReadOnlyList(FasterList list) { _list = list; } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + public T this[int index] { get { return _list[index]; } set { throw new NotImplementedException(); } } public FasterListEnumerator 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 IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } readonly FasterList _list; } public struct FasterReadOnlyListCast : IList where U:T { + public static FasterList DefaultList = new FasterList(); + + public int Count { get { return _list.Count; } } + public bool IsReadOnly { get { return true; } } + public FasterReadOnlyListCast(FasterList list) { _list = list; } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + public U this[int index] { get { return (U)_list[index]; } set { throw new NotImplementedException(); } } public FasterListEnumeratorCast 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 IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } readonly FasterList _list; - static public FasterList DefaultList = new FasterList(); } public class FasterList : IList { + 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 PreFill(int initialSize) where U:T, new() + { + var list = new FasterList(initialSize); + + for (int i = 0; i < initialSize; i++) + list.Add(new U()); + + list.Clear(); + + return list; + } + public void AddRange(IEnumerable items, int count) + { + AddRange(items.GetEnumerator(), count); + } + + public void AddRange(IEnumerator 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 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 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(this); } + /// + /// Careful, you could keep on holding references you don't want to hold to anymore + /// Use DeepClear in case. + /// 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(_buffer, Count); } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - public int IndexOf(T item) { var comp = EqualityComparer.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 comparer) + public void Sort(IComparer 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 IEnumerable.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; } } diff --git a/DataStructures/FasterList.cs.meta b/DataStructures/FasterList.cs.meta index 6c1704e..b678658 100644 --- a/DataStructures/FasterList.cs.meta +++ b/DataStructures/FasterList.cs.meta @@ -1,5 +1,7 @@ fileFormatVersion: 2 -guid: 0859454e6e6d968498cea54757578eda +guid: d19e59cbec974dd4d821a7dd21f87a88 +timeCreated: 1472488070 +licenseType: Free MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/DataStructures/HashableWeakReference.cs b/DataStructures/HashableWeakReference.cs new file mode 100644 index 0000000..62edfa6 --- /dev/null +++ b/DataStructures/HashableWeakReference.cs @@ -0,0 +1,56 @@ +using System; + +namespace Svelto.DataStructures +{ + class HashableWeakRef : IEquatable> 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 a, HashableWeakRef b) + { + return !(a == b); + } + + public static bool operator ==(HashableWeakRef a, HashableWeakRef 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) + return this.Equals((HashableWeakRef)other); + + return false; + } + + public bool Equals(HashableWeakRef other) + { + return (this == other); + } + + public override int GetHashCode() + { + return _hash; + } + + int _hash; + WeakReference _weakRef; + } +} diff --git a/Ticker/TickBehaviour.cs.meta b/DataStructures/HashableWeakReference.cs.meta similarity index 69% rename from Ticker/TickBehaviour.cs.meta rename to DataStructures/HashableWeakReference.cs.meta index f042c9a..6d88c48 100644 --- a/Ticker/TickBehaviour.cs.meta +++ b/DataStructures/HashableWeakReference.cs.meta @@ -1,5 +1,7 @@ fileFormatVersion: 2 -guid: 82d08d9d100803c47b036667fdc671cf +guid: 0cfa789b3147c2e4e80d067693a58103 +timeCreated: 1455809369 +licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/Observer.meta b/DataStructures/Priority Queue.meta similarity index 63% rename from Observer.meta rename to DataStructures/Priority Queue.meta index 1e0c65f..8f88283 100644 --- a/Observer.meta +++ b/DataStructures/Priority Queue.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 48e872bc336b396409c9316cdbfc045a +guid: 8fffa27a4e8919a4db6fe1369e22e1b5 folderAsset: yes DefaultImporter: userData: diff --git a/DataStructures/Priority Queue/HeapPriorityQueue.cs b/DataStructures/Priority Queue/HeapPriorityQueue.cs new file mode 100644 index 0000000..c30e06d --- /dev/null +++ b/DataStructures/Priority Queue/HeapPriorityQueue.cs @@ -0,0 +1,327 @@ +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Svelto.DataStructures; + +namespace Svelto.DataStructures +{ + /// + /// An implementation of a min-Priority Queue using a heap. Has O(1) .Contains()! + /// See https://bitbucket.org/BlueRaja/high-speed-priority-queue-for-c/wiki/Getting%20Started for more information + /// + /// The values in the queue. Must implement the PriorityQueueNode interface + public sealed class HeapPriorityQueue : IPriorityQueue + where T : PriorityQueueNode + { + private int _numNodes; + private readonly FasterList _nodes; + private long _numNodesEverEnqueued; + + /// + /// Instantiate a new Priority Queue + /// + /// The max nodes ever allowed to be enqueued (going over this will cause an exception) + public HeapPriorityQueue() + { + _numNodes = 0; + _nodes = new FasterList(); + _numNodesEverEnqueued = 0; + } + + public HeapPriorityQueue(int initialSize) + { + _numNodes = 0; + _nodes = new FasterList(initialSize); + _numNodesEverEnqueued = 0; + } + + /// + /// Returns the number of nodes in the queue. O(1) + /// + public int Count + { + get + { + return _numNodes; + } + } + + /// + /// 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) + /// + public int MaxSize + { + get + { + return _nodes.Count - 1; + } + } + + /// + /// Removes every node from the queue. O(n) (So, don't do this often!) + /// + #if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + #endif + public void Clear() + { + _nodes.Clear(); + + _numNodes = 0; + } + + /// + /// Returns (in O(1)!) whether the given node is in the queue. O(1) + /// + #if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + #endif + public bool Contains(T node) + { + return (_nodes[node.QueueIndex] == node); + } + + /// + /// Enqueue a node - .Priority must be set beforehand! O(log n) + /// + #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; + } + } + } + + /// + /// 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 + /// + #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)); + } + + /// + /// Removes the head of the queue (node with highest priority; ties are broken by order of insertion), and returns it. O(log n) + /// + public T Dequeue() + { + T returnMe = _nodes[1]; + Remove(returnMe); + return returnMe; + } + + /// + /// Returns the head of the queue, without removing it (use Dequeue() for that). O(1) + /// + public T First + { + get + { + return _nodes[1]; + } + } + + /// + /// This method must be called on a node every time its priority changes while it is in the queue. + /// Forgetting to call this method will result in a corrupted queue! + /// O(log n) + /// + #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); + } + } + + /// + /// Removes a node from the queue. Note that the node does not need to be the head of the queue. O(log n) + /// + 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 GetEnumerator() + { + for(int i = 1; i <= _numNodes; i++) + yield return _nodes[i]; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Should not be called in production code. + /// Checks to make sure the queue is still in a valid state. Used for testing/debugging the queue. + /// + 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; + } + } +} \ No newline at end of file diff --git a/DataStructures/Priority Queue/HeapPriorityQueue.cs.meta b/DataStructures/Priority Queue/HeapPriorityQueue.cs.meta new file mode 100644 index 0000000..b994121 --- /dev/null +++ b/DataStructures/Priority Queue/HeapPriorityQueue.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ac754b29409379046935c0890bab6dc5 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/DataStructures/Priority Queue/IPriorityQueue.cs b/DataStructures/Priority Queue/IPriorityQueue.cs new file mode 100644 index 0000000..632820e --- /dev/null +++ b/DataStructures/Priority Queue/IPriorityQueue.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Svelto.DataStructures +{ + /// + /// 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. + /// + public interface IPriorityQueue : IEnumerable + 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); + } +} diff --git a/DataStructures/Priority Queue/IPriorityQueue.cs.meta b/DataStructures/Priority Queue/IPriorityQueue.cs.meta new file mode 100644 index 0000000..21920be --- /dev/null +++ b/DataStructures/Priority Queue/IPriorityQueue.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5d399e4c2c1fe1f47833d6b70bf16184 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/DataStructures/Priority Queue/PriorityQueueNode.cs b/DataStructures/Priority Queue/PriorityQueueNode.cs new file mode 100644 index 0000000..b09f3a4 --- /dev/null +++ b/DataStructures/Priority Queue/PriorityQueueNode.cs @@ -0,0 +1,24 @@ +namespace Svelto.DataStructures +{ + public class PriorityQueueNode + { + /// + /// The Priority to insert this node at. Must be set BEFORE adding a node to the queue + /// + public double Priority { get; + set; + } + + /// + /// Used by the priority queue - do not edit this value. + /// Represents the order the node was inserted in + /// + public long InsertionIndex { get; set; } + + /// + /// Used by the priority queue - do not edit this value. + /// Represents the current position in the queue + /// + public int QueueIndex { get; set; } + } +} diff --git a/DataStructures/Priority Queue/PriorityQueueNode.cs.meta b/DataStructures/Priority Queue/PriorityQueueNode.cs.meta new file mode 100644 index 0000000..1002889 --- /dev/null +++ b/DataStructures/Priority Queue/PriorityQueueNode.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2a6cc02a61a6ff549b1dcac74c71681f +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/DataStructures/ReadOnlyDictionary.cs b/DataStructures/ReadOnlyDictionary.cs index 58b5840..8b09f3a 100644 --- a/DataStructures/ReadOnlyDictionary.cs +++ b/DataStructures/ReadOnlyDictionary.cs @@ -1,28 +1,11 @@ -//note: ripped from openstacknetsdk - using System; using System.Collections; using System.Collections.Generic; namespace Svelto.DataStructures { - public struct ReadOnlyDictionary : IDictionary, IDictionary + public struct ReadOnlyDictionary : IDictionary { - private readonly IDictionary _dictionary; - - /// - /// Initializes a new instance of the class - /// that is a wrapper around the specified dictionary. - /// - /// The dictionary to wrap. - public ReadOnlyDictionary(IDictionary dictionary) - { - if (dictionary == null) - throw new ArgumentNullException("dictionary"); - - _dictionary = dictionary; - } - public bool isInitialized { get { return _dictionary != null; } } /// @@ -40,45 +23,6 @@ namespace Svelto.DataStructures } } - /// - /// - /// Gets the element that has the specified key. - /// - /// If the property is set. - TValue IDictionary.this[TKey key] - { - get - { - return this[key]; - } - - set - { - throw new NotSupportedException(); - } - } - - /// - /// - /// Gets the element that has the specified key. - /// - /// If the property is set. - object IDictionary.this[object key] - { - get - { - if (!(key is TKey)) - return null; - - return this[(TKey)key]; - } - - set - { - throw new NotSupportedException(); - } - } - /// /// Gets the number of items in the dictionary. /// @@ -88,8 +32,8 @@ namespace Svelto.DataStructures public int Count { get - { - return _dictionary.Count; + { + return _dictionary.Count; } } @@ -107,24 +51,6 @@ namespace Svelto.DataStructures } } - /// - ICollection IDictionary.Keys - { - get - { - return Keys; - } - } - - /// - ICollection IDictionary.Keys - { - get - { - return Keys; - } - } - /// /// Gets a collection that contains the values in the dictionary. /// @@ -139,155 +65,82 @@ namespace Svelto.DataStructures } } - /// - ICollection IDictionary.Values + /// + /// Initializes a new instance of the class + /// that is a wrapper around the specified dictionary. + /// + /// The dictionary to wrap. + public ReadOnlyDictionary(Dictionary dictionary) { - get - { - return Values; - } - } + if (dictionary == null) + throw new ArgumentNullException("dictionary"); - /// - ICollection IDictionary.Values - { - get - { - return Values; - } + _dictionary = dictionary; } /// - bool ICollection>.IsReadOnly + /// + /// Gets the element that has the specified key. + /// + /// If the property is set. + TValue IDictionary.this[TKey key] { get { - return true; + return this[key]; } - } - /// - bool IDictionary.IsFixedSize - { - get + set { - return true; + throw new NotSupportedException(); } } /// - bool IDictionary.IsReadOnly + ICollection IDictionary.Keys { get { - return true; + return Keys; } } /// - bool ICollection.IsSynchronized + ICollection IDictionary.Values { get { - return false; + return Values; } } /// - object ICollection.SyncRoot + bool ICollection>.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; } } - /// - /// Determines whether the dictionary contains an element that has the specified key. - /// - /// The key to locate in the dictionary. - /// if the dictionary contains an element that has the specified key; otherwise, . - public bool ContainsKey(TKey key) - { - return _dictionary.ContainsKey(key); - } - - /// - bool IDictionary.Contains(object key) - { - if (key == null) - throw new ArgumentNullException("key"); - - if (key is TKey) - return ContainsKey((TKey)key); - - return false; - } - - /// - /// Returns an enumerator that iterates through the . - /// - /// An enumerator that can be used to iterate through the collection. - public IEnumerator> GetEnumerator() - { - return _dictionary.GetEnumerator(); - } - /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - /// - IDictionaryEnumerator IDictionary.GetEnumerator() - { - IDictionary dictionary = _dictionary as IDictionary; - if (dictionary != null) - return dictionary.GetEnumerator(); - - return new DictionaryEnumerator(_dictionary); - } - - /// - /// Retrieves the value that is associated with the specified key. - /// - /// The key whose value will be retrieved. - /// 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 parameter. This parameter is passed uninitialized. - /// if the object that implements contains an element with the specified key; otherwise, . - public bool TryGetValue(TKey key, out TValue value) - { - return _dictionary.TryGetValue(key, out value); - } - /// void IDictionary.Add(TKey key, TValue value) { throw new NotSupportedException(); } - /// - void IDictionary.Add(object key, object value) - { - throw new NotSupportedException(); - } - /// bool IDictionary.Remove(TKey key) { throw new NotSupportedException(); } - /// - void IDictionary.Remove(object key) - { - throw new NotSupportedException(); - } - /// void ICollection>.Add(KeyValuePair item) { @@ -318,29 +171,60 @@ namespace Svelto.DataStructures throw new NotSupportedException(); } - /// - void IDictionary.Clear() + bool IDictionary.ContainsKey(TKey key) { - throw new NotSupportedException(); + return _dictionary.ContainsKey(key); } - /// - void ICollection.CopyTo(Array array, int index) + bool IDictionary.TryGetValue(TKey key, out TValue value) { - throw new NotImplementedException(); + return _dictionary.TryGetValue(key, out value); } -/// + int ICollection>.Count + { + get { return _dictionary.Count; } + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + + /// + /// Determines whether the dictionary contains an element that has the specified key. + /// + /// The key to locate in the dictionary. + /// if the dictionary contains an element that has the specified key; otherwise, . + public bool ContainsKey(TKey key) + { + return _dictionary.ContainsKey(key); + } + + public DictionaryEnumerator GetEnumerator() + { + return new DictionaryEnumerator(_dictionary); + } + + /// + /// Retrieves the value that is associated with the specified key. + /// + /// The key whose value will be retrieved. + /// 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 parameter. This parameter is passed uninitialized. + /// if the object that implements contains an element with the specified key; otherwise, . + public bool TryGetValue(TKey key, out TValue value) + { + return _dictionary.TryGetValue(key, out value); + } + + readonly Dictionary _dictionary; + + /// /// Represents a read-only collection of the keys of a object. /// public struct KeyCollection : ICollection, ICollection { - /// - /// The wrapped collection of keys. - /// - private readonly ICollection _keys; - - /// + /// /// Initializes a new instance of the class /// as a wrapper around the specified collection of keys. /// @@ -354,48 +238,54 @@ namespace Svelto.DataStructures _keys = keys; } - /// - /// Gets the number of elements in the collection. - /// - /// - /// The number of elements in the collection. - /// - public int Count + /// + bool ICollection.IsSynchronized { get { - return _keys.Count; + return false; } } - /// - bool ICollection.IsReadOnly + /// + object ICollection.SyncRoot { get { - return true; + throw new NotImplementedException(); } } - /// - bool ICollection.IsSynchronized + /// + void ICollection.CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + /// + /// Gets the number of elements in the collection. + /// + /// + /// The number of elements in the collection. + /// + public int Count { get { - return false; + return _keys.Count; } } - /// - object ICollection.SyncRoot + /// + bool ICollection.IsReadOnly { get { - throw new NotImplementedException(); + return true; } } - /// + /// /// Copies the elements of the collection to an array, starting at a specific array index. /// /// The one-dimensional array that is the destination of the elements copied from the collection. The array must have zero-based indexing. @@ -414,13 +304,7 @@ namespace Svelto.DataStructures _keys.CopyTo(array, arrayIndex); } - /// - void ICollection.CopyTo(Array array, int index) - { - throw new NotImplementedException(); - } - - /// + /// /// Returns an enumerator that iterates through the collection. /// /// An enumerator that can be used to iterate through the collection. @@ -429,35 +313,40 @@ namespace Svelto.DataStructures return _keys.GetEnumerator(); } - /// + /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - /// + /// bool ICollection.Contains(TKey item) { return _keys.Contains(item); } - /// + /// void ICollection.Add(TKey item) { throw new NotSupportedException(); } - /// + /// bool ICollection.Remove(TKey item) { throw new NotSupportedException(); } - /// + /// void ICollection.Clear() { throw new NotSupportedException(); } + + /// + /// The wrapped collection of keys. + /// + readonly ICollection _keys; } /// @@ -465,11 +354,6 @@ namespace Svelto.DataStructures /// public struct ValueCollection : ICollection, ICollection { - /// - /// The wrapped collection of values. - /// - private readonly ICollection _values; - /// /// Initializes a new instance of the class /// as a wrapper around the specified collection of values. @@ -484,44 +368,50 @@ namespace Svelto.DataStructures _values = values; } - /// - /// Gets the number of elements in the collection. - /// - /// - /// The number of elements in the collection. - /// - public int Count + /// + bool ICollection.IsSynchronized { get { - return _values.Count; + return false; } } /// - bool ICollection.IsReadOnly + object ICollection.SyncRoot { get { - return true; + throw new NotImplementedException(); } } /// - bool ICollection.IsSynchronized + void ICollection.CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + /// + /// Gets the number of elements in the collection. + /// + /// + /// The number of elements in the collection. + /// + public int Count { get { - return false; + return _values.Count; } } /// - object ICollection.SyncRoot + bool ICollection.IsReadOnly { get { - throw new NotImplementedException(); + return true; } } @@ -544,12 +434,6 @@ namespace Svelto.DataStructures _values.CopyTo(array, arrayIndex); } - /// - void ICollection.CopyTo(Array array, int index) - { - throw new NotImplementedException(); - } - /// /// Returns an enumerator that iterates through the collection. /// @@ -588,54 +472,47 @@ namespace Svelto.DataStructures { throw new NotSupportedException(); } + + /// + /// The wrapped collection of values. + /// + readonly ICollection _values; } - struct DictionaryEnumerator : IDictionaryEnumerator + public struct DictionaryEnumerator:IEnumerator> { - private readonly IEnumerator> _enumerator; - - public DictionaryEnumerator(IDictionary dictionary) - { - if (dictionary == null) - throw new ArgumentNullException("dictionary"); - - _enumerator = dictionary.GetEnumerator(); - } - /// - public DictionaryEntry Entry + public TKey Key { get { - KeyValuePair current = _enumerator.Current; - return new DictionaryEntry(current.Key, current.Value); + return _enumerator.Current.Key; } } /// - public object Key + public TValue Value { get { - return _enumerator.Current.Key; + return _enumerator.Current.Value; } } - /// - public object Value + public DictionaryEnumerator(IDictionary dictionary) { - get - { - return _enumerator.Current.Value; - } + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + _enumerator = dictionary.GetEnumerator(); } /// - public object Current + public KeyValuePair 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> _enumerator; } } } diff --git a/DataStructures/SerializableDictionary.cs b/DataStructures/SerializableDictionary.cs new file mode 100644 index 0000000..93aae33 --- /dev/null +++ b/DataStructures/SerializableDictionary.cs @@ -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 +{ + public TKey Key; + public TVal Value; +} + +[Serializable()] +public class SerializableDictionary : Dictionary, +#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 dictionary) + : base(dictionary) + { + } + + public SerializableDictionary(IEqualityComparer comparer) + : base(comparer) + { + } + + public SerializableDictionary(int capacity) + : base(capacity) + { + } + + public SerializableDictionary(IDictionary dictionary, IEqualityComparer comparer) + : base(dictionary, comparer) + { + } + + public SerializableDictionary(int capacity, IEqualityComparer 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 kvp = (KeyValueSerialization)info.GetValue(String.Format("Im{0}", i), typeof(KeyValueSerialization)); + + this.Add(kvp.Key, kvp.Value); + } + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("count", this.Count); + int itemIdx = 0; + foreach (KeyValuePair kvp in this) + { + KeyValueSerialization kvs = new KeyValueSerialization(); + kvs.Key = kvp.Key; + kvs.Value = kvp.Value; + + info.AddValue(String.Format("Im{0}", itemIdx), kvs, typeof(KeyValueSerialization)); + itemIdx++; + } + } + + #endregion + #if XML_ENABLED + #region IXmlSerializable Members + + void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) + { + //writer.WriteStartElement(DictionaryNodeName); + foreach (KeyValuePair 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 +} + diff --git a/DataStructures/SerializableDictionary.cs.meta b/DataStructures/SerializableDictionary.cs.meta new file mode 100644 index 0000000..8ec8a55 --- /dev/null +++ b/DataStructures/SerializableDictionary.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b4a9cb2903cd07946a8650aeefb8d853 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/DataStructures/ThreadSafeDictionary.cs b/DataStructures/ThreadSafeDictionary.cs new file mode 100644 index 0000000..94a3646 --- /dev/null +++ b/DataStructures/ThreadSafeDictionary.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Svelto.DataStructures +{ + /// + /// original code: http://devplanet.com/blogs/brianr/archive/2008/09/29/thread-safe-dictionary-update.aspx + /// simplified (not an IDictionary) and apdated (uses FasterList) + /// + /// + /// + [Serializable] + public class ThreadSafeDictionary + { + // 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 Keys + { + get + { + using (new ReadOnlyLock(dictionaryLock)) + { + return new FasterList(dict.Keys); + } + } + } + + public virtual FasterList Values + { + get + { + using (new ReadOnlyLock(dictionaryLock)) + { + return new FasterList(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 item) + { + using (new WriteLock(dictionaryLock)) + { + dict.Add(item); + } + } + + public virtual void Clear() + { + using (new WriteLock(dictionaryLock)) + { + dict.Clear(); + } + } + + public virtual bool Contains(KeyValuePair item) + { + using (new ReadOnlyLock(dictionaryLock)) + { + return dict.Contains(item); + } + } + + public virtual void CopyTo(KeyValuePair[] array, int arrayIndex) + { + using (new ReadOnlyLock(dictionaryLock)) + { + dict.CopyTo(array, arrayIndex); + } + } + + public virtual bool Remove(KeyValuePair 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); + } + } + + /// + /// Merge does a blind remove, and then add. Basically a blind Upsert. + /// + /// Key to lookup + /// New Value + 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); + } + } + + /// + /// This is a blind remove. Prevents the need to check for existence first. + /// + /// Key to remove + 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 dict = new Dictionary(); + + [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); + } + } +} diff --git a/Ticker/UnityTicker.cs.meta b/DataStructures/ThreadSafeDictionary.cs.meta similarity index 69% rename from Ticker/UnityTicker.cs.meta rename to DataStructures/ThreadSafeDictionary.cs.meta index 26f9208..07fdea7 100644 --- a/Ticker/UnityTicker.cs.meta +++ b/DataStructures/ThreadSafeDictionary.cs.meta @@ -1,5 +1,7 @@ fileFormatVersion: 2 -guid: b62a08472017f914d9fc399eeb95108e +guid: db7c720ce4e437f48b1380223ba08192 +timeCreated: 1470829214 +licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/DataStructures/ThreadSafeQueue.cs b/DataStructures/ThreadSafeQueue.cs index b9b8f7d..4505dd8 100644 --- a/DataStructures/ThreadSafeQueue.cs +++ b/DataStructures/ThreadSafeQueue.cs @@ -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 DequeueAll() + public FasterList DequeueAll() { LockQ.EnterWriteLock(); try { - List returnList = new List(); + FasterList returnList = new FasterList(); while (m_Queue.Count > 0) returnList.Add(m_Queue.Dequeue()); @@ -107,6 +106,40 @@ namespace Svelto.DataStructures } } + public void DequeueAllInto(FasterList list) + { + LockQ.EnterWriteLock(); + try + { + while (m_Queue.Count > 0) + list.Add(m_Queue.Dequeue()); + } + + finally + { + LockQ.ExitWriteLock(); + } + } + + public FasterList DequeueAllAs() where U:class + { + LockQ.EnterWriteLock(); + try + { + FasterList returnList = new FasterList(); + + 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(); diff --git a/DataStructures/WeakReference.cs b/DataStructures/WeakReference.cs index cd69d9c..4a8d018 100644 --- a/DataStructures/WeakReference.cs +++ b/DataStructures/WeakReference.cs @@ -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; } + } } diff --git a/ECS.meta b/ECS.meta index 78d0f09..e1b48eb 100644 --- a/ECS.meta +++ b/ECS.meta @@ -3,3 +3,5 @@ guid: 5015ea56c030d6542bd4daa46f59e549 folderAsset: yes DefaultImporter: userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Dispatcher/Dispatcher.cs b/ECS/Dispatcher/Dispatcher.cs deleted file mode 100644 index 8682a53..0000000 --- a/ECS/Dispatcher/Dispatcher.cs +++ /dev/null @@ -1,39 +0,0 @@ -public class Dispatcher -{ - public event System.Action 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 -{ - public event System.Action subscribers; - - private Dispatcher() { } - - public Dispatcher(S sender) - { - _sender = sender; - } - - public void Dispatch() - { - if (subscribers != null) - subscribers(_sender); - } - - readonly S _sender; -} diff --git a/ECS/Dispatcher/Dispatcher.cs.meta b/ECS/Dispatcher/Dispatcher.cs.meta deleted file mode 100644 index 8e801c6..0000000 --- a/ECS/Dispatcher/Dispatcher.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: d8886d5d5f5929c479998a1316264c23 -timeCreated: 1436121137 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/ECS/Dispatcher/DispatcherOnChange.cs b/ECS/Dispatcher/DispatcherOnChange.cs index fea28a0..2a265d6 100644 --- a/ECS/Dispatcher/DispatcherOnChange.cs +++ b/ECS/Dispatcher/DispatcherOnChange.cs @@ -1,34 +1,24 @@ using System.Collections.Generic; -public class DispatcherOnChange: Dispatcher +namespace Svelto.ECS { - public DispatcherOnChange(S sender) : base(sender) { } - - public T value + public class DispatcherOnChange : DispatcherOnSet { - set + public DispatcherOnChange(int senderID) : base(senderID) + { } + + public new T value { - if (EqualityComparer.Default.Equals(value, _value) == false) + set { - _value = value; + if (EqualityComparer.Default.Equals(value, _value) == false) + base.value = _value; + } - Dispatch(ref value); + get + { + return _value; } } } - - T _value; } - -public class DispatcherOnSet: Dispatcher -{ - public DispatcherOnSet(S sender) : base(sender) { } - - public T value - { - set - { - Dispatch(ref value); - } - } -} \ No newline at end of file diff --git a/ECS/Dispatcher/DispatcherOnSet.cs b/ECS/Dispatcher/DispatcherOnSet.cs new file mode 100644 index 0000000..0514064 --- /dev/null +++ b/ECS/Dispatcher/DispatcherOnSet.cs @@ -0,0 +1,48 @@ +namespace Svelto.ECS +{ + public class DispatcherOnSet + { + public DispatcherOnSet(int senderID) + { + _senderID = senderID; + _subscribers = new System.Collections.Generic.HashSet>(); + } + + public T value + { + set + { + _value = value; + + if (_subscribers != null) + { + using (var enumerator = _subscribers.GetEnumerator()) + { + while (enumerator.MoveNext() == true) + enumerator.Current.Invoke(_senderID, _value); + } + } + } + + get + { + return _value; + } + } + + public void NotifyOnDataChange(System.Action action) + { + _subscribers.Add(new WeakAction(action)); + } + + public void StopNotifyOnDataChange(System.Action action) + { + _subscribers.Remove(new WeakAction(action)); + } + + protected T _value; + protected int _senderID; + + protected System.Collections.Generic.HashSet> _subscribers; + } +} diff --git a/ECS/Dispatcher/DispatcherOnSet.cs.meta b/ECS/Dispatcher/DispatcherOnSet.cs.meta new file mode 100644 index 0000000..d868b59 --- /dev/null +++ b/ECS/Dispatcher/DispatcherOnSet.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: cbb1d54b291794b4686526222ee2bbb6 +timeCreated: 1471250507 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/EngineNodeDB.cs b/ECS/EngineNodeDB.cs index 3eb6f61..1ed8944 100644 --- a/ECS/EngineNodeDB.cs +++ b/ECS/EngineNodeDB.cs @@ -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> nodesDB, Dictionary> nodesDBdic) + internal EngineNodeDB( Dictionary> nodesDB, + Dictionary> nodesDBdic, + Dictionary> nodesDBgroups) { - this._nodesDB = nodesDB; - this._nodesDBdic = nodesDBdic; + _nodesDB = nodesDB; + _nodesDBdic = nodesDBdic; + _nodesDBgroups = nodesDBgroups; } public FasterReadOnlyListCast QueryNodes() where T:INode @@ -17,7 +20,7 @@ namespace Svelto.ES var type = typeof(T); if (_nodesDB.ContainsKey(type) == false) - return new FasterReadOnlyListCast(FasterReadOnlyListCast.DefaultList); + return RetrieveEmptyNodeList(); return new FasterReadOnlyListCast(_nodesDB[type]); } @@ -32,6 +35,26 @@ namespace Svelto.ES return new ReadOnlyDictionary(_nodesDBdic[type]); } + public T QueryNodeFromGroup(int groupID) where T : INode + { + return QueryNode(groupID); + } + + public bool QueryNodeFromGroup(int groupID, out T node) where T : INode + { + return QueryNode(groupID, out node); + } + + public FasterReadOnlyListCast QueryNodesFromGroups() where T : INode + { + var type = typeof(T); + + if (_nodesDBgroups.ContainsKey(type) == false) + return RetrieveEmptyNodeList(); + + return new FasterReadOnlyListCast(_nodesDBgroups[type]); + } + public bool QueryNode(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 RetrieveEmptyNodeList() where T : INode + { + return new FasterReadOnlyListCast(FasterReadOnlyListCast.DefaultList); + } + Dictionary> _nodesDB; - Dictionary> _nodesDBdic; + Dictionary> _nodesDBdic; + Dictionary> _nodesDBgroups; - ReadOnlyDictionary _defaultEmptyNodeDict = new ReadOnlyDictionary(new Dictionary()); + ReadOnlyDictionary _defaultEmptyNodeDict = new ReadOnlyDictionary(new Dictionary()); } } diff --git a/ECS/EnginesRoot.cs b/ECS/EnginesRoot.cs index 1aea454..6d43243 100644 --- a/ECS/EnginesRoot.cs +++ b/ECS/EnginesRoot.cs @@ -1,38 +1,117 @@ using System; +using System.Collections; using System.Collections.Generic; using Svelto.DataStructures; -using Svelto.Ticker; -using Nodes.Player; +using UnityEngine; -namespace Svelto.ES +#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR +using Svelto.ECS.Profiler; +#endif + +namespace Svelto.ECS { - public sealed class EnginesRoot: IEnginesRoot, IEndOfFrameTickable, IEntityFactory + class Scheduler : MonoBehaviour { - public EnginesRoot(ITicker ticker) + IEnumerator Start() { - ticker.Add(this); + while (true) + { + yield return new WaitForEndOfFrame(); + + OnTick(); + } + } + + internal Action OnTick; + } + public sealed class EnginesRoot : IEnginesRoot, IEntityFactory + { + public EnginesRoot() + { _nodeEngines = new Dictionary>>(); _engineRootWeakReference = new WeakReference(this); _otherEnginesReferences = new FasterList(); - + _nodesDB = new Dictionary>(); _nodesDBdic = new Dictionary>(); - _nodesToAdd = new Queue(); - _nodesToRemove = new Queue(); + _nodesToAdd = new FasterList(); + _groupNodesToAdd = new FasterList(); + + _nodesDBgroups = new Dictionary>(); + + GameObject go = new GameObject("ECSScheduler"); + + go.AddComponent().OnTick += SubmitNodes; + +#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR + GameObject debugEngineObject = new GameObject("Engine Debugger"); + debugEngineObject.gameObject.AddComponent(); +#endif } - public void EndOfFrameTick(float deltaSec) + void SubmitNodes() { - while (_nodesToAdd.Count > 0) InternalAdd(_nodesToAdd.Dequeue()); - while (_nodesToRemove.Count > 0) InternalRemove(_nodesToRemove.Dequeue()); + int groupNodesCount; + int nodesCount; + bool newNodesHaveBeenAddedWhileIterating; + int startNodes = 0; + int startGroupNodes = 0; + int numberOfReenteringLoops = 0; + + do + { + groupNodesCount = _groupNodesToAdd.Count; + nodesCount = _nodesToAdd.Count; + + for (int i = startNodes; i < nodesCount; i++) + { + var node = _nodesToAdd[i]; + AddNodeToTheDB(node, node.GetType()); + } + + for (int i = startGroupNodes; i < groupNodesCount; i++) + { + var node = _groupNodesToAdd[i]; + AddNodeToGroupDB(node, node.GetType()); + } + + for (int i = startNodes; i < nodesCount; i++) + { + var node = _nodesToAdd[i]; + AddNodeToTheSuitableEngines(node, node.GetType()); + } + + for (int i = startGroupNodes; i < groupNodesCount; i++) + { + var node = _groupNodesToAdd[i]; + AddNodeToTheSuitableEngines(node, node.GetType()); + } + + newNodesHaveBeenAddedWhileIterating = _groupNodesToAdd.Count > groupNodesCount || _nodesToAdd.Count > nodesCount; + + startNodes = nodesCount; + startGroupNodes = groupNodesCount; + + if (numberOfReenteringLoops > 5) + throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method"); + + numberOfReenteringLoops++; + + } while (newNodesHaveBeenAddedWhileIterating); + + _nodesToAdd.Clear(); + _groupNodesToAdd.Clear(); } public void AddEngine(IEngine engine) { +#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR + EngineProfiler.AddEngine(engine); +#endif if (engine is IQueryableNodeEngine) - (engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic); + (engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _nodesDBgroups); if (engine is INodesEngine) { @@ -47,7 +126,7 @@ namespace Svelto.ES if (baseType.IsGenericType) { var genericType = baseType.GetGenericTypeDefinition(); - + if (genericType == typeof(SingleNodeEngine<>)) { AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines); @@ -56,7 +135,7 @@ namespace Svelto.ES } } - _otherEnginesReferences.Add(engine); + _otherEnginesReferences.Add(engine); } public void BuildEntity(int ID, EntityDescriptor ed) @@ -64,14 +143,24 @@ namespace Svelto.ES var entityNodes = ed.BuildNodes(ID, (node) => { if (_engineRootWeakReference.IsValid == true) - _engineRootWeakReference.Target._nodesToRemove.Enqueue(node); + InternalRemove(node); }); - for (int i = 0; i < entityNodes.Count; i++) - _nodesToAdd.Enqueue(entityNodes[i]); + _nodesToAdd.AddRange(entityNodes); } - static void AddEngine(T engine, Type[] types, Dictionary>> engines) where T:INodeEngine + 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 engine, Type[] types, Dictionary>> engines) where T : INodeEngine { for (int i = 0; i < types.Length; i++) { @@ -90,20 +179,15 @@ namespace Svelto.ES } } - void InternalAdd(T node) where T:INode + void AddNodeToGroupDB(INode node, Type nodeType) { - Type nodeType = node.GetType(); - - AddNodeToTheSuitableEngines(node, nodeType); - AddNodeToTheDB(node, nodeType); - } + FasterList nodes; + if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == false) + nodes = _nodesDBgroups[nodeType] = new FasterList(); - void InternalRemove(T node) where T:INode - { - Type nodeType = node.GetType(); + nodes.Add(node); - RemoveNodeFromEngines(node, nodeType); - RemoveNodeFromTheDB(node, nodeType); + AddNodeToNodesDictionary(node, nodeType); } void AddNodeToTheDB(T node, Type nodeType) where T : INode @@ -114,12 +198,17 @@ namespace Svelto.ES nodes.Add(node); + AddNodeToNodesDictionary(node, nodeType); + } + + void AddNodeToNodesDictionary(T node, Type nodeType) where T : INode + { if (node is NodeWithID) { Dictionary nodesDic; if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false) nodesDic = _nodesDBdic[nodeType] = new Dictionary(); - + nodesDic[(node as NodeWithID).ID] = node; } } @@ -129,16 +218,29 @@ namespace Svelto.ES FasterList> 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 node, Type nodeType) where T : INode { FasterList 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 node, Type nodeType) where T : INode + { if (node is NodeWithID) { Dictionary nodesDic; @@ -148,13 +250,57 @@ namespace Svelto.ES } } + void RemoveNodeFromGroupDB(T node, Type nodeType) where T : INode + { + FasterList 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 node, Type nodeType) where T : INode { FasterList> 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 engine, INode node) + { + engine.Add(node); + } + + void RemoveNodeFromEngine(INodeEngine engine, INode node) + { + engine.Remove(node); + } +#endif + + void InternalRemove(T node) where T : INode + { + Type nodeType = node.GetType(); + + RemoveNodeFromEngines(node, nodeType); + RemoveNodeFromTheDB(node, node.GetType()); + } + + void InternalGroupRemove(T node) where T : INode + { + Type nodeType = node.GetType(); + + RemoveNodeFromEngines(node, nodeType); + RemoveNodeFromGroupDB(node, node.GetType()); } Dictionary>> _nodeEngines; @@ -163,11 +309,13 @@ namespace Svelto.ES Dictionary> _nodesDB; Dictionary> _nodesDBdic; - Queue _nodesToAdd; - Queue _nodesToRemove; + Dictionary> _nodesDBgroups; + + FasterList _nodesToAdd; + FasterList _groupNodesToAdd; WeakReference _engineRootWeakReference; - + //integrated pooling system //add debug panel like Entitas has //GCHandle should be used to reduce the number of strong references diff --git a/ECS/EntityDescriptor.cs b/ECS/EntityDescriptor.cs index 384d2db..6183fd5 100644 --- a/ECS/EntityDescriptor.cs +++ b/ECS/EntityDescriptor.cs @@ -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 BuildNodes(int ID, Action removeAction) - { - var nodes = new FasterList(); + public virtual FasterList BuildNodes(int ID, Action removeAction) + { + var nodes = new FasterList(); + + 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 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 diff --git a/ECS/IComponent.cs b/ECS/IComponent.cs deleted file mode 100644 index fc5858f..0000000 --- a/ECS/IComponent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Svelto.ES -{ - public interface IComponent - { - } -} \ No newline at end of file diff --git a/ECS/IEngine.cs b/ECS/IEngine.cs index 0d295ef..5bddc66 100644 --- a/ECS/IEngine.cs +++ b/ECS/IEngine.cs @@ -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 : INodeEngine where TNodeType:class, INode - { - void INodeEngine.Add(INode obj) - { - Add(obj as TNodeType); - } - - void INodeEngine.Remove(INode obj) - { - Remove(obj as TNodeType); - } - - protected abstract void Add(TNodeType node); - protected abstract void Remove(TNodeType node); - } } diff --git a/ECS/IEngineNodeDB.cs b/ECS/IEngineNodeDB.cs index 5e7b46f..069d532 100644 --- a/ECS/IEngineNodeDB.cs +++ b/ECS/IEngineNodeDB.cs @@ -1,12 +1,19 @@ using Svelto.DataStructures; -namespace Svelto.ES +namespace Svelto.ECS { public interface IEngineNodeDB { ReadOnlyDictionary QueryIndexableNodes() where T:INode; + bool QueryNode(int ID, out T node) where T:INode; T QueryNode(int ID) where T:INode; + FasterReadOnlyListCast QueryNodes() where T:INode; + + bool QueryNodeFromGroup(int ID, out T node) where T : INode; + T QueryNodeFromGroup(int ID) where T : INode; + FasterReadOnlyListCast QueryNodesFromGroups() where T : INode; } } + diff --git a/ECS/IEnginesRoot.cs b/ECS/IEnginesRoot.cs index 8fa7b5d..72c317a 100644 --- a/ECS/IEnginesRoot.cs +++ b/ECS/IEnginesRoot.cs @@ -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); } } diff --git a/ECS/IEntityDescriptorHolder.cs b/ECS/IEntityDescriptorHolder.cs index 9ee697a..2c70278 100644 --- a/ECS/IEntityDescriptorHolder.cs +++ b/ECS/IEntityDescriptorHolder.cs @@ -1,10 +1,11 @@ -namespace Svelto.ES +namespace Svelto.ECS { - /// - /// please use [DisallowMultipleComponent] in your monobehaviours that implement IEntityDescriptorHolder - /// + /// + /// please use [DisallowMultipleComponent] in your monobehaviours that implement IEntityDescriptorHolder + /// public interface IEntityDescriptorHolder { - EntityDescriptor BuildDescriptorType(); + //I must find a nicer solution for the extraImplentors + EntityDescriptor BuildDescriptorType(object[] extraImplentors = null); } } diff --git a/ECS/IEntityDescriptorHolder.cs.meta b/ECS/IEntityDescriptorHolder.cs.meta index cbd5e0b..bf596a5 100644 --- a/ECS/IEntityDescriptorHolder.cs.meta +++ b/ECS/IEntityDescriptorHolder.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5a8db83410a8aaa40b824fdc3d968f67 +guid: 0badb413a22f42a4b9f68e356e88b07f timeCreated: 1463438461 licenseType: Free MonoImporter: diff --git a/ECS/IImplementor.cs b/ECS/IImplementor.cs new file mode 100644 index 0000000..738c8e6 --- /dev/null +++ b/ECS/IImplementor.cs @@ -0,0 +1,6 @@ +namespace Svelto.ECS +{ + public interface IImplementor + { + } +} diff --git a/ECS/IComponent.cs.meta b/ECS/IImplementor.cs.meta similarity index 100% rename from ECS/IComponent.cs.meta rename to ECS/IImplementor.cs.meta diff --git a/ECS/INode.cs b/ECS/INode.cs index 7646bbe..30abb9f 100644 --- a/ECS/INode.cs +++ b/ECS/INode.cs @@ -1,11 +1,11 @@ -namespace Svelto.ES +namespace Svelto.ECS { public interface INode {} public class NodeWithID: INode { - public static TNodeType BuildNode(int ID) where TNodeType: NodeWithID, new() + public static TNodeType BuildNode(int ID) where TNodeType: NodeWithID, new() { return new TNodeType { _ID = ID }; } diff --git a/ECS/IRemoveEntityComponent.cs b/ECS/IRemoveEntityComponent.cs index b6acb5d..a3f1525 100644 --- a/ECS/IRemoveEntityComponent.cs +++ b/ECS/IRemoveEntityComponent.cs @@ -1,6 +1,6 @@ using System; -namespace Svelto.ES +namespace Svelto.ECS { public interface IRemoveEntityComponent { diff --git a/ECS/Profiler.meta b/ECS/Profiler.meta new file mode 100644 index 0000000..3a39d53 --- /dev/null +++ b/ECS/Profiler.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 8d07544bb8b417f4a9eaefe0d4771d89 +folderAsset: yes +timeCreated: 1462355668 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/Editor.meta b/ECS/Profiler/Editor.meta new file mode 100644 index 0000000..48dd9b3 --- /dev/null +++ b/ECS/Profiler/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 71a4fc836cd9bde4bbb936a073804ec5 +folderAsset: yes +timeCreated: 1480683133 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/Editor/EngineProfiler.meta b/ECS/Profiler/Editor/EngineProfiler.meta new file mode 100644 index 0000000..b802d05 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 777a9420fd80746428f9d2c5b718fd2f +folderAsset: yes +timeCreated: 1462351213 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs new file mode 100644 index 0000000..94683c2 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs @@ -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 _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(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 +} diff --git a/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs.meta b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs.meta new file mode 100644 index 0000000..02c6f19 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a2202d1747b86dc428310091a9c1a7ef +timeCreated: 1462469401 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs new file mode 100644 index 0000000..2c20e59 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs @@ -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, ""); + } + } +} diff --git a/ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs.meta b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs.meta new file mode 100644 index 0000000..b487da4 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e7b6bbeaaa16ab84aaa99c3311199efa +timeCreated: 1462351229 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs b/ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs new file mode 100644 index 0000000..0d568a4 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs @@ -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; + } + } +} diff --git a/ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs.meta b/ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs.meta new file mode 100644 index 0000000..aee00e5 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 30f9f7f6468a96d4da2525c65ecfe637 +timeCreated: 1467633311 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs b/ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs new file mode 100644 index 0000000..d012cac --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs @@ -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(string title) where T : EditorWindow + { + var window = EditorWindow.GetWindow(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(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(); + } + } +} diff --git a/ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs.meta b/ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs.meta new file mode 100644 index 0000000..77f5e52 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1c9cf391ddfdd92429ce90eeb73a936a +timeCreated: 1462527509 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/EngineInfo.cs b/ECS/Profiler/EngineInfo.cs new file mode 100644 index 0000000..ceffef9 --- /dev/null +++ b/ECS/Profiler/EngineInfo.cs @@ -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[] _updateFrameTimes = new Queue[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(); + } + 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; + } + } +} diff --git a/ECS/Profiler/EngineInfo.cs.meta b/ECS/Profiler/EngineInfo.cs.meta new file mode 100644 index 0000000..2463af8 --- /dev/null +++ b/ECS/Profiler/EngineInfo.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 93c5ba48b51186e44b7094eef7028c90 +timeCreated: 1462357591 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/EngineProfiler.cs b/ECS/Profiler/EngineProfiler.cs new file mode 100644 index 0000000..75e30e1 --- /dev/null +++ b/ECS/Profiler/EngineProfiler.cs @@ -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, INode> addingFunc, INodeEngine 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, INode> removeFunc, INodeEngine 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) + { + EngineInfo info; + if (engineInfos.TryGetValue((tickable as INodeEngine).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) + { + EngineInfo info; + if (engineInfos.TryGetValue((tickable as INodeEngine).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) + { + EngineInfo info; + if (engineInfos.TryGetValue((tickable as INodeEngine).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 engineInfos = new Dictionary(); + } +} diff --git a/ECS/Profiler/EngineProfiler.cs.meta b/ECS/Profiler/EngineProfiler.cs.meta new file mode 100644 index 0000000..7c2b637 --- /dev/null +++ b/ECS/Profiler/EngineProfiler.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 74b59045fe5033b4b98facadd4d6b114 +timeCreated: 1462355668 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/EngineProfilerBehaviour.cs b/ECS/Profiler/EngineProfilerBehaviour.cs new file mode 100644 index 0000000..9039a2a --- /dev/null +++ b/ECS/Profiler/EngineProfilerBehaviour.cs @@ -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.ValueCollection engines { get { return EngineProfiler.engineInfos.Values; } } + + public void ResetDurations() + { + EngineProfiler.ResetDurations(); + } + } +} diff --git a/ECS/Profiler/EngineProfilerBehaviour.cs.meta b/ECS/Profiler/EngineProfilerBehaviour.cs.meta new file mode 100644 index 0000000..17446f2 --- /dev/null +++ b/ECS/Profiler/EngineProfilerBehaviour.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 688f5cf68b171ef4fa0f6aab49644c48 +timeCreated: 1462469401 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/SingleNodeEngine.cs b/ECS/SingleNodeEngine.cs new file mode 100644 index 0000000..6b43243 --- /dev/null +++ b/ECS/SingleNodeEngine.cs @@ -0,0 +1,18 @@ +namespace Svelto.ECS +{ + public abstract class SingleNodeEngine : INodeEngine where TNodeType : class, INode + { + void INodeEngine.Add(INode obj) + { + Add(obj as TNodeType); + } + + void INodeEngine.Remove(INode obj) + { + Remove(obj as TNodeType); + } + + protected abstract void Add(TNodeType node); + protected abstract void Remove(TNodeType node); + } +} diff --git a/ECS/SingleNodeEngine.cs.meta b/ECS/SingleNodeEngine.cs.meta new file mode 100644 index 0000000..cdb2bea --- /dev/null +++ b/ECS/SingleNodeEngine.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 406a5e818b179b743b582c8fec087ac1 +timeCreated: 1469806920 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/note.txt b/ECS/note.txt index c041193..a55d53b 100644 --- a/ECS/note.txt +++ b/ECS/note.txt @@ -1,7 +1,7 @@ systems do not hold component data, but only system states systems cannot be injected systems are SRP and OCP -systems communicates between component, mediators, producer/consumer, observers. producer/consumer and observers must be defined in the layer of the engine. +systems communicates between component, mediators, producer/consumer, Svelto.ECS.Example.Observers. producer/consumer and observers must be defined in the layer of the engine. systems can have injected dependencies components don't have logic diff --git a/Factories.meta b/Factories.meta index 5cf0144..8a250da 100644 --- a/Factories.meta +++ b/Factories.meta @@ -1,8 +1,8 @@ fileFormatVersion: 2 -guid: 354b7801c1d782b47b5d9ff9aba85e83 +guid: 7f7896118ef3c4949898b5c30aea0c47 folderAsset: yes -timeCreated: 1434752394 -licenseType: Free +timeCreated: 1484044925 +licenseType: Pro DefaultImporter: userData: assetBundleName: diff --git a/Observer/Observable.cs b/Observer/Observable.cs deleted file mode 100644 index 9e56fd3..0000000 --- a/Observer/Observable.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; - -namespace Svelto.Observer -{ - public delegate void ObserverAction(ref DispatchType parameter); - - public interface IObservable - { - event Action Notify; - - void Dispatch(); - } - - public interface IObservable - { - event ObserverAction Notify; - - void Dispatch(ref DispatchType parameter); - } - - public class Observable:IObservable - { - public event ObserverAction 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(); - } - } -} diff --git a/Observer/Observable.cs.meta b/Observer/Observable.cs.meta deleted file mode 100644 index 8f93bb4..0000000 --- a/Observer/Observable.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: e9d378efdf4e5dd439400d001b6775ec -timeCreated: 1440356670 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Observer/Observer.cs b/Observer/Observer.cs deleted file mode 100644 index 7871f47..0000000 --- a/Observer/Observer.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; - -namespace Svelto.Observer.InterNamespace -{ - public abstract class Observer : IObserver - { - protected Observer(Observable observable) - { - observable.Notify += OnObservableDispatched; - - _unsubscribe = () => observable.Notify -= OnObservableDispatched; - } - - public void AddAction(ObserverAction action) - { - _actions += action; - } - - public void RemoveAction(ObserverAction 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 _actions; - Action _unsubscribe; - } -} - -namespace Svelto.Observer.IntraNamespace -{ - public class Observer : InterNamespace.Observer - { - public Observer(Observable 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 - { - void AddAction(ObserverAction action); - void RemoveAction(ObserverAction action); - - void Unsubscribe(); - } - - public interface IObserver - { - void AddAction(Action action); - void RemoveAction(Action action); - - void Unsubscribe(); - } -} \ No newline at end of file diff --git a/Observer/Observer.cs.meta b/Observer/Observer.cs.meta deleted file mode 100644 index 5277627..0000000 --- a/Observer/Observer.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 7b4eff431179a5047a4b9110fe27ecf6 -timeCreated: 1440352946 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Ticker.meta b/Ticker.meta deleted file mode 100644 index 9a9ebf6..0000000 --- a/Ticker.meta +++ /dev/null @@ -1,5 +0,0 @@ -fileFormatVersion: 2 -guid: 86d00787627f3e643bfb37812067fbc5 -folderAsset: yes -DefaultImporter: - userData: diff --git a/Ticker/ITickable.cs b/Ticker/ITickable.cs deleted file mode 100644 index f1c773c..0000000 --- a/Ticker/ITickable.cs +++ /dev/null @@ -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 - { - } -} diff --git a/Ticker/ITicker.cs b/Ticker/ITicker.cs deleted file mode 100644 index 58180d6..0000000 --- a/Ticker/ITicker.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Svelto.Ticker -{ - public interface ITicker - { - void Add(ITickableBase tickable); - void Remove(ITickableBase tickable); - } -} diff --git a/Ticker/IntervaledTickAttribute.cs b/Ticker/IntervaledTickAttribute.cs deleted file mode 100644 index ef3a879..0000000 --- a/Ticker/IntervaledTickAttribute.cs +++ /dev/null @@ -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; - } - } -} diff --git a/Ticker/IntervaledTickAttribute.cs.meta b/Ticker/IntervaledTickAttribute.cs.meta deleted file mode 100644 index e80216c..0000000 --- a/Ticker/IntervaledTickAttribute.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: fe6689309725deb4f975fb62e1e061e4 -timeCreated: 1435431616 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Ticker/TickBehaviour.cs b/Ticker/TickBehaviour.cs deleted file mode 100644 index 2a59d5d..0000000 --- a/Ticker/TickBehaviour.cs +++ /dev/null @@ -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 _lateTicked = new List(); - List _physicallyTicked = new List(); - List _ticked = new List(); - List _endOfFrameTicked = new List(); - - Dictionary _intervalledTicked = new Dictionary(); - } -} diff --git a/Ticker/UnityTicker.cs b/Ticker/UnityTicker.cs deleted file mode 100644 index 8ac0b0a..0000000 --- a/Ticker/UnityTicker.cs +++ /dev/null @@ -1,57 +0,0 @@ -using UnityEngine; - -namespace Svelto.Ticker -{ - class UnityTicker : ITicker - { - public UnityTicker() - { - _ticker = Object.FindObjectOfType(); - - if (_ticker == null) - { - var go = new GameObject("SveltoTicker"); - - _ticker = go.AddComponent(); - } - } - - 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; - } -} diff --git a/Utilities.meta b/Utilities.meta index 0f482d6..d093762 100644 --- a/Utilities.meta +++ b/Utilities.meta @@ -1,8 +1,8 @@ fileFormatVersion: 2 -guid: 9581aacbfc73076489f471e3e83ba71d +guid: b7374ca2eb247d44c8783d7d23f9b89e folderAsset: yes -timeCreated: 1440947158 -licenseType: Free +timeCreated: 1484394896 +licenseType: Pro DefaultImporter: userData: assetBundleName: diff --git a/Utilities/Print.cs b/Utilities/Print.cs index a693c3a..b3c8e9a 100644 --- a/Utilities/Print.cs +++ b/Utilities/Print.cs @@ -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); } /// - /// This function should never be used explicitly + /// Use this function if you don't want the message to be batched /// /// 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 } } diff --git a/Utilities/WeakAction.cs b/Utilities/WeakAction.cs new file mode 100644 index 0000000..0264419 --- /dev/null +++ b/Utilities/WeakAction.cs @@ -0,0 +1,99 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public struct WeakAction:IEquatable> +{ + 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(T1 data1, T2 data2) + { + if (ObjectRef.IsAllocated && ObjectRef.Target != null) + { + Method.Invoke(ObjectRef.Target, new object[] { data1, data2 }); + return true; + } + + return false; + } + + public bool Equals(WeakAction other) + { + return (Method.Equals(other.Method)); + } + + readonly GCHandle ObjectRef; + readonly MethodInfo Method; +} + +public struct WeakAction:IEquatable> +{ + 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(T data) + { + if (ObjectRef.IsAllocated && ObjectRef.Target != null) + { + Method.Invoke(ObjectRef.Target, new object[] { data }); + return true; + } + + return false; + } + + public bool Equals(WeakAction other) + { + return (Method.Equals(other.Method)); + } + + readonly GCHandle ObjectRef; + readonly MethodInfo Method; +} + +public struct WeakAction:IEquatable +{ + public WeakAction(Action listener) + { + ObjectRef = GCHandle.Alloc(listener.Target, GCHandleType.Weak); + Method = listener.Method; + + if (Method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0) + throw new ArgumentException("Cannot create weak event to anonymous method with closure."); + } + + public bool Invoke() + { + if (ObjectRef.IsAllocated && ObjectRef.Target != null) + { + Method.Invoke(ObjectRef.Target, null); + return true; + } + + return false; + } + + public bool Equals(WeakAction other) + { + return (Method.Equals(other.Method)); + } + + readonly GCHandle ObjectRef; + readonly MethodInfo Method; +} + + diff --git a/Utilities/WeakAction.cs.meta b/Utilities/WeakAction.cs.meta new file mode 100644 index 0000000..eb12418 --- /dev/null +++ b/Utilities/WeakAction.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: eaffccbdbcacfa449b997dc1f7181bb4 +timeCreated: 1461942385 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 0686664e8a6e706c0154fc3cf59e0af24ff305fc Mon Sep 17 00:00:00 2001 From: sebas77 Date: Sun, 15 Jan 2017 18:13:06 +0000 Subject: [PATCH 2/3] First Sequencer draft --- ECS/Dispatcher/DispatcherOnChange.cs | 4 +- ECS/Dispatcher/DispatcherOnSet.cs | 27 ++-- ECS/GenericEntityDescriptor.cs | 127 ++++++++++++++++++ .../GenericEntityDescriptor.cs.meta | 4 +- ECS/Sequencer.cs | 52 +++++++ ECS/Sequencer.cs.meta | 12 ++ ECS/SingleNodeEngine.cs | 6 +- Utilities/WeakAction.cs | 99 -------------- Utilities/WeakActionStruct.cs | 122 +++++++++++++++++ Utilities/WeakActionStruct.cs.meta | 12 ++ Utilities/WeakEvent.cs | 35 +++++ Utilities/WeakEvent.cs.meta | 12 ++ 12 files changed, 391 insertions(+), 121 deletions(-) create mode 100644 ECS/GenericEntityDescriptor.cs rename Utilities/WeakAction.cs.meta => ECS/GenericEntityDescriptor.cs.meta (75%) create mode 100644 ECS/Sequencer.cs create mode 100644 ECS/Sequencer.cs.meta delete mode 100644 Utilities/WeakAction.cs create mode 100644 Utilities/WeakActionStruct.cs create mode 100644 Utilities/WeakActionStruct.cs.meta create mode 100644 Utilities/WeakEvent.cs create mode 100644 Utilities/WeakEvent.cs.meta diff --git a/ECS/Dispatcher/DispatcherOnChange.cs b/ECS/Dispatcher/DispatcherOnChange.cs index 2a265d6..0601df8 100644 --- a/ECS/Dispatcher/DispatcherOnChange.cs +++ b/ECS/Dispatcher/DispatcherOnChange.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; namespace Svelto.ECS { - public class DispatcherOnChange : DispatcherOnSet + public class DispatchOnChange : DispatchOnSet { - public DispatcherOnChange(int senderID) : base(senderID) + public DispatchOnChange(int senderID) : base(senderID) { } public new T value diff --git a/ECS/Dispatcher/DispatcherOnSet.cs b/ECS/Dispatcher/DispatcherOnSet.cs index 0514064..2c9f069 100644 --- a/ECS/Dispatcher/DispatcherOnSet.cs +++ b/ECS/Dispatcher/DispatcherOnSet.cs @@ -1,11 +1,13 @@ +using BetterWeakEvents; + namespace Svelto.ECS { - public class DispatcherOnSet + public class DispatchOnSet { - public DispatcherOnSet(int senderID) + public DispatchOnSet(int senderID) { _senderID = senderID; - _subscribers = new System.Collections.Generic.HashSet>(); + _subscribers = new WeakEvent(); } public T value @@ -14,14 +16,7 @@ namespace Svelto.ECS { _value = value; - if (_subscribers != null) - { - using (var enumerator = _subscribers.GetEnumerator()) - { - while (enumerator.MoveNext() == true) - enumerator.Current.Invoke(_senderID, _value); - } - } + _subscribers.Invoke(_senderID, value); } get @@ -30,19 +25,19 @@ namespace Svelto.ECS } } - public void NotifyOnDataChange(System.Action action) + public void NotifyOnValueSet(System.Action action) { - _subscribers.Add(new WeakAction(action)); + _subscribers += action; } - public void StopNotifyOnDataChange(System.Action action) + public void StopNotify(System.Action action) { - _subscribers.Remove(new WeakAction(action)); + _subscribers -= action; } protected T _value; protected int _senderID; - protected System.Collections.Generic.HashSet> _subscribers; + protected WeakEvent _subscribers; } } diff --git a/ECS/GenericEntityDescriptor.cs b/ECS/GenericEntityDescriptor.cs new file mode 100644 index 0000000..16c2687 --- /dev/null +++ b/ECS/GenericEntityDescriptor.cs @@ -0,0 +1,127 @@ +namespace Svelto.ECS +{ + class GenericEntityDescriptor : EntityDescriptor + where T : NodeWithID, new() + { + static GenericEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new NodeBuilder() + }; + } + public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + { + } + static INodeBuilder[] _nodesToBuild; + } + + class GenericEntityDescriptor : EntityDescriptor + where T : NodeWithID, new() + where U : NodeWithID, new() + { + static GenericEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new NodeBuilder(), + new NodeBuilder() + }; + } + public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + { + } + static INodeBuilder[] _nodesToBuild; + } + + class GenericEntityDescriptor : EntityDescriptor + where T : NodeWithID, new() + where U : NodeWithID, new() + where V : NodeWithID, new() + { + static GenericEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder() + }; + } + public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + { + } + static INodeBuilder[] _nodesToBuild; + } + + class GenericEntityDescriptor : EntityDescriptor + where T : NodeWithID, new() + where U : NodeWithID, new() + where V : NodeWithID, new() + where W : NodeWithID, new() + { + static GenericEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder() + }; + } + public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + { + } + static INodeBuilder[] _nodesToBuild; + } + + class GenericEntityDescriptor : 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(), + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder() + }; + } + public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + { + } + static INodeBuilder[] _nodesToBuild; + } + class GenericEntityDescriptor : 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(), + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder() + }; + } + public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + { + } + static INodeBuilder[] _nodesToBuild; + } +} diff --git a/Utilities/WeakAction.cs.meta b/ECS/GenericEntityDescriptor.cs.meta similarity index 75% rename from Utilities/WeakAction.cs.meta rename to ECS/GenericEntityDescriptor.cs.meta index eb12418..840728d 100644 --- a/Utilities/WeakAction.cs.meta +++ b/ECS/GenericEntityDescriptor.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: eaffccbdbcacfa449b997dc1f7181bb4 -timeCreated: 1461942385 +guid: fd70d1b3107162f4790b1b71a129818f +timeCreated: 1484485852 licenseType: Pro MonoImporter: serializedVersion: 2 diff --git a/ECS/Sequencer.cs b/ECS/Sequencer.cs new file mode 100644 index 0000000..f9d0efd --- /dev/null +++ b/ECS/Sequencer.cs @@ -0,0 +1,52 @@ +using System; +using Steps = System.Collections.Generic.Dictionary>; + +namespace Svelto.ECS +{ + public interface IStep + { } + + public interface IStep:IStep + { + void Step(ref T token, Enum condition); + } + + public class Sequencer + { + public Sequencer() + {} + + public void SetSequence(Steps steps) + { + _steps = steps; + } + + public void Next(IEngine engine, ref T param) + { + Next(engine, ref param, Condition.always); + } + + public void Next(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)steps[i]).Step(ref param, condition); + } + + Steps _steps; + } + + public enum Condition + { + always + } + + public enum DamageCondition + { + damage, + dead + } +} \ No newline at end of file diff --git a/ECS/Sequencer.cs.meta b/ECS/Sequencer.cs.meta new file mode 100644 index 0000000..87596cb --- /dev/null +++ b/ECS/Sequencer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4e1f40937ca5027428ed7193729be1a5 +timeCreated: 1484487023 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/SingleNodeEngine.cs b/ECS/SingleNodeEngine.cs index 6b43243..f888ee2 100644 --- a/ECS/SingleNodeEngine.cs +++ b/ECS/SingleNodeEngine.cs @@ -12,7 +12,9 @@ Remove(obj as TNodeType); } - protected abstract void Add(TNodeType node); - protected abstract void Remove(TNodeType node); + protected virtual void Add(TNodeType node) + {} + protected virtual void Remove(TNodeType node) + {} } } diff --git a/Utilities/WeakAction.cs b/Utilities/WeakAction.cs deleted file mode 100644 index 0264419..0000000 --- a/Utilities/WeakAction.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -public struct WeakAction:IEquatable> -{ - 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(T1 data1, T2 data2) - { - if (ObjectRef.IsAllocated && ObjectRef.Target != null) - { - Method.Invoke(ObjectRef.Target, new object[] { data1, data2 }); - return true; - } - - return false; - } - - public bool Equals(WeakAction other) - { - return (Method.Equals(other.Method)); - } - - readonly GCHandle ObjectRef; - readonly MethodInfo Method; -} - -public struct WeakAction:IEquatable> -{ - 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(T data) - { - if (ObjectRef.IsAllocated && ObjectRef.Target != null) - { - Method.Invoke(ObjectRef.Target, new object[] { data }); - return true; - } - - return false; - } - - public bool Equals(WeakAction other) - { - return (Method.Equals(other.Method)); - } - - readonly GCHandle ObjectRef; - readonly MethodInfo Method; -} - -public struct WeakAction:IEquatable -{ - public WeakAction(Action listener) - { - ObjectRef = GCHandle.Alloc(listener.Target, GCHandleType.Weak); - Method = listener.Method; - - if (Method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0) - throw new ArgumentException("Cannot create weak event to anonymous method with closure."); - } - - public bool Invoke() - { - if (ObjectRef.IsAllocated && ObjectRef.Target != null) - { - Method.Invoke(ObjectRef.Target, null); - return true; - } - - return false; - } - - public bool Equals(WeakAction other) - { - return (Method.Equals(other.Method)); - } - - readonly GCHandle ObjectRef; - readonly MethodInfo Method; -} - - diff --git a/Utilities/WeakActionStruct.cs b/Utilities/WeakActionStruct.cs new file mode 100644 index 0000000..17158e1 --- /dev/null +++ b/Utilities/WeakActionStruct.cs @@ -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 : IEquatable> + { + 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(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 other) + { + return (Method.Equals(other.Method)); + } + + public void Release() + { + ObjectRef.Free(); + } + + readonly GCHandle ObjectRef; + readonly MethodInfo Method; + } + + public struct WeakAction : IEquatable> + { + 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(T data) + { + if (ObjectRef.IsAllocated && ObjectRef.Target != null) + { + Method.Invoke(ObjectRef.Target, new object[] { data }); + 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; + } + + public struct WeakAction : IEquatable + { + 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; + } + + +} \ No newline at end of file diff --git a/Utilities/WeakActionStruct.cs.meta b/Utilities/WeakActionStruct.cs.meta new file mode 100644 index 0000000..49656b3 --- /dev/null +++ b/Utilities/WeakActionStruct.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1662357721652524593d7b0f731aef45 +timeCreated: 1484483913 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Utilities/WeakEvent.cs b/Utilities/WeakEvent.cs new file mode 100644 index 0000000..e912490 --- /dev/null +++ b/Utilities/WeakEvent.cs @@ -0,0 +1,35 @@ +using Svelto.DataStructures; +using System; + +namespace BetterWeakEvents +{ + public class WeakEvent + { + public static WeakEvent operator+(WeakEvent c1, Action x) + { + c1._subscribers.Add(new WeakAction(x)); + return c1; + } + + public static WeakEvent operator-(WeakEvent c1, Action x) + { + c1._subscribers.UnorderredRemove(new WeakAction(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> _subscribers = new FasterList>(); + } +} \ No newline at end of file diff --git a/Utilities/WeakEvent.cs.meta b/Utilities/WeakEvent.cs.meta new file mode 100644 index 0000000..884293e --- /dev/null +++ b/Utilities/WeakEvent.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e2ee3763e7dffd4459c588eda6867ddd +timeCreated: 1484480295 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 384abc94f8c52fc50bb8a7ac3db211bd039b9735 Mon Sep 17 00:00:00 2001 From: sebas77 Date: Sat, 21 Jan 2017 17:22:36 +0000 Subject: [PATCH 3/3] Fix a bug in DispatchOnSet --- ECS/Dispatcher/DispatcherOnChange.cs | 2 +- ECS/note.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ECS/Dispatcher/DispatcherOnChange.cs b/ECS/Dispatcher/DispatcherOnChange.cs index 0601df8..3ab3199 100644 --- a/ECS/Dispatcher/DispatcherOnChange.cs +++ b/ECS/Dispatcher/DispatcherOnChange.cs @@ -12,7 +12,7 @@ namespace Svelto.ECS set { if (EqualityComparer.Default.Equals(value, _value) == false) - base.value = _value; + base.value = value; } get diff --git a/ECS/note.txt b/ECS/note.txt index a55d53b..c041193 100644 --- a/ECS/note.txt +++ b/ECS/note.txt @@ -1,7 +1,7 @@ systems do not hold component data, but only system states systems cannot be injected systems are SRP and OCP -systems communicates between component, mediators, producer/consumer, Svelto.ECS.Example.Observers. producer/consumer and observers must be defined in the layer of the engine. +systems communicates between component, mediators, producer/consumer, observers. producer/consumer and observers must be defined in the layer of the engine. systems can have injected dependencies components don't have logic