diff --git a/Context/ContextNotifier.cs b/Context/ContextNotifier.cs index 74df7ef..dfe4aa9 100644 --- a/Context/ContextNotifier.cs +++ b/Context/ContextNotifier.cs @@ -1,28 +1,31 @@ +using Svelto.DataStructures; using System; using System.Collections.Generic; namespace Svelto.Context { - class ContextNotifier: IContextNotifer + class ContextNotifier : IContextNotifer { public ContextNotifier() { _toInitialize = new List>(); - _toDeinitialize = new List> (); + _toDeinitialize = new List>(); } - /// - /// A Context is meant to be initialized only once in its timelife - /// - public void NotifyFrameworkInitialized() + public void AddFrameworkDestructionListener(IWaitForFrameworkDestruction obj) { - foreach (WeakReference obj in _toInitialize) - { - if (obj.IsAlive == true) - obj.Target.OnFrameworkInitialized(); - } + if (_toDeinitialize != null) + _toDeinitialize.Add(new WeakReference(obj)); + else + throw new Exception("An object is expected to be initialized after the framework has been deinitialized. Type: " + obj.GetType()); + } - _toInitialize = null; + public void AddFrameworkInitializationListener(IWaitForFrameworkInitialization obj) + { + if (_toInitialize != null) + _toInitialize.Add(new WeakReference(obj)); + else + throw new Exception("An object is expected to be initialized after the framework has been initialized. Type: " + obj.GetType()); } /// @@ -30,32 +33,46 @@ namespace Svelto.Context /// public void NotifyFrameworkDeinitialized() { - foreach (WeakReference obj in _toDeinitialize) + for (int i = _toDeinitialize.Count - 1; i >= 0; --i) { - if (obj.IsAlive == true) - obj.Target.OnFrameworkDestroyed(); + try + { + var obj = _toDeinitialize[i]; + if (obj.IsAlive == true) + (obj.Target as IWaitForFrameworkDestruction).OnFrameworkDestroyed(); + } + catch (Exception e) + { + Utility.Console.LogException(e); + } } _toDeinitialize = null; } - public void AddFrameworkInitializationListener(IWaitForFrameworkInitialization obj) + /// + /// A Context is meant to be initialized only once in its timelife + /// + public void NotifyFrameworkInitialized() { - if (_toInitialize != null) - _toInitialize.Add(new WeakReference(obj)); - else - throw new Exception("An object is expected to be initialized after the framework has been initialized. Type: " + obj.GetType()); - } + for (int i = _toInitialize.Count - 1; i >= 0; --i) + { + try + { + var obj = _toInitialize[i]; + if (obj.IsAlive == true) + (obj.Target as IWaitForFrameworkInitialization).OnFrameworkInitialized(); + } + catch (Exception e) + { + Utility.Console.LogException(e); + } + } - public void AddFrameworkDestructionListener(IWaitForFrameworkDestruction obj) - { - if (_toDeinitialize != null) - _toDeinitialize.Add(new WeakReference(obj)); - else - throw new Exception("An object is expected to be initialized after the framework has been deinitialized. Type: " + obj.GetType()); + _toInitialize = null; } - List> _toInitialize; List> _toDeinitialize; + List> _toInitialize; } } diff --git a/Context/Factories.meta b/Context/Factories.meta index cdfbf81..f14959d 100644 --- a/Context/Factories.meta +++ b/Context/Factories.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 88190f2317429cb49bfdf63e448efa52 +guid: ec4bd87e898bf7c42823fb5ec2456b43 folderAsset: yes timeCreated: 1431630831 licenseType: Free diff --git a/Context/Factories/GameObjectFactory.cs b/Context/Factories/GameObjectFactory.cs index c51069f..3f9c806 100644 --- a/Context/Factories/GameObjectFactory.cs +++ b/Context/Factories/GameObjectFactory.cs @@ -7,37 +7,19 @@ using UnityEngine; namespace Svelto.Context { - public class GameObjectFactory: Factories.IGameObjectFactory - { - public GameObjectFactory(IUnityContextHierarchyChangedListener root) - { - _unityContext = root; - - _prefabs = new Dictionary(); - } - - /// - /// Register a prefab to be built later using a string ID. - /// - /// original prefab - /// prefab name - /// optional gameobject to specify as parent later - - 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); - } - - public GameObject Build(string prefabName) - { - DesignByContract.Check.Require(_prefabs.ContainsKey(prefabName), "Svelto.Factories.IGameObjectFactory - Invalid Prefab Type"); - - var go = Build(_prefabs[prefabName][0]); - + public class GameObjectFactory : Factories.IGameObjectFactory + { + public GameObjectFactory() + { + _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) @@ -54,31 +36,34 @@ namespace Svelto.Context transform.localPosition = position; transform.localRotation = rotation; - transform.localScale = scale; + transform.localScale = scale; } - - return go; - } - - public GameObject Build(GameObject go) - { - var copy = Object.Instantiate(go) as GameObject; - var components = copy.GetComponentsInChildren(true); - for (var i = 0; i < components.Length; ++i) - if (components[i] != null) - _unityContext.OnMonobehaviourAdded(components[i]); + return go; + } - _unityContext.OnGameObjectAdded(copy); + /// + /// Register a prefab to be built later using a string ID. + /// + /// original prefab + public GameObject Build(GameObject prefab) + { + Profiler.BeginSample("GameObject Factory Build"); - copy.AddComponent().unityContext = _unityContext; - copy.AddComponent().unityContext = _unityContext; + var copy = Object.Instantiate(prefab) as GameObject; return copy; - } + } + + public void RegisterPrefab(GameObject prefab, string prefabName, GameObject parent = null) + { + var objects = new GameObject[2]; - IUnityContextHierarchyChangedListener _unityContext; - Dictionary _prefabs; + objects[0] = prefab; objects[1] = parent; + + _prefabs.Add(prefabName, objects); + } + + Dictionary _prefabs; } } - diff --git a/Context/Factories/MonoBehaviourFactory.cs b/Context/Factories/MonoBehaviourFactory.cs index 19e1dc0..62502f6 100644 --- a/Context/Factories/MonoBehaviourFactory.cs +++ b/Context/Factories/MonoBehaviourFactory.cs @@ -1,34 +1,24 @@ #region using System; +using Svelto.DataStructures; using UnityEngine; #endregion namespace Svelto.Context { - public class MonoBehaviourFactory: Factories.IMonoBehaviourFactory - { - IUnityContextHierarchyChangedListener _unityContext; + public class MonoBehaviourFactory : Factories.IMonoBehaviourFactory + { + public MonoBehaviourFactory() + { + } - public MonoBehaviourFactory(IUnityContextHierarchyChangedListener unityContext) - { - _unityContext = unityContext; - } - - public M Build(Func constructor) where M:MonoBehaviour - { - var mb = constructor(); - - _unityContext.OnMonobehaviourAdded(mb); - - GameObject go = mb.gameObject; - - if (go.GetComponent() == null) - go.AddComponent().unityContext = _unityContext; + public M Build(Func constructor) where M : MonoBehaviour + { + var mb = constructor(); return mb; - } - } + } + } } - diff --git a/Context/ICompositionRoot.cs b/Context/ICompositionRoot.cs index 92521f3..bb2af15 100644 --- a/Context/ICompositionRoot.cs +++ b/Context/ICompositionRoot.cs @@ -1,12 +1,11 @@ -using UnityEngine; - namespace Svelto.Context { - public interface ICompositionRoot + public interface ICompositionRoot { - void OnContextInitialized(); - void OnContextDestroyed(); - } + void OnContextCreated(UnityContext contextHolder); + void OnContextInitialized(); + void OnContextDestroyed(); + } } diff --git a/Context/IContextNotifer.cs b/Context/IContextNotifer.cs index 6ac2c14..13f744f 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 deleted file mode 100644 index 3f07295..0000000 --- a/Context/IUnityContextHierarchyChangedListener.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UnityEngine; - -namespace Svelto.Context -{ - public interface IUnityContextHierarchyChangedListener - { - void OnMonobehaviourAdded(MonoBehaviour component); - void OnMonobehaviourRemoved(MonoBehaviour component); - - void OnGameObjectAdded(GameObject entity); - void OnGameObjectRemoved(GameObject entity); - } -} diff --git a/Context/Legacy.meta b/Context/Legacy.meta new file mode 100644 index 0000000..525dc3f --- /dev/null +++ b/Context/Legacy.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 5f61f9718491ceb498626e61ab9f48d4 +folderAsset: yes +timeCreated: 1434754873 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Context/Unity.meta b/Context/Unity.meta index 2f9be3d..75e174f 100644 --- a/Context/Unity.meta +++ b/Context/Unity.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 403de662aab64bb4e917559caa6b7946 +guid: 14144568b327bbe40876c12a777e5f05 folderAsset: yes timeCreated: 1434752394 licenseType: Free diff --git a/Context/Unity/NotifyComponentsRemoved.cs b/Context/Unity/NotifyComponentsRemoved.cs deleted file mode 100644 index bf433b0..0000000 --- a/Context/Unity/NotifyComponentsRemoved.cs +++ /dev/null @@ -1,28 +0,0 @@ -using UnityEngine; - -namespace Svelto.Context -{ - public class NotifyComponentsRemoved : MonoBehaviour - { - public IUnityContextHierarchyChangedListener unityContext { private get; set; } - - void OnDestroy() - { - MonoBehaviour[] components = gameObject.GetComponentsInChildren(true); - - for (int i = 0; i < components.Length; ++i) - if (components[i] != null) - unityContext.OnMonobehaviourRemoved(components[i]); - } - } - - public class NotifyEntityRemoved : MonoBehaviour - { - public IUnityContextHierarchyChangedListener unityContext { private get; set; } - - void OnDestroy() - { - unityContext.OnGameObjectRemoved(gameObject); - } - } -} diff --git a/Context/UnityContext.cs b/Context/UnityContext.cs index 40193b0..70782f4 100644 --- a/Context/UnityContext.cs +++ b/Context/UnityContext.cs @@ -1,64 +1,44 @@ -using UnityEngine; -using Svelto.Context; using System.Collections; +using Svelto.Context; +using UnityEngine; -public class UnityContext:MonoBehaviour -{} - -public class UnityContext: UnityContext where T:class, ICompositionRoot, IUnityContextHierarchyChangedListener, new() +public abstract class UnityContext:MonoBehaviour { - virtual protected void Awake() - { - Init(); - } - - void Init() - { - _applicationRoot = new T(); - - MonoBehaviour[] behaviours = transform.GetComponentsInChildren(true); + protected abstract void OnAwake(); - for (int i = 0; i < behaviours.Length; i++) - { - var component = behaviours[i]; + void Awake() + { + OnAwake(); + } +} - if (component != null) - _applicationRoot.OnMonobehaviourAdded(component); - } +public class UnityContext: UnityContext where T:class, ICompositionRoot, new() +{ + protected override void OnAwake() + { + _applicationRoot = new T(); - Transform[] children = transform.GetComponentsInChildren(true); + _applicationRoot.OnContextCreated(this); + } - for (int i = 0; i < children.Length; i++) - { - var child = children[i]; + void OnDestroy() + { + _applicationRoot.OnContextDestroyed(); + } - if (child != null) - _applicationRoot.OnGameObjectAdded(child.gameObject); - } + void Start() + { + if (Application.isPlaying == true) + StartCoroutine(WaitForFrameworkInitialization()); } - - void OnDestroy() - { - FrameworkDestroyed(); - } - void Start() - { - if (Application.isPlaying == true) - StartCoroutine(WaitForFrameworkInitialization()); - } + IEnumerator WaitForFrameworkInitialization() + { + //let's wait until the end of the frame, so we are sure that all the awake and starts are called + yield return new WaitForEndOfFrame(); + + _applicationRoot.OnContextInitialized(); + } - IEnumerator WaitForFrameworkInitialization() - { - yield return new WaitForEndOfFrame(); //let's wait until next frame, so we are sure that all the awake and starts are called - - _applicationRoot.OnContextInitialized(); - } - - void FrameworkDestroyed() - { - _applicationRoot.OnContextDestroyed(); - } - - private T _applicationRoot = null; + T _applicationRoot; } diff --git a/DataStructures.meta b/DataStructures.meta new file mode 100644 index 0000000..0f993f8 --- /dev/null +++ b/DataStructures.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6663fc83070cb0842b4665b38356feb2 +folderAsset: yes +timeCreated: 1463348260 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/DataStructures/FasterList.cs b/DataStructures/FasterList.cs new file mode 100644 index 0000000..da36e94 --- /dev/null +++ b/DataStructures/FasterList.cs @@ -0,0 +1,431 @@ +/** + * Custom modified version of Michael Lyashenko's BetterList + **/ + +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Svelto.DataStructures +{ + public struct FasterListEnumerator : IEnumerator + { + public T Current + { + get { return _current; } + } + + object IEnumerator.Current + { + get { return _current; } + } + + T IEnumerator.Current + { + get { return _current; } + } + + public FasterListEnumerator(T[] buffer, int size) + { + _size = size; + _counter = 0; + _buffer = buffer; + _current = default(T); + } + + public void Dispose() + { + _buffer = null; + } + + bool IEnumerator.MoveNext() + { + return MoveNext(); + } + + void IEnumerator.Reset() + { + Reset(); + } + + public bool MoveNext() + { + if (_counter < _size) + { + _current = _buffer[_counter++]; + + return true; + } + + _current = default(T); + + return false; + } + + public void Reset() + { + _counter = 0; + } + + T[] _buffer; + int _counter; + int _size; + T _current; + } + + public struct FasterReadOnlyList : IList + { + public FasterReadOnlyList(FasterList list) + { + _list = list; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public FasterListEnumerator GetEnumerator() + { + return _list.GetEnumerator(); + } + + public void Add(T item) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(T item) + { + return _list.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + _list.CopyTo(array, arrayIndex); + } + + public bool Remove(T item) + { + 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); + } + + public void Insert(int index, T item) + { + throw new NotImplementedException(); + } + + public void RemoveAt(int index) + { + throw new NotImplementedException(); + } + + public T this[int index] { get { return _list[index]; } set { throw new NotImplementedException(); } } + + readonly FasterList _list; + } + + public class FasterList : IList + { + public int Count + { + get { return _count; } + } + + public bool IsReadOnly + { + get { return false; } + } + + public FasterList() + { + _count = 0; + + _buffer = new T[MIN_SIZE]; + } + + public FasterList(int initialSize) + { + _count = 0; + + _buffer = new T[initialSize]; + } + + public FasterList(ICollection collection) + { + _buffer = new T[collection.Count]; + + collection.CopyTo(_buffer, 0); + + _count = _buffer.Length; + } + + public FasterList(FasterList listCopy) + { + _buffer = new T[listCopy.Count]; + + listCopy.CopyTo(_buffer, 0); + + _count = listCopy.Count; + } + + public void Add(T item) + { + if (_count == _buffer.Length) + AllocateMore(); + + _buffer[_count++] = item; + } + + public void AddRange(IEnumerable 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; + } + } + + 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; + } + } + + 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; + } + + public void AddRange(T[] items) + { + var count = items.Length; + if (_count + count >= _buffer.Length) + AllocateMore(_count + count); + + Array.Copy(items, 0, _buffer, _count, count); + _count += count; + } + + public FasterReadOnlyList AsReadOnly() + { + return new FasterReadOnlyList(this); + } + + public void Clear() + { + _count = 0; + } + + public bool Contains(T item) + { + var index = IndexOf(item); + + return index != -1; + } + + public void CopyTo(T[] array, int arrayIndex) + { + Array.Copy(_buffer, 0, array, arrayIndex, Count); + } + + public FasterListEnumerator GetEnumerator() + { + return new FasterListEnumerator(_buffer, Count); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public int IndexOf(T item) + { + var comp = EqualityComparer.Default; + + for (var index = _count - 1; index >= 0; --index) + if (comp.Equals(_buffer[index], item)) + return index; + + return -1; + } + + public void Insert(int index, T item) + { + DesignByContract.Check.Require(index < _count, "out of bound index"); + + if (_count == _buffer.Length) AllocateMore(); + + Array.Copy(_buffer, index, _buffer, index + 1, _count - index); + + _buffer[index] = item; + ++_count; + } + + public void Release() + { + _count = 0; + _buffer = null; + } + + public bool Remove(T item) + { + var index = IndexOf(item); + + if (index == -1) + return false; + + RemoveAt(index); + + return true; + } + + public void RemoveAt(int index) + { + DesignByContract.Check.Require(index < _count, "out of bound index"); + + --_count; + + if (index == _count) + return; + + Array.Copy(_buffer, index + 1, _buffer, index, _count - index); + } + + public void Resize(int newSize) + { + Array.Resize(ref _buffer, newSize); + + _count = newSize; + } + + public void SetAt(int index, T value) + { + if (index >= _buffer.Length) + AllocateMore(index + 1); + + if (_count <= index) + _count = index + 1; + + this[index] = value; + } + + public void Sort(Comparison comparer) + { + Trim(); + Array.Sort(_buffer, comparer); + } + + public T[] ToArray() + { + T[] destinationArray = new T[_count]; + + Array.Copy(_buffer, 0, destinationArray, 0, _count); + + return destinationArray; + } + + /// + /// This function exists to allow fast iterations. The size of the array returned cannot be + /// used. The list count must be used instead. + /// + /// + public T[] ToArrayFast() + { + return _buffer; + } + + public bool UnorderredRemove(T item) + { + var index = IndexOf(item); + + if (index == -1) + return false; + + UnorderredRemoveAt(index); + + return true; + } + + public void UnorderredRemoveAt(int index) + { + DesignByContract.Check.Require(index < _count, "out of bound index"); + + _buffer[index] = _buffer[--_count]; + } + + private 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) + { + var oldLength = _buffer.Length; + + while (oldLength < newSize) + oldLength <<= 1; + + var newList = new T[oldLength]; + if (_count > 0) _buffer.CopyTo(newList, 0); + _buffer = newList; + } + + private void Trim() + { + if (_count < _buffer.Length) + Resize(_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; } + } + + private const int MIN_SIZE = 32; + private T[] _buffer; + private int _count; + } +} diff --git a/DataStructures/FasterList.cs.meta b/DataStructures/FasterList.cs.meta new file mode 100644 index 0000000..6c1704e --- /dev/null +++ b/DataStructures/FasterList.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 0859454e6e6d968498cea54757578eda +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/DataStructures/ReadOnlyDictionary.cs b/DataStructures/ReadOnlyDictionary.cs new file mode 100644 index 0000000..58b5840 --- /dev/null +++ b/DataStructures/ReadOnlyDictionary.cs @@ -0,0 +1,655 @@ +//note: ripped from openstacknetsdk + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Svelto.DataStructures +{ + public struct ReadOnlyDictionary : IDictionary, 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; } } + + /// + /// Gets the element that has the specified key. + /// + /// The key of the element to get. + /// The element that has the specified key. + /// If is . + /// If property is retrieved and is not found. + public TValue this[TKey key] + { + get + { + return _dictionary[key]; + } + } + + /// + /// + /// 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. + /// + /// + /// The number of items in the dictionary. + /// + public int Count + { + get + { + return _dictionary.Count; + } + } + + /// + /// Gets a key collection that contains the keys of the dictionary. + /// + /// + /// A key collection that contains the keys of the dictionary. + /// + public KeyCollection Keys + { + get + { + return new KeyCollection(_dictionary.Keys); + } + } + + /// + ICollection IDictionary.Keys + { + get + { + return Keys; + } + } + + /// + ICollection IDictionary.Keys + { + get + { + return Keys; + } + } + + /// + /// Gets a collection that contains the values in the dictionary. + /// + /// + /// A collection that contains the values in the object that implements . + /// + public ValueCollection Values + { + get + { + return new ValueCollection(_dictionary.Values); + } + } + + /// + ICollection IDictionary.Values + { + get + { + return Values; + } + } + + /// + ICollection IDictionary.Values + { + get + { + return Values; + } + } + + /// + bool ICollection>.IsReadOnly + { + get + { + return true; + } + } + + /// + bool IDictionary.IsFixedSize + { + get + { + return true; + } + } + + /// + bool IDictionary.IsReadOnly + { + get + { + return true; + } + } + + /// + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + /// + object ICollection.SyncRoot + { + get + { + ICollection collection = this as ICollection; + if (collection == null) + return collection.SyncRoot; + + throw new NotSupportedException("The current object does not support the SyncRoot property."); + } + } + + /// + /// 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) + { + throw new NotSupportedException(); + } + + /// + void ICollection>.Clear() + { + throw new NotSupportedException(); + } + + /// + bool ICollection>.Contains(KeyValuePair item) + { + throw new NotImplementedException(); + } + + /// + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + /// + bool ICollection>.Remove(KeyValuePair item) + { + throw new NotSupportedException(); + } + + /// + void IDictionary.Clear() + { + throw new NotSupportedException(); + } + + /// + void ICollection.CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + +/// + /// 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. + /// + /// The collection of keys to wrap. + /// If is . + internal KeyCollection(ICollection keys) + { + if (keys == null) + throw new ArgumentNullException("keys"); + + _keys = keys; + } + + /// + /// Gets the number of elements in the collection. + /// + /// + /// The number of elements in the collection. + /// + public int Count + { + get + { + return _keys.Count; + } + } + + /// + bool ICollection.IsReadOnly + { + get + { + return true; + } + } + + /// + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + /// + object ICollection.SyncRoot + { + get + { + throw new NotImplementedException(); + } + } + + /// + /// 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. + /// The zero-based index in at which copying begins. + /// If is . + /// If is less than 0. + /// + /// If is multidimensional. + /// -or- + /// If the number of elements in the source collection is greater than the available space from to the end of the destination . + /// -or- + /// If the type cannot be cast automatically to the type of the destination . + /// + public void CopyTo(TKey[] array, int arrayIndex) + { + _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. + public IEnumerator GetEnumerator() + { + 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(); + } + } + + /// + /// Represents a read-only collection of the values of a object. + /// + 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. + /// + /// The collection of values to wrap. + /// If is . + internal ValueCollection(ICollection values) + { + if (values == null) + throw new ArgumentNullException("values"); + + _values = values; + } + + /// + /// Gets the number of elements in the collection. + /// + /// + /// The number of elements in the collection. + /// + public int Count + { + get + { + return _values.Count; + } + } + + /// + bool ICollection.IsReadOnly + { + get + { + return true; + } + } + + /// + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + /// + object ICollection.SyncRoot + { + get + { + throw new NotImplementedException(); + } + } + + /// + /// 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. + /// The zero-based index in at which copying begins. + /// If is . + /// If is less than 0. + /// + /// If is multidimensional. + /// -or- + /// If the number of elements in the source collection is greater than the available space from to the end of the destination . + /// -or- + /// If the type cannot be cast automatically to the type of the destination . + /// + public void CopyTo(TValue[] array, int arrayIndex) + { + _values.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. + public IEnumerator GetEnumerator() + { + return _values.GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + bool ICollection.Contains(TValue item) + { + return _values.Contains(item); + } + + /// + void ICollection.Add(TValue item) + { + throw new NotSupportedException(); + } + + /// + bool ICollection.Remove(TValue item) + { + throw new NotSupportedException(); + } + + /// + void ICollection.Clear() + { + throw new NotSupportedException(); + } + } + + struct DictionaryEnumerator : IDictionaryEnumerator + { + private readonly IEnumerator> _enumerator; + + public DictionaryEnumerator(IDictionary dictionary) + { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + _enumerator = dictionary.GetEnumerator(); + } + + /// + public DictionaryEntry Entry + { + get + { + KeyValuePair current = _enumerator.Current; + return new DictionaryEntry(current.Key, current.Value); + } + } + + /// + public object Key + { + get + { + return _enumerator.Current.Key; + } + } + + /// + public object Value + { + get + { + return _enumerator.Current.Value; + } + } + + /// + public object Current + { + get + { + return Entry; + } + } + + /// + public bool MoveNext() + { + return _enumerator.MoveNext(); + } + + /// + public void Reset() + { + _enumerator.Reset(); + } + } + } +} diff --git a/DataStructures/ReadOnlyDictionary.cs.meta b/DataStructures/ReadOnlyDictionary.cs.meta new file mode 100644 index 0000000..9d767f0 --- /dev/null +++ b/DataStructures/ReadOnlyDictionary.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e13aaf89d27048246bcf0d30c0993cf3 +timeCreated: 1459332966 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/DataStructures/ThreadSafeQueue.cs b/DataStructures/ThreadSafeQueue.cs new file mode 100644 index 0000000..b9b8f7d --- /dev/null +++ b/DataStructures/ThreadSafeQueue.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace Svelto.DataStructures +{ + public class ThreadSafeQueue + { + private readonly Queue m_Queue; + + private readonly ReaderWriterLockSlim LockQ = new ReaderWriterLockSlim(); + + public ThreadSafeQueue() + { + m_Queue = new Queue(); + } + + public ThreadSafeQueue(int capacity) + { + m_Queue = new Queue(capacity); + } + + public ThreadSafeQueue(IEnumerable collection) + { + m_Queue = new Queue(collection); + } + + public IEnumerator GetEnumerator() + { + Queue localQ; + + LockQ.EnterReadLock(); + try + { + localQ = new Queue(m_Queue); + } + + finally + { + LockQ.ExitReadLock(); + } + + foreach (T item in localQ) + yield return item; + } + + public void Enqueue(T item) + { + LockQ.EnterWriteLock(); + try + { + m_Queue.Enqueue(item); + } + + finally + { + LockQ.ExitWriteLock(); + } + } + + public T Dequeue() + { + LockQ.EnterWriteLock(); + try + { + return m_Queue.Dequeue(); + } + + finally + { + LockQ.ExitWriteLock(); + } + } + + public void EnqueueAll(IEnumerable ItemsToQueue) + { + LockQ.EnterWriteLock(); + try + { + foreach (T item in ItemsToQueue) + m_Queue.Enqueue(item); + } + + finally + { + LockQ.ExitWriteLock(); + } + } + + public List DequeueAll() + { + LockQ.EnterWriteLock(); + try + { + List returnList = new List(); + + while (m_Queue.Count > 0) + returnList.Add(m_Queue.Dequeue()); + + return returnList; + } + + finally + { + LockQ.ExitWriteLock(); + } + } + + public T Peek() + { + LockQ.EnterWriteLock(); + try + { + T item = default(T); + + if (m_Queue.Count > 0) + item = m_Queue.Peek(); + + return item; + } + + finally + { + LockQ.ExitWriteLock(); + } + } + + public bool TryDequeue(out T item) + { + LockQ.EnterWriteLock(); + try + { + if (m_Queue.Count > 0) + { + item = m_Queue.Dequeue(); + + return true; + } + else + { + item = default(T); + + return false; + } + } + + finally + { + LockQ.ExitWriteLock(); + } + } + + public int Count + { + get + { + LockQ.EnterWriteLock(); + try + { + return m_Queue.Count; + } + + finally + { + LockQ.ExitWriteLock(); + } + } + } + } +} diff --git a/Context/IUnityContextHierarchyChangedListener.cs.meta b/DataStructures/ThreadSafeQueue.cs.meta similarity index 78% rename from Context/IUnityContextHierarchyChangedListener.cs.meta rename to DataStructures/ThreadSafeQueue.cs.meta index f26268b..4b4d365 100644 --- a/Context/IUnityContextHierarchyChangedListener.cs.meta +++ b/DataStructures/ThreadSafeQueue.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 905bfd795bede6d448694b23dd88e0be +guid: 57d029cda5232f8468d50d4eb3bf5489 MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/DataStructures/WeakReference.cs b/DataStructures/WeakReference.cs new file mode 100644 index 0000000..74a199e --- /dev/null +++ b/DataStructures/WeakReference.cs @@ -0,0 +1,56 @@ +using System; +using System.Runtime.Serialization; +/// +/// Represents a weak reference, which references an object while still allowing +/// that object to be reclaimed by garbage collection. +/// +/// The type of the object that is referenced. + +namespace Svelto.DataStructures +{ + [Serializable] + public class WeakReference + : WeakReference where T : class + { + /// + /// Gets or sets the object (the target) referenced by the + /// current WeakReference{T} object. + /// + public new T Target + { + get + { + return (T)base.Target; + } + set + { + base.Target = value; + } + } + /// + /// Initializes a new instance of the WeakReference{T} class, referencing + /// the specified object. + /// + /// The object to reference. + public WeakReference(T target) + : base(target) + { } + + /// + /// Initializes a new instance of the WeakReference{T} class, referencing + /// the specified object and using the specified resurrection tracking. + /// + /// An object to track. + /// Indicates when to stop tracking the object. + /// If true, the object is tracked + /// after finalization; if false, the object is only tracked + /// until finalization. + public WeakReference(T target, bool trackResurrection) + : base(target, trackResurrection) + { } + + protected WeakReference(SerializationInfo info, StreamingContext context) + : base(info, context) + { } + } +} diff --git a/DataStructures/WeakReference.cs.meta b/DataStructures/WeakReference.cs.meta new file mode 100644 index 0000000..c9d3693 --- /dev/null +++ b/DataStructures/WeakReference.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 205cba76ee94d0e47aba9510ca92c540 +timeCreated: 1452175408 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Dispatcher.meta b/ECS/Dispatcher.meta new file mode 100644 index 0000000..41b7ff9 --- /dev/null +++ b/ECS/Dispatcher.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 2eae335fd33e33c4f951bf28bbf4914f +folderAsset: yes +timeCreated: 1462445195 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Dispatcher/Dispatcher.cs b/ECS/Dispatcher/Dispatcher.cs new file mode 100644 index 0000000..a0ef5f2 --- /dev/null +++ b/ECS/Dispatcher/Dispatcher.cs @@ -0,0 +1,19 @@ +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; +} diff --git a/Utilities/Dispatcher.cs.meta b/ECS/Dispatcher/Dispatcher.cs.meta similarity index 100% rename from Utilities/Dispatcher.cs.meta rename to ECS/Dispatcher/Dispatcher.cs.meta diff --git a/Utilities/DispatcherOnChange.cs b/ECS/Dispatcher/DispatcherOnChange.cs similarity index 60% rename from Utilities/DispatcherOnChange.cs rename to ECS/Dispatcher/DispatcherOnChange.cs index 19599f8..fea28a0 100644 --- a/Utilities/DispatcherOnChange.cs +++ b/ECS/Dispatcher/DispatcherOnChange.cs @@ -12,7 +12,7 @@ public class DispatcherOnChange: Dispatcher { _value = value; - Dispatch(value); + Dispatch(ref value); } } } @@ -20,3 +20,15 @@ public class DispatcherOnChange: Dispatcher 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/Context/Unity/NotifyComponentsRemoved.cs.meta b/ECS/Dispatcher/DispatcherOnChange.cs.meta similarity index 78% rename from Context/Unity/NotifyComponentsRemoved.cs.meta rename to ECS/Dispatcher/DispatcherOnChange.cs.meta index 4418602..ec4d3e7 100644 --- a/Context/Unity/NotifyComponentsRemoved.cs.meta +++ b/ECS/Dispatcher/DispatcherOnChange.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 13278f0cd187d8d43acc6a02479e459b +guid: 908688d3b784d644e88a06e20f9ea159 MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/ECS/Engine.meta b/ECS/Engine.meta deleted file mode 100644 index a28bd0e..0000000 --- a/ECS/Engine.meta +++ /dev/null @@ -1,5 +0,0 @@ -fileFormatVersion: 2 -guid: b1f3314cac8b8484f965651234415ca8 -folderAsset: yes -DefaultImporter: - userData: diff --git a/ECS/Engine/IEngine.cs b/ECS/Engine/IEngine.cs deleted file mode 100644 index 6698ac1..0000000 --- a/ECS/Engine/IEngine.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Svelto.ES -{ - public interface IEngine - {} - - public interface INodeEngine : IEngine - { - System.Type[] AcceptedNodes(); - - void Add(INode obj); - void Remove(INode obj); - } - -} diff --git a/ECS/EngineNodeDB.cs b/ECS/EngineNodeDB.cs new file mode 100644 index 0000000..33842c4 --- /dev/null +++ b/ECS/EngineNodeDB.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using Svelto.DataStructures; + +namespace Svelto.ES +{ + class EngineNodeDB : IEngineNodeDB + { + internal EngineNodeDB(Dictionary> nodesDB, Dictionary> nodesDBdic) + { + this._nodesDB = nodesDB; + this._nodesDBdic = nodesDBdic; + } + + public FasterReadOnlyList QueryNodes() where T:INode + { + var type = typeof(T); + + if (_nodesDB.ContainsKey(type) == false) + return _defaultEmptyNodeList; + + return new FasterReadOnlyList(_nodesDB[type]); + } + + public ReadOnlyDictionary QueryIndexableNodes() where T:INode + { + var type = typeof(T); + + if (_nodesDBdic.ContainsKey(type) == false) + return _defaultEmptyNodeDict; + + return new ReadOnlyDictionary(_nodesDBdic[type]); + } + + public bool QueryNode(int ID, out T node) where T:INode + { + var type = typeof(T); + + INode internalNode; + + if (_nodesDBdic.ContainsKey(type) && _nodesDBdic[type].TryGetValue(ID, out internalNode)) + { + node = (T)internalNode; + + return true; + } + + node = default(T); + + return false; + } + + Dictionary> _nodesDB; + Dictionary> _nodesDBdic; + + FasterReadOnlyList _defaultEmptyNodeList = new FasterReadOnlyList(new FasterList()); + ReadOnlyDictionary _defaultEmptyNodeDict = new ReadOnlyDictionary(new Dictionary()); + } +} diff --git a/ECS/EngineNodeDB.cs.meta b/ECS/EngineNodeDB.cs.meta new file mode 100644 index 0000000..3bc56aa --- /dev/null +++ b/ECS/EngineNodeDB.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b8aebbeeaf3d65d438359008c1f5d351 +timeCreated: 1459422025 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/EnginesRoot.cs b/ECS/EnginesRoot.cs index efec7a5..9836516 100644 --- a/ECS/EnginesRoot.cs +++ b/ECS/EnginesRoot.cs @@ -1,69 +1,173 @@ using System; using System.Collections.Generic; - +using Svelto.DataStructures; +using Svelto.Ticker; namespace Svelto.ES { - public sealed class EnginesRoot: INodeEnginesRoot - { - public EnginesRoot() - { - _nodeEngines = new Dictionary>(); - } + public sealed class EnginesRoot: IEnginesRoot, IEndOfFrameTickable, IEntityFactory + { + const int _INITIAL_SIZE = 100; + const int _INITIAL_INTERNAL_SIZE = 10; - public void AddEngine(IEngine engine) + public EnginesRoot(ITicker ticker) { - if (engine is INodeEngine) - AddNodeEngine(engine as INodeEngine); + ticker.Add(this); + + _nodeEngines = new Dictionary>>(); + _engineRootWeakReference = new WeakReference(this); + _otherEnginesReferences = new FasterList(); + + _nodesDB = new Dictionary>(_INITIAL_SIZE); + _nodesDBdic = new Dictionary>(_INITIAL_SIZE); + + _nodesToAdd = new Queue(_INITIAL_SIZE); } - public void Add(INode node) + public void EndOfFrameTick(float deltaSec) { - Type nodeType = node.GetType(); - - List value; - if (_nodeEngines.TryGetValue(nodeType, out value)) - for (int j = 0; j < value.Count; j++) - value[j].Add(node); + while (_nodesToAdd.Count > 0) InternalAdd(_nodesToAdd.Dequeue()); } - public void Remove(INode node) + public void AddEngine(IEngine engine) { - Type nodeType = node.GetType(); + if (engine is IQueryableNodeEngine) + (engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic); + + if (engine is INodesEngine) + { + var nodesEngine = engine as INodesEngine; - List value; - if (_nodeEngines.TryGetValue(nodeType, out value)) - for (int j = 0; j < value.Count; j++) - value[j].Remove(node); + AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines); + + return; + } + + var baseType = engine.GetType().BaseType; + if (baseType.IsGenericType) + { + var genericType = baseType.GetGenericTypeDefinition(); + + if (genericType == typeof(SingleManagedNodeEngine<>)) + { + AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines); + + return; + } + } + + _otherEnginesReferences.Add(engine); } - void AddNodeEngine(INodeEngine engine) + public void BuildEntity(int ID, EntityDescriptor ed) { - AddEngine(engine, engine.AcceptedNodes(), _nodeEngines); + var entityNodes = ed.BuildNodes(ID, (node) => _engineRootWeakReference.Target.InternalRemove(node)); + + for (int i = 0; i < entityNodes.Count; i++) + _nodesToAdd.Enqueue(entityNodes[i]); } - void AddEngine(T engine, Type[] types, Dictionary> engines) + static void AddEngine(T engine, Type[] types, Dictionary>> engines) where T:INodeEngine { for (int i = 0; i < types.Length; i++) { - List value; + FasterList> list; var type = types[i]; - if (engines.TryGetValue(type, out value) == false) + if (engines.TryGetValue(type, out list) == false) { - List list = new List(); - - list.Add(engine); + list = new FasterList>(); engines.Add(type, list); } - else - value.Add(engine); + + list.Add(engine); } } - Dictionary> _nodeEngines; + void InternalAdd(T node) where T:INode + { + Type nodeType = node.GetType(); + + AddNodeToTheSuitableEngines(node, nodeType); + AddNodeToTheDB(node, nodeType); + } + + void InternalRemove(T node) where T:INode + { + Type nodeType = node.GetType(); + + RemoveNodeFromEngines(node, nodeType); + RemoveNodeFromTheDB(node, nodeType); + } + + void AddNodeToTheDB(T node, Type nodeType) where T : INode + { + FasterList nodes; + if (_nodesDB.TryGetValue(nodeType, out nodes) == false) + nodes = _nodesDB[nodeType] = new FasterList(_INITIAL_INTERNAL_SIZE); + + nodes.Add(node); + + if (node is NodeWithID) + { + Dictionary nodesDic; + if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false) + nodesDic = _nodesDBdic[nodeType] = new Dictionary(_INITIAL_INTERNAL_SIZE); + + nodesDic[(node as NodeWithID).ID] = node; + } + } + + void AddNodeToTheSuitableEngines(T node, Type nodeType) where T : INode + { + FasterList> enginesForNode; + + if (_nodeEngines.TryGetValue(nodeType, out enginesForNode)) + for (int j = 0; j < enginesForNode.Count; j++) + enginesForNode[j].Add(node); + } + + 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? + + if (node is NodeWithID) + { + Dictionary nodesDic; + + if (_nodesDBdic.TryGetValue(nodeType, out nodesDic)) + nodesDic.Remove((node as NodeWithID).ID); + } + } + + 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++) + enginesForNode[j].Remove(node); + } + + Dictionary>> _nodeEngines; + FasterList _otherEnginesReferences; + + Dictionary> _nodesDB; + Dictionary> _nodesDBdic; + + Queue _nodesToAdd; + + WeakReference _engineRootWeakReference; + //integrated pooling system + //add debug panel like Entitas has + //GCHandle should be used to reduce the number of strong references + //datastructure could be thread safe + + //future enhancements: } } diff --git a/ECS/EntityDescriptor.cs b/ECS/EntityDescriptor.cs new file mode 100644 index 0000000..384d2db --- /dev/null +++ b/ECS/EntityDescriptor.cs @@ -0,0 +1,97 @@ +using System; +using System.Reflection; +using Svelto.DataStructures; + +namespace Svelto.ES +{ + public class EntityDescriptor + { + EntityDescriptor() + {} + + protected EntityDescriptor(INodeBuilder[] nodesToBuild, params object[] componentsImplementor) + { + _implementors = componentsImplementor; _nodesToBuild = nodesToBuild; + } + + virtual public 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]); + } + ); + + nodes.Add (node); + } + + return nodes; + } + + TNode FillNode(TNode node, Action removeAction) where TNode: INode + { + var fields = node.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); + + for (int i = fields.Length - 1; i >=0 ; --i) + { + var field = fields[i]; + Type fieldType = field.FieldType; + object component = null; + + for (int j = 0; j < _implementors.Length; j++) + { + var implementor = _implementors[j]; + + if (fieldType.IsAssignableFrom(implementor.GetType())) + { + component = implementor; + + if (fieldType.IsAssignableFrom(typeof(IRemoveEntityComponent))) + (component as IRemoveEntityComponent).removeEntity = removeAction; + + break; + } + } + + 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); + + throw e; + } + + field.SetValue(node, component); + } + + return node; + } + + readonly object[] _implementors; + + INodeBuilder[] _nodesToBuild; + } + + public interface INodeBuilder + { + NodeWithID Build(int ID); + } + + public class NodeBuilder : INodeBuilder where NodeType:NodeWithID, new() + { + public NodeWithID Build(int ID) + { + NodeWithID node = NodeWithID.BuildNode(ID); + + return (NodeType)node; + } + } +} diff --git a/ECS/EntityDescriptor.cs.meta b/ECS/EntityDescriptor.cs.meta new file mode 100644 index 0000000..0fdabbc --- /dev/null +++ b/ECS/EntityDescriptor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 42ab9afd9889862468f4afdac4fffd8b +timeCreated: 1457096903 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/IComponent.cs b/ECS/IComponent.cs new file mode 100644 index 0000000..fc5858f --- /dev/null +++ b/ECS/IComponent.cs @@ -0,0 +1,6 @@ +namespace Svelto.ES +{ + public interface IComponent + { + } +} \ No newline at end of file diff --git a/Utilities/DispatcherOnChange.cs.meta b/ECS/IComponent.cs.meta similarity index 76% rename from Utilities/DispatcherOnChange.cs.meta rename to ECS/IComponent.cs.meta index 54c805e..9592743 100644 --- a/Utilities/DispatcherOnChange.cs.meta +++ b/ECS/IComponent.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 103dfa72bd2486d46ac7e6927c805778 -timeCreated: 1435956509 +guid: 644f7615a18450b4e92f4dcab1f75e57 +timeCreated: 1463519273 licenseType: Free MonoImporter: serializedVersion: 2 diff --git a/ECS/IEngine.cs b/ECS/IEngine.cs new file mode 100644 index 0000000..fa6b1aa --- /dev/null +++ b/ECS/IEngine.cs @@ -0,0 +1,37 @@ +namespace Svelto.ES +{ + public interface IEngine + {} + + public interface INodeEngine:IEngine where TNodeType:INode + { + void Add(TNodeType obj); + void Remove(TNodeType obj); + } + + public interface INodesEngine : INodeEngine + { + System.Type[] AcceptedNodes(); + } + + public interface IQueryableNodeEngine:IEngine + { + IEngineNodeDB nodesDB { set; } + } + + public abstract class SingleManagedNodeEngine : 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/Engine/IEngine.cs.meta b/ECS/IEngine.cs.meta similarity index 100% rename from ECS/Engine/IEngine.cs.meta rename to ECS/IEngine.cs.meta diff --git a/ECS/IEngineNodeDB.cs b/ECS/IEngineNodeDB.cs new file mode 100644 index 0000000..e6868a9 --- /dev/null +++ b/ECS/IEngineNodeDB.cs @@ -0,0 +1,11 @@ +using Svelto.DataStructures; + +namespace Svelto.ES +{ + public interface IEngineNodeDB + { + ReadOnlyDictionary QueryIndexableNodes() where T : INode; + bool QueryNode(int ID, out T node) where T:INode; + FasterReadOnlyList QueryNodes() where T : INode; + } +} diff --git a/ECS/IEngineNodeDB.cs.meta b/ECS/IEngineNodeDB.cs.meta new file mode 100644 index 0000000..c346d94 --- /dev/null +++ b/ECS/IEngineNodeDB.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 44c24b91989f46048abc15039fd070bc +timeCreated: 1459422024 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/IEnginesRoot.cs b/ECS/IEnginesRoot.cs index ca3567a..8fa7b5d 100644 --- a/ECS/IEnginesRoot.cs +++ b/ECS/IEnginesRoot.cs @@ -5,9 +5,8 @@ namespace Svelto.ES void AddEngine(IEngine engine); } - public interface INodeEnginesRoot: IEnginesRoot + public interface IEntityFactory { - void Add(INode node); - void Remove(INode node); + void BuildEntity(int ID, EntityDescriptor ED); } } diff --git a/ECS/IEntityDescriptorHolder.cs b/ECS/IEntityDescriptorHolder.cs new file mode 100644 index 0000000..9ee697a --- /dev/null +++ b/ECS/IEntityDescriptorHolder.cs @@ -0,0 +1,10 @@ +namespace Svelto.ES +{ + /// + /// please use [DisallowMultipleComponent] in your monobehaviours that implement IEntityDescriptorHolder + /// + public interface IEntityDescriptorHolder + { + EntityDescriptor BuildDescriptorType(); + } +} diff --git a/Utilities/WeakReference.cs.meta b/ECS/IEntityDescriptorHolder.cs.meta similarity index 76% rename from Utilities/WeakReference.cs.meta rename to ECS/IEntityDescriptorHolder.cs.meta index 951b563..cbd5e0b 100644 --- a/Utilities/WeakReference.cs.meta +++ b/ECS/IEntityDescriptorHolder.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 2bf8d1cb161e6aa428300dba9323892e -timeCreated: 1434750269 +guid: 5a8db83410a8aaa40b824fdc3d968f67 +timeCreated: 1463438461 licenseType: Free MonoImporter: serializedVersion: 2 diff --git a/ECS/INode.cs b/ECS/INode.cs new file mode 100644 index 0000000..7646bbe --- /dev/null +++ b/ECS/INode.cs @@ -0,0 +1,17 @@ +namespace Svelto.ES +{ + public interface INode + {} + + public class NodeWithID: INode + { + public static TNodeType BuildNode(int ID) where TNodeType: NodeWithID, new() + { + return new TNodeType { _ID = ID }; + } + + public int ID { get { return _ID; } } + + protected int _ID; + } +} diff --git a/ECS/Node/INode.cs.meta b/ECS/INode.cs.meta similarity index 100% rename from ECS/Node/INode.cs.meta rename to ECS/INode.cs.meta diff --git a/ECS/IRemoveEntityComponent.cs b/ECS/IRemoveEntityComponent.cs new file mode 100644 index 0000000..b6acb5d --- /dev/null +++ b/ECS/IRemoveEntityComponent.cs @@ -0,0 +1,9 @@ +using System; + +namespace Svelto.ES +{ + public interface IRemoveEntityComponent + { + Action removeEntity { get; set; } + } +} diff --git a/ECS/IRemoveEntityComponent.cs.meta b/ECS/IRemoveEntityComponent.cs.meta new file mode 100644 index 0000000..d449680 --- /dev/null +++ b/ECS/IRemoveEntityComponent.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: bb9e778a1f85b0d46a065bbd5b549acd +timeCreated: 1462977663 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Node.meta b/ECS/Node.meta deleted file mode 100644 index f40a80e..0000000 --- a/ECS/Node.meta +++ /dev/null @@ -1,5 +0,0 @@ -fileFormatVersion: 2 -guid: 8e3c19ee9185a664aa837e567c4db122 -folderAsset: yes -DefaultImporter: - userData: diff --git a/ECS/Node/INode.cs b/ECS/Node/INode.cs deleted file mode 100644 index 513fd86..0000000 --- a/ECS/Node/INode.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Svelto.ES -{ - public interface INode - { - } - - public interface INodeWithReferenceID : INode where T : class - { - T ID { get; } - } - - public interface INodeWithValueID : INode where T : struct - { - T ID { get; } - } -} diff --git a/ECS/Node/INodeHolder.cs b/ECS/Node/INodeHolder.cs deleted file mode 100644 index a21c1a9..0000000 --- a/ECS/Node/INodeHolder.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Svelto.ES -{ - interface INodeHolder - { - INode node { get; } - INodeEnginesRoot engineRoot { set; } - } -} diff --git a/ECS/Node/INodeHolder.cs.meta b/ECS/Node/INodeHolder.cs.meta deleted file mode 100644 index dceb188..0000000 --- a/ECS/Node/INodeHolder.cs.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a4c0061442f32644cb1e434b6544d56c -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: diff --git a/ECS/Unity.meta b/ECS/Unity.meta deleted file mode 100644 index 81bc6f8..0000000 --- a/ECS/Unity.meta +++ /dev/null @@ -1,5 +0,0 @@ -fileFormatVersion: 2 -guid: e369cc976007e884d92f906e4d1054d7 -folderAsset: yes -DefaultImporter: - userData: diff --git a/ECS/Unity/UnityNodeHolder.cs b/ECS/Unity/UnityNodeHolder.cs deleted file mode 100644 index faf55cb..0000000 --- a/ECS/Unity/UnityNodeHolder.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Reflection; -using UnityEngine; - -namespace Svelto.ES -{ - public abstract class BaseNodeHolder:MonoBehaviour, INodeHolder where NodeType : INode - { - public INode node { get { if (_node != null) return _node; else return _node = ReturnNode(); } } - - public INodeEnginesRoot engineRoot { set { _engineRoot = value; } } - - protected abstract NodeType ReturnNode(); - - void Start() - { - if (_engineRoot != null) - _engineRoot.Add(node); - } - - NodeType _node; - INodeEnginesRoot _engineRoot; - } - - public abstract class UnityNodeHolder:BaseNodeHolder where NodeType : INode - { - protected abstract NodeType GenerateNode(); - - override protected NodeType ReturnNode() - { - NodeType node = GenerateNode(); - - FieldInfo[] fields = typeof(NodeType).GetFields(BindingFlags.Public | BindingFlags.Instance); - - for (int i = fields.Length - 1; i >=0 ; --i) - { - var field = fields[i]; - - var component = transform.GetComponentsInChildren(field.FieldType, true); //can't use inactive components - - if (component.Length == 0) - { - Exception e = new Exception("Svelto.ES: An Entity must hold all the components needed for a Node. Type: " + field.FieldType.Name + "Entity name: " + name); - - Debug.LogException(e, gameObject); - - throw e; - } - if (component.Length > 1) - { - Exception e = new Exception("Svelto.ES: An Entity can hold only one component of the same type. Type: " + field.FieldType.Name + "Entity name: " + name); - - Debug.LogException(e, gameObject); - - throw e; - } - - field.SetValue(node, component[0]); - } - - return node; - } - } -} diff --git a/ECS/Unity/UnityNodeHolder.cs.meta b/ECS/Unity/UnityNodeHolder.cs.meta deleted file mode 100644 index f531773..0000000 --- a/ECS/Unity/UnityNodeHolder.cs.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 50ed7d1adb836dd4786fef832cb4e808 -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: diff --git a/ECS/UnityEnginesRoot.cs b/ECS/UnityEnginesRoot.cs deleted file mode 100644 index c280c18..0000000 --- a/ECS/UnityEnginesRoot.cs +++ /dev/null @@ -1,65 +0,0 @@ -using UnityEngine; - -namespace Svelto.ES -{ - public class UnityEnginesRoot : INodeEnginesRoot - { - public void AddEngine(IEngine engine) - { - _engineRoot.AddEngine(engine); - } - - public void Add(INode node) - { - _engineRoot.Add(node); - } - - public void Remove(INode node) - { - _engineRoot.Remove(node); - } - - public void AddGameObjectEntity(GameObject entity) - { -#if UNITY_5_0 - INodeHolder[] nodeHolders = entity.GetComponents(); - - for (int i = 0; i < nodeHolders.Length; i++) - nodeHolders[i].engineRoot = this; -#else - MonoBehaviour[] nodeHolders = entity.GetComponents(); - - for (int i = 0; i < nodeHolders.Length; i++) - { - var nodeHolder = nodeHolders[i]; - - if (nodeHolder is INodeHolder) - (nodeHolders[i] as INodeHolder).engineRoot = this; - } -#endif - - } - - public void RemoveGameObjectEntity(GameObject entity) - { -#if UNITY_5_0 - INodeHolder[] nodeHolders = entity.GetComponents(); - - for (int i = 0; i < nodeHolders.Length; i++) - Remove(nodeHolders[i].node); -#else - MonoBehaviour[] nodeHolders = entity.GetComponents(); - - for (int i = 0; i < nodeHolders.Length; i++) - { - var nodeHolder = nodeHolders[i]; - - if (nodeHolder is INodeHolder) - Remove((nodeHolder as INodeHolder).node); - } -#endif - } - - EnginesRoot _engineRoot = new EnginesRoot(); - } -} diff --git a/ECS/UnityEnginesRoot.cs.meta b/ECS/UnityEnginesRoot.cs.meta deleted file mode 100644 index 61a53bf..0000000 --- a/ECS/UnityEnginesRoot.cs.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 6e4e1cde52e768148a0aaf8effbb9ba3 -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: diff --git a/ECS/note.txt b/ECS/note.txt new file mode 100644 index 0000000..c041193 --- /dev/null +++ b/ECS/note.txt @@ -0,0 +1,111 @@ +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 can have injected dependencies + +components don't have logic +components can have only getter and setter +components cannot define patterns that requires logic. +components cannot issues commands + +High Cohesion[edit] +Main article: Cohesion (computer science) +High Cohesion is an evaluative pattern that attempts to keep objects appropriately focused, manageable and understandable. High cohesion is generally used in support of Low Coupling. High cohesion means that the responsibilities of a given element are strongly related and highly focused. Breaking programs into classes and subsystems is an example of activities that increase the cohesive properties of a system. Alternatively, low cohesion is a situation in which a given element has too many unrelated responsibilities. Elements with low cohesion often suffer from being hard to comprehend, hard to reuse, hard to maintain and averse to change.[3] + +Low Coupling[edit] +Main article: Loose coupling +Low Coupling is an evaluative pattern, which dictates how to assign responsibilities to support: + +lower dependency between the classes, +change in one class having lower impact on other classes, +higher reuse potential. + +events, observers and mediators have the inconvience to hold the reference to the engine, which forces to use cleanup if the engine must be removed. +Observers are easy to clean up from the engine. Mediators needs to be integrated to the framework to be simple to clean up. Component events need the clean up function. +producer/consumer has the inconvienent to force check the number of jobs available everyframe + +Engine can't be removed, they can only be disabled, but the logic of disabling must be handled by the engine itself + +Should components have just one element? Should engines use just nodes? Components are ditacted by the entities and Nodes by the engines + +http://thelinuxlich.github.io/artemis_CSharp/ + +differences: components no events, everthing must be update +give more responsabiltiy to the user, semplicity + +https://github.com/sschmid/Entitas-CSharp/wiki/Overview + +no groups, no queries + +http://entity-systems.wikidot.com/es-articles + +http://www.ashframework.org/ + +it's very important to give a namespace to the engines. In this way it's impossible to create semantically wrong nodes (PlayerNode Vs TargetNode) + +ToDo: + +it's not safe to remove an engine without having called being denitialised internal states. A special ClearOnRemove function must be added for each engine + +namespace GameFramework.RayCast +{ + public class RayCastEngineEngine + { + public RayCastEngine(RayCastEmployer jobList) + { + jobList.onJobassigned += OnRaycastRequested; + } + + public void Add(IComponent obj) + {} + + public void Remove(IComponent obj) + {} + + void OnRaycastRequested(RayCastJob job) + { + RaycastHit shootHit; + + Physics.Raycast(job.rayVector, out shootHit, job.range, _enemyMask); + + job.Done(shootHit); + } + + RayCastEmployer _employer; + + int _enemyMask; + } + + public struct RayCastJob + { + readonly public Ray rayVector; + readonly public float range; + readonly public Action Done; + + public RayCastJob(Ray direction, float distance, Action OnDone) + { + rayVector = direction; + range = distance; + Done = OnDone; + } + } + + public class RayCastEmployer + { + public event Action onJobassigned; + + public void AssignJob(RayCastJob data, Action onJobdone) + { + onJobassigned(data); + } + } +} + +if your code can be read as + +A tells B to do something is direct +A register B event is indirect +althoggh if B tells A something through event is direct again. B must say something like I don't know who you are, but this just happened. you say B.SomethingHappenedToMe() not B.YouMustDoThis(); + +un engine non deve mai avere concetti di un altro engine. dire all'engine sonoro suona morte � sbagliato. � l'engine death che triggera l'evento e l'engine sound ad ascoltarlo. diff --git a/ECS/note.txt.meta b/ECS/note.txt.meta new file mode 100644 index 0000000..9b67328 --- /dev/null +++ b/ECS/note.txt.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 11a3defaf51bc2147904eb737af5efb7 +TextScriptImporter: + userData: diff --git a/Factories/IGameObjectFactory.cs b/Factories/IGameObjectFactory.cs index f7231da..7879bf3 100644 --- a/Factories/IGameObjectFactory.cs +++ b/Factories/IGameObjectFactory.cs @@ -7,6 +7,6 @@ namespace Svelto.Factories void RegisterPrefab(GameObject prefab, string type, GameObject parent = null); GameObject Build(string type); - GameObject Build(GameObject go); + GameObject Build(GameObject prefab); } } diff --git a/Observer/Observable.cs b/Observer/Observable.cs index 289f984..9e56fd3 100644 --- a/Observer/Observable.cs +++ b/Observer/Observable.cs @@ -2,20 +2,41 @@ using System; namespace Svelto.Observer { + public delegate void ObserverAction(ref DispatchType parameter); + + public interface IObservable + { + event Action Notify; + + void Dispatch(); + } + public interface IObservable - { - event Action Notify; + { + event ObserverAction Notify; - void Dispatch(DispatchType parameter); + void Dispatch(ref DispatchType parameter); } public class Observable:IObservable { - public event Action Notify; + 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(DispatchType parameter) + public void Dispatch() { - Notify(parameter); + if (Notify != null) + Notify(); } } } diff --git a/Observer/Observable.cs.meta b/Observer/Observable.cs.meta index 6dacf05..8f93bb4 100644 --- a/Observer/Observable.cs.meta +++ b/Observer/Observable.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c2a9f8efc13196f40bdc41e36e9f78fb +guid: e9d378efdf4e5dd439400d001b6775ec timeCreated: 1440356670 licenseType: Free MonoImporter: diff --git a/Observer/Observer.cs b/Observer/Observer.cs index 5979d6f..7871f47 100644 --- a/Observer/Observer.cs +++ b/Observer/Observer.cs @@ -1,24 +1,24 @@ using System; -namespace Svelto.Observer +namespace Svelto.Observer.InterNamespace { - public abstract class Observer: IObserver + public abstract class Observer : IObserver { - public Observer(Observable observable) + protected Observer(Observable observable) { - observable.Notify += OnObservableDispatched; + observable.Notify += OnObservableDispatched; _unsubscribe = () => observable.Notify -= OnObservableDispatched; } - public void AddAction(Action action) + public void AddAction(ObserverAction action) { _actions += action; } - public void RemoveAction(Action action) + public void RemoveAction(ObserverAction action) { - _actions += action; + _actions -= action; } public void Unsubscribe() @@ -26,33 +26,86 @@ namespace Svelto.Observer _unsubscribe(); } - private void OnObservableDispatched(DispatchType dispatchNotification) + void OnObservableDispatched(ref DispatchType dispatchNotification) { - _actions(TypeMap(dispatchNotification)); + if (_actions != null) + { + var actionType = TypeMap(ref dispatchNotification); + + _actions(ref actionType); + } } - abstract protected ActionType TypeMap(DispatchType dispatchNotification); + protected abstract ActionType TypeMap(ref DispatchType dispatchNotification); - Action _actions; - Action _unsubscribe; + ObserverAction _actions; + Action _unsubscribe; } +} - public class Observer: Observer +namespace Svelto.Observer.IntraNamespace +{ + public class Observer : InterNamespace.Observer { - public Observer(Observable observable):base(observable) - {} + public Observer(Observable observable) : base(observable) + { } - protected override DispatchType TypeMap(DispatchType dispatchNotification) + 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(Action action); - void RemoveAction(Action action); + 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 index e1a901b..5277627 100644 --- a/Observer/Observer.cs.meta +++ b/Observer/Observer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 632ffa30ee5cc9d40af433bd31a2f57a +guid: 7b4eff431179a5047a4b9110fe27ecf6 timeCreated: 1440352946 licenseType: Free MonoImporter: diff --git a/Ticker/ITickable.cs b/Ticker/ITickable.cs index 61485ad..f1c773c 100644 --- a/Ticker/ITickable.cs +++ b/Ticker/ITickable.cs @@ -1,26 +1,31 @@ namespace Svelto.Ticker { - public interface ITickableBase + public interface ILateTickable : ITickableBase { + void LateTick(float deltaSec); } - public interface ITickable: ITickableBase + public interface IPhysicallyTickable : ITickableBase { - void Tick(float deltaSec); + void PhysicsTick(float deltaSec); } - public interface ILateTickable : ITickableBase + public interface ITickable : ITickableBase { - void LateTick(float deltaSec); + void Tick(float deltaSec); } - public interface IPhysicallyTickable : ITickableBase + public interface IEndOfFrameTickable : ITickableBase { - void PhysicsTick(float deltaSec); - } + void EndOfFrameTick(float deltaSec); + } public interface IIntervaledTickable : ITickableBase { void IntervaledTick(); } + + public interface ITickableBase + { + } } diff --git a/Ticker/ITickable.cs.meta b/Ticker/ITickable.cs.meta index e628cd5..0f9fefa 100644 --- a/Ticker/ITickable.cs.meta +++ b/Ticker/ITickable.cs.meta @@ -1,7 +1,10 @@ fileFormatVersion: 2 -guid: b9ebca86eb6d1f64ba93813b675c4f88 +guid: 8830537b810b3fa489b14e66327f0af2 MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Ticker/ITicker.cs b/Ticker/ITicker.cs index a1190ba..58180d6 100644 --- a/Ticker/ITicker.cs +++ b/Ticker/ITicker.cs @@ -1,12 +1,8 @@ -using System; - namespace Svelto.Ticker { - public interface ITicker - { - void Add(ITickableBase tickable); + public interface ITicker + { + void Add(ITickableBase tickable); void Remove(ITickableBase tickable); - } + } } - - diff --git a/Ticker/ITicker.cs.meta b/Ticker/ITicker.cs.meta index 079ce10..c872bc0 100644 --- a/Ticker/ITicker.cs.meta +++ b/Ticker/ITicker.cs.meta @@ -1,7 +1,10 @@ fileFormatVersion: 2 -guid: 5a726e8ced6bca340b272e7bd64ebcad +guid: 0bf92a08bb722064ab845dea9463933e MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Ticker/TickBehaviour.cs b/Ticker/TickBehaviour.cs index f5ff11d..2a59d5d 100644 --- a/Ticker/TickBehaviour.cs +++ b/Ticker/TickBehaviour.cs @@ -1,42 +1,118 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Reflection; using UnityEngine; namespace Svelto.Ticker { - public class TickBehaviour:MonoBehaviour - { - internal void Add(ITickable tickable) - { - _ticked.Add(tickable); - } - - internal void Remove(ITickable tickable) - { - _ticked.Remove(tickable); - } - - internal void AddPhysic(IPhysicallyTickable tickable) - { - _physicallyTicked.Add(tickable); - } - - internal void RemovePhysic(IPhysicallyTickable tickable) - { - _physicallyTicked.Remove(tickable); - } - - internal void AddLate(ILateTickable tickable) - { - _lateTicked.Add(tickable); - } - - internal void RemoveLate(ILateTickable tickable) - { - _lateTicked.Remove(tickable); - } + 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) { @@ -57,33 +133,21 @@ namespace Svelto.Ticker if (_intervalledTicked.TryGetValue(tickable, out enumerator)) { StopCoroutine(enumerator); + _intervalledTicked.Remove(tickable); } } - void Update() - { - for (int i = _ticked.Count - 1; i >= 0; --i) - _ticked[i].Tick(Time.deltaTime); - } - void LateUpdate() - { - for (int i = _lateTicked.Count - 1; i >= 0; --i) - _lateTicked[i].LateTick(Time.deltaTime); - } - void FixedUpdate() - { - for (int i = _physicallyTicked.Count - 1; i >= 0; --i) - _physicallyTicked[i].PhysicsTick(Time.deltaTime); - } IEnumerator IntervaledUpdate(IIntervaledTickable tickable, float seconds) { while (true) { DateTime next = DateTime.UtcNow.AddSeconds(seconds); while (DateTime.UtcNow < next) yield return null; tickable.IntervaledTick(); } } - private List _ticked = new List(); - private List _lateTicked = new List(); - private List _physicallyTicked = new List(); - private Dictionary _intervalledTicked = new Dictionary(); - } + List _lateTicked = new List(); + List _physicallyTicked = new List(); + List _ticked = new List(); + List _endOfFrameTicked = new List(); + + Dictionary _intervalledTicked = new Dictionary(); + } } diff --git a/Ticker/TickBehaviour.cs.meta b/Ticker/TickBehaviour.cs.meta index 9ed94d3..f042c9a 100644 --- a/Ticker/TickBehaviour.cs.meta +++ b/Ticker/TickBehaviour.cs.meta @@ -1,7 +1,10 @@ fileFormatVersion: 2 -guid: b522dd9c9c9c5a941a49ff6a52222824 +guid: 82d08d9d100803c47b036667fdc671cf MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Ticker/UnityTicker.cs b/Ticker/UnityTicker.cs index e1ad40b..8ac0b0a 100644 --- a/Ticker/UnityTicker.cs +++ b/Ticker/UnityTicker.cs @@ -2,24 +2,24 @@ using UnityEngine; namespace Svelto.Ticker { - internal class UnityTicker: ITicker - { - public UnityTicker() - { - _ticker = GameObject.FindObjectOfType(); + class UnityTicker : ITicker + { + public UnityTicker() + { + _ticker = Object.FindObjectOfType(); if (_ticker == null) - { - GameObject go = new GameObject("SveltoTicker"); - - _ticker = go.AddComponent(); - } - } - - public void Add(ITickableBase tickable) - { + { + var go = new GameObject("SveltoTicker"); + + _ticker = go.AddComponent(); + } + } + + public void Add(ITickableBase tickable) + { if (tickable is ITickable) - _ticker.Add(tickable as ITickable); + _ticker.Add(tickable as ITickable); if (tickable is IPhysicallyTickable) _ticker.AddPhysic(tickable as IPhysicallyTickable); @@ -27,12 +27,15 @@ namespace Svelto.Ticker 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) - { + + public void Remove(ITickableBase tickable) + { if (tickable is ITickable) _ticker.Remove(tickable as ITickable); @@ -42,15 +45,13 @@ namespace Svelto.Ticker 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); } - private TickBehaviour _ticker; - } + readonly TickBehaviour _ticker; + } } - - - - - diff --git a/Ticker/UnityTicker.cs.meta b/Ticker/UnityTicker.cs.meta index 8f1aea0..26f9208 100644 --- a/Ticker/UnityTicker.cs.meta +++ b/Ticker/UnityTicker.cs.meta @@ -1,7 +1,10 @@ fileFormatVersion: 2 -guid: 8972f22f09349484b8537133b3905062 +guid: b62a08472017f914d9fc399eeb95108e MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Utilities/Dispatcher.cs b/Utilities/Dispatcher.cs deleted file mode 100644 index 137ebdc..0000000 --- a/Utilities/Dispatcher.cs +++ /dev/null @@ -1,20 +0,0 @@ -public class Dispatcher -{ - public event System.Action observers; - - private Dispatcher() { } - - public Dispatcher(S sender) - { - _sender = sender; - } - - virtual public void Dispatch(T value) - { - if (observers != null) - observers(_sender, value); - } - - S _sender; -} - diff --git a/Utilities/Print.cs b/Utilities/Print.cs new file mode 100644 index 0000000..a693c3a --- /dev/null +++ b/Utilities/Print.cs @@ -0,0 +1,165 @@ +using System; +using System.Diagnostics; +using System.Text; +using Debug = UnityEngine.Debug; + +public static class FastConcatUtility +{ + static readonly StringBuilder _stringBuilder = new StringBuilder(256); + + public static string FastConcat(this string str1, string str2) + { + lock (_stringBuilder) + { + _stringBuilder.Length = 0; + + _stringBuilder.Append(str1); + _stringBuilder.Append(str2); + + return _stringBuilder.ToString(); + } + } + + public static string FastConcat(this string str1, string str2, string str3) + { + lock (_stringBuilder) + { + _stringBuilder.Length = 0; + + _stringBuilder.Append(str1); + _stringBuilder.Append(str2); + _stringBuilder.Append(str3); + + return _stringBuilder.ToString(); + } + } + + public static string FastConcat(this string str1, string str2, string str3, string str4) + { + lock (_stringBuilder) + { + _stringBuilder.Length = 0; + + _stringBuilder.Append(str1); + _stringBuilder.Append(str2); + _stringBuilder.Append(str3); + _stringBuilder.Append(str4); + + + return _stringBuilder.ToString(); + } + } + + public static string FastConcat(this string str1, string str2, string str3, string str4, string str5) + { + lock (_stringBuilder) + { + _stringBuilder.Length = 0; + + _stringBuilder.Append(str1); + _stringBuilder.Append(str2); + _stringBuilder.Append(str3); + _stringBuilder.Append(str4); + _stringBuilder.Append(str5); + + return _stringBuilder.ToString(); + } + } +} + +namespace Utility +{ + public static class Console + { + static StringBuilder _stringBuilder = new StringBuilder(256); + + public static void Log(string txt) + { + Debug.Log(txt); + } + + public static void LogError(string txt) + { + string toPrint; + + lock (_stringBuilder) + { + _stringBuilder.Length = 0; + _stringBuilder.Append("-!!!!!!-> "); + _stringBuilder.Append(txt); + + toPrint = _stringBuilder.ToString(); + } + + Debug.LogError(toPrint); + } + + public static void LogException(Exception e) + { + LogException(e, null); + } + + public static void LogException(Exception e, UnityEngine.Object obj) + { + string toPrint; + + lock (_stringBuilder) + { + _stringBuilder.Length = 0; + _stringBuilder.Append("-!!!!!!-> ").Append(e); + + toPrint = _stringBuilder.ToString(); + } + + Exception ex = new Exception(e.ToString()); + + Debug.Log(toPrint); + Debug.LogException(ex, obj); + } + + public static void LogWarning(string txt) + { + string toPrint; + + lock (_stringBuilder) + { + _stringBuilder.Length = 0; + _stringBuilder.Append("------> "); + _stringBuilder.Append(txt); + + toPrint = _stringBuilder.ToString(); + } + + Debug.LogWarning(toPrint); + } + + /// + /// This function should never be used explicitly + /// + /// + public static void SystemLog(string txt) + { + string toPrint; + + lock (_stringBuilder) + { + string currentTimeString = DateTime.UtcNow.ToLongTimeString(); //ensure includes seconds + string processTimeString = (DateTime.Now - Process.GetCurrentProcess().StartTime).ToString(); + + _stringBuilder.Length = 0; + _stringBuilder.Append("[").Append(currentTimeString); + _stringBuilder.Append("][").Append(processTimeString); + _stringBuilder.Length = _stringBuilder.Length - 3; //remove some precision that we don't need + _stringBuilder.Append("] ").AppendLine(txt); + + toPrint = _stringBuilder.ToString(); + } + +#if !UNITY_EDITOR + System.Console.WriteLine(toPrint); +#else + Debug.Log(toPrint); +#endif + } + } +} diff --git a/Utilities/Print.cs.meta b/Utilities/Print.cs.meta new file mode 100644 index 0000000..121f044 --- /dev/null +++ b/Utilities/Print.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1f5eede659a66c64a9af2ec703db2691 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Utilities/WeakReference.cs b/Utilities/WeakReference.cs deleted file mode 100644 index 5dae223..0000000 --- a/Utilities/WeakReference.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Runtime.Serialization; -/// -/// Represents a weak reference, which references an object while still allowing -/// that object to be reclaimed by garbage collection. -/// -/// The type of the object that is referenced. -[Serializable] -public class WeakReference - : WeakReference where T : class -{ - /// - /// Initializes a new instance of the WeakReference{T} class, referencing - /// the specified object. - /// - /// The object to reference. - public WeakReference(T target) - : base(target) - { } - /// - /// Initializes a new instance of the WeakReference{T} class, referencing - /// the specified object and using the specified resurrection tracking. - /// - /// An object to track. - /// Indicates when to stop tracking the object. - /// If true, the object is tracked - /// after finalization; if false, the object is only tracked - /// until finalization. - public WeakReference(T target, bool trackResurrection) - : base(target, trackResurrection) - { } - protected WeakReference(SerializationInfo info, StreamingContext context) - : base(info, context) - { } - /// - /// Gets or sets the object (the target) referenced by the - /// current WeakReference{T} object. - /// - public new T Target - { - get - { - return (T)base.Target; - } - set - { - base.Target = value; - } - } -}