diff options
51 files changed, 5515 insertions, 2767 deletions
diff --git a/Documents/实现/20minutes.txt b/Documents/实现/20minutes.txt new file mode 100644 index 0000000..189a923 --- /dev/null +++ b/Documents/实现/20minutes.txt @@ -0,0 +1,7 @@ +Character Passive InputAction +Perk Trigger Action NotificationCenter +Pickup OnTriggerEnter +Buff IDamageModifier.GetMod() ValueModifier Buff NotificationCenter +Powerup NotificationCenter UnityEvent +Rune NotificationCenter + diff --git a/Documents/实现/消息通知.xlsx b/Documents/实现/消息通知.xlsx Binary files differnew file mode 100644 index 0000000..bcac959 --- /dev/null +++ b/Documents/实现/消息通知.xlsx diff --git a/WorldlineKeepers/Assets/Plugins/Trinary Software/Timing.cs b/WorldlineKeepers/Assets/Plugins/Trinary Software/Timing.cs deleted file mode 100644 index c61b93e..0000000 --- a/WorldlineKeepers/Assets/Plugins/Trinary Software/Timing.cs +++ /dev/null @@ -1,2738 +0,0 @@ -using UnityEngine; -using System.Collections.Generic; -using UnityEngine.Assertions; -#if UNITY_5_5_OR_NEWER -using UnityEngine.Profiling; -#endif - -// ///////////////////////////////////////////////////////////////////////////////////////// -// More Effective Coroutines -// v3.10.2 -// -// This is an improved implementation of coroutines that boasts zero per-frame memory allocations, -// runs about twice as fast as Unity's built in coroutines and has a range of extra features. -// -// This is the free version. MEC also has a pro version, which can be found here: -// https://www.assetstore.unity3d.com/en/#!/content/68480 -// The pro version contains exactly the same core that the free version uses, but also -// contains many additional features. Every function that exists in MEC Free also exists in MEC Pro, -// so you can upgrade at any time without breaking existing code. -// -// For manual, support, or upgrade guide visit http://trinary.tech/ -// -// Created by Teal Rogers -// Trinary Software -// All rights preserved -// ///////////////////////////////////////////////////////////////////////////////////////// - -namespace MEC -{ - public class Timing : MonoBehaviour - { - /// <summary> - /// The time between calls to SlowUpdate. - /// </summary> - [Tooltip("How quickly the SlowUpdate segment ticks.")] - public float TimeBetweenSlowUpdateCalls = 1f / 7f; - /// <summary> - /// The amount that each coroutine should be seperated inside the Unity profiler. NOTE: When the profiler window - /// is not open this value is ignored and all coroutines behave as if "None" is selected. - /// </summary> - [Tooltip("How much data should be sent to the profiler window when it's open.")] - public DebugInfoType ProfilerDebugAmount; - /// <summary> - /// The number of coroutines that are being run in the Update segment. - /// </summary> - [Tooltip("A count of the number of Update coroutines that are currently running."), Space(12)] - public int UpdateCoroutines; - /// <summary> - /// The number of coroutines that are being run in the FixedUpdate segment. - /// </summary> - [Tooltip("A count of the number of FixedUpdate coroutines that are currently running.")] - public int FixedUpdateCoroutines; - /// <summary> - /// The number of coroutines that are being run in the LateUpdate segment. - /// </summary> - [Tooltip("A count of the number of LateUpdate coroutines that are currently running.")] - public int LateUpdateCoroutines; - /// <summary> - /// The number of coroutines that are being run in the SlowUpdate segment. - /// </summary> - [Tooltip("A count of the number of SlowUpdate coroutines that are currently running.")] - public int SlowUpdateCoroutines; - /// <summary> - /// The time in seconds that the current segment has been running. - /// </summary> - [System.NonSerialized] - public float localTime; - /// <summary> - /// The time in seconds that the current segment has been running. - /// </summary> - public static float LocalTime { get { return Instance.localTime; } } - /// <summary> - /// The amount of time in fractional seconds that elapsed between this frame and the last frame. - /// </summary> - [System.NonSerialized] - public float deltaTime; - /// <summary> - /// The amount of time in fractional seconds that elapsed between this frame and the last frame. - /// </summary> - public static float DeltaTime { get { return Instance.deltaTime; } } - /// <summary> - /// Used for advanced coroutine control. - /// </summary> - public static System.Func<IEnumerator<float>, CoroutineHandle, IEnumerator<float>> ReplacementFunction; - /// <summary> - /// This event fires just before each segment is run. - /// </summary> - public static event System.Action OnPreExecute; - /// <summary> - /// You can use "yield return Timing.WaitForOneFrame;" inside a coroutine function to go to the next frame. - /// </summary> - public const float WaitForOneFrame = float.NegativeInfinity; - /// <summary> - /// The main thread that (almost) everything in unity runs in. - /// </summary> - public static System.Threading.Thread MainThread { get; private set; } - /// <summary> - /// The handle of the current coroutine that is running. - /// </summary> - public static CoroutineHandle CurrentCoroutine - { - get - { - for (int i = 0; i < ActiveInstances.Length; i++) - if (ActiveInstances[i] != null && ActiveInstances[i].currentCoroutine.IsValid) - return ActiveInstances[i].currentCoroutine; - return default(CoroutineHandle); - } - } - /// <summary> - /// The handle of the current coroutine that is running. - /// </summary> - public CoroutineHandle currentCoroutine { get; private set; } - - private static object _tmpRef; - private static bool _tmpBool; - private static CoroutineHandle _tmpHandle; - - private int _currentUpdateFrame; - private int _currentLateUpdateFrame; - private int _currentSlowUpdateFrame; - private int _nextUpdateProcessSlot; - private int _nextLateUpdateProcessSlot; - private int _nextFixedUpdateProcessSlot; - private int _nextSlowUpdateProcessSlot; - private int _lastUpdateProcessSlot; - private int _lastLateUpdateProcessSlot; - private int _lastFixedUpdateProcessSlot; - private int _lastSlowUpdateProcessSlot; - private float _lastUpdateTime; - private float _lastLateUpdateTime; - private float _lastFixedUpdateTime; - private float _lastSlowUpdateTime; - private float _lastSlowUpdateDeltaTime; - private ushort _framesSinceUpdate; - private ushort _expansions = 1; - [SerializeField, HideInInspector] - private byte _instanceID; - - private readonly Dictionary<CoroutineHandle, HashSet<CoroutineHandle>> _waitingTriggers = new Dictionary<CoroutineHandle, HashSet<CoroutineHandle>>(); - private readonly HashSet<CoroutineHandle> _allWaiting = new HashSet<CoroutineHandle>(); - private readonly Dictionary<CoroutineHandle, ProcessIndex> _handleToIndex = new Dictionary<CoroutineHandle, ProcessIndex>(); - private readonly Dictionary<ProcessIndex, CoroutineHandle> _indexToHandle = new Dictionary<ProcessIndex, CoroutineHandle>(); - private readonly Dictionary<CoroutineHandle, string> _processTags = new Dictionary<CoroutineHandle, string>(); - private readonly Dictionary<string, HashSet<CoroutineHandle>> _taggedProcesses = new Dictionary<string, HashSet<CoroutineHandle>>(); - - private IEnumerator<float>[] UpdateProcesses = new IEnumerator<float>[InitialBufferSizeLarge]; - private IEnumerator<float>[] LateUpdateProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; - private IEnumerator<float>[] FixedUpdateProcesses = new IEnumerator<float>[InitialBufferSizeMedium]; - private IEnumerator<float>[] SlowUpdateProcesses = new IEnumerator<float>[InitialBufferSizeMedium]; - - private bool[] UpdatePaused = new bool[InitialBufferSizeLarge]; - private bool[] LateUpdatePaused = new bool[InitialBufferSizeSmall]; - private bool[] FixedUpdatePaused = new bool[InitialBufferSizeMedium]; - private bool[] SlowUpdatePaused = new bool[InitialBufferSizeMedium]; - private bool[] UpdateHeld = new bool[InitialBufferSizeLarge]; - private bool[] LateUpdateHeld = new bool[InitialBufferSizeSmall]; - private bool[] FixedUpdateHeld = new bool[InitialBufferSizeMedium]; - private bool[] SlowUpdateHeld = new bool[InitialBufferSizeMedium]; - - private const ushort FramesUntilMaintenance = 64; - private const int ProcessArrayChunkSize = 64; - private const int InitialBufferSizeLarge = 256; - private const int InitialBufferSizeMedium = 64; - private const int InitialBufferSizeSmall = 8; - - private static Timing[] ActiveInstances = new Timing[16]; - private static Timing _instance; - - public static Timing Instance - { - get - { - if (_instance == null || !_instance.gameObject) - { - GameObject instanceHome = GameObject.Find("Timing Controller"); - - if (instanceHome == null) - { - instanceHome = new GameObject { name = "Timing Controller" }; - - DontDestroyOnLoad(instanceHome); - } - - _instance = instanceHome.GetComponent<Timing>() ?? instanceHome.AddComponent<Timing>(); - - _instance.InitializeInstanceID(); - } - - return _instance; - } - set { _instance = value; } - } - - void OnDestroy() - { - if (_instance == this) - _instance = null; - } - - void OnEnable() - { - if (MainThread == null) - MainThread = System.Threading.Thread.CurrentThread; - - InitializeInstanceID(); - } - - void OnDisable() - { - if (_instanceID < ActiveInstances.Length) - ActiveInstances[_instanceID] = null; - } - - private void InitializeInstanceID() - { - if (ActiveInstances[_instanceID] == null) - { - if (_instanceID == 0x00) - _instanceID++; - - for (; _instanceID <= 0x10; _instanceID++) - { - if (_instanceID == 0x10) - { - GameObject.Destroy(gameObject); - throw new System.OverflowException("You are only allowed 15 different contexts for MEC to run inside at one time."); - } - - if (ActiveInstances[_instanceID] == null) - { - ActiveInstances[_instanceID] = this; - break; - } - } - } - } - - void Update() - { - if (OnPreExecute != null) - OnPreExecute(); - - if (_lastSlowUpdateTime + TimeBetweenSlowUpdateCalls < Time.realtimeSinceStartup && _nextSlowUpdateProcessSlot > 0) - { - ProcessIndex coindex = new ProcessIndex { seg = Segment.SlowUpdate }; - if (UpdateTimeValues(coindex.seg)) - _lastSlowUpdateProcessSlot = _nextSlowUpdateProcessSlot; - - for (coindex.i = 0; coindex.i < _lastSlowUpdateProcessSlot; coindex.i++) - { - try - { - if (!SlowUpdatePaused[coindex.i] && !SlowUpdateHeld[coindex.i] && SlowUpdateProcesses[coindex.i] != null && !(localTime < SlowUpdateProcesses[coindex.i].Current)) - { - currentCoroutine = _indexToHandle[coindex]; - - if (ProfilerDebugAmount != DebugInfoType.None && _indexToHandle.ContainsKey(coindex)) - { - Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine (Slow Update)" + - (_processTags.ContainsKey(_indexToHandle[coindex]) ? ", tag " + _processTags[_indexToHandle[coindex]] : ", no tag")) - : "Processing Coroutine (Slow Update)"); - } - - if (!SlowUpdateProcesses[coindex.i].MoveNext()) - { - if (_indexToHandle.ContainsKey(coindex)) - KillCoroutinesOnInstance(_indexToHandle[coindex]); - } - else if (SlowUpdateProcesses[coindex.i] != null && float.IsNaN(SlowUpdateProcesses[coindex.i].Current)) - { - if (ReplacementFunction != null) - { - SlowUpdateProcesses[coindex.i] = ReplacementFunction(SlowUpdateProcesses[coindex.i], _indexToHandle[coindex]); - ReplacementFunction = null; - } - coindex.i--; - } - - if (ProfilerDebugAmount != DebugInfoType.None) - Profiler.EndSample(); - } - } - catch (System.Exception ex) - { - Debug.LogException(ex); - - if (ex is MissingReferenceException) - Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" - + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject), Segment.SlowUpdate);"); - } - } - } - - if (_nextUpdateProcessSlot > 0) - { - ProcessIndex coindex = new ProcessIndex { seg = Segment.Update }; - if (UpdateTimeValues(coindex.seg)) - _lastUpdateProcessSlot = _nextUpdateProcessSlot; - - for (coindex.i = 0; coindex.i < _lastUpdateProcessSlot; coindex.i++) - { - try - { - if (!UpdatePaused[coindex.i] && !UpdateHeld[coindex.i] && UpdateProcesses[coindex.i] != null && !(localTime < UpdateProcesses[coindex.i].Current)) - { - currentCoroutine = _indexToHandle[coindex]; - - if (ProfilerDebugAmount != DebugInfoType.None && _indexToHandle.ContainsKey(coindex)) - { - Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine" + - (_processTags.ContainsKey(_indexToHandle[coindex]) ? ", tag " + _processTags[_indexToHandle[coindex]] : ", no tag")) - : "Processing Coroutine"); - } - - if (!UpdateProcesses[coindex.i].MoveNext()) - { - if (_indexToHandle.ContainsKey(coindex)) - KillCoroutinesOnInstance(_indexToHandle[coindex]); - } - else if (UpdateProcesses[coindex.i] != null && float.IsNaN(UpdateProcesses[coindex.i].Current)) - { - if (ReplacementFunction != null) - { - UpdateProcesses[coindex.i] = ReplacementFunction(UpdateProcesses[coindex.i], _indexToHandle[coindex]); - ReplacementFunction = null; - } - coindex.i--; - } - - if (ProfilerDebugAmount != DebugInfoType.None) - Profiler.EndSample(); - } - } - catch (System.Exception ex) - { - Debug.LogException(ex); - - if (ex is MissingReferenceException) - Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" - + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject));"); - } - } - } - - currentCoroutine = default(CoroutineHandle); - - if(++_framesSinceUpdate > FramesUntilMaintenance) - { - _framesSinceUpdate = 0; - - if (ProfilerDebugAmount != DebugInfoType.None) - Profiler.BeginSample("Maintenance Task"); - - RemoveUnused(); - - if (ProfilerDebugAmount != DebugInfoType.None) - Profiler.EndSample(); - } - } - - void FixedUpdate() - { - if (OnPreExecute != null) - OnPreExecute(); - - if (_nextFixedUpdateProcessSlot > 0) - { - ProcessIndex coindex = new ProcessIndex { seg = Segment.FixedUpdate }; - if (UpdateTimeValues(coindex.seg)) - _lastFixedUpdateProcessSlot = _nextFixedUpdateProcessSlot; - - for (coindex.i = 0; coindex.i < _lastFixedUpdateProcessSlot; coindex.i++) - { - try - { - if (!FixedUpdatePaused[coindex.i] && !FixedUpdateHeld[coindex.i] && FixedUpdateProcesses[coindex.i] != null && !(localTime < FixedUpdateProcesses[coindex.i].Current)) - { - currentCoroutine = _indexToHandle[coindex]; - - - if (ProfilerDebugAmount != DebugInfoType.None && _indexToHandle.ContainsKey(coindex)) - { - Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine" + - (_processTags.ContainsKey(_indexToHandle[coindex]) ? ", tag " + _processTags[_indexToHandle[coindex]] : ", no tag")) - : "Processing Coroutine"); - } - - if (!FixedUpdateProcesses[coindex.i].MoveNext()) - { - if (_indexToHandle.ContainsKey(coindex)) - KillCoroutinesOnInstance(_indexToHandle[coindex]); - } - else if (FixedUpdateProcesses[coindex.i] != null && float.IsNaN(FixedUpdateProcesses[coindex.i].Current)) - { - if (ReplacementFunction != null) - { - FixedUpdateProcesses[coindex.i] = ReplacementFunction(FixedUpdateProcesses[coindex.i], _indexToHandle[coindex]); - ReplacementFunction = null; - } - coindex.i--; - } - - if (ProfilerDebugAmount != DebugInfoType.None) - Profiler.EndSample(); - } - } - catch (System.Exception ex) - { - Debug.LogException(ex); - - if (ex is MissingReferenceException) - Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" - + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject), Segment.FixedUpdate);"); - } - } - - currentCoroutine = default(CoroutineHandle); - } - } - - void LateUpdate() - { - if (OnPreExecute != null) - OnPreExecute(); - - if (_nextLateUpdateProcessSlot > 0) - { - ProcessIndex coindex = new ProcessIndex { seg = Segment.LateUpdate }; - if (UpdateTimeValues(coindex.seg)) - _lastLateUpdateProcessSlot = _nextLateUpdateProcessSlot; - - for (coindex.i = 0; coindex.i < _lastLateUpdateProcessSlot; coindex.i++) - { - try - { - if (!LateUpdatePaused[coindex.i] && !LateUpdateHeld[coindex.i] && LateUpdateProcesses[coindex.i] != null && !(localTime < LateUpdateProcesses[coindex.i].Current)) - { - currentCoroutine = _indexToHandle[coindex]; - - - if (ProfilerDebugAmount != DebugInfoType.None && _indexToHandle.ContainsKey(coindex)) - { - Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine" + - (_processTags.ContainsKey(_indexToHandle[coindex]) ? ", tag " + _processTags[_indexToHandle[coindex]] : ", no tag")) - : "Processing Coroutine"); - } - - if (!LateUpdateProcesses[coindex.i].MoveNext()) - { - if (_indexToHandle.ContainsKey(coindex)) - KillCoroutinesOnInstance(_indexToHandle[coindex]); - } - else if (LateUpdateProcesses[coindex.i] != null && float.IsNaN(LateUpdateProcesses[coindex.i].Current)) - { - if (ReplacementFunction != null) - { - LateUpdateProcesses[coindex.i] = ReplacementFunction(LateUpdateProcesses[coindex.i], _indexToHandle[coindex]); - ReplacementFunction = null; - } - coindex.i--; - } - - if (ProfilerDebugAmount != DebugInfoType.None) - Profiler.EndSample(); - } - } - catch (System.Exception ex) - { - Debug.LogException(ex); - - if (ex is MissingReferenceException) - Debug.LogError("This exception can probably be fixed by adding \"CancelWith(gameObject)\" when you run the coroutine.\n" - + "Example: Timing.RunCoroutine(_foo().CancelWith(gameObject), Segment.LateUpdate);"); - } - } - currentCoroutine = default(CoroutineHandle); - } - } - - private void RemoveUnused() - { - var waitTrigsEnum = _waitingTriggers.GetEnumerator(); - while (waitTrigsEnum.MoveNext()) - { - if (waitTrigsEnum.Current.Value.Count == 0) - { - _waitingTriggers.Remove(waitTrigsEnum.Current.Key); - waitTrigsEnum = _waitingTriggers.GetEnumerator(); - continue; - } - - if (_handleToIndex.ContainsKey(waitTrigsEnum.Current.Key) && CoindexIsNull(_handleToIndex[waitTrigsEnum.Current.Key])) - { - CloseWaitingProcess(waitTrigsEnum.Current.Key); - waitTrigsEnum = _waitingTriggers.GetEnumerator(); - } - } - - ProcessIndex outer, inner; - outer.seg = inner.seg = Segment.Update; - - for (outer.i = inner.i = 0; outer.i < _nextUpdateProcessSlot; outer.i++) - { - if (UpdateProcesses[outer.i] != null) - { - if (outer.i != inner.i) - { - UpdateProcesses[inner.i] = UpdateProcesses[outer.i]; - UpdatePaused[inner.i] = UpdatePaused[outer.i]; - UpdateHeld[inner.i] = UpdateHeld[outer.i]; - - if (_indexToHandle.ContainsKey(inner)) - { - RemoveTag(_indexToHandle[inner]); - _handleToIndex.Remove(_indexToHandle[inner]); - _indexToHandle.Remove(inner); - } - - _handleToIndex[_indexToHandle[outer]] = inner; - _indexToHandle.Add(inner, _indexToHandle[outer]); - _indexToHandle.Remove(outer); - } - inner.i++; - } - } - for (outer.i = inner.i; outer.i < _nextUpdateProcessSlot; outer.i++) - { - UpdateProcesses[outer.i] = null; - UpdatePaused[outer.i] = false; - UpdateHeld[outer.i] = false; - - if (_indexToHandle.ContainsKey(outer)) - { - RemoveTag(_indexToHandle[outer]); - - _handleToIndex.Remove(_indexToHandle[outer]); - _indexToHandle.Remove(outer); - } - } - - _lastUpdateProcessSlot -= _nextUpdateProcessSlot - inner.i; - UpdateCoroutines = _nextUpdateProcessSlot = inner.i; - - outer.seg = inner.seg = Segment.FixedUpdate; - for (outer.i = inner.i = 0; outer.i < _nextFixedUpdateProcessSlot; outer.i++) - { - if (FixedUpdateProcesses[outer.i] != null) - { - if (outer.i != inner.i) - { - FixedUpdateProcesses[inner.i] = FixedUpdateProcesses[outer.i]; - FixedUpdatePaused[inner.i] = FixedUpdatePaused[outer.i]; - FixedUpdateHeld[inner.i] = FixedUpdateHeld[outer.i]; - - if (_indexToHandle.ContainsKey(inner)) - { - RemoveTag(_indexToHandle[inner]); - _handleToIndex.Remove(_indexToHandle[inner]); - _indexToHandle.Remove(inner); - } - - _handleToIndex[_indexToHandle[outer]] = inner; - _indexToHandle.Add(inner, _indexToHandle[outer]); - _indexToHandle.Remove(outer); - } - inner.i++; - } - } - for (outer.i = inner.i; outer.i < _nextFixedUpdateProcessSlot; outer.i++) - { - FixedUpdateProcesses[outer.i] = null; - FixedUpdatePaused[outer.i] = false; - FixedUpdateHeld[outer.i] = false; - - if (_indexToHandle.ContainsKey(outer)) - { - RemoveTag(_indexToHandle[outer]); - - _handleToIndex.Remove(_indexToHandle[outer]); - _indexToHandle.Remove(outer); - } - } - - _lastFixedUpdateProcessSlot -= _nextFixedUpdateProcessSlot - inner.i; - FixedUpdateCoroutines = _nextFixedUpdateProcessSlot = inner.i; - - outer.seg = inner.seg = Segment.LateUpdate; - for (outer.i = inner.i = 0; outer.i < _nextLateUpdateProcessSlot; outer.i++) - { - if (LateUpdateProcesses[outer.i] != null) - { - if (outer.i != inner.i) - { - LateUpdateProcesses[inner.i] = LateUpdateProcesses[outer.i]; - LateUpdatePaused[inner.i] = LateUpdatePaused[outer.i]; - LateUpdateHeld[inner.i] = LateUpdateHeld[outer.i]; - - if (_indexToHandle.ContainsKey(inner)) - { - RemoveTag(_indexToHandle[inner]); - _handleToIndex.Remove(_indexToHandle[inner]); - _indexToHandle.Remove(inner); - } - - _handleToIndex[_indexToHandle[outer]] = inner; - _indexToHandle.Add(inner, _indexToHandle[outer]); - _indexToHandle.Remove(outer); - } - inner.i++; - } - } - for (outer.i = inner.i; outer.i < _nextLateUpdateProcessSlot; outer.i++) - { - LateUpdateProcesses[outer.i] = null; - LateUpdatePaused[outer.i] = false; - LateUpdateHeld[outer.i] = false; - - if (_indexToHandle.ContainsKey(outer)) - { - RemoveTag(_indexToHandle[outer]); - - _handleToIndex.Remove(_indexToHandle[outer]); - _indexToHandle.Remove(outer); - } - } - - _lastLateUpdateProcessSlot -= _nextLateUpdateProcessSlot - inner.i; - LateUpdateCoroutines = _nextLateUpdateProcessSlot = inner.i; - - outer.seg = inner.seg = Segment.SlowUpdate; - for (outer.i = inner.i = 0; outer.i < _nextSlowUpdateProcessSlot; outer.i++) - { - if (SlowUpdateProcesses[outer.i] != null) - { - if (outer.i != inner.i) - { - SlowUpdateProcesses[inner.i] = SlowUpdateProcesses[outer.i]; - SlowUpdatePaused[inner.i] = SlowUpdatePaused[outer.i]; - SlowUpdateHeld[inner.i] = SlowUpdateHeld[outer.i]; - - if (_indexToHandle.ContainsKey(inner)) - { - RemoveTag(_indexToHandle[inner]); - _handleToIndex.Remove(_indexToHandle[inner]); - _indexToHandle.Remove(inner); - } - - _handleToIndex[_indexToHandle[outer]] = inner; - _indexToHandle.Add(inner, _indexToHandle[outer]); - _indexToHandle.Remove(outer); - } - inner.i++; - } - } - for (outer.i = inner.i; outer.i < _nextSlowUpdateProcessSlot; outer.i++) - { - SlowUpdateProcesses[outer.i] = null; - SlowUpdatePaused[outer.i] = false; - SlowUpdateHeld[outer.i] = false; - - if (_indexToHandle.ContainsKey(outer)) - { - RemoveTag(_indexToHandle[outer]); - - _handleToIndex.Remove(_indexToHandle[outer]); - _indexToHandle.Remove(outer); - } - } - - _lastSlowUpdateProcessSlot -= _nextSlowUpdateProcessSlot - inner.i; - SlowUpdateCoroutines = _nextSlowUpdateProcessSlot = inner.i; - } - - /// <summary> - /// Run a new coroutine in the Update segment. - /// </summary> - /// <param name="coroutine">The new coroutine's handle.</param> - /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> - public static CoroutineHandle RunCoroutine(IEnumerator<float> coroutine) - { - return coroutine == null ? new CoroutineHandle() - : Instance.RunCoroutineInternal(coroutine, Segment.Update, null, new CoroutineHandle(Instance._instanceID), true); - } - - /// <summary> - /// Run a new coroutine in the Update segment. - /// </summary> - /// <param name="coroutine">The new coroutine's handle.</param> - /// <param name="tag">An optional tag to attach to the coroutine which can later be used for Kill operations.</param> - /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> - public static CoroutineHandle RunCoroutine(IEnumerator<float> coroutine, string tag) - { - return coroutine == null ? new CoroutineHandle() - : Instance.RunCoroutineInternal(coroutine, Segment.Update, tag, new CoroutineHandle(Instance._instanceID), true); - } - - /// <summary> - /// Run a new coroutine. - /// </summary> - /// <param name="coroutine">The new coroutine's handle.</param> - /// <param name="segment">The segment that the coroutine should run in.</param> - /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> - public static CoroutineHandle RunCoroutine(IEnumerator<float> coroutine, Segment segment) - { - return coroutine == null ? new CoroutineHandle() - : Instance.RunCoroutineInternal(coroutine, segment, null, new CoroutineHandle(Instance._instanceID), true); - } - - /// <summary> - /// Run a new coroutine. - /// </summary> - /// <param name="coroutine">The new coroutine's handle.</param> - /// <param name="segment">The segment that the coroutine should run in.</param> - /// <param name="tag">An optional tag to attach to the coroutine which can later be used for Kill operations.</param> - /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> - public static CoroutineHandle RunCoroutine(IEnumerator<float> coroutine, Segment segment, string tag) - { - return coroutine == null ? new CoroutineHandle() - : Instance.RunCoroutineInternal(coroutine, segment, tag, new CoroutineHandle(Instance._instanceID), true); - } - - /// <summary> - /// Run a new coroutine on this Timing instance in the Update segment. - /// </summary> - /// <param name="coroutine">The new coroutine's handle.</param> - /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> - public CoroutineHandle RunCoroutineOnInstance(IEnumerator<float> coroutine) - { - return coroutine == null ? new CoroutineHandle() - : RunCoroutineInternal(coroutine, Segment.Update, null, new CoroutineHandle(_instanceID), true); - } - - /// <summary> - /// Run a new coroutine on this Timing instance in the Update segment. - /// </summary> - /// <param name="coroutine">The new coroutine's handle.</param> - /// <param name="tag">An optional tag to attach to the coroutine which can later be used for Kill operations.</param> - /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> - public CoroutineHandle RunCoroutineOnInstance(IEnumerator<float> coroutine, string tag) - { - return coroutine == null ? new CoroutineHandle() - : RunCoroutineInternal(coroutine, Segment.Update, tag, new CoroutineHandle(_instanceID), true); - } - - /// <summary> - /// Run a new coroutine on this Timing instance. - /// </summary> - /// <param name="coroutine">The new coroutine's handle.</param> - /// <param name="segment">The segment that the coroutine should run in.</param> - /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> - public CoroutineHandle RunCoroutineOnInstance(IEnumerator<float> coroutine, Segment segment) - { - return coroutine == null ? new CoroutineHandle() - : RunCoroutineInternal(coroutine, segment, null, new CoroutineHandle(_instanceID), true); - } - - /// <summary> - /// Run a new coroutine on this Timing instance. - /// </summary> - /// <param name="coroutine">The new coroutine's handle.</param> - /// <param name="segment">The segment that the coroutine should run in.</param> - /// <param name="tag">An optional tag to attach to the coroutine which can later be used for Kill operations.</param> - /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> - public CoroutineHandle RunCoroutineOnInstance(IEnumerator<float> coroutine, Segment segment, string tag) - { - return coroutine == null ? new CoroutineHandle() - : RunCoroutineInternal(coroutine, segment, tag, new CoroutineHandle(_instanceID), true); - } - - - private CoroutineHandle RunCoroutineInternal(IEnumerator<float> coroutine, Segment segment, string tag, CoroutineHandle handle, bool prewarm) - { - ProcessIndex slot = new ProcessIndex { seg = segment }; - - if (_handleToIndex.ContainsKey(handle)) - { - _indexToHandle.Remove(_handleToIndex[handle]); - _handleToIndex.Remove(handle); - } - - float currentLocalTime = localTime; - float currentDeltaTime = deltaTime; - CoroutineHandle cachedHandle = currentCoroutine; - currentCoroutine = handle; - - switch (segment) - { - case Segment.Update: - - if (_nextUpdateProcessSlot >= UpdateProcesses.Length) - { - IEnumerator<float>[] oldProcArray = UpdateProcesses; - bool[] oldPausedArray = UpdatePaused; - bool[] oldHeldArray = UpdateHeld; - - UpdateProcesses = new IEnumerator<float>[UpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; - UpdatePaused = new bool[UpdateProcesses.Length]; - UpdateHeld = new bool[UpdateProcesses.Length]; - - for (int i = 0; i < oldProcArray.Length; i++) - { - UpdateProcesses[i] = oldProcArray[i]; - UpdatePaused[i] = oldPausedArray[i]; - UpdateHeld[i] = oldHeldArray[i]; - } - } - - if (UpdateTimeValues(slot.seg)) - _lastUpdateProcessSlot = _nextUpdateProcessSlot; - - slot.i = _nextUpdateProcessSlot++; - UpdateProcesses[slot.i] = coroutine; - - if (null != tag) - AddTag(tag, handle); - - _indexToHandle.Add(slot, handle); - _handleToIndex.Add(handle, slot); - - while (prewarm) - { - if (!UpdateProcesses[slot.i].MoveNext()) - { - if (_indexToHandle.ContainsKey(slot)) - KillCoroutinesOnInstance(_indexToHandle[slot]); - - prewarm = false; - } - else if (UpdateProcesses[slot.i] != null && float.IsNaN(UpdateProcesses[slot.i].Current)) - { - if (ReplacementFunction != null) - { - UpdateProcesses[slot.i] = ReplacementFunction(UpdateProcesses[slot.i], _indexToHandle[slot]); - ReplacementFunction = null; - } - prewarm = !UpdatePaused[slot.i] && !UpdateHeld[slot.i]; - } - else - { - prewarm = false; - } - } - - break; - - case Segment.FixedUpdate: - - if (_nextFixedUpdateProcessSlot >= FixedUpdateProcesses.Length) - { - IEnumerator<float>[] oldProcArray = FixedUpdateProcesses; - bool[] oldPausedArray = FixedUpdatePaused; - bool[] oldHeldArray = FixedUpdateHeld; - - FixedUpdateProcesses = new IEnumerator<float>[FixedUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; - FixedUpdatePaused = new bool[FixedUpdateProcesses.Length]; - FixedUpdateHeld = new bool[FixedUpdateProcesses.Length]; - - for (int i = 0; i < oldProcArray.Length; i++) - { - FixedUpdateProcesses[i] = oldProcArray[i]; - FixedUpdatePaused[i] = oldPausedArray[i]; - FixedUpdateHeld[i] = oldHeldArray[i]; - } - } - - if (UpdateTimeValues(slot.seg)) - _lastFixedUpdateProcessSlot = _nextFixedUpdateProcessSlot; - - slot.i = _nextFixedUpdateProcessSlot++; - FixedUpdateProcesses[slot.i] = coroutine; - - if (null != tag) - AddTag(tag, handle); - - _indexToHandle.Add(slot, handle); - _handleToIndex.Add(handle, slot); - - while (prewarm) - { - if (!FixedUpdateProcesses[slot.i].MoveNext()) - { - if (_indexToHandle.ContainsKey(slot)) - KillCoroutinesOnInstance(_indexToHandle[slot]); - - prewarm = false; - } - else if (FixedUpdateProcesses[slot.i] != null && float.IsNaN(FixedUpdateProcesses[slot.i].Current)) - { - if (ReplacementFunction != null) - { - FixedUpdateProcesses[slot.i] = ReplacementFunction(FixedUpdateProcesses[slot.i], _indexToHandle[slot]); - ReplacementFunction = null; - } - prewarm = !FixedUpdatePaused[slot.i] && !FixedUpdateHeld[slot.i]; - } - else - { - prewarm = false; - } - } - - break; - - case Segment.LateUpdate: - - if (_nextLateUpdateProcessSlot >= LateUpdateProcesses.Length) - { - IEnumerator<float>[] oldProcArray = LateUpdateProcesses; - bool[] oldPausedArray = LateUpdatePaused; - bool[] oldHeldArray = LateUpdateHeld; - - LateUpdateProcesses = new IEnumerator<float>[LateUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; - LateUpdatePaused = new bool[LateUpdateProcesses.Length]; - LateUpdateHeld = new bool[LateUpdateProcesses.Length]; - - for (int i = 0; i < oldProcArray.Length; i++) - { - LateUpdateProcesses[i] = oldProcArray[i]; - LateUpdatePaused[i] = oldPausedArray[i]; - LateUpdateHeld[i] = oldHeldArray[i]; - } - } - - if (UpdateTimeValues(slot.seg)) - _lastLateUpdateProcessSlot = _nextLateUpdateProcessSlot; - - slot.i = _nextLateUpdateProcessSlot++; - LateUpdateProcesses[slot.i] = coroutine; - - if (tag != null) - AddTag(tag, handle); - - _indexToHandle.Add(slot, handle); - _handleToIndex.Add(handle, slot); - - while (prewarm) - { - if (!LateUpdateProcesses[slot.i].MoveNext()) - { - if (_indexToHandle.ContainsKey(slot)) - KillCoroutinesOnInstance(_indexToHandle[slot]); - - prewarm = false; - } - else if (LateUpdateProcesses[slot.i] != null && float.IsNaN(LateUpdateProcesses[slot.i].Current)) - { - if (ReplacementFunction != null) - { - LateUpdateProcesses[slot.i] = ReplacementFunction(LateUpdateProcesses[slot.i], _indexToHandle[slot]); - ReplacementFunction = null; - } - prewarm = !LateUpdatePaused[slot.i] && !LateUpdateHeld[slot.i]; - } - else - { - prewarm = false; - } - } - - break; - - case Segment.SlowUpdate: - - if (_nextSlowUpdateProcessSlot >= SlowUpdateProcesses.Length) - { - IEnumerator<float>[] oldProcArray = SlowUpdateProcesses; - bool[] oldPausedArray = SlowUpdatePaused; - bool[] oldHeldArray = SlowUpdateHeld; - - SlowUpdateProcesses = new IEnumerator<float>[SlowUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; - SlowUpdatePaused = new bool[SlowUpdateProcesses.Length]; - SlowUpdateHeld = new bool[SlowUpdateProcesses.Length]; - - for (int i = 0; i < oldProcArray.Length; i++) - { - SlowUpdateProcesses[i] = oldProcArray[i]; - SlowUpdatePaused[i] = oldPausedArray[i]; - SlowUpdateHeld[i] = oldHeldArray[i]; - } - } - - if (UpdateTimeValues(slot.seg)) - _lastSlowUpdateProcessSlot = _nextSlowUpdateProcessSlot; - - slot.i = _nextSlowUpdateProcessSlot++; - SlowUpdateProcesses[slot.i] = coroutine; - - if (tag != null) - AddTag(tag, handle); - - _indexToHandle.Add(slot, handle); - _handleToIndex.Add(handle, slot); - - while (prewarm) - { - if (!SlowUpdateProcesses[slot.i].MoveNext()) - { - if (_indexToHandle.ContainsKey(slot)) - KillCoroutinesOnInstance(_indexToHandle[slot]); - - prewarm = false; - } - else if (SlowUpdateProcesses[slot.i] != null && float.IsNaN(SlowUpdateProcesses[slot.i].Current)) - { - if (ReplacementFunction != null) - { - SlowUpdateProcesses[slot.i] = ReplacementFunction(SlowUpdateProcesses[slot.i], _indexToHandle[slot]); - ReplacementFunction = null; - } - prewarm = !SlowUpdatePaused[slot.i] && !SlowUpdateHeld[slot.i]; - } - else - { - prewarm = false; - } - } - - break; - - default: - handle = new CoroutineHandle(); - break; - } - - localTime = currentLocalTime; - deltaTime = currentDeltaTime; - currentCoroutine = cachedHandle; - - return handle; - } - - /// <summary> - /// This will kill all coroutines running on the main MEC instance and reset the context. - /// NOTE: If you call this function from within a running coroutine then you MUST end the current - /// coroutine. If the running coroutine has more work to do you may run a new "part 2" coroutine - /// function to complete the task before ending the current one. - /// </summary> - /// <returns>The number of coroutines that were killed.</returns> - public static int KillCoroutines() - { - return _instance == null ? 0 : _instance.KillCoroutinesOnInstance(); - } - - /// <summary> - /// This will kill all coroutines running on the current MEC instance and reset the context. - /// NOTE: If you call this function from within a running coroutine then you MUST end the current - /// coroutine. If the running coroutine has more work to do you may run a new "part 2" coroutine - /// function to complete the task before ending the current one. - /// </summary> - /// <returns>The number of coroutines that were killed.</returns> - public int KillCoroutinesOnInstance() - { - int retVal = _nextUpdateProcessSlot + _nextLateUpdateProcessSlot + _nextFixedUpdateProcessSlot + _nextSlowUpdateProcessSlot; - - UpdateProcesses = new IEnumerator<float>[InitialBufferSizeLarge]; - UpdatePaused = new bool[InitialBufferSizeLarge]; - UpdateHeld = new bool[InitialBufferSizeLarge]; - UpdateCoroutines = 0; - _nextUpdateProcessSlot = 0; - - LateUpdateProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; - LateUpdatePaused = new bool[InitialBufferSizeSmall]; - LateUpdateHeld = new bool[InitialBufferSizeSmall]; - LateUpdateCoroutines = 0; - _nextLateUpdateProcessSlot = 0; - - FixedUpdateProcesses = new IEnumerator<float>[InitialBufferSizeMedium]; - FixedUpdatePaused = new bool[InitialBufferSizeMedium]; - FixedUpdateHeld = new bool[InitialBufferSizeMedium]; - FixedUpdateCoroutines = 0; - _nextFixedUpdateProcessSlot = 0; - - SlowUpdateProcesses = new IEnumerator<float>[InitialBufferSizeMedium]; - SlowUpdatePaused = new bool[InitialBufferSizeMedium]; - SlowUpdateHeld = new bool[InitialBufferSizeMedium]; - SlowUpdateCoroutines = 0; - _nextSlowUpdateProcessSlot = 0; - - _processTags.Clear(); - _taggedProcesses.Clear(); - _handleToIndex.Clear(); - _indexToHandle.Clear(); - _waitingTriggers.Clear(); - _expansions = (ushort)((_expansions / 2) + 1); - - return retVal; - } - - /// <summary> - /// Kills the instances of the coroutine handle if it exists. - /// </summary> - /// <param name="handle">The handle of the coroutine to kill.</param> - /// <returns>The number of coroutines that were found and killed (0 or 1).</returns> - public static int KillCoroutines(CoroutineHandle handle) - { - return ActiveInstances[handle.Key] != null ? GetInstance(handle.Key).KillCoroutinesOnInstance(handle) : 0; - } - - /// <summary> - /// Kills the instance of the coroutine handle on this Timing instance if it exists. - /// </summary> - /// <param name="handle">The handle of the coroutine to kill.</param> - /// <returns>The number of coroutines that were found and killed (0 or 1).</returns> - public int KillCoroutinesOnInstance(CoroutineHandle handle) - { - bool foundOne = false; - - if (_handleToIndex.ContainsKey(handle)) - { - if (_waitingTriggers.ContainsKey(handle)) - CloseWaitingProcess(handle); - - foundOne = CoindexExtract(_handleToIndex[handle]) != null; - RemoveTag(handle); - } - - return foundOne ? 1 : 0; - } - - /// <summary> - /// Kills all coroutines that have the given tag. - /// </summary> - /// <param name="tag">All coroutines with this tag will be killed.</param> - /// <returns>The number of coroutines that were found and killed.</returns> - public static int KillCoroutines(string tag) - { - return _instance == null ? 0 : _instance.KillCoroutinesOnInstance(tag); - } - - /// <summary> - /// Kills all coroutines that have the given tag. - /// </summary> - /// <param name="tag">All coroutines with this tag will be killed.</param> - /// <returns>The number of coroutines that were found and killed.</returns> - public int KillCoroutinesOnInstance(string tag) - { - if (tag == null) return 0; - int numberFound = 0; - - while (_taggedProcesses.ContainsKey(tag)) - { - var matchEnum = _taggedProcesses[tag].GetEnumerator(); - matchEnum.MoveNext(); - - if (Nullify(_handleToIndex[matchEnum.Current])) - { - if (_waitingTriggers.ContainsKey(matchEnum.Current)) - CloseWaitingProcess(matchEnum.Current); - - numberFound++; - } - - RemoveTag(matchEnum.Current); - - if (_handleToIndex.ContainsKey(matchEnum.Current)) - { - _indexToHandle.Remove(_handleToIndex[matchEnum.Current]); - _handleToIndex.Remove(matchEnum.Current); - } - } - - return numberFound; - } - - /// <summary> - /// This will pause all coroutines running on the current MEC instance until ResumeCoroutines is called. - /// </summary> - /// <returns>The number of coroutines that were paused.</returns> - public static int PauseCoroutines() - { - return _instance == null ? 0 : _instance.PauseCoroutinesOnInstance(); - } - - /// <summary> - /// This will pause all coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. - /// </summary> - /// <returns>The number of coroutines that were paused.</returns> - public int PauseCoroutinesOnInstance() - { - int count = 0; - int i; - for (i = 0; i < _nextUpdateProcessSlot; i++) - { - if (!UpdatePaused[i] && UpdateProcesses[i] != null) - { - count++; - UpdatePaused[i] = true; - - if (UpdateProcesses[i].Current > GetSegmentTime(Segment.Update)) - UpdateProcesses[i] = _InjectDelay(UpdateProcesses[i], - UpdateProcesses[i].Current - GetSegmentTime(Segment.Update)); - } - } - - for (i = 0; i < _nextLateUpdateProcessSlot; i++) - { - if (!LateUpdatePaused[i] && LateUpdateProcesses[i] != null) - { - count++; - LateUpdatePaused[i] = true; - - if (LateUpdateProcesses[i].Current > GetSegmentTime(Segment.LateUpdate)) - LateUpdateProcesses[i] = _InjectDelay(LateUpdateProcesses[i], - LateUpdateProcesses[i].Current - GetSegmentTime(Segment.LateUpdate)); - } - } - - for (i = 0; i < _nextFixedUpdateProcessSlot; i++) - { - if (!FixedUpdatePaused[i] && FixedUpdateProcesses[i] != null) - { - count++; - FixedUpdatePaused[i] = true; - - if (FixedUpdateProcesses[i].Current > GetSegmentTime(Segment.FixedUpdate)) - FixedUpdateProcesses[i] = _InjectDelay(FixedUpdateProcesses[i], - FixedUpdateProcesses[i].Current - GetSegmentTime(Segment.FixedUpdate)); - } - } - - for (i = 0; i < _nextSlowUpdateProcessSlot; i++) - { - if (!SlowUpdatePaused[i] && SlowUpdateProcesses[i] != null) - { - count++; - SlowUpdatePaused[i] = true; - - if (SlowUpdateProcesses[i].Current > GetSegmentTime(Segment.SlowUpdate)) - SlowUpdateProcesses[i] = _InjectDelay(SlowUpdateProcesses[i], - SlowUpdateProcesses[i].Current - GetSegmentTime(Segment.SlowUpdate)); - } - } - - return count; - } - - /// <summary> - /// This will pause any matching coroutines until ResumeCoroutines is called. - /// </summary> - /// <param name="handle">The handle of the coroutine to pause.</param> - /// <returns>The number of coroutines that were paused (0 or 1).</returns> - public static int PauseCoroutines(CoroutineHandle handle) - { - return ActiveInstances[handle.Key] != null ? GetInstance(handle.Key).PauseCoroutinesOnInstance(handle) : 0; - } - - /// <summary> - /// This will pause any matching coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. - /// </summary> - /// <param name="handle">The handle of the coroutine to pause.</param> - /// <returns>The number of coroutines that were paused (0 or 1).</returns> - public int PauseCoroutinesOnInstance(CoroutineHandle handle) - { - return _handleToIndex.ContainsKey(handle) && !CoindexIsNull(_handleToIndex[handle]) && !SetPause(_handleToIndex[handle], true) ? 1 : 0; - } - - /// <summary> - /// This will pause any matching coroutines running on the current MEC instance until ResumeCoroutines is called. - /// </summary> - /// <param name="tag">Any coroutines with a matching tag will be paused.</param> - /// <returns>The number of coroutines that were paused.</returns> - public static int PauseCoroutines(string tag) - { - return _instance == null ? 0 : _instance.PauseCoroutinesOnInstance(tag); - } - - /// <summary> - /// This will pause any matching coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. - /// </summary> - /// <param name="tag">Any coroutines with a matching tag will be paused.</param> - /// <returns>The number of coroutines that were paused.</returns> - public int PauseCoroutinesOnInstance(string tag) - { - if (tag == null || !_taggedProcesses.ContainsKey(tag)) - return 0; - - int count = 0; - var matchesEnum = _taggedProcesses[tag].GetEnumerator(); - - while (matchesEnum.MoveNext()) - if (!CoindexIsNull(_handleToIndex[matchesEnum.Current]) && !SetPause(_handleToIndex[matchesEnum.Current], true)) - count++; - - return count; - } - - /// <summary> - /// This resumes all coroutines on the current MEC instance if they are currently paused, otherwise it has - /// no effect. - /// </summary> - /// <returns>The number of coroutines that were resumed.</returns> - public static int ResumeCoroutines() - { - return _instance == null ? 0 : _instance.ResumeCoroutinesOnInstance(); - } - - /// <summary> - /// This resumes all coroutines on this MEC instance if they are currently paused, otherwise it has no effect. - /// </summary> - /// <returns>The number of coroutines that were resumed.</returns> - public int ResumeCoroutinesOnInstance() - { - int count = 0; - ProcessIndex coindex; - for (coindex.i = 0, coindex.seg = Segment.Update; coindex.i < _nextUpdateProcessSlot; coindex.i++) - { - if (UpdatePaused[coindex.i] && UpdateProcesses[coindex.i] != null) - { - UpdatePaused[coindex.i] = false; - count++; - } - } - - for (coindex.i = 0, coindex.seg = Segment.LateUpdate; coindex.i < _nextLateUpdateProcessSlot; coindex.i++) - { - if (LateUpdatePaused[coindex.i] && LateUpdateProcesses[coindex.i] != null) - { - LateUpdatePaused[coindex.i] = false; - count++; - } - } - - for (coindex.i = 0, coindex.seg = Segment.FixedUpdate; coindex.i < _nextFixedUpdateProcessSlot; coindex.i++) - { - if (FixedUpdatePaused[coindex.i] && FixedUpdateProcesses[coindex.i] != null) - { - FixedUpdatePaused[coindex.i] = false; - count++; - } - } - - for (coindex.i = 0, coindex.seg = Segment.SlowUpdate; coindex.i < _nextSlowUpdateProcessSlot; coindex.i++) - { - if (SlowUpdatePaused[coindex.i] && SlowUpdateProcesses[coindex.i] != null) - { - SlowUpdatePaused[coindex.i] = false; - count++; - } - } - - return count; - } - - /// <summary> - /// This will resume any matching coroutines. - /// </summary> - /// <param name="handle">The handle of the coroutine to resume.</param> - /// <returns>The number of coroutines that were resumed (0 or 1).</returns> - public static int ResumeCoroutines(CoroutineHandle handle) - { - return ActiveInstances[handle.Key] != null ? GetInstance(handle.Key).ResumeCoroutinesOnInstance(handle) : 0; - } - - /// <summary> - /// This will resume any matching coroutines running on this MEC instance. - /// </summary> - /// <param name="handle">The handle of the coroutine to resume.</param> - /// <returns>The number of coroutines that were resumed (0 or 1).</returns> - public int ResumeCoroutinesOnInstance(CoroutineHandle handle) - { - return _handleToIndex.ContainsKey(handle) && - !CoindexIsNull(_handleToIndex[handle]) && SetPause(_handleToIndex[handle], false) ? 1 : 0; - } - - /// <summary> - /// This resumes any matching coroutines on the current MEC instance if they are currently paused, otherwise it has - /// no effect. - /// </summary> - /// <param name="tag">Any coroutines previously paused with a matching tag will be resumend.</param> - /// <returns>The number of coroutines that were resumed.</returns> - public static int ResumeCoroutines(string tag) - { - return _instance == null ? 0 : _instance.ResumeCoroutinesOnInstance(tag); - } - - /// <summary> - /// This resumes any matching coroutines on this MEC instance if they are currently paused, otherwise it has no effect. - /// </summary> - /// <param name="tag">Any coroutines previously paused with a matching tag will be resumend.</param> - /// <returns>The number of coroutines that were resumed.</returns> - public int ResumeCoroutinesOnInstance(string tag) - { - if (tag == null || !_taggedProcesses.ContainsKey(tag)) - return 0; - int count = 0; - - var indexesEnum = _taggedProcesses[tag].GetEnumerator(); - while (indexesEnum.MoveNext()) - { - if (!CoindexIsNull(_handleToIndex[indexesEnum.Current]) && SetPause(_handleToIndex[indexesEnum.Current], false)) - { - count++; - } - } - - return count; - } - - private bool UpdateTimeValues(Segment segment) - { - switch(segment) - { - case Segment.Update: - if (_currentUpdateFrame != Time.frameCount) - { - deltaTime = Time.deltaTime; - _lastUpdateTime += deltaTime; - localTime = _lastUpdateTime; - _currentUpdateFrame = Time.frameCount; - return true; - } - else - { - deltaTime = Time.deltaTime; - localTime = _lastUpdateTime; - return false; - } - case Segment.LateUpdate: - if (_currentLateUpdateFrame != Time.frameCount) - { - deltaTime = Time.deltaTime; - _lastLateUpdateTime += deltaTime; - localTime = _lastLateUpdateTime; - _currentLateUpdateFrame = Time.frameCount; - return true; - } - else - { - deltaTime = Time.deltaTime; - localTime = _lastLateUpdateTime; - return false; - } - case Segment.FixedUpdate: - deltaTime = Time.fixedDeltaTime; - localTime = Time.fixedTime; - - if (_lastFixedUpdateTime + 0.0001f < Time.fixedTime) - { - _lastFixedUpdateTime = Time.fixedTime; - return true; - } - - return false; - case Segment.SlowUpdate: - if (_currentSlowUpdateFrame != Time.frameCount) - { - deltaTime = _lastSlowUpdateDeltaTime = Time.realtimeSinceStartup - _lastSlowUpdateTime; - localTime = _lastSlowUpdateTime = Time.realtimeSinceStartup; - _currentSlowUpdateFrame = Time.frameCount; - return true; - } - else - { - deltaTime = _lastSlowUpdateDeltaTime; - localTime = _lastSlowUpdateTime; - return false; - } - } - return true; - } - - private float GetSegmentTime(Segment segment) - { - switch (segment) - { - case Segment.Update: - if (_currentUpdateFrame == Time.frameCount) - return _lastUpdateTime; - else - return _lastUpdateTime + Time.deltaTime; - case Segment.LateUpdate: - if (_currentUpdateFrame == Time.frameCount) - return _lastLateUpdateTime; - else - return _lastLateUpdateTime + Time.deltaTime; - case Segment.FixedUpdate: - return Time.fixedTime; - case Segment.SlowUpdate: - return Time.realtimeSinceStartup; - default: - return 0f; - } - } - - /// <summary> - /// Retrieves the MEC manager that corresponds to the supplied instance id. - /// </summary> - /// <param name="ID">The instance ID.</param> - /// <returns>The manager, or null if not found.</returns> - public static Timing GetInstance(byte ID) - { - if (ID >= 0x10) - return null; - return ActiveInstances[ID]; - } - - private void AddTag(string tag, CoroutineHandle coindex) - { - _processTags.Add(coindex, tag); - - if (_taggedProcesses.ContainsKey(tag)) - _taggedProcesses[tag].Add(coindex); - else - _taggedProcesses.Add(tag, new HashSet<CoroutineHandle> { coindex }); - } - - private void RemoveTag(CoroutineHandle coindex) - { - if (_processTags.ContainsKey(coindex)) - { - if (_taggedProcesses[_processTags[coindex]].Count > 1) - _taggedProcesses[_processTags[coindex]].Remove(coindex); - else - _taggedProcesses.Remove(_processTags[coindex]); - - _processTags.Remove(coindex); - } - } - - /// <returns>Whether it was already null.</returns> - private bool Nullify(ProcessIndex coindex) - { - bool retVal; - - switch (coindex.seg) - { - case Segment.Update: - retVal = UpdateProcesses[coindex.i] != null; - UpdateProcesses[coindex.i] = null; - return retVal; - case Segment.FixedUpdate: - retVal = FixedUpdateProcesses[coindex.i] != null; - FixedUpdateProcesses[coindex.i] = null; - return retVal; - case Segment.LateUpdate: - retVal = LateUpdateProcesses[coindex.i] != null; - LateUpdateProcesses[coindex.i] = null; - return retVal; - case Segment.SlowUpdate: - retVal = SlowUpdateProcesses[coindex.i] != null; - SlowUpdateProcesses[coindex.i] = null; - return retVal; - default: - return false; - } - } - - private IEnumerator<float> CoindexExtract(ProcessIndex coindex) - { - IEnumerator<float> retVal; - - switch (coindex.seg) - { - case Segment.Update: - retVal = UpdateProcesses[coindex.i]; - UpdateProcesses[coindex.i] = null; - return retVal; - case Segment.FixedUpdate: - retVal = FixedUpdateProcesses[coindex.i]; - FixedUpdateProcesses[coindex.i] = null; - return retVal; - case Segment.LateUpdate: - retVal = LateUpdateProcesses[coindex.i]; - LateUpdateProcesses[coindex.i] = null; - return retVal; - case Segment.SlowUpdate: - retVal = SlowUpdateProcesses[coindex.i]; - SlowUpdateProcesses[coindex.i] = null; - return retVal; - default: - return null; - } - } - - private IEnumerator<float> CoindexPeek(ProcessIndex coindex) - { - switch (coindex.seg) - { - case Segment.Update: - return UpdateProcesses[coindex.i]; - case Segment.FixedUpdate: - return FixedUpdateProcesses[coindex.i]; - case Segment.LateUpdate: - return LateUpdateProcesses[coindex.i]; - case Segment.SlowUpdate: - return SlowUpdateProcesses[coindex.i]; - default: - return null; - } - } - - private bool CoindexIsNull(ProcessIndex coindex) - { - switch (coindex.seg) - { - case Segment.Update: - return UpdateProcesses[coindex.i] == null; - case Segment.FixedUpdate: - return FixedUpdateProcesses[coindex.i] == null; - case Segment.LateUpdate: - return LateUpdateProcesses[coindex.i] == null; - case Segment.SlowUpdate: - return SlowUpdateProcesses[coindex.i] == null; - default: - return true; - } - } - - private bool SetPause(ProcessIndex coindex, bool newPausedState) - { - if (CoindexPeek(coindex) == null) - return false; - - bool isPaused; - - switch (coindex.seg) - { - case Segment.Update: - isPaused = UpdatePaused[coindex.i]; - UpdatePaused[coindex.i] = newPausedState; - - if (newPausedState && UpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) - UpdateProcesses[coindex.i] = _InjectDelay(UpdateProcesses[coindex.i], - UpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); - - return isPaused; - case Segment.FixedUpdate: - isPaused = FixedUpdatePaused[coindex.i]; - FixedUpdatePaused[coindex.i] = newPausedState; - - if (newPausedState && FixedUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) - FixedUpdateProcesses[coindex.i] = _InjectDelay(FixedUpdateProcesses[coindex.i], - FixedUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); - - return isPaused; - case Segment.LateUpdate: - isPaused = LateUpdatePaused[coindex.i]; - LateUpdatePaused[coindex.i] = newPausedState; - - if (newPausedState && LateUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) - LateUpdateProcesses[coindex.i] = _InjectDelay(LateUpdateProcesses[coindex.i], - LateUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); - - return isPaused; - case Segment.SlowUpdate: - isPaused = SlowUpdatePaused[coindex.i]; - SlowUpdatePaused[coindex.i] = newPausedState; - - if (newPausedState && SlowUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) - SlowUpdateProcesses[coindex.i] = _InjectDelay(SlowUpdateProcesses[coindex.i], - SlowUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); - - return isPaused; - default: - return false; - } - } - - private bool SetHeld(ProcessIndex coindex, bool newHeldState) - { - if (CoindexPeek(coindex) == null) - return false; - - bool isHeld; - - switch (coindex.seg) - { - case Segment.Update: - isHeld = UpdateHeld[coindex.i]; - UpdateHeld[coindex.i] = newHeldState; - - if (newHeldState && UpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) - UpdateProcesses[coindex.i] = _InjectDelay(UpdateProcesses[coindex.i], - UpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); - - return isHeld; - case Segment.FixedUpdate: - isHeld = FixedUpdateHeld[coindex.i]; - FixedUpdateHeld[coindex.i] = newHeldState; - - if (newHeldState && FixedUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) - FixedUpdateProcesses[coindex.i] = _InjectDelay(FixedUpdateProcesses[coindex.i], - FixedUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); - - return isHeld; - case Segment.LateUpdate: - isHeld = LateUpdateHeld[coindex.i]; - LateUpdateHeld[coindex.i] = newHeldState; - - if (newHeldState && LateUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) - LateUpdateProcesses[coindex.i] = _InjectDelay(LateUpdateProcesses[coindex.i], - LateUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); - - return isHeld; - case Segment.SlowUpdate: - isHeld = SlowUpdateHeld[coindex.i]; - SlowUpdateHeld[coindex.i] = newHeldState; - - if (newHeldState && SlowUpdateProcesses[coindex.i].Current > GetSegmentTime(coindex.seg)) - SlowUpdateProcesses[coindex.i] = _InjectDelay(SlowUpdateProcesses[coindex.i], - SlowUpdateProcesses[coindex.i].Current - GetSegmentTime(coindex.seg)); - - return isHeld; - default: - return false; - } - } - - private IEnumerator<float> _InjectDelay(IEnumerator<float> proc, float delayTime) - { - yield return WaitForSecondsOnInstance(delayTime); - - _tmpRef = proc; - ReplacementFunction = ReturnTmpRefForRepFunc; - yield return float.NaN; - } - - private bool CoindexIsPaused(ProcessIndex coindex) - { - switch (coindex.seg) - { - case Segment.Update: - return UpdatePaused[coindex.i]; - case Segment.FixedUpdate: - return FixedUpdatePaused[coindex.i]; - case Segment.LateUpdate: - return LateUpdatePaused[coindex.i]; - case Segment.SlowUpdate: - return SlowUpdatePaused[coindex.i]; - default: - return false; - } - } - - private bool CoindexIsHeld(ProcessIndex coindex) - { - switch (coindex.seg) - { - case Segment.Update: - return UpdateHeld[coindex.i]; - case Segment.FixedUpdate: - return FixedUpdateHeld[coindex.i]; - case Segment.LateUpdate: - return LateUpdateHeld[coindex.i]; - case Segment.SlowUpdate: - return SlowUpdateHeld[coindex.i]; - default: - return false; - } - } - - private void CoindexReplace(ProcessIndex coindex, IEnumerator<float> replacement) - { - switch (coindex.seg) - { - case Segment.Update: - UpdateProcesses[coindex.i] = replacement; - return; - case Segment.FixedUpdate: - FixedUpdateProcesses[coindex.i] = replacement; - return; - case Segment.LateUpdate: - LateUpdateProcesses[coindex.i] = replacement; - return; - case Segment.SlowUpdate: - SlowUpdateProcesses[coindex.i] = replacement; - return; - } - } - - /// <summary> - /// Use "yield return Timing.WaitForSeconds(time);" to wait for the specified number of seconds. - /// </summary> - /// <param name="waitTime">Number of seconds to wait.</param> - public static float WaitForSeconds(float waitTime) - { - if (float.IsNaN(waitTime)) waitTime = 0f; - return LocalTime + waitTime; - } - - /// <summary> - /// Use "yield return timingInstance.WaitForSecondsOnInstance(time);" to wait for the specified number of seconds. - /// </summary> - /// <param name="waitTime">Number of seconds to wait.</param> - public float WaitForSecondsOnInstance(float waitTime) - { - if (float.IsNaN(waitTime)) waitTime = 0f; - return localTime + waitTime; - } - - /// <summary> - /// Use the command "yield return Timing.WaitUntilDone(otherCoroutine);" to pause the current - /// coroutine until otherCoroutine is done. - /// </summary> - /// <param name="otherCoroutine">The coroutine to pause for.</param> - public static float WaitUntilDone(CoroutineHandle otherCoroutine) - { - return WaitUntilDone(otherCoroutine, true); - } - - /// <summary> - /// Use the command "yield return Timing.WaitUntilDone(otherCoroutine, false);" to pause the current - /// coroutine until otherCoroutine is done, supressing warnings. - /// </summary> - /// <param name="otherCoroutine">The coroutine to pause for.</param> - /// <param name="warnOnIssue">Post a warning to the console if no hold action was actually performed.</param> - public static float WaitUntilDone(CoroutineHandle otherCoroutine, bool warnOnIssue) - { - Timing inst = GetInstance(otherCoroutine.Key); - - if (inst != null && inst._handleToIndex.ContainsKey(otherCoroutine)) - { - if (inst.CoindexIsNull(inst._handleToIndex[otherCoroutine])) - return 0f; - - if (!inst._waitingTriggers.ContainsKey(otherCoroutine)) - { - inst.CoindexReplace(inst._handleToIndex[otherCoroutine], - inst._StartWhenDone(otherCoroutine, inst.CoindexPeek(inst._handleToIndex[otherCoroutine]))); - inst._waitingTriggers.Add(otherCoroutine, new HashSet<CoroutineHandle>()); - } - - if (inst.currentCoroutine == otherCoroutine) - { - Assert.IsFalse(warnOnIssue, "A coroutine cannot wait for itself."); - return WaitForOneFrame; - } - if (!inst.currentCoroutine.IsValid) - { - Assert.IsFalse(warnOnIssue, "The two coroutines are not running on the same MEC instance."); - return WaitForOneFrame; - } - - inst._waitingTriggers[otherCoroutine].Add(inst.currentCoroutine); - if (!inst._allWaiting.Contains(inst.currentCoroutine)) - inst._allWaiting.Add(inst.currentCoroutine); - inst.SetHeld(inst._handleToIndex[inst.currentCoroutine], true); - inst.SwapToLast(otherCoroutine, inst.currentCoroutine); - - return float.NaN; - } - - Assert.IsFalse(warnOnIssue, "WaitUntilDone cannot hold: The coroutine handle that was passed in is invalid.\n" + otherCoroutine); - return WaitForOneFrame; - } - - private IEnumerator<float> _StartWhenDone(CoroutineHandle handle, IEnumerator<float> proc) - { - if (!_waitingTriggers.ContainsKey(handle)) yield break; - - try - { - if (proc.Current > localTime) - yield return proc.Current; - - while (proc.MoveNext()) - yield return proc.Current; - } - finally - { - CloseWaitingProcess(handle); - } - } - - private void SwapToLast(CoroutineHandle firstHandle, CoroutineHandle lastHandle) - { - if (firstHandle.Key != lastHandle.Key) - return; - - ProcessIndex firstIndex = _handleToIndex[firstHandle]; - ProcessIndex lastIndex = _handleToIndex[lastHandle]; - - if (firstIndex.seg != lastIndex.seg || firstIndex.i < lastIndex.i) - return; - - IEnumerator<float> tempCoptr = CoindexPeek(firstIndex); - CoindexReplace(firstIndex, CoindexPeek(lastIndex)); - CoindexReplace(lastIndex, tempCoptr); - - _indexToHandle[firstIndex] = lastHandle; - _indexToHandle[lastIndex] = firstHandle; - _handleToIndex[firstHandle] = lastIndex; - _handleToIndex[lastHandle] = firstIndex; - bool tmpB = SetPause(firstIndex, CoindexIsPaused(lastIndex)); - SetPause(lastIndex, tmpB); - tmpB = SetHeld(firstIndex, CoindexIsHeld(lastIndex)); - SetHeld(lastIndex, tmpB); - - if (_waitingTriggers.ContainsKey(lastHandle)) - { - var trigsEnum = _waitingTriggers[lastHandle].GetEnumerator(); - while (trigsEnum.MoveNext()) - SwapToLast(lastHandle, trigsEnum.Current); - } - - if (_allWaiting.Contains(firstHandle)) - { - var keyEnum = _waitingTriggers.GetEnumerator(); - while (keyEnum.MoveNext()) - { - var valueEnum = keyEnum.Current.Value.GetEnumerator(); - while (valueEnum.MoveNext()) - if (valueEnum.Current == firstHandle) - SwapToLast(keyEnum.Current.Key, firstHandle); - } - } - } - - private void CloseWaitingProcess(CoroutineHandle handle) - { - if (!_waitingTriggers.ContainsKey(handle)) return; - - var tasksEnum = _waitingTriggers[handle].GetEnumerator(); - _waitingTriggers.Remove(handle); - - while (tasksEnum.MoveNext()) - { - if (_handleToIndex.ContainsKey(tasksEnum.Current) && !HandleIsInWaitingList(tasksEnum.Current)) - { - SetHeld(_handleToIndex[tasksEnum.Current], false); - _allWaiting.Remove(tasksEnum.Current); - } - } - } - - private bool HandleIsInWaitingList(CoroutineHandle handle) - { - var triggersEnum = _waitingTriggers.GetEnumerator(); - while (triggersEnum.MoveNext()) - if (triggersEnum.Current.Value.Contains(handle)) - return true; - - return false; - } - - private static IEnumerator<float> ReturnTmpRefForRepFunc(IEnumerator<float> coptr, CoroutineHandle handle) - { - return _tmpRef as IEnumerator<float>; - } - -#if !UNITY_2018_3_OR_NEWER - /// <summary> - /// Use the command "yield return Timing.WaitUntilDone(wwwObject);" to pause the current - /// coroutine until the wwwObject is done. - /// </summary> - /// <param name="wwwObject">The www object to pause for.</param> - public static float WaitUntilDone(WWW wwwObject) - { - if (wwwObject == null || wwwObject.isDone) return 0f; - - _tmpRef = wwwObject; - ReplacementFunction = WaitUntilDoneWwwHelper; - return float.NaN; - } - - - private static IEnumerator<float> WaitUntilDoneWwwHelper(IEnumerator<float> coptr, CoroutineHandle handle) - { - return _StartWhenDone(_tmpRef as WWW, coptr); - } - - private static IEnumerator<float> _StartWhenDone(WWW www, IEnumerator<float> pausedProc) - { - while (!www.isDone) - yield return WaitForOneFrame; - - _tmpRef = pausedProc; - ReplacementFunction = ReturnTmpRefForRepFunc; - yield return float.NaN; - } -#endif - - /// <summary> - /// Use the command "yield return Timing.WaitUntilDone(operation);" to pause the current - /// coroutine until the operation is done. - /// </summary> - /// <param name="operation">The operation variable returned.</param> - public static float WaitUntilDone(AsyncOperation operation) - { - if (operation == null || operation.isDone) return float.NaN; - - CoroutineHandle handle = CurrentCoroutine; - Timing inst = GetInstance(CurrentCoroutine.Key); - if (inst == null) return float.NaN; - - _tmpRef = _StartWhenDone(operation, inst.CoindexPeek(inst._handleToIndex[handle])); - ReplacementFunction = ReturnTmpRefForRepFunc; - return float.NaN; - } - - private static IEnumerator<float> _StartWhenDone(AsyncOperation operation, IEnumerator<float> pausedProc) - { - while (!operation.isDone) - yield return WaitForOneFrame; - - _tmpRef = pausedProc; - ReplacementFunction = ReturnTmpRefForRepFunc; - yield return float.NaN; - } - - /// <summary> - /// Use the command "yield return Timing.WaitUntilDone(operation);" to pause the current - /// coroutine until the operation is done. - /// </summary> - /// <param name="operation">The operation variable returned.</param> - public static float WaitUntilDone(CustomYieldInstruction operation) - { - if (operation == null || !operation.keepWaiting) return float.NaN; - - CoroutineHandle handle = CurrentCoroutine; - Timing inst = GetInstance(CurrentCoroutine.Key); - if (inst == null) return float.NaN; - - _tmpRef = _StartWhenDone(operation, inst.CoindexPeek(inst._handleToIndex[handle])); - ReplacementFunction = ReturnTmpRefForRepFunc; - return float.NaN; - } - - private static IEnumerator<float> _StartWhenDone(CustomYieldInstruction operation, IEnumerator<float> pausedProc) - { - while (operation.keepWaiting) - yield return WaitForOneFrame; - - _tmpRef = pausedProc; - ReplacementFunction = ReturnTmpRefForRepFunc; - yield return float.NaN; - } - - /// <summary> - /// Keeps this coroutine from executing until UnlockCoroutine is called with a matching key. - /// </summary> - /// <param name="coroutine">The handle to the coroutine to be locked.</param> - /// <param name="key">The key to use. A new key can be generated by calling "new CoroutineHandle(0)".</param> - /// <returns>Whether the lock was successful.</returns> - public bool LockCoroutine(CoroutineHandle coroutine, CoroutineHandle key) - { - if (coroutine.Key != _instanceID || key == new CoroutineHandle() || key.Key != 0) - return false; - - if (!_waitingTriggers.ContainsKey(key)) - _waitingTriggers.Add(key, new HashSet<CoroutineHandle> { coroutine }); - else - _waitingTriggers[key].Add(coroutine); - - _allWaiting.Add(coroutine); - - SetHeld(_handleToIndex[coroutine], true); - - return true; - } - - /// <summary> - /// Unlocks a coroutine that has been locked, so long as the key matches. - /// </summary> - /// <param name="coroutine">The handle to the coroutine to be unlocked.</param> - /// <param name="key">The key that the coroutine was previously locked with.</param> - /// <returns>Whether the coroutine was successfully unlocked.</returns> - public bool UnlockCoroutine(CoroutineHandle coroutine, CoroutineHandle key) - { - if (coroutine.Key != _instanceID || key == new CoroutineHandle() || - !_handleToIndex.ContainsKey(coroutine) || !_waitingTriggers.ContainsKey(key)) - return false; - - if (_waitingTriggers[key].Count == 1) - _waitingTriggers.Remove(key); - else - _waitingTriggers[key].Remove(coroutine); - - if (!HandleIsInWaitingList(coroutine)) - { - SetHeld(_handleToIndex[coroutine], false); - _allWaiting.Remove(coroutine); - } - - return true; - } - - /// <summary> - /// Calls the specified action after a specified number of seconds. - /// </summary> - /// <param name="delay">The number of seconds to wait before calling the action.</param> - /// <param name="action">The action to call.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public static CoroutineHandle CallDelayed(float delay, System.Action action) - { - return action == null ? new CoroutineHandle() : RunCoroutine(Instance._DelayedCall(delay, action, null)); - } - - /// <summary> - /// Calls the specified action after a specified number of seconds. - /// </summary> - /// <param name="delay">The number of seconds to wait before calling the action.</param> - /// <param name="action">The action to call.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public CoroutineHandle CallDelayedOnInstance(float delay, System.Action action) - { - return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_DelayedCall(delay, action, null)); - } - - /// <summary> - /// Calls the specified action after a specified number of seconds. - /// </summary> - /// <param name="delay">The number of seconds to wait before calling the action.</param> - /// <param name="action">The action to call.</param> - /// <param name="cancelWith">A GameObject that will be checked to make sure it hasn't been destroyed before calling the action.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public static CoroutineHandle CallDelayed(float delay, System.Action action, GameObject cancelWith) - { - return action == null ? new CoroutineHandle() : RunCoroutine(Instance._DelayedCall(delay, action, cancelWith)); - } - - /// <summary> - /// Calls the specified action after a specified number of seconds. - /// </summary> - /// <param name="delay">The number of seconds to wait before calling the action.</param> - /// <param name="action">The action to call.</param> - /// <param name="cancelWith">A GameObject that will be checked to make sure it hasn't been destroyed before calling the action.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public CoroutineHandle CallDelayedOnInstance(float delay, System.Action action, GameObject cancelWith) - { - return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_DelayedCall(delay, action, cancelWith)); - } - - /// <summary> - /// Calls the specified action after a specified number of seconds. - /// </summary> - /// <param name="delay">The number of seconds to wait before calling the action.</param> - /// <param name="action">The action to call.</param> - /// <param name="segment">The timing segment that the call should be made in.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public static CoroutineHandle CallDelayed(float delay, Segment segment, System.Action action) - { - return action == null ? new CoroutineHandle() : RunCoroutine(Instance._DelayedCall(delay, action, null), segment); - } - - /// <summary> - /// Calls the specified action after a specified number of seconds. - /// </summary> - /// <param name="delay">The number of seconds to wait before calling the action.</param> - /// <param name="action">The action to call.</param> - /// <param name="segment">The timing segment that the call should be made in.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public CoroutineHandle CallDelayedOnInstance(float delay, Segment segment, System.Action action) - { - return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_DelayedCall(delay, action, null), segment); - } - - /// <summary> - /// Calls the specified action after a specified number of seconds. - /// </summary> - /// <param name="delay">The number of seconds to wait before calling the action.</param> - /// <param name="action">The action to call.</param> - /// <param name="gameObject">A GameObject that will be checked to make sure it hasn't been destroyed - /// before calling the action.</param> - /// <param name="segment">The timing segment that the call should be made in.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public static CoroutineHandle CallDelayed(float delay, Segment segment, System.Action action, GameObject gameObject) - { - return action == null ? new CoroutineHandle() : RunCoroutine(Instance._DelayedCall(delay, action, gameObject), segment); - } - - /// <summary> - /// Calls the specified action after a specified number of seconds. - /// </summary> - /// <param name="delay">The number of seconds to wait before calling the action.</param> - /// <param name="action">The action to call.</param> - /// <param name="gameObject">A GameObject that will be tagged onto the coroutine and checked to make sure it hasn't been destroyed - /// before calling the action.</param> - /// <param name="segment">The timing segment that the call should be made in.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public CoroutineHandle CallDelayedOnInstance(float delay, Segment segment, System.Action action, GameObject gameObject) - { - return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_DelayedCall(delay, action, gameObject), segment); - } - - private IEnumerator<float> _DelayedCall(float delay, System.Action action, GameObject cancelWith) - { - yield return WaitForSecondsOnInstance(delay); - - if(ReferenceEquals(cancelWith, null) || cancelWith != null) - action(); - } - - /// <summary> - /// Calls the supplied action at the given rate for a given number of seconds. - /// </summary> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="period">The amount of time between calls.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public static CoroutineHandle CallPeriodically(float timeframe, float period, System.Action action, System.Action onDone = null) - { - return action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(timeframe, period, action, onDone), Segment.Update); - } - - /// <summary> - /// Calls the supplied action at the given rate for a given number of seconds. - /// </summary> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="period">The amount of time between calls.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public CoroutineHandle CallPeriodicallyOnInstance(float timeframe, float period, System.Action action, System.Action onDone = null) - { - return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(timeframe, period, action, onDone), Segment.Update); - } - - /// <summary> - /// Calls the supplied action at the given rate for a given number of seconds. - /// </summary> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="period">The amount of time between calls.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="segment">The timing segment to run in.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public static CoroutineHandle CallPeriodically(float timeframe, float period, System.Action action, Segment segment, System.Action onDone = null) - { - return action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(timeframe, period, action, onDone), segment); - } - - /// <summary> - /// Calls the supplied action at the given rate for a given number of seconds. - /// </summary> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="period">The amount of time between calls.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="segment">The timing segment to run in.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public CoroutineHandle CallPeriodicallyOnInstance(float timeframe, float period, System.Action action, Segment segment, System.Action onDone = null) - { - return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(timeframe, period, action, onDone), segment); - } - - /// <summary> - /// Calls the supplied action at the given rate for a given number of seconds. - /// </summary> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public static CoroutineHandle CallContinuously(float timeframe, System.Action action, System.Action onDone = null) - { - return action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(timeframe, 0f, action, onDone), Segment.Update); - } - - /// <summary> - /// Calls the supplied action at the given rate for a given number of seconds. - /// </summary> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public CoroutineHandle CallContinuouslyOnInstance(float timeframe, System.Action action, System.Action onDone = null) - { - return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(timeframe, 0f, action, onDone), Segment.Update); - } - - /// <summary> - /// Calls the supplied action every frame for a given number of seconds. - /// </summary> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="timing">The timing segment to run in.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public static CoroutineHandle CallContinuously(float timeframe, System.Action action, Segment timing, System.Action onDone = null) - { - return action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(timeframe, 0f, action, onDone), timing); - } - - /// <summary> - /// Calls the supplied action every frame for a given number of seconds. - /// </summary> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="timing">The timing segment to run in.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public CoroutineHandle CallContinuouslyOnInstance(float timeframe, System.Action action, Segment timing, System.Action onDone = null) - { - return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(timeframe, 0f, action, onDone), timing); - } - - private IEnumerator<float> _CallContinuously(float timeframe, float period, System.Action action, System.Action onDone) - { - double startTime = localTime; - while (localTime <= startTime + timeframe) - { - yield return WaitForSecondsOnInstance(period); - - action(); - } - - if (onDone != null) - onDone(); - } - - /// <summary> - /// Calls the supplied action at the given rate for a given number of seconds. - /// </summary> - /// <param name="reference">A value that will be passed in to the supplied action each period.</param> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="period">The amount of time between calls.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public static CoroutineHandle CallPeriodically<T> - (T reference, float timeframe, float period, System.Action<T> action, System.Action<T> onDone = null) - { - return action == null ? new CoroutineHandle() : - RunCoroutine(Instance._CallContinuously(reference, timeframe, period, action, onDone), Segment.Update); - } - - /// <summary> - /// Calls the supplied action at the given rate for a given number of seconds. - /// </summary> - /// <param name="reference">A value that will be passed in to the supplied action each period.</param> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="period">The amount of time between calls.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public CoroutineHandle CallPeriodicallyOnInstance<T> - (T reference, float timeframe, float period, System.Action<T> action, System.Action<T> onDone = null) - { - return action == null ? new CoroutineHandle() : - RunCoroutineOnInstance(_CallContinuously(reference, timeframe, period, action, onDone), Segment.Update); - } - - /// <summary> - /// Calls the supplied action at the given rate for a given number of seconds. - /// </summary> - /// <param name="reference">A value that will be passed in to the supplied action each period.</param> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="period">The amount of time between calls.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="timing">The timing segment to run in.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public static CoroutineHandle CallPeriodically<T>(T reference, float timeframe, float period, System.Action<T> action, - Segment timing, System.Action<T> onDone = null) - { - return action == null ? new CoroutineHandle() : - RunCoroutine(Instance._CallContinuously(reference, timeframe, period, action, onDone), timing); - } - - /// <summary> - /// Calls the supplied action at the given rate for a given number of seconds. - /// </summary> - /// <param name="reference">A value that will be passed in to the supplied action each period.</param> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="period">The amount of time between calls.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="timing">The timing segment to run in.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public CoroutineHandle CallPeriodicallyOnInstance<T>(T reference, float timeframe, float period, System.Action<T> action, - Segment timing, System.Action<T> onDone = null) - { - return action == null ? new CoroutineHandle() : - RunCoroutineOnInstance(_CallContinuously(reference, timeframe, period, action, onDone), timing); - } - - /// <summary> - /// Calls the supplied action every frame for a given number of seconds. - /// </summary> - /// <param name="reference">A value that will be passed in to the supplied action each frame.</param> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public static CoroutineHandle CallContinuously<T>(T reference, float timeframe, System.Action<T> action, System.Action<T> onDone = null) - { - return action == null ? new CoroutineHandle() : - RunCoroutine(Instance._CallContinuously(reference, timeframe, 0f, action, onDone), Segment.Update); - } - - /// <summary> - /// Calls the supplied action every frame for a given number of seconds. - /// </summary> - /// <param name="reference">A value that will be passed in to the supplied action each frame.</param> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public CoroutineHandle CallContinuouslyOnInstance<T>(T reference, float timeframe, System.Action<T> action, System.Action<T> onDone = null) - { - return action == null ? new CoroutineHandle() : - RunCoroutineOnInstance(_CallContinuously(reference, timeframe, 0f, action, onDone), Segment.Update); - } - - /// <summary> - /// Calls the supplied action every frame for a given number of seconds. - /// </summary> - /// <param name="reference">A value that will be passed in to the supplied action each frame.</param> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="timing">The timing segment to run in.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public static CoroutineHandle CallContinuously<T>(T reference, float timeframe, System.Action<T> action, - Segment timing, System.Action<T> onDone = null) - { - return action == null ? new CoroutineHandle() : - RunCoroutine(Instance._CallContinuously(reference, timeframe, 0f, action, onDone), timing); - } - - /// <summary> - /// Calls the supplied action every frame for a given number of seconds. - /// </summary> - /// <param name="reference">A value that will be passed in to the supplied action each frame.</param> - /// <param name="timeframe">The number of seconds that this function should run.</param> - /// <param name="action">The action to call every frame.</param> - /// <param name="timing">The timing segment to run in.</param> - /// <param name="onDone">An optional action to call when this function finishes.</param> - /// <returns>The handle to the coroutine that is started by this function.</returns> - public CoroutineHandle CallContinuouslyOnInstance<T>(T reference, float timeframe, System.Action<T> action, - Segment timing, System.Action<T> onDone = null) - { - return action == null ? new CoroutineHandle() : - RunCoroutineOnInstance(_CallContinuously(reference, timeframe, 0f, action, onDone), timing); - } - - private IEnumerator<float> _CallContinuously<T>(T reference, float timeframe, float period, - System.Action<T> action, System.Action<T> onDone = null) - { - double startTime = localTime; - while (localTime <= startTime + timeframe) - { - yield return WaitForSecondsOnInstance(period); - - action(reference); - } - - if (onDone != null) - onDone(reference); - } - - private struct ProcessIndex : System.IEquatable<ProcessIndex> - { - public Segment seg; - public int i; - - public bool Equals(ProcessIndex other) - { - return seg == other.seg && i == other.i; - } - - public override bool Equals(object other) - { - if (other is ProcessIndex) - return Equals((ProcessIndex)other); - return false; - } - - public static bool operator ==(ProcessIndex a, ProcessIndex b) - { - return a.seg == b.seg && a.i == b.i; - } - - public static bool operator !=(ProcessIndex a, ProcessIndex b) - { - return a.seg != b.seg || a.i != b.i; - } - - public override int GetHashCode() - { - return (((int)seg - 2) * (int.MaxValue / 3)) + i; - } - } - - [System.Obsolete("Unity coroutine function, use RunCoroutine instead.", true)] - public new Coroutine StartCoroutine(System.Collections.IEnumerator routine) { return null; } - - [System.Obsolete("Unity coroutine function, use RunCoroutine instead.", true)] - public new Coroutine StartCoroutine(string methodName, object value) { return null; } - - [System.Obsolete("Unity coroutine function, use RunCoroutine instead.", true)] - public new Coroutine StartCoroutine(string methodName) { return null; } - - [System.Obsolete("Unity coroutine function, use RunCoroutine instead.", true)] - public new Coroutine StartCoroutine_Auto(System.Collections.IEnumerator routine) { return null; } - - [System.Obsolete("Unity coroutine function, use KillCoroutines instead.", true)] - public new void StopCoroutine(string methodName) { } - - [System.Obsolete("Unity coroutine function, use KillCoroutines instead.", true)] - public new void StopCoroutine(System.Collections.IEnumerator routine) { } - - [System.Obsolete("Unity coroutine function, use KillCoroutines instead.", true)] - public new void StopCoroutine(Coroutine routine) { } - - [System.Obsolete("Unity coroutine function, use KillCoroutines instead.", true)] - public new void StopAllCoroutines() { } - - [System.Obsolete("Use your own GameObject for this.", true)] - public new static void Destroy(Object obj) { } - - [System.Obsolete("Use your own GameObject for this.", true)] - public new static void Destroy(Object obj, float f) { } - - [System.Obsolete("Use your own GameObject for this.", true)] - public new static void DestroyObject(Object obj) { } - - [System.Obsolete("Use your own GameObject for this.", true)] - public new static void DestroyObject(Object obj, float f) { } - - [System.Obsolete("Use your own GameObject for this.", true)] - public new static void DestroyImmediate(Object obj) { } - - [System.Obsolete("Use your own GameObject for this.", true)] - public new static void DestroyImmediate(Object obj, bool b) { } - - [System.Obsolete("Use your own GameObject for this.", true)] - public new static void Instantiate(Object obj) { } - - [System.Obsolete("Use your own GameObject for this.", true)] - public new static void Instantiate(Object original, Vector3 position, Quaternion rotation) { } - - [System.Obsolete("Use your own GameObject for this.", true)] - public new static void Instantiate<T>(T original) where T : Object { } - - [System.Obsolete("Just.. no.", true)] - public new static T FindObjectOfType<T>() where T : Object { return null; } - - [System.Obsolete("Just.. no.", true)] - public new static Object FindObjectOfType(System.Type t) { return null; } - - [System.Obsolete("Just.. no.", true)] - public new static T[] FindObjectsOfType<T>() where T : Object { return null; } - - [System.Obsolete("Just.. no.", true)] - public new static Object[] FindObjectsOfType(System.Type t) { return null; } - - [System.Obsolete("Just.. no.", true)] - public new static void print(object message) { } - } - - /// <summary> - /// The timing segment that a coroutine is running in or should be run in. - /// </summary> - public enum Segment - { - /// <summary> - /// Sometimes returned as an error state - /// </summary> - Invalid = -1, - /// <summary> - /// This is the default timing segment - /// </summary> - Update, - /// <summary> - /// This is primarily used for physics calculations - /// </summary> - FixedUpdate, - /// <summary> - /// This is run immediately after update - /// </summary> - LateUpdate, - /// <summary> - /// This executes, by default, about as quickly as the eye can detect changes in a text field - /// </summary> - SlowUpdate - } - - /// <summary> - /// How much debug info should be sent to the Unity profiler. NOTE: Setting this to anything above none shows up in the profiler as a - /// decrease in performance and a memory alloc. Those effects do not translate onto device. - /// </summary> - public enum DebugInfoType - { - /// <summary> - /// None coroutines will be separated in the Unity profiler - /// </summary> - None, - /// <summary> - /// The Unity profiler will identify each coroutine individually - /// </summary> - SeperateCoroutines, - /// <summary> - /// Coroutines will be separated and any tags or layers will be identified - /// </summary> - SeperateTags - } - - /// <summary> - /// A handle for a MEC coroutine. - /// </summary> - public struct CoroutineHandle : System.IEquatable<CoroutineHandle> - { - private const byte ReservedSpace = 0x0F; - private readonly static int[] NextIndex = { ReservedSpace + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - private readonly int _id; - - public byte Key { get { return (byte)(_id & ReservedSpace); } } - - public CoroutineHandle(byte ind) - { - if (ind > ReservedSpace) - ind -= ReservedSpace; - - _id = NextIndex[ind] + ind; - NextIndex[ind] += ReservedSpace + 1; - } - - public bool Equals(CoroutineHandle other) - { - return _id == other._id; - } - - public override bool Equals(object other) - { - if (other is CoroutineHandle) - return Equals((CoroutineHandle)other); - return false; - } - - public static bool operator==(CoroutineHandle a, CoroutineHandle b) - { - return a._id == b._id; - } - - public static bool operator!=(CoroutineHandle a, CoroutineHandle b) - { - return a._id != b._id; - } - - public override int GetHashCode() - { - return _id; - } - - /// <summary> - /// Is true if this handle may have been a valid handle at some point. (i.e. is not an uninitialized handle, error handle, or a key to a coroutine lock) - /// </summary> - public bool IsValid - { - get { return Key != 0; } - } - } - - public static class MECExtensionMethods1 - { - /// <summary> - /// Run a new coroutine in the Update segment. - /// </summary> - /// <param name="coroutine">The new coroutine's handle.</param> - /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> - public static CoroutineHandle RunCoroutine(this IEnumerator<float> coroutine) - { - return Timing.RunCoroutine(coroutine); - } - - /// <summary> - /// Run a new coroutine in the Update segment. - /// </summary> - /// <param name="coroutine">The new coroutine's handle.</param> - /// <param name="tag">An optional tag to attach to the coroutine which can later be used to identify this coroutine.</param> - /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> - public static CoroutineHandle RunCoroutine(this IEnumerator<float> coroutine, string tag) - { - return Timing.RunCoroutine(coroutine, tag); - } - - /// <summary> - /// Run a new coroutine. - /// </summary> - /// <param name="coroutine">The new coroutine's handle.</param> - /// <param name="segment">The segment that the coroutine should run in.</param> - /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> - public static CoroutineHandle RunCoroutine(this IEnumerator<float> coroutine, Segment segment) - { - return Timing.RunCoroutine(coroutine, segment); - } - - /// <summary> - /// Run a new coroutine. - /// </summary> - /// <param name="coroutine">The new coroutine's handle.</param> - /// <param name="segment">The segment that the coroutine should run in.</param> - /// <param name="tag">An optional tag to attach to the coroutine which can later be used to identify this coroutine.</param> - /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> - public static CoroutineHandle RunCoroutine(this IEnumerator<float> coroutine, Segment segment, string tag) - { - return Timing.RunCoroutine(coroutine, segment, tag); - } - } -} - -public static class MECExtensionMethods2 -{ - /// <summary> - /// Cancels this coroutine when the supplied game object is destroyed or made inactive. - /// </summary> - /// <param name="coroutine">The coroutine handle to act upon.</param> - /// <param name="gameObject">The GameObject to test.</param> - /// <returns>The modified coroutine handle.</returns> - public static IEnumerator<float> CancelWith(this IEnumerator<float> coroutine, GameObject gameObject) - { - while (MEC.Timing.MainThread != System.Threading.Thread.CurrentThread || (gameObject && gameObject.activeInHierarchy && coroutine.MoveNext())) - yield return coroutine.Current; - } - - /// <summary> - /// Cancels this coroutine when the supplied game objects are destroyed or made inactive. - /// </summary> - /// <param name="coroutine">The coroutine handle to act upon.</param> - /// <param name="gameObject1">The first GameObject to test.</param> - /// <param name="gameObject2">The second GameObject to test</param> - /// <returns>The modified coroutine handle.</returns> - public static IEnumerator<float> CancelWith(this IEnumerator<float> coroutine, GameObject gameObject1, GameObject gameObject2) - { - while (MEC.Timing.MainThread != System.Threading.Thread.CurrentThread || (gameObject1 && gameObject1.activeInHierarchy && - gameObject2 && gameObject2.activeInHierarchy && coroutine.MoveNext())) - yield return coroutine.Current; - } - - /// <summary> - /// Cancels this coroutine when the supplied game objects are destroyed or made inactive. - /// </summary> - /// <param name="coroutine">The coroutine handle to act upon.</param> - /// <param name="gameObject1">The first GameObject to test.</param> - /// <param name="gameObject2">The second GameObject to test</param> - /// <param name="gameObject3">The third GameObject to test.</param> - /// <returns>The modified coroutine handle.</returns> - public static IEnumerator<float> CancelWith(this IEnumerator<float> coroutine, - GameObject gameObject1, GameObject gameObject2, GameObject gameObject3) - { - while (MEC.Timing.MainThread != System.Threading.Thread.CurrentThread || (gameObject1 && gameObject1.activeInHierarchy && - gameObject2 && gameObject2.activeInHierarchy && gameObject3 && gameObject3.activeInHierarchy && coroutine.MoveNext())) - yield return coroutine.Current; - } -} diff --git a/WorldlineKeepers/Assets/Scenes/0_Test.unity b/WorldlineKeepers/Assets/Scenes/0_Test.unity index 0c1e493..5b24157 100644 --- a/WorldlineKeepers/Assets/Scenes/0_Test.unity +++ b/WorldlineKeepers/Assets/Scenes/0_Test.unity @@ -9988,6 +9988,7 @@ GameObject: - component: {fileID: 1552119909} - component: {fileID: 1552119911} - component: {fileID: 1552119913} + - component: {fileID: 1552119914} m_Layer: 0 m_Name: Main m_TagString: Untagged @@ -10058,6 +10059,25 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 0825a89e4815bc94eb58cd23aa6f4814, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!114 &1552119914 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1552119908} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2d2433f4570c1404da40af9ea0b12741, type: 3} + m_Name: + m_EditorClassIdentifier: + TimeBetweenSlowUpdateCalls: 0.14285715 + ProfilerDebugAmount: 0 + UpdateCoroutines: 0 + FixedUpdateCoroutines: 0 + LateUpdateCoroutines: 0 + SlowUpdateCoroutines: 0 + _instanceID: 0 --- !u!1 &1566097397 GameObject: m_ObjectHideFlags: 0 diff --git a/WorldlineKeepers/Assets/Scripts/Application/ApplicationMain.cs b/WorldlineKeepers/Assets/Scripts/Application/ApplicationMain.cs index a4698f4..214a785 100644 --- a/WorldlineKeepers/Assets/Scripts/Application/ApplicationMain.cs +++ b/WorldlineKeepers/Assets/Scripts/Application/ApplicationMain.cs @@ -25,36 +25,54 @@ namespace WK public void OnAwake() { onAwakeHandler?.Invoke(); + + GameStageManager.Instance.OnAwake(); + } public void OnStart() { - onStartHandler?.Invoke(); + onStartHandler?.Invoke(); + + + GameStageManager.Instance.OnStart(); + } public void OnUpdate() { - onUpdateHandler?.Invoke(); + onUpdateHandler?.Invoke(); + + GameStageManager.Instance.OnUpdate(); } public void OnFixedUpdate() { - onFixedUpdateHandler?.Invoke(); + onFixedUpdateHandler?.Invoke(); + + GameStageManager.Instance.OnFixedUpdate(); + } public void OnDestroy() { - onDestroyHandler?.Invoke(); + onDestroyHandler?.Invoke(); + + GameStageManager.Instance.OnDestroy(); } public void OnApplicationQuit() { - onApplicationQuitHandler?.Invoke(); + onApplicationQuitHandler?.Invoke(); + + GameStageManager.Instance.OnApplicationQuit(); } public void OnApplicationPause() { onApplicationPauseHandler?.Invoke(); + + GameStageManager.Instance.OnApplicationPause(); } } diff --git a/WorldlineKeepers/Assets/Scripts/Battle/SceneManager.cs b/WorldlineKeepers/Assets/Scripts/Battle/SceneManager.cs index ff7ac65..ac800ee 100644 --- a/WorldlineKeepers/Assets/Scripts/Battle/SceneManager.cs +++ b/WorldlineKeepers/Assets/Scripts/Battle/SceneManager.cs @@ -19,7 +19,7 @@ namespace WK } } - public void LoadScene(string sceneName, UnityEngine.SceneManagement.LoadSceneMode loadMode) + public void LoadScene(string sceneName, UnityEngine.SceneManagement.LoadSceneMode loadMode = UnityEngine.SceneManagement.LoadSceneMode.Single) { m_AsyncOpt = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(sceneName, loadMode); } diff --git a/WorldlineKeepers/Assets/Scripts/Common/SingletonMB.cs b/WorldlineKeepers/Assets/Scripts/Common/SingletonMB.cs index 5debe57..458f642 100644 --- a/WorldlineKeepers/Assets/Scripts/Common/SingletonMB.cs +++ b/WorldlineKeepers/Assets/Scripts/Common/SingletonMB.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json.Utilities; using UnityEngine; +using WK; public abstract class SingletonMB<T> : MonoBehaviour where T : class { @@ -52,7 +53,7 @@ public abstract class SingletonMB<T> : MonoBehaviour where T : class } else { - Debug.LogError("m_Instance is already null!! type is " + typeof(T).ToString()); + LogHelper.LogError("m_Instance is already null!! type is " + typeof(T).ToString()); } } }
\ No newline at end of file diff --git a/WorldlineKeepers/Assets/Scripts/Physics/Quadtree.cs b/WorldlineKeepers/Assets/Scripts/Physics/Quadtree.cs index a012d26..bb20c76 100644 --- a/WorldlineKeepers/Assets/Scripts/Physics/Quadtree.cs +++ b/WorldlineKeepers/Assets/Scripts/Physics/Quadtree.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Runtime.InteropServices.WindowsRuntime; using Unity.VisualScripting; using UnityEngine; +using WK; // https://gamedev.stackexchange.com/questions/6345/quad-tree-vs-grid-based-collision-detection @@ -104,7 +105,7 @@ namespace mh { if(!m_IsRoot) { - Debug.LogError("Quadtree.Rebound()ֻ"); + LogHelper.LogError("Quadtree.Rebound()ֻ"); return; } m_Bounds = bounds; diff --git a/WorldlineKeepers/Assets/Scripts/Rendering/SpriteAnimationController.cs b/WorldlineKeepers/Assets/Scripts/Rendering/SpriteAnimationController.cs index 63ba394..3c4bf2b 100644 --- a/WorldlineKeepers/Assets/Scripts/Rendering/SpriteAnimationController.cs +++ b/WorldlineKeepers/Assets/Scripts/Rendering/SpriteAnimationController.cs @@ -38,6 +38,9 @@ namespace WK.Rendering StartCoroutine(CoPlayAnimation(m_SpriteAnimation.duration)); m_IsPlaying = true; + + + m_SpriteRenderer.sprite = m_SpriteAnimation.sprites[0]; } IEnumerator CoPlayAnimation(float duration = 1f) diff --git a/WorldlineKeepers/Assets/Scripts/Stages/GameStageBase.cs b/WorldlineKeepers/Assets/Scripts/Stages/GameStageBase.cs index 73ded6e..9b53354 100644 --- a/WorldlineKeepers/Assets/Scripts/Stages/GameStageBase.cs +++ b/WorldlineKeepers/Assets/Scripts/Stages/GameStageBase.cs @@ -9,6 +9,7 @@ namespace WK public class GameStageBase : AsyncStatemachine.State { + GameStageManager owner = GameStageManager.Instance; public override IEnumerator<float> OnStart() { diff --git a/WorldlineKeepers/Assets/Scripts/Stages/GameStageManager.cs b/WorldlineKeepers/Assets/Scripts/Stages/GameStageManager.cs index a92a3d8..2e1ea6b 100644 --- a/WorldlineKeepers/Assets/Scripts/Stages/GameStageManager.cs +++ b/WorldlineKeepers/Assets/Scripts/Stages/GameStageManager.cs @@ -27,7 +27,12 @@ namespace WK { m_Statemachine = new AsyncStatemachine(); + stages[(int)EGameStage.Launch] = m_Statemachine.RegisterState(new GameStage_Launch()); + stages[(int)EGameStage.Main] = m_Statemachine.RegisterState(new GameStage_Main()); + stages[(int)EGameStage.Battle] = m_Statemachine.RegisterState(new GameStage_Battle()); + stages[(int)EGameStage.Dojo] = m_Statemachine.RegisterState(new GameStage_Dojo()); + m_Statemachine.Start(stages[(int)EGameStage.Dojo]); } public void OnStart() diff --git a/WorldlineKeepers/Assets/Scripts/Stages/GameStage_Dojo.cs b/WorldlineKeepers/Assets/Scripts/Stages/GameStage_Dojo.cs index fbb92a4..fa4e8ed 100644 --- a/WorldlineKeepers/Assets/Scripts/Stages/GameStage_Dojo.cs +++ b/WorldlineKeepers/Assets/Scripts/Stages/GameStage_Dojo.cs @@ -8,7 +8,22 @@ namespace WK public class GameStage_Dojo : GameStageBase { + public override IEnumerator<float> OnStart() + { + GameSceneManager.Instance.LoadScene("Scenes/3_Dojo"); + yield break; + } + public override IEnumerator<float> OnEnd() + { + yield break; + } + + public override void OnUpdate(float deltaTime) + { + + + } } diff --git a/WorldlineKeepers/Assets/Scripts/Stages/GameStage_Main.cs b/WorldlineKeepers/Assets/Scripts/Stages/GameStage_Main.cs index 01488d9..8a1d5eb 100644 --- a/WorldlineKeepers/Assets/Scripts/Stages/GameStage_Main.cs +++ b/WorldlineKeepers/Assets/Scripts/Stages/GameStage_Main.cs @@ -5,7 +5,7 @@ using UnityEngine; namespace WK { - public class GameStage_Lobby + public class GameStage_Main : GameStageBase { diff --git a/WorldlineKeepers/Assets/Scripts/Tests/TestCSV.cs b/WorldlineKeepers/Assets/Scripts/Tests/TestCSV.cs index b24a8c7..09f810c 100644 --- a/WorldlineKeepers/Assets/Scripts/Tests/TestCSV.cs +++ b/WorldlineKeepers/Assets/Scripts/Tests/TestCSV.cs @@ -54,11 +54,11 @@ namespace WK styled.AppendLine(); } - Debug.Log(styled.ToString()); // Unity + LogHelper.Log(styled.ToString()); // Unity Console.WriteLine(styled.ToString()); // C# List<CharacterStatsMetadata> stats = CSVReader.Read<CharacterStatsMetadata>(text.text); - Debug.Log(stats.Count); + LogHelper.Log(stats.Count); } diff --git a/WorldlineKeepers/Assets/Scripts/Tests/TestEvent.cs b/WorldlineKeepers/Assets/Scripts/Tests/TestEvent.cs index 54df05b..482595e 100644 --- a/WorldlineKeepers/Assets/Scripts/Tests/TestEvent.cs +++ b/WorldlineKeepers/Assets/Scripts/Tests/TestEvent.cs @@ -36,7 +36,7 @@ public class TestEvent : MonoBehaviour, INotification private void OnEventCheck(params object[] p) { - Debug.Log("msg" + p[0]); + LogHelper.Log("msg" + p[0]); } } diff --git a/WorldlineKeepers/Assets/Scripts/Tests/TestJson.cs b/WorldlineKeepers/Assets/Scripts/Tests/TestJson.cs index dbfa8dc..762b8e4 100644 --- a/WorldlineKeepers/Assets/Scripts/Tests/TestJson.cs +++ b/WorldlineKeepers/Assets/Scripts/Tests/TestJson.cs @@ -2,6 +2,7 @@ using LitJson; using System.Collections; using System.Collections.Generic; using UnityEngine; +using WK; public class SaveData { @@ -61,7 +62,7 @@ public class TestJson : MonoBehaviour //解析Json SaveDatas sd = JsonMapper.ToObject<SaveDatas>(Datas); - Debug.Log(Datas); + LogHelper.Log(Datas); } diff --git a/WorldlineKeepers/Assets/Scripts/Tools/Commands.meta b/WorldlineKeepers/Assets/Scripts/Tools/Commands.meta new file mode 100644 index 0000000..e9f33f2 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/Commands.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 43e010091f201b6468a42e7ea26b6885 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Tools/Commands/Command.cs b/WorldlineKeepers/Assets/Scripts/Tools/Commands/Command.cs new file mode 100644 index 0000000..120eb08 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/Commands/Command.cs @@ -0,0 +1,47 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace WK +{ + + public abstract class Command + { + + public abstract void Execute(); + + } + + public class CommandList + { + private List<Command> m_Commands = new List<Command>(); + + public void AddCommand(Command cmd) + { + if (cmd == null) + { + return; + } + m_Commands.Add(cmd); + } + + public void RemoveCommand(Command cmd) + { + if (cmd == null) + { + return; + } + m_Commands.Remove(cmd); + } + + public void Execute() + { + for (int i = 0; i < m_Commands.Count; ++i) + { + m_Commands[i].Execute(); + } + } + + } + +} diff --git a/WorldlineKeepers/Assets/Scripts/Tools/Commands/Command.cs.meta b/WorldlineKeepers/Assets/Scripts/Tools/Commands/Command.cs.meta new file mode 100644 index 0000000..651a018 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/Commands/Command.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f81999dab5f39be408c8e5c5f809eddf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Tools/Commands/CommandGroup.cs b/WorldlineKeepers/Assets/Scripts/Tools/Commands/CommandGroup.cs new file mode 100644 index 0000000..8e53888 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/Commands/CommandGroup.cs @@ -0,0 +1,96 @@ +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Diagnostics.Tracing; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; +using UnityEngine.UIElements; + +namespace WK +{ + //http://warmcat.org/chai/blog/?p=2343 + + public abstract class CommandsGroup + { + + public abstract class Command + { + public CommandID ID; + public Command(CommandID id) + { + this.ID = id; + } + /// <summary> + /// ִ + /// </summary> + public abstract void Execute(); + /// <summary> + /// עIJ + /// </summary> + /// <param name="_param"></param> + public abstract void Register(params object[] _param); + } + + protected List<Command> commandQueue = new List<Command>(); + protected void AddCommand(Command cmd) + { + commandQueue.Add(cmd); + } + + public CommandsGroup() + { + SetupCommands(); + } + + /// <summary> + /// commandQueue + /// </summary> + protected abstract void SetupCommands(); + + public void Execute() + { + foreach (Command e in commandQueue) + { + e.Execute(); + } + } + + public void RegisterParams(params object[] data) + { + if (data.Length < 1) + return; + CommandID id = (CommandID)data[0]; + int len = data.Length; + foreach (Command e in commandQueue) + { + if (e.ID == id) + { + e.Register(data.Skip(1).Take(len - 1)); + break; + } + } + } + } + + /* + /// <summary> + /// ʱִе + /// </summary> + class MainSceneLoadCommandsGroup : CommandsGroup + { + public MainSceneLoadCommandsGroup() : base() + { + // Ҫִе + + AddCommand(new OpenPanelCommand()); + // + //AddCommand(new MessageBox()); + //AddCommand(new OpenChest()); + //... + } + } + + */ + +} diff --git a/WorldlineKeepers/Assets/Scripts/Tools/Commands/CommandGroup.cs.meta b/WorldlineKeepers/Assets/Scripts/Tools/Commands/CommandGroup.cs.meta new file mode 100644 index 0000000..3df093c --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/Commands/CommandGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61d8f0e869532c64395b21a493d17a45 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Tools/GameObjectPool.cs b/WorldlineKeepers/Assets/Scripts/Tools/GameObjectPool.cs new file mode 100644 index 0000000..1d948e6 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/GameObjectPool.cs @@ -0,0 +1,51 @@ +using UnityEngine; +using WK; + +namespace WK +{ + public class GameObjectPool : System.IDisposable, UnityObjectPool<GameObject>.IPooledInstanceInitializer + { + UnityObjectPool<GameObject> _pool; + Transform _poolParent; + + public GameObjectPool(GameObject sourceObject, Transform poolParent, int initialSize = 0) + { + _pool = new UnityObjectPool<GameObject>(sourceObject, poolParent, this, initialSize); + _poolParent = poolParent; + } + + public GameObject Acquire(Transform parent) + { + GameObject instance = _pool.Acquire(); + instance.transform.SetParent(parent); + instance.transform.ResetLocal(); + instance.SetActive(true); + + return instance; + } + + public void Release(GameObject instance) + { + instance.SetActive(false); + instance.transform.SetParent(_poolParent); + _pool.Release(instance); + } + + public void InitPooledInstance(GameObject instance) + { + instance.SetActive(false); + } + + public void DestroyPooledInstance(GameObject instance) + { + UnityEngine.Object.Destroy(instance); + } + + public void Dispose() + { + _pool.Dispose(); + _pool = null; + _poolParent = null; + } + } +}
\ No newline at end of file diff --git a/WorldlineKeepers/Assets/Scripts/Tools/GameObjectPool.cs.meta b/WorldlineKeepers/Assets/Scripts/Tools/GameObjectPool.cs.meta new file mode 100644 index 0000000..d3234ce --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/GameObjectPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6df40d153f41aa446b2746e80abc19d9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Tools/Notification/NotificationCenter.cs b/WorldlineKeepers/Assets/Scripts/Tools/Notification/NotificationCenter.cs index bb4e08e..4e52656 100644 --- a/WorldlineKeepers/Assets/Scripts/Tools/Notification/NotificationCenter.cs +++ b/WorldlineKeepers/Assets/Scripts/Tools/Notification/NotificationCenter.cs @@ -5,7 +5,7 @@ using UnityEngine; namespace WK { - public class NotificationCenter : Singleton<NotificationCenter> + public sealed class NotificationCenter : Singleton<NotificationCenter> { public delegate void NotificatonHandler(params object[] args); diff --git a/WorldlineKeepers/Assets/Scripts/Tools/Notification/ScopedNotification.cs b/WorldlineKeepers/Assets/Scripts/Tools/Notification/ScopedNotification.cs index 0002f14..5a1171e 100644 --- a/WorldlineKeepers/Assets/Scripts/Tools/Notification/ScopedNotification.cs +++ b/WorldlineKeepers/Assets/Scripts/Tools/Notification/ScopedNotification.cs @@ -5,6 +5,9 @@ using UnityEngine; namespace WK { + /// <summary> + /// ΧϢ֪ͨ + /// </summary> public class ScopedNotification { public delegate void NotificatonHandler(params object[] args); diff --git a/WorldlineKeepers/Assets/Scripts/Tools/ObjectPool.cs b/WorldlineKeepers/Assets/Scripts/Tools/ObjectPool.cs new file mode 100644 index 0000000..ac18a9a --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/ObjectPool.cs @@ -0,0 +1,51 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Events; + +namespace WK +{ + class ObjectPool<T> where T : new() + { + private readonly Stack<T> m_Stack = new Stack<T>(); + private readonly UnityAction<T> m_ActionOnGet; + private readonly UnityAction<T> m_ActionOnRelease; + + public int countAll { get; private set; } + public int countActive { get { return countAll - countInactive; } } + public int countInactive { get { return m_Stack.Count; } } + + public ObjectPool(UnityAction<T> actionOnGet, UnityAction<T> actionOnRelease) + { + m_ActionOnGet = actionOnGet; + m_ActionOnRelease = actionOnRelease; + } + + public T Get() + { + T element; + if (m_Stack.Count == 0) + { + element = new T(); + countAll++; + } + else + { + element = m_Stack.Pop(); + } + if (m_ActionOnGet != null) + m_ActionOnGet(element); + return element; + } + + public void Release(T element) + { + if (m_Stack.Count > 0 && ReferenceEquals(m_Stack.Peek(), element)) + Debug.LogError("Internal error. Trying to destroy object that is already released to pool."); + if (m_ActionOnRelease != null) + m_ActionOnRelease(element); + m_Stack.Push(element); + } + } + +}
\ No newline at end of file diff --git a/WorldlineKeepers/Assets/Scripts/Tools/ObjectPool.cs.meta b/WorldlineKeepers/Assets/Scripts/Tools/ObjectPool.cs.meta new file mode 100644 index 0000000..b9cdb21 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/ObjectPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bc1e2a929537ad04bb2ae5f26ce4ad77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Tools/Recycle.cs b/WorldlineKeepers/Assets/Scripts/Tools/Recycle.cs new file mode 100644 index 0000000..a53289f --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/Recycle.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; + +namespace WK +{ + + /// <summary> + /// ʵִ˽ӿûնijԱֵ + /// </summary> + public interface IRcycle + { + void Release(); + } + + public class Recycle<T> where T : IRcycle, new() + { + private List<T> cachedNodeList; + private int maxCapacity; + + public Recycle(int initCapacity, int maxCapacity) + { + this.maxCapacity = maxCapacity; + if (cachedNodeList == null) + { + cachedNodeList = new List<T>(initCapacity); + for (int i = 0; i < initCapacity; ++i) + { + cachedNodeList.Add(new T()); + } + } + } + + public T GetUnusedNode() + { + if (cachedNodeList.Count > 0) + { + // remove and return last node + int i = cachedNodeList.Count - 1; + T t = cachedNodeList[i]; + cachedNodeList.RemoveAt(i); + return t; + } + return new T(); + } + + //նԭΪʼֵ(0null) + public bool ReleaseNode(ref T t) + { + if (t == null) + return false; + + T refT = t; + t = default(T); + + if (cachedNodeList.Count > maxCapacity) + return false; + + if (cachedNodeList.Contains(refT)) + { + LogHelper.LogError("ظ"); + return false; + } + + refT.Release(); + cachedNodeList.Add(refT); + return true; + } + + } +} diff --git a/WorldlineKeepers/Assets/Scripts/Tools/Recycle.cs.meta b/WorldlineKeepers/Assets/Scripts/Tools/Recycle.cs.meta new file mode 100644 index 0000000..e0a21a6 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/Recycle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 413419b3354bc304691bcf8c075052fd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Tools/ScopedEvent.cs b/WorldlineKeepers/Assets/Scripts/Tools/ScopedEvent.cs new file mode 100644 index 0000000..23bfaa8 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/ScopedEvent.cs @@ -0,0 +1,54 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.AI; + +namespace WK +{ + + /// <summary> + /// UnityEventǸ + /// </summary> + public class ScopedEvent + { + public delegate void EventHandler(params object[] args); + + private List<EventHandler> m_Handlers = new List<EventHandler>(); + + public void AddListener(EventHandler handler) + { + if(handler == null) + { + return; + } + if (m_Handlers.Contains(handler)) + return; + m_Handlers.Add(handler); + } + + public void RemoveListener(EventHandler handler) + { + + if (handler == null) + { + return; + } + m_Handlers.Remove(handler); + } + + public bool HasHandler(EventHandler handler) + { + return m_Handlers.Contains(handler); + } + + public void Invoke(params object[] args) + { + for(int i = 0; i < m_Handlers.Count; ++i) + { + m_Handlers[i](args); + } + } + + } + +} diff --git a/WorldlineKeepers/Assets/Scripts/Tools/ScopedEvent.cs.meta b/WorldlineKeepers/Assets/Scripts/Tools/ScopedEvent.cs.meta new file mode 100644 index 0000000..bcbce6e --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/ScopedEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 332ddd24c01402a498f667a06c63b3cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Tools/Statemachine/AsyncStatemachine.cs b/WorldlineKeepers/Assets/Scripts/Tools/Statemachine/AsyncStatemachine.cs index 605f737..67c791d 100644 --- a/WorldlineKeepers/Assets/Scripts/Tools/Statemachine/AsyncStatemachine.cs +++ b/WorldlineKeepers/Assets/Scripts/Tools/Statemachine/AsyncStatemachine.cs @@ -1,7 +1,7 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; -using MEC; +using MovementEffects; namespace WK { @@ -43,7 +43,7 @@ namespace WK return NULL_STATE_ID; if (stateIDDistributor + 1 >= int.MaxValue) { - Debug.LogError("״̬״̬ʧܣ״̬ӵ״̬"); + LogHelper.LogError("״̬״̬ʧܣ״̬ӵ״̬"); return NULL_STATE_ID; } ++stateIDDistributor; @@ -54,7 +54,7 @@ namespace WK allState.Add(stateIDDistributor, newState); return stateIDDistributor; } - Debug.LogError("״̬״̬ʧܣ״̬Ѿ"); + LogHelper.LogError("״̬״̬ʧܣ״̬Ѿ"); return NULL_STATE_ID; } @@ -64,7 +64,7 @@ namespace WK { return RemoveState(state.stateID); } - Debug.LogError("״̬ɾ״̬ʧܣ״̬Ϊ"); + LogHelper.LogError("״̬ɾ״̬ʧܣ״̬Ϊ"); return false; } @@ -75,7 +75,7 @@ namespace WK allState.Remove(stateID); return true; } - Debug.LogError("״̬ɾ״̬ʧܣ״̬ڣstateID = " + stateID); + LogHelper.LogError("״̬ɾ״̬ʧܣ״̬ڣstateID = " + stateID); return false; } @@ -147,7 +147,7 @@ namespace WK { isUpdateActive = false; CoroutineHandle handle = Timing.RunCoroutine(EndState(curStateID)); - while (handle.IsValid) + while (handle.IsRunning) { yield return Timing.WaitForSeconds(COROUINT_DELTIME); } @@ -155,7 +155,7 @@ namespace WK if (!skipStartFunc) { CoroutineHandle handle2 = Timing.RunCoroutine(StartState(curStateID)); - while (handle2.IsValid) + while (handle2.IsRunning) { yield return Timing.WaitForSeconds(COROUINT_DELTIME); } @@ -215,7 +215,7 @@ namespace WK if (state != null) { CoroutineHandle handle = Timing.RunCoroutine(state.OnStart()); - while (handle.IsValid) + while (handle.IsRunning) { yield return Timing.WaitForSeconds(COROUINT_DELTIME); } @@ -231,7 +231,7 @@ namespace WK if (state != null) { CoroutineHandle handle = Timing.RunCoroutine(state.OnEnd()); - while (handle.IsValid) + while (handle.IsRunning) { yield return Timing.WaitForSeconds(COROUINT_DELTIME); } diff --git a/WorldlineKeepers/Assets/Scripts/Tools/Statemachine/Statemachine.cs b/WorldlineKeepers/Assets/Scripts/Tools/Statemachine/Statemachine.cs index 56dd28b..b54b5e1 100644 --- a/WorldlineKeepers/Assets/Scripts/Tools/Statemachine/Statemachine.cs +++ b/WorldlineKeepers/Assets/Scripts/Tools/Statemachine/Statemachine.cs @@ -42,7 +42,7 @@ namespace WK { if (stateIdDistributor + 1 > int.MaxValue) { - Debug.LogError("״̬״̬ʧܣһ״̬˹״̬"); + LogHelper.LogError("״̬״̬ʧܣһ״̬˹״̬"); return -1; } @@ -55,7 +55,7 @@ namespace WK return stateIdDistributor; } } - Debug.LogError("״̬״̬ʧܣЧ״̬״̬Ѵڣ"); + LogHelper.LogError("״̬״̬ʧܣЧ״̬״̬Ѵڣ"); return -1; } @@ -63,7 +63,7 @@ namespace WK { if (toBeRemoveState != null) return RemoveState(toBeRemoveState.stateId); - Debug.LogError("״̬ɾ״̬ʧܣЧ״̬"); + LogHelper.LogError("״̬ɾ״̬ʧܣЧ״̬"); return false; } @@ -74,7 +74,7 @@ namespace WK stateDic.Remove(stateId); return true; } - Debug.LogError("״̬ɾ״̬ʧܣ״̬ڣ"); + LogHelper.LogError("״̬ɾ״̬ʧܣ״̬ڣ"); return false; } diff --git a/WorldlineKeepers/Assets/Scripts/Tools/ThreadSafeElapsedTime.cs b/WorldlineKeepers/Assets/Scripts/Tools/ThreadSafeElapsedTime.cs new file mode 100644 index 0000000..50da91b --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/ThreadSafeElapsedTime.cs @@ -0,0 +1,87 @@ +using System; +using System.Diagnostics; + +/// <summary> +/// 线程安全的时间流逝类 +/// 从游戏运行开始计时 +/// </summary> +public static class ThreadSafeElapsedTime +{ + private static bool _isStart = false; + private static Stopwatch _stopwatch; + private static long _curRawElapsedTicks; + private static float _curRawElapsedSeconds; + + static private double ticks2seconds = 1 / (double) TimeSpan.TicksPerSecond; + + + //必须在启动后调用 + public static void Start() + { + if (!_isStart) + { + _isStart = true; + _stopwatch = new Stopwatch(); + _stopwatch.Start(); + _curRawElapsedTicks = 0; + _curRawElapsedSeconds = 0; + } + } + + public static void Stop() + { + if (_isStart) + { + _isStart = false; + _stopwatch.Stop(); + } + } + + public static void Update() + { + if (_isStart) + { + _curRawElapsedTicks = _stopwatch.ElapsedTicks; + //_curRawElapsedSeconds = (int)(_curRawElapsedTicks / System.TimeSpan.TicksPerSecond); + _curRawElapsedSeconds = (float)(((double) _curRawElapsedTicks ) * ticks2seconds); + } + } + + /// <summary> + /// 自游戏启动以来的ticks + /// </summary> + /// <returns></returns> + public static long GetElapsedTicksSinceStartUp() + { + #if UNITY_EDITOR + Start(); + Update(); + #endif + return _curRawElapsedTicks; + } + /// <summary> + /// 自游戏启动以来的seconds + /// </summary> + /// <returns></returns> + public static float GetElapsedSecondsSinceStartUp() + { +#if UNITY_EDITOR + Start(); + Update(); +#endif + return _curRawElapsedSeconds; + } + + /// <summary> + /// 自游戏启动以来的miniseconds + /// </summary> + /// <returns></returns> + public static int GetElapsedMiniSecondsSinceStartUp() + { +#if UNITY_EDITOR + Start(); + Update(); +#endif + return (int)(_curRawElapsedSeconds * 1000); + } +} diff --git a/WorldlineKeepers/Assets/Scripts/Tools/ThreadSafeElapsedTime.cs.meta b/WorldlineKeepers/Assets/Scripts/Tools/ThreadSafeElapsedTime.cs.meta new file mode 100644 index 0000000..072611a --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/ThreadSafeElapsedTime.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6767239866ef68a4b841b36c5bd140b9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Plugins/Trinary Software.meta b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software.meta index 616b6fb..616b6fb 100644 --- a/WorldlineKeepers/Assets/Plugins/Trinary Software.meta +++ b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software.meta diff --git a/WorldlineKeepers/Assets/Plugins/Trinary Software/Editor.meta b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Editor.meta index 2e833c9..2e833c9 100644 --- a/WorldlineKeepers/Assets/Plugins/Trinary Software/Editor.meta +++ b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Editor.meta diff --git a/WorldlineKeepers/Assets/Plugins/Trinary Software/Editor/MecIcon.png b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Editor/MecIcon.png Binary files differindex caf3365..caf3365 100644 --- a/WorldlineKeepers/Assets/Plugins/Trinary Software/Editor/MecIcon.png +++ b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Editor/MecIcon.png diff --git a/WorldlineKeepers/Assets/Plugins/Trinary Software/Editor/MecIcon.png.meta b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Editor/MecIcon.png.meta index 733447a..733447a 100644 --- a/WorldlineKeepers/Assets/Plugins/Trinary Software/Editor/MecIcon.png.meta +++ b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Editor/MecIcon.png.meta diff --git a/WorldlineKeepers/Assets/Plugins/Trinary Software/Quick Start Guide.pdf b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Quick Start Guide.pdf Binary files differindex e2f028f..e2f028f 100644 --- a/WorldlineKeepers/Assets/Plugins/Trinary Software/Quick Start Guide.pdf +++ b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Quick Start Guide.pdf diff --git a/WorldlineKeepers/Assets/Plugins/Trinary Software/Quick Start Guide.pdf.meta b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Quick Start Guide.pdf.meta index 4a34c89..4a34c89 100644 --- a/WorldlineKeepers/Assets/Plugins/Trinary Software/Quick Start Guide.pdf.meta +++ b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Quick Start Guide.pdf.meta diff --git a/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Timing.cs b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Timing.cs new file mode 100644 index 0000000..6e38b0c --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Timing.cs @@ -0,0 +1,4655 @@ +using UnityEngine; +using System.Collections.Generic; +#if UNITY_EDITOR +using UnityEditor; +#endif +#if UNITY_5_5_OR_NEWER +using UnityEngine.Profiling; +#endif + +// ///////////////////////////////////////////////////////////////////////////////////////// +// More Effective Coroutines Pro +// v2.03.0 +// +// This is an improved implementation of coroutines that boasts zero per-frame memory allocations, +// runs about twice as fast as Unity's built in coroutines and has a range of extra features. +// +// For manual, support, or upgrade guide visit http://trinary.tech/ +// +// Created by Teal Rogers +// Trinary Software +// All rights preserved +// trinaryllc@gmail.com +// ///////////////////////////////////////////////////////////////////////////////////////// + +namespace MovementEffects +{ + public class Timing : MonoBehaviour + { + public enum DebugInfoType + { + None, + SeperateCoroutines, + SeperateTags + } + + /// <summary> + /// The time between calls to SlowUpdate. + /// </summary> + [Tooltip("How quickly the SlowUpdate segment ticks.")] + public float TimeBetweenSlowUpdateCalls = 1f / 7f; + /// <summary> + /// The amount that each coroutine should be seperated inside the Unity profiler. NOTE: When the profiler window + /// is not open this value is ignored and all coroutines behave as if "None" is selected. + /// </summary> + [Tooltip("How much data should be sent to the profiler window when it's open.")] + public DebugInfoType ProfilerDebugAmount = DebugInfoType.None; + /// <summary> + /// Whether the manual timeframe should automatically trigger during the update segment. + /// </summary> + [Tooltip("When using manual timeframe, should it run automatically after the update loop or only when TriggerManualTimframeUpdate is called.")] + public bool AutoTriggerManualTimeframe = true; + /// <summary> + /// The number of coroutines that are being run in the Update segment. + /// </summary> + [Tooltip("A count of the number of Update coroutines that are currently running."), Space(12)] + public int UpdateCoroutines; + /// <summary> + /// The number of coroutines that are being run in the FixedUpdate segment. + /// </summary> + [Tooltip("A count of the number of FixedUpdate coroutines that are currently running.")] + public int FixedUpdateCoroutines; + /// <summary> + /// The number of coroutines that are being run in the LateUpdate segment. + /// </summary> + [Tooltip("A count of the number of LateUpdate coroutines that are currently running.")] + public int LateUpdateCoroutines; + /// <summary> + /// The number of coroutines that are being run in the SlowUpdate segment. + /// </summary> + [Tooltip("A count of the number of SlowUpdate coroutines that are currently running.")] + public int SlowUpdateCoroutines; + /// <summary> + /// The number of coroutines that are being run in the RealtimeUpdate segment. + /// </summary> + [Tooltip("A count of the number of RealtimeUpdate coroutines that are currently running.")] + public int RealtimeUpdateCoroutines; + /// <summary> + /// The number of coroutines that are being run in the EditorUpdate segment. + /// </summary> + [Tooltip("A count of the number of EditorUpdate coroutines that are currently running.")] + public int EditorUpdateCoroutines; + /// <summary> + /// The number of coroutines that are being run in the EditorSlowUpdate segment. + /// </summary> + [Tooltip("A count of the number of EditorSlowUpdate coroutines that are currently running.")] + public int EditorSlowUpdateCoroutines; + /// <summary> + /// The number of coroutines that are being run in the EndOfFrame segment. + /// </summary> + [Tooltip("A count of the number of EndOfFrame coroutines that are currently running.")] + public int EndOfFrameCoroutines; + /// <summary> + /// The number of coroutines that are being run in the ManualTimeframe segment. + /// </summary> + [Tooltip("A count of the number of ManualTimeframe coroutines that are currently running.")] + public int ManualTimeframeCoroutines; + + /// <summary> + /// The time in seconds that the current segment has been running. + /// </summary> + [HideInInspector] + public double localTime; + /// <summary> + /// The time in seconds that the current segment has been running. + /// </summary> + public static float LocalTime { get { return (float)Instance.localTime; } } + /// <summary> + /// The amount of time in fractional seconds that elapsed between this frame and the last frame. + /// </summary> + [HideInInspector] + public float deltaTime; + /// <summary> + /// The amount of time in fractional seconds that elapsed between this frame and the last frame. + /// </summary> + public static float DeltaTime { get { return Instance.deltaTime; } } + /// <summary> + /// When defined, all errors from inside coroutines will be passed into this function instead of falling through to the Unity console. + /// </summary> + public System.Action<System.Exception> OnError; + /// <summary> + /// When defined, this function will be called every time manual timeframe needs to be set. The last manual timeframe time is passed in, and + /// the new manual timeframe time needs to be returned. If this function is left as null, manual timeframe will be set to the current Time.time. + /// </summary> + public System.Func<double, double> SetManualTimeframeTime; + /// <summary> + /// Used for advanced coroutine control. + /// </summary> + public static System.Func<IEnumerator<float>, Timing, CoroutineHandle, IEnumerator<float>> ReplacementFunction; + + private bool _runningUpdate; + private bool _runningFixedUpdate; + private bool _runningLateUpdate; + private bool _runningRealtimeUpdate; + private bool _runningEditorUpdate; + private int _nextUpdateProcessSlot; + private int _nextLateUpdateProcessSlot; + private int _nextFixedUpdateProcessSlot; + private int _nextSlowUpdateProcessSlot; + private int _nextRealtimeUpdateProcessSlot; + private int _nextEditorUpdateProcessSlot; + private int _nextEditorSlowUpdateProcessSlot; + private int _nextEndOfFrameProcessSlot; + private int _nextManualTimeframeProcessSlot; + private double _lastUpdateTime; + private double _lastLateUpdateTime; + private double _lastFixedUpdateTime; + private double _lastSlowUpdateTime; + private double _lastRealtimeUpdateTime; + private double _lastEditorUpdateTime; + private double _lastEditorSlowUpdateTime; + private double _lastManualTimeframeTime; + private ushort _framesSinceUpdate; + private ushort _expansions = 1; + private byte _instanceID; + private bool _EOFPumpRan; + + private readonly WaitForEndOfFrame _EOFWaitObject = new WaitForEndOfFrame(); + private readonly Dictionary<CoroutineHandle, HashSet<ProcessData>> _waitingTriggers = new Dictionary<CoroutineHandle, HashSet<ProcessData>>(); + private readonly Queue<System.Exception> _exceptions = new Queue<System.Exception>(); + private readonly Dictionary<CoroutineHandle, ProcessIndex> _handleToIndex = new Dictionary<CoroutineHandle, ProcessIndex>(); + private readonly Dictionary<ProcessIndex, CoroutineHandle> _indexToHandle = new Dictionary<ProcessIndex, CoroutineHandle>(); + private readonly Dictionary<ProcessIndex, string> _processTags = new Dictionary<ProcessIndex, string>(); + private readonly Dictionary<string, HashSet<ProcessIndex>> _taggedProcesses = new Dictionary<string, HashSet<ProcessIndex>>(); + private readonly Dictionary<ProcessIndex, int> _processLayers = new Dictionary<ProcessIndex, int>(); + private readonly Dictionary<int, HashSet<ProcessIndex>> _layeredProcesses = new Dictionary<int, HashSet<ProcessIndex>>(); + + private IEnumerator<float>[] UpdateProcesses = new IEnumerator<float>[InitialBufferSizeLarge]; + private IEnumerator<float>[] LateUpdateProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; + private IEnumerator<float>[] FixedUpdateProcesses = new IEnumerator<float>[InitialBufferSizeMedium]; + private IEnumerator<float>[] SlowUpdateProcesses = new IEnumerator<float>[InitialBufferSizeMedium]; + private IEnumerator<float>[] RealtimeUpdateProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; + private IEnumerator<float>[] EditorUpdateProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; + private IEnumerator<float>[] EditorSlowUpdateProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; + private IEnumerator<float>[] EndOfFrameProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; + private IEnumerator<float>[] ManualTimeframeProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; + + private volatile bool[] UpdatePaused = new bool[InitialBufferSizeLarge]; + private volatile bool[] LateUpdatePaused = new bool[InitialBufferSizeSmall]; + private volatile bool[] FixedUpdatePaused = new bool[InitialBufferSizeMedium]; + private volatile bool[] SlowUpdatePaused = new bool[InitialBufferSizeMedium]; + private volatile bool[] RealtimeUpdatePaused = new bool[InitialBufferSizeSmall]; + private volatile bool[] EditorUpdatePaused = new bool[InitialBufferSizeSmall]; + private volatile bool[] EditorSlowUpdatePaused = new bool[InitialBufferSizeSmall]; + private volatile bool[] EndOfFramePaused = new bool[InitialBufferSizeSmall]; + private volatile bool[] ManualTimeframePaused = new bool[InitialBufferSizeSmall]; + + private const ushort FramesUntilMaintenance = 64; + private const int ProcessArrayChunkSize = 64; + private const int InitialBufferSizeLarge = 256; + private const int InitialBufferSizeMedium = 64; + private const int InitialBufferSizeSmall = 8; + + private static readonly Dictionary<byte, Timing> ActiveInstances = new Dictionary<byte, Timing>(); + private static Timing _instance; + + public static Timing Instance + { + get + { + if (_instance == null || !_instance.gameObject) + { + GameObject instanceHome = GameObject.Find("Movement Effects"); + System.Type movementType = + System.Type.GetType("MovementEffects.Movement, MovementOverTime, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + + if (instanceHome == null) + { + instanceHome = new GameObject { name = "Movement Effects" }; + DontDestroyOnLoad(instanceHome); + + if (movementType != null) + instanceHome.AddComponent(movementType); + + _instance = instanceHome.AddComponent<Timing>(); + } + else + { + if (movementType != null && instanceHome.GetComponent(movementType) == null) + instanceHome.AddComponent(movementType); + + _instance = instanceHome.GetComponent<Timing>() ?? instanceHome.AddComponent<Timing>(); + } + } + + return _instance; + } + + set { _instance = value; } + } + + void Awake() + { + if (_instance == null) + _instance = this; + else + deltaTime = Instance.deltaTime; + + _instanceID = 0x01; + while (ActiveInstances.ContainsKey(_instanceID)) + _instanceID++; + + if (_instanceID == 0x20) + { + GameObject.Destroy(gameObject); + throw new System.OverflowException("You are only allowed 31 instances of MEC at one time."); + } + + ActiveInstances.Add(_instanceID, this); + } + + void OnDestroy() + { + if (_instance == this) + _instance = null; + + ActiveInstances.Remove(_instanceID); + } + + void OnEnable() + { + if (_nextEditorUpdateProcessSlot > 0 || _nextEditorSlowUpdateProcessSlot > 0) + OnEditorStart(); + + if (_nextEndOfFrameProcessSlot > 0) + RunCoroutineSingletonOnInstance(_EOFPumpWatcher(), "MEC_EOFPumpWatcher"); + } + + void Update() + { + if (_lastSlowUpdateTime + TimeBetweenSlowUpdateCalls < ThreadSafeElapsedTime.GetElapsedSecondsSinceStartUp() && _nextSlowUpdateProcessSlot > 0) + { + ProcessIndex coindex = new ProcessIndex { seg = Segment.SlowUpdate }; + UpdateTimeValues(coindex.seg); + + for (coindex.i = 0; coindex.i < _nextSlowUpdateProcessSlot; coindex.i++) + { + if (!SlowUpdatePaused[coindex.i] && SlowUpdateProcesses[coindex.i] != null && !(localTime < SlowUpdateProcesses[coindex.i].Current)) + { + if (ProfilerDebugAmount != DebugInfoType.None) + { + Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine (Slow Update), " + + (_processLayers.ContainsKey(coindex) ? "layer " + _processLayers[coindex] : "no layer") + + (_processTags.ContainsKey(coindex) ? ", tag " + _processTags[coindex] : ", no tag")) + : "Processing Coroutine (Slow Update)"); + } + + try + { + if (!SlowUpdateProcesses[coindex.i].MoveNext()) + { + SlowUpdateProcesses[coindex.i] = null; + } + else if (SlowUpdateProcesses[coindex.i] != null && float.IsNaN(SlowUpdateProcesses[coindex.i].Current)) + { + if (ReplacementFunction == null) + { + SlowUpdateProcesses[coindex.i] = null; + } + else + { + SlowUpdateProcesses[coindex.i] = ReplacementFunction(SlowUpdateProcesses[coindex.i], this, _indexToHandle[coindex]); + + ReplacementFunction = null; + coindex.i--; + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + SlowUpdateProcesses[coindex.i] = null; + } + + if (ProfilerDebugAmount != DebugInfoType.None) + Profiler.EndSample(); + } + } + } + + if (_nextRealtimeUpdateProcessSlot > 0) + { + ProcessIndex coindex = new ProcessIndex { seg = Segment.RealtimeUpdate }; + _runningRealtimeUpdate = true; + UpdateTimeValues(coindex.seg); + + for (coindex.i = 0; coindex.i < _nextRealtimeUpdateProcessSlot; coindex.i++) + { + if (!RealtimeUpdatePaused[coindex.i] && RealtimeUpdateProcesses[coindex.i] != null && !(localTime < RealtimeUpdateProcesses[coindex.i].Current)) + { + if (ProfilerDebugAmount != DebugInfoType.None) + { + Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine (Realtime Update), " + + (_processLayers.ContainsKey(coindex) ? "layer " + _processLayers[coindex] : "no layer") + + (_processTags.ContainsKey(coindex) ? ", tag " + _processTags[coindex] : ", no tag")) + : "Processing Coroutine (Realtime Update)"); + } + + try + { + if (!RealtimeUpdateProcesses[coindex.i].MoveNext()) + { + RealtimeUpdateProcesses[coindex.i] = null; + } + else if (RealtimeUpdateProcesses[coindex.i] != null && float.IsNaN(RealtimeUpdateProcesses[coindex.i].Current)) + { + if (ReplacementFunction == null) + { + RealtimeUpdateProcesses[coindex.i] = null; + } + else + { + RealtimeUpdateProcesses[coindex.i] = ReplacementFunction(RealtimeUpdateProcesses[coindex.i], this, _indexToHandle[coindex]); + + ReplacementFunction = null; + coindex.i--; + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + RealtimeUpdateProcesses[coindex.i] = null; + } + + if (ProfilerDebugAmount != DebugInfoType.None) + Profiler.EndSample(); + } + } + + _runningRealtimeUpdate = false; + } + + if (_nextUpdateProcessSlot > 0) + { + ProcessIndex coindex = new ProcessIndex { seg = Segment.Update }; + _runningUpdate = true; + UpdateTimeValues(coindex.seg); + + for (coindex.i = 0; coindex.i < _nextUpdateProcessSlot; coindex.i++) + { + if (!UpdatePaused[coindex.i] && UpdateProcesses[coindex.i] != null && !(localTime < UpdateProcesses[coindex.i].Current)) + { + if (ProfilerDebugAmount != DebugInfoType.None) + { + Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine, " + + (_processLayers.ContainsKey(coindex) ? "layer " + _processLayers[coindex] : "no layer") + + (_processTags.ContainsKey(coindex) ? ", tag " + _processTags[coindex] : ", no tag")) : "Processing Coroutine"); + } + + try + { + if (!UpdateProcesses[coindex.i].MoveNext()) + { + UpdateProcesses[coindex.i] = null; + } + else if (UpdateProcesses[coindex.i] != null && float.IsNaN(UpdateProcesses[coindex.i].Current)) + { + if (ReplacementFunction == null) + { + UpdateProcesses[coindex.i] = null; + } + else + { + UpdateProcesses[coindex.i] = ReplacementFunction(UpdateProcesses[coindex.i], this, _indexToHandle[coindex]); + + ReplacementFunction = null; + coindex.i--; + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + UpdateProcesses[coindex.i] = null; + } + + if (ProfilerDebugAmount != DebugInfoType.None) + Profiler.EndSample(); + } + } + + _runningUpdate = false; + } + + if (AutoTriggerManualTimeframe) + { + TriggerManualTimeframeUpdate(); + } + else + { + if (++_framesSinceUpdate > FramesUntilMaintenance) + { + _framesSinceUpdate = 0; + + if (ProfilerDebugAmount != DebugInfoType.None) + Profiler.BeginSample("Maintenance Task"); + + RemoveUnused(); + + if (ProfilerDebugAmount != DebugInfoType.None) + Profiler.EndSample(); + } + + if (_exceptions.Count > 0) + throw _exceptions.Dequeue(); + } + } + + void FixedUpdate() + { + if (_nextFixedUpdateProcessSlot > 0) + { + ProcessIndex coindex = new ProcessIndex { seg = Segment.FixedUpdate }; + _runningFixedUpdate = true; + UpdateTimeValues(coindex.seg); + + for (coindex.i = 0; coindex.i < _nextFixedUpdateProcessSlot; coindex.i++) + { + if (!FixedUpdatePaused[coindex.i] && FixedUpdateProcesses[coindex.i] != null && !(localTime < FixedUpdateProcesses[coindex.i].Current)) + { + if (ProfilerDebugAmount != DebugInfoType.None) + { + Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine, " + + (_processLayers.ContainsKey(coindex) ? "layer " + _processLayers[coindex] : "no layer") + + (_processTags.ContainsKey(coindex) ? ", tag " + _processTags[coindex] : ", no tag")) : "Processing Coroutine"); + } + + try + { + if (!FixedUpdateProcesses[coindex.i].MoveNext()) + { + FixedUpdateProcesses[coindex.i] = null; + } + else if (FixedUpdateProcesses[coindex.i] != null && float.IsNaN(FixedUpdateProcesses[coindex.i].Current)) + { + if (ReplacementFunction == null) + { + FixedUpdateProcesses[coindex.i] = null; + } + else + { + FixedUpdateProcesses[coindex.i] = ReplacementFunction(FixedUpdateProcesses[coindex.i], this, _indexToHandle[coindex]); + + ReplacementFunction = null; + coindex.i--; + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + FixedUpdateProcesses[coindex.i] = null; + } + + if (ProfilerDebugAmount != DebugInfoType.None) + Profiler.EndSample(); + } + } + + _runningFixedUpdate = false; + } + + if (_exceptions.Count > 0) + throw _exceptions.Dequeue(); + } + + void LateUpdate() + { + if (_nextLateUpdateProcessSlot > 0) + { + ProcessIndex coindex = new ProcessIndex { seg = Segment.LateUpdate }; + _runningLateUpdate = true; + UpdateTimeValues(coindex.seg); + + for (coindex.i = 0; coindex.i < _nextLateUpdateProcessSlot; coindex.i++) + { + if (!LateUpdatePaused[coindex.i] && LateUpdateProcesses[coindex.i] != null && !(localTime < LateUpdateProcesses[coindex.i].Current)) + { + if (ProfilerDebugAmount != DebugInfoType.None) + { + Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine, " + + (_processLayers.ContainsKey(coindex) ? "layer " + _processLayers[coindex] : "no layer") + + (_processTags.ContainsKey(coindex) ? ", tag " + _processTags[coindex] : ", no tag")) : "Processing Coroutine"); + } + + try + { + if (!LateUpdateProcesses[coindex.i].MoveNext()) + { + LateUpdateProcesses[coindex.i] = null; + } + else if (LateUpdateProcesses[coindex.i] != null && float.IsNaN(LateUpdateProcesses[coindex.i].Current)) + { + if (ReplacementFunction == null) + { + LateUpdateProcesses[coindex.i] = null; + } + else + { + LateUpdateProcesses[coindex.i] = ReplacementFunction(LateUpdateProcesses[coindex.i], this, _indexToHandle[coindex]); + + ReplacementFunction = null; + coindex.i--; + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + LateUpdateProcesses[coindex.i] = null; + } + + if (ProfilerDebugAmount != DebugInfoType.None) + Profiler.EndSample(); + } + } + + _runningLateUpdate = false; + } + + if (_exceptions.Count > 0) + throw _exceptions.Dequeue(); + } + + /// <summary> + /// This will trigger an update in the manual timeframe segment. If the AutoTriggerManualTimeframeDuringUpdate variable is set to true + /// then this function will be automitically called every Update, so you would normally want to set that variable to false before + /// calling this function yourself. + /// </summary> + public void TriggerManualTimeframeUpdate() + { + if (_nextManualTimeframeProcessSlot > 0) + { + ProcessIndex coindex = new ProcessIndex { seg = Segment.ManualTimeframe }; + UpdateTimeValues(coindex.seg); + + for (coindex.i = 0; coindex.i < _nextManualTimeframeProcessSlot; coindex.i++) + { + if (!ManualTimeframePaused[coindex.i] && ManualTimeframeProcesses[coindex.i] != null && + !(localTime < ManualTimeframeProcesses[coindex.i].Current)) + { + if (ProfilerDebugAmount != DebugInfoType.None) + { + Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine (Manual Timeframe), " + + (_processLayers.ContainsKey(coindex) ? "layer " + _processLayers[coindex] : "no layer") + + (_processTags.ContainsKey(coindex) ? ", tag " + _processTags[coindex] : ", no tag")) + : "Processing Coroutine (Manual Timeframe)"); + } + + try + { + if (!ManualTimeframeProcesses[coindex.i].MoveNext()) + { + ManualTimeframeProcesses[coindex.i] = null; + } + else if (ManualTimeframeProcesses[coindex.i] != null && float.IsNaN(ManualTimeframeProcesses[coindex.i].Current)) + { + if (ReplacementFunction == null) + { + ManualTimeframeProcesses[coindex.i] = null; + } + else + { + ManualTimeframeProcesses[coindex.i] = ReplacementFunction(ManualTimeframeProcesses[coindex.i], + this, _indexToHandle[coindex]); + + ReplacementFunction = null; + coindex.i--; + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + ManualTimeframeProcesses[coindex.i] = null; + } + + if (ProfilerDebugAmount != DebugInfoType.None) + Profiler.EndSample(); + } + } + } + + if (++_framesSinceUpdate > FramesUntilMaintenance) + { + _framesSinceUpdate = 0; + + if (ProfilerDebugAmount != DebugInfoType.None) + Profiler.BeginSample("Maintenance Task"); + + RemoveUnused(); + + if (ProfilerDebugAmount != DebugInfoType.None) + Profiler.EndSample(); + } + + if (_exceptions.Count > 0) + throw _exceptions.Dequeue(); + } + + private bool OnEditorStart() + { +#if UNITY_EDITOR + if (EditorApplication.isPlayingOrWillChangePlaymode) + return false; + + if (_lastEditorUpdateTime == 0d) + _lastEditorUpdateTime = EditorApplication.timeSinceStartup; + + EditorApplication.update -= OnEditorUpdate; + + EditorApplication.update += OnEditorUpdate; + + return true; +#else + return false; +#endif + } + +#if UNITY_EDITOR + private void OnEditorUpdate() + { + if (EditorApplication.isPlayingOrWillChangePlaymode) + { + for (int i = 0; i < _nextEditorUpdateProcessSlot; i++) + EditorUpdateProcesses[i] = null; + _nextEditorUpdateProcessSlot = 0; + for (int i = 0; i < _nextEditorSlowUpdateProcessSlot; i++) + EditorSlowUpdateProcesses[i] = null; + _nextEditorSlowUpdateProcessSlot = 0; + + EditorApplication.update -= OnEditorUpdate; + _instance = null; + } + + if (_lastEditorSlowUpdateTime + TimeBetweenSlowUpdateCalls < EditorApplication.timeSinceStartup && _nextEditorSlowUpdateProcessSlot > 0) + { + ProcessIndex coindex = new ProcessIndex { seg = Segment.EditorSlowUpdate }; + UpdateTimeValues(coindex.seg); + + for (coindex.i = 0; coindex.i < _nextEditorSlowUpdateProcessSlot; coindex.i++) + { + if (!EditorSlowUpdatePaused[coindex.i] && EditorSlowUpdateProcesses[coindex.i] != null && + !(EditorApplication.timeSinceStartup < EditorSlowUpdateProcesses[coindex.i].Current)) + { + try + { + if (!EditorSlowUpdateProcesses[coindex.i].MoveNext()) + { + EditorSlowUpdateProcesses[coindex.i] = null; + } + else if (EditorSlowUpdateProcesses[coindex.i] != null && float.IsNaN(EditorSlowUpdateProcesses[coindex.i].Current)) + { + if (ReplacementFunction == null) + { + EditorSlowUpdateProcesses[coindex.i] = null; + } + else + { + EditorSlowUpdateProcesses[coindex.i] = ReplacementFunction(EditorSlowUpdateProcesses[coindex.i], + this, _indexToHandle[coindex]); + + ReplacementFunction = null; + coindex.i--; + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + EditorSlowUpdateProcesses[coindex.i] = null; + } + } + } + } + + if (_nextEditorUpdateProcessSlot > 0) + { + ProcessIndex coindex = new ProcessIndex { seg = Segment.EditorUpdate }; + _runningEditorUpdate = true; + UpdateTimeValues(coindex.seg); + + for (coindex.i = 0; coindex.i < _nextEditorUpdateProcessSlot; coindex.i++) + { + if (!EditorUpdatePaused[coindex.i] && EditorUpdateProcesses[coindex.i] != null && + !(EditorApplication.timeSinceStartup < EditorUpdateProcesses[coindex.i].Current)) + { + try + { + if (!EditorUpdateProcesses[coindex.i].MoveNext()) + { + EditorUpdateProcesses[coindex.i] = null; + } + else if (EditorUpdateProcesses[coindex.i] != null && float.IsNaN(EditorUpdateProcesses[coindex.i].Current)) + { + if (ReplacementFunction == null) + { + EditorUpdateProcesses[coindex.i] = null; + } + else + { + EditorUpdateProcesses[coindex.i] = ReplacementFunction(EditorUpdateProcesses[coindex.i], this, _indexToHandle[coindex]); + + ReplacementFunction = null; + coindex.i--; + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + EditorUpdateProcesses[coindex.i] = null; + } + } + } + + _runningEditorUpdate = false; + } + + if (++_framesSinceUpdate > FramesUntilMaintenance) + { + _framesSinceUpdate = 0; + + EditorRemoveUnused(); + } + + if (_exceptions.Count > 0) + throw _exceptions.Dequeue(); + } +#endif + + private IEnumerator<float> _EOFPumpWatcher() + { + while (_nextEndOfFrameProcessSlot > 0) + { + if (!_EOFPumpRan) + base.StartCoroutine(_EOFPump()); + + _EOFPumpRan = false; + + yield return 0f; + } + + _EOFPumpRan = false; + } + + private System.Collections.IEnumerator _EOFPump() + { + while (_nextEndOfFrameProcessSlot > 0) + { + yield return _EOFWaitObject; + + ProcessIndex coindex = new ProcessIndex { seg = Segment.EndOfFrame }; + _EOFPumpRan = true; + UpdateTimeValues(coindex.seg); + + for (coindex.i = 0; coindex.i < _nextEndOfFrameProcessSlot; coindex.i++) + { + if (!EndOfFramePaused[coindex.i] && EndOfFrameProcesses[coindex.i] != null && !(localTime < EndOfFrameProcesses[coindex.i].Current)) + { + if (ProfilerDebugAmount != DebugInfoType.None) + { + Profiler.BeginSample(ProfilerDebugAmount == DebugInfoType.SeperateTags ? ("Processing Coroutine, " + + (_processLayers.ContainsKey(coindex) ? "layer " + _processLayers[coindex] : "no layer") + + (_processTags.ContainsKey(coindex) ? ", tag " + _processTags[coindex] : ", no tag")) : "Processing Coroutine"); + } + + try + { + if (!EndOfFrameProcesses[coindex.i].MoveNext()) + { + EndOfFrameProcesses[coindex.i] = null; + } + else if (EndOfFrameProcesses[coindex.i] != null && float.IsNaN(EndOfFrameProcesses[coindex.i].Current)) + { + if (ReplacementFunction == null) + { + EndOfFrameProcesses[coindex.i] = null; + } + else + { + EndOfFrameProcesses[coindex.i] = ReplacementFunction(EndOfFrameProcesses[coindex.i], this, _indexToHandle[coindex]); + + ReplacementFunction = null; + coindex.i--; + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + EndOfFrameProcesses[coindex.i] = null; + } + + if (ProfilerDebugAmount != DebugInfoType.None) + Profiler.EndSample(); + } + } + } + } + + private void RemoveUnused() + { + var waitTrigsEnum = _waitingTriggers.GetEnumerator(); + while (waitTrigsEnum.MoveNext()) + { + if (waitTrigsEnum.Current.Value.Count == 0) + { + _waitingTriggers.Remove(waitTrigsEnum.Current.Key); + waitTrigsEnum = _waitingTriggers.GetEnumerator(); + continue; + } + + if (_handleToIndex.ContainsKey(waitTrigsEnum.Current.Key) && CoindexIsNull(_handleToIndex[waitTrigsEnum.Current.Key])) + { + CloseWaitingProcess(waitTrigsEnum.Current.Key); + waitTrigsEnum = _waitingTriggers.GetEnumerator(); + } + } + + ProcessIndex outer, inner; + outer.seg = inner.seg = Segment.Update; + for (outer.i = inner.i = 0; outer.i < _nextUpdateProcessSlot; outer.i++) + { + if (UpdateProcesses[outer.i] != null) + { + if (outer.i != inner.i) + { + UpdateProcesses[inner.i] = UpdateProcesses[outer.i]; + UpdatePaused[inner.i] = UpdatePaused[outer.i]; + MoveGraffiti(outer, inner); + } + inner.i++; + } + } + for (outer.i = inner.i; outer.i < _nextUpdateProcessSlot; outer.i++) + { + UpdateProcesses[outer.i] = null; + UpdatePaused[outer.i] = false; + RemoveGraffiti(outer); + } + + UpdateCoroutines = _nextUpdateProcessSlot = inner.i; + + outer.seg = inner.seg = Segment.FixedUpdate; + for (outer.i = inner.i = 0; outer.i < _nextFixedUpdateProcessSlot; outer.i++) + { + if (FixedUpdateProcesses[outer.i] != null) + { + if (outer.i != inner.i) + { + FixedUpdateProcesses[inner.i] = FixedUpdateProcesses[outer.i]; + FixedUpdatePaused[inner.i] = FixedUpdatePaused[outer.i]; + MoveGraffiti(outer, inner); + } + inner.i++; + } + } + for (outer.i = inner.i; outer.i < _nextFixedUpdateProcessSlot; outer.i++) + { + FixedUpdateProcesses[outer.i] = null; + FixedUpdatePaused[outer.i] = false; + RemoveGraffiti(outer); + } + + FixedUpdateCoroutines = _nextFixedUpdateProcessSlot = inner.i; + + outer.seg = inner.seg = Segment.LateUpdate; + for (outer.i = inner.i = 0; outer.i < _nextLateUpdateProcessSlot; outer.i++) + { + if (LateUpdateProcesses[outer.i] != null) + { + if (outer.i != inner.i) + { + LateUpdateProcesses[inner.i] = LateUpdateProcesses[outer.i]; + LateUpdatePaused[inner.i] = LateUpdatePaused[outer.i]; + MoveGraffiti(outer, inner); + } + inner.i++; + } + } + for (outer.i = inner.i; outer.i < _nextLateUpdateProcessSlot; outer.i++) + { + LateUpdateProcesses[outer.i] = null; + LateUpdatePaused[outer.i] = false; + RemoveGraffiti(outer); + } + + LateUpdateCoroutines = _nextLateUpdateProcessSlot = inner.i; + + outer.seg = inner.seg = Segment.SlowUpdate; + for (outer.i = inner.i = 0; outer.i < _nextSlowUpdateProcessSlot; outer.i++) + { + if (SlowUpdateProcesses[outer.i] != null) + { + if (outer.i != inner.i) + { + SlowUpdateProcesses[inner.i] = SlowUpdateProcesses[outer.i]; + SlowUpdatePaused[inner.i] = SlowUpdatePaused[outer.i]; + MoveGraffiti(outer, inner); + } + inner.i++; + } + } + for (outer.i = inner.i; outer.i < _nextSlowUpdateProcessSlot; outer.i++) + { + SlowUpdateProcesses[outer.i] = null; + SlowUpdatePaused[outer.i] = false; + RemoveGraffiti(outer); + } + + SlowUpdateCoroutines = _nextSlowUpdateProcessSlot = inner.i; + + outer.seg = inner.seg = Segment.RealtimeUpdate; + for (outer.i = inner.i = 0; outer.i < _nextRealtimeUpdateProcessSlot; outer.i++) + { + if (RealtimeUpdateProcesses[outer.i] != null) + { + if (outer.i != inner.i) + { + RealtimeUpdateProcesses[inner.i] = RealtimeUpdateProcesses[outer.i]; + RealtimeUpdatePaused[inner.i] = RealtimeUpdatePaused[outer.i]; + MoveGraffiti(outer, inner); + } + inner.i++; + } + } + for (outer.i = inner.i; outer.i < _nextRealtimeUpdateProcessSlot; outer.i++) + { + RealtimeUpdateProcesses[outer.i] = null; + RealtimeUpdatePaused[outer.i] = false; + RemoveGraffiti(outer); + } + + RealtimeUpdateCoroutines = _nextRealtimeUpdateProcessSlot = inner.i; + + outer.seg = inner.seg = Segment.EndOfFrame; + for (outer.i = inner.i = 0; outer.i < _nextEndOfFrameProcessSlot; outer.i++) + { + if (EndOfFrameProcesses[outer.i] != null) + { + if (outer.i != inner.i) + { + EndOfFrameProcesses[inner.i] = EndOfFrameProcesses[outer.i]; + EndOfFramePaused[inner.i] = EndOfFramePaused[outer.i]; + MoveGraffiti(outer, inner); + } + inner.i++; + } + } + for (outer.i = inner.i; outer.i < _nextEndOfFrameProcessSlot; outer.i++) + { + EndOfFrameProcesses[outer.i] = null; + EndOfFramePaused[outer.i] = false; + RemoveGraffiti(outer); + } + + EndOfFrameCoroutines = _nextEndOfFrameProcessSlot = inner.i; + + outer.seg = inner.seg = Segment.ManualTimeframe; + for (outer.i = inner.i = 0; outer.i < _nextManualTimeframeProcessSlot; outer.i++) + { + if (ManualTimeframeProcesses[outer.i] != null) + { + if (outer.i != inner.i) + { + ManualTimeframeProcesses[inner.i] = ManualTimeframeProcesses[outer.i]; + ManualTimeframePaused[inner.i] = ManualTimeframePaused[outer.i]; + MoveGraffiti(outer, inner); + } + inner.i++; + } + } + for (outer.i = inner.i; outer.i < _nextManualTimeframeProcessSlot; outer.i++) + { + ManualTimeframeProcesses[outer.i] = null; + ManualTimeframePaused[outer.i] = false; + RemoveGraffiti(outer); + } + + ManualTimeframeCoroutines = _nextManualTimeframeProcessSlot = inner.i; + } + + private void EditorRemoveUnused() + { + var waitTrigsEnum = _waitingTriggers.GetEnumerator(); + while (waitTrigsEnum.MoveNext()) + { + if (_handleToIndex.ContainsKey(waitTrigsEnum.Current.Key) && CoindexIsNull(_handleToIndex[waitTrigsEnum.Current.Key])) + { + CloseWaitingProcess(waitTrigsEnum.Current.Key); + waitTrigsEnum = _waitingTriggers.GetEnumerator(); + } + } + + ProcessIndex outer, inner; + outer.seg = inner.seg = Segment.EditorUpdate; + for (outer.i = inner.i = 0; outer.i < _nextEditorUpdateProcessSlot; outer.i++) + { + if (EditorUpdateProcesses[outer.i] != null) + { + if (outer.i != inner.i) + { + EditorUpdateProcesses[inner.i] = EditorUpdateProcesses[outer.i]; + EditorUpdatePaused[inner.i] = EditorUpdatePaused[outer.i]; + MoveGraffiti(outer, inner); + } + inner.i++; + } + } + for (outer.i = inner.i; outer.i < _nextEditorUpdateProcessSlot; outer.i++) + { + EditorUpdateProcesses[outer.i] = null; + EditorUpdatePaused[outer.i] = false; + RemoveGraffiti(outer); + } + + EditorUpdateCoroutines = _nextEditorUpdateProcessSlot = inner.i; + + outer.seg = inner.seg = Segment.EditorSlowUpdate; + for (outer.i = inner.i = 0; outer.i < _nextEditorSlowUpdateProcessSlot; outer.i++) + { + if (EditorSlowUpdateProcesses[outer.i] != null) + { + if (outer.i != inner.i) + { + EditorSlowUpdateProcesses[inner.i] = EditorSlowUpdateProcesses[outer.i]; + EditorUpdatePaused[inner.i] = EditorUpdatePaused[outer.i]; + MoveGraffiti(outer, inner); + } + inner.i++; + } + } + for (outer.i = inner.i; outer.i < _nextEditorSlowUpdateProcessSlot; outer.i++) + { + EditorSlowUpdateProcesses[outer.i] = null; + EditorSlowUpdatePaused[outer.i] = false; + RemoveGraffiti(outer); + } + + EditorSlowUpdateCoroutines = _nextEditorSlowUpdateProcessSlot = inner.i; + } + + /// <summary> + /// Retrieves the MEC manager that corresponds to the supplied instance id. + /// </summary> + /// <param name="ID">The instance ID.</param> + /// <returns>The manager, or null if not found.</returns> + public static Timing GetInstance(byte ID) + { + return ActiveInstances.ContainsKey(ID) ? ActiveInstances[ID] : null; + } + + /// <summary> + /// Run a new coroutine in the Update segment. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public static CoroutineHandle RunCoroutine(IEnumerator<float> coroutine) + { + return coroutine == null ? new CoroutineHandle() + : Instance.RunCoroutineInternal(coroutine, Segment.Update, null, null, new CoroutineHandle(Instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine in the Update segment. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="tag">An optional tag to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public static CoroutineHandle RunCoroutine(IEnumerator<float> coroutine, string tag) + { + return coroutine == null ? new CoroutineHandle() + : Instance.RunCoroutineInternal(coroutine, Segment.Update, null, tag, new CoroutineHandle(Instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine in the Update segment. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="layer">An optional layer to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public static CoroutineHandle RunCoroutine(IEnumerator<float> coroutine, int layer) + { + return coroutine == null ? new CoroutineHandle() + : Instance.RunCoroutineInternal(coroutine, Segment.Update, layer, null, new CoroutineHandle(Instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine in the Update segment. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="tag">An optional tag to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <param name="layer">An optional layer to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public static CoroutineHandle RunCoroutine(IEnumerator<float> coroutine, int layer, string tag) + { + return coroutine == null ? new CoroutineHandle() + : Instance.RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(Instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public static CoroutineHandle RunCoroutine(IEnumerator<float> coroutine, Segment timing) + { + return coroutine == null ? new CoroutineHandle() + : Instance.RunCoroutineInternal(coroutine, timing, null, null, new CoroutineHandle(Instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <param name="tag">An optional tag to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public static CoroutineHandle RunCoroutine(IEnumerator<float> coroutine, Segment timing, string tag) + { + return coroutine == null ? new CoroutineHandle() + : Instance.RunCoroutineInternal(coroutine, timing, null, tag, new CoroutineHandle(Instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <param name="layer">An optional layer to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public static CoroutineHandle RunCoroutine(IEnumerator<float> coroutine, Segment timing, int layer) + { + return coroutine == null ? new CoroutineHandle() + : Instance.RunCoroutineInternal(coroutine, timing, layer, null, new CoroutineHandle(Instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <param name="tag">An optional tag to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <param name="layer">An optional layer to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public static CoroutineHandle RunCoroutine(IEnumerator<float> coroutine, Segment timing, int layer, string tag) + { + return coroutine == null ? new CoroutineHandle() + : Instance.RunCoroutineInternal(coroutine, timing, layer, tag, new CoroutineHandle(Instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine on this Timing instance in the Update segment. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public CoroutineHandle RunCoroutineOnInstance(IEnumerator<float> coroutine) + { + return coroutine == null ? new CoroutineHandle() + : RunCoroutineInternal(coroutine, Segment.Update, null, null, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine on this Timing instance in the Update segment. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="tag">An optional tag to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public CoroutineHandle RunCoroutineOnInstance(IEnumerator<float> coroutine, string tag) + { + return coroutine == null ? new CoroutineHandle() + : RunCoroutineInternal(coroutine, Segment.Update, null, tag, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine on this Timing instance in the Update segment. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="layer">An optional layer to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public CoroutineHandle RunCoroutineOnInstance(IEnumerator<float> coroutine, int layer) + { + return coroutine == null ? new CoroutineHandle() + : RunCoroutineInternal(coroutine, Segment.Update, layer, null, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine on this Timing instance in the Update segment. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="layer">An optional layer to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <param name="tag">An optional tag to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public CoroutineHandle RunCoroutineOnInstance(IEnumerator<float> coroutine, int layer, string tag) + { + return coroutine == null ? new CoroutineHandle() + : RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine on this Timing instance. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public CoroutineHandle RunCoroutineOnInstance(IEnumerator<float> coroutine, Segment timing) + { + return coroutine == null ? new CoroutineHandle() + : RunCoroutineInternal(coroutine, timing, null, null, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine on this Timing instance. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <param name="tag">An optional tag to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public CoroutineHandle RunCoroutineOnInstance(IEnumerator<float> coroutine, Segment timing, string tag) + { + return coroutine == null ? new CoroutineHandle() + : RunCoroutineInternal(coroutine, timing, null, tag, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine on this Timing instance. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <param name="layer">An optional layer to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public CoroutineHandle RunCoroutineOnInstance(IEnumerator<float> coroutine, Segment timing, int layer) + { + return coroutine == null ? new CoroutineHandle() + : RunCoroutineInternal(coroutine, timing, layer, null, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine on this Timing instance. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <param name="layer">An optional layer to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <param name="tag">An optional tag to attach to the coroutine which can later be used to identify this coroutine.</param> + /// <returns>The coroutine's handle, which can be used for Wait and Kill operations.</returns> + public CoroutineHandle RunCoroutineOnInstance(IEnumerator<float> coroutine, Segment timing, int layer, string tag) + { + return coroutine == null ? new CoroutineHandle() + : RunCoroutineInternal(coroutine, timing, layer, tag, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine in the Update segment with the supplied tag unless there is already one or more coroutines running with that tag. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="tag">A tag to attach to the coroutine, and to check for existing instances.</param> + /// <param name="overwrite">True will kill any pre-existing coroutines. False will only start this coroutine if none exist.</param> + /// <returns>The newly created or existing handle.</returns> + public static CoroutineHandle RunCoroutineSingleton(IEnumerator<float> coroutine, string tag, bool overwrite = false) + { + if (_instance == null || coroutine == null) + return new CoroutineHandle(); + + if (overwrite) + { + KillCoroutines(tag); + } + else if (_instance._taggedProcesses.ContainsKey(tag)) + { + var indexEnum = _instance._taggedProcesses[tag].GetEnumerator(); + if (indexEnum.MoveNext()) + return _instance._indexToHandle[indexEnum.Current]; + } + + return _instance.RunCoroutineInternal(coroutine, Segment.Update, null, tag, new CoroutineHandle(_instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine in the Update segment with the supplied tag unless there is already one or more coroutines running with that tag. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="layer">A layer to attach to the coroutine, and to check for existing instances.</param> + /// <param name="overwrite">True will kill any pre-existing coroutines. False will only start this coroutine if none exist.</param> + /// <returns>The newly created or existing handle.</returns> + public static CoroutineHandle RunCoroutineSingleton(IEnumerator<float> coroutine, int layer, bool overwrite = false) + { + if (_instance == null || coroutine == null) + return new CoroutineHandle(); + + if (overwrite) + { + KillCoroutines(layer); + } + else if (_instance._layeredProcesses.ContainsKey(layer)) + { + var indexEnum = _instance._layeredProcesses[layer].GetEnumerator(); + if (indexEnum.MoveNext()) + return _instance._indexToHandle[indexEnum.Current]; + } + + return _instance.RunCoroutineInternal(coroutine, Segment.Update, layer, null, new CoroutineHandle(_instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine in the Update segment with the supplied tag unless there is already one or more coroutines running with that tag. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="layer">A layer to attach to the coroutine, and to check for existing instances.</param> + /// <param name="tag">A tag to attach to the coroutine, and to check for existing instances.</param> + /// <param name="overwrite">True will kill any pre-existing coroutines. False will only start this coroutine if none exist.</param> + /// <returns>The newly created or existing handle.</returns> + public static CoroutineHandle RunCoroutineSingleton(IEnumerator<float> coroutine, int layer, string tag, bool overwrite = false) + { + if (_instance == null || coroutine == null) + return new CoroutineHandle(); + + if (overwrite) + { + KillCoroutines(layer, tag); + return _instance.RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instance._instanceID), true); + } + + if (!_instance._taggedProcesses.ContainsKey(tag) || !_instance._layeredProcesses.ContainsKey(layer)) + return _instance.RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instance._instanceID), true); + + var matchesEnum = _instance._taggedProcesses[tag].GetEnumerator(); + while (matchesEnum.MoveNext()) + if (_instance._processLayers.ContainsKey(matchesEnum.Current) && _instance._processLayers[matchesEnum.Current] == layer) + return _instance._indexToHandle[matchesEnum.Current]; + + return _instance.RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine with the supplied tag unless there is already one or more coroutines running with that tag. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <param name="layer">A layer to attach to the coroutine, and to check for existing instances.</param> + /// <param name="overwrite">True will kill any pre-existing coroutines. False will only start this coroutine if none exist.</param> + /// <returns>The newly created or existing handle.</returns> + public static CoroutineHandle RunCoroutineSingleton(IEnumerator<float> coroutine, Segment timing, int layer, bool overwrite = false) + { + if (_instance == null || coroutine == null) + return new CoroutineHandle(); + + if (overwrite) + { + KillCoroutines(layer); + } + else if (_instance._layeredProcesses.ContainsKey(layer)) + { + var indexEnum = _instance._layeredProcesses[layer].GetEnumerator(); + if (indexEnum.MoveNext()) + return _instance._indexToHandle[indexEnum.Current]; + } + + return _instance.RunCoroutineInternal(coroutine, timing, layer, null, new CoroutineHandle(_instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine with the supplied tag unless there is already one or more coroutines running with that tag. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <param name="tag">A tag to attach to the coroutine, and to check for existing instances.</param> + /// <param name="overwrite">True will kill any pre-existing coroutines. False will only start this coroutine if none exist.</param> + /// <returns>The newly created or existing handle.</returns> + public static CoroutineHandle RunCoroutineSingleton(IEnumerator<float> coroutine, Segment timing, string tag, bool overwrite = false) + { + if (_instance == null || coroutine == null) + return new CoroutineHandle(); + + if (overwrite) + { + KillCoroutines(tag); + } + else if (_instance._taggedProcesses.ContainsKey(tag)) + { + var indexEnum = _instance._taggedProcesses[tag].GetEnumerator(); + if (indexEnum.MoveNext()) + return _instance._indexToHandle[indexEnum.Current]; + } + + return _instance.RunCoroutineInternal(coroutine, timing, null, tag, new CoroutineHandle(_instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine with the supplied tag unless there is already one or more coroutines running with that tag. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <param name="layer">A layer to attach to the coroutine, and to check for existing instances.</param> + /// <param name="tag">A tag to attach to the coroutine, and to check for existing instances.</param> + /// <param name="overwrite">True will kill any pre-existing coroutines. False will only start this coroutine if none exist.</param> + /// <returns>The newly created or existing handle.</returns> + public static CoroutineHandle RunCoroutineSingleton(IEnumerator<float> coroutine, Segment timing, int layer, string tag, bool overwrite = false) + { + if (_instance == null || coroutine == null) + return new CoroutineHandle(); + + if (overwrite) + { + KillCoroutines(layer, tag); + return _instance.RunCoroutineInternal(coroutine, timing, layer, tag, new CoroutineHandle(_instance._instanceID), true); + } + + if (!_instance._taggedProcesses.ContainsKey(tag) || !_instance._layeredProcesses.ContainsKey(layer)) + return _instance.RunCoroutineInternal(coroutine, timing, layer, tag, new CoroutineHandle(_instance._instanceID), true); + + var matchesEnum = _instance._taggedProcesses[tag].GetEnumerator(); + while (matchesEnum.MoveNext()) + if (_instance._processLayers.ContainsKey(matchesEnum.Current) && _instance._processLayers[matchesEnum.Current] == layer) + return _instance._indexToHandle[matchesEnum.Current]; + + return _instance.RunCoroutineInternal(coroutine, timing, layer, tag, new CoroutineHandle(_instance._instanceID), true); + } + + /// <summary> + /// Run a new coroutine in the Update segment with the supplied tag unless there is already one or more coroutines running with that tag. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="layer">A layer to attach to the coroutine, and to check for existing instances.</param> + /// <param name="overwrite">True will kill any pre-existing coroutines. False will only start this coroutine if none exist.</param> + /// <returns>The newly created or existing handle.</returns> + public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator<float> coroutine, int layer, bool overwrite = false) + { + if (coroutine == null) + return new CoroutineHandle(); + + if (overwrite) + { + KillCoroutines(layer); + } + else if (_layeredProcesses.ContainsKey(layer)) + { + var indexEnum = _layeredProcesses[layer].GetEnumerator(); + if (indexEnum.MoveNext()) + return _indexToHandle[indexEnum.Current]; + } + + return RunCoroutineInternal(coroutine, Segment.Update, layer, null, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine in the Update segment with the supplied tag unless there is already one or more coroutines running with that tag. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="tag">A tag to attach to the coroutine, and to check for existing instances.</param> + /// <param name="overwrite">True will kill any pre-existing coroutines. False will only start this coroutine if none exist.</param> + /// <returns>The newly created or existing handle.</returns> + public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator<float> coroutine, string tag, bool overwrite = false) + { + if (coroutine == null) + return new CoroutineHandle(); + + if (overwrite) + { + KillCoroutines(tag); + } + else if (_taggedProcesses.ContainsKey(tag)) + { + var indexEnum = _taggedProcesses[tag].GetEnumerator(); + if (indexEnum.MoveNext()) + return _indexToHandle[indexEnum.Current]; + } + + return RunCoroutineInternal(coroutine, Segment.Update, null, tag, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine in the Update segment with the supplied tag unless there is already one or more coroutines running with that tag. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="layer">A layer to attach to the coroutine, and to check for existing instances.</param> + /// <param name="tag">A tag to attach to the coroutine, and to check for existing instances.</param> + /// <param name="overwrite">True will kill any pre-existing coroutines. False will only start this coroutine if none exist.</param> + /// <returns>The newly created or existing handle.</returns> + public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator<float> coroutine, int layer, string tag, bool overwrite = false) + { + if (coroutine == null) + return new CoroutineHandle(); + + if (overwrite) + { + KillCoroutines(layer, tag); + return RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instanceID), true); + } + + if (!_taggedProcesses.ContainsKey(tag) || !_layeredProcesses.ContainsKey(layer)) + return RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instanceID), true); + + var matchesEnum = _taggedProcesses[tag].GetEnumerator(); + while (matchesEnum.MoveNext()) + if (_processLayers.ContainsKey(matchesEnum.Current) && _processLayers[matchesEnum.Current] == layer) + return _indexToHandle[matchesEnum.Current]; + + return RunCoroutineInternal(coroutine, Segment.Update, layer, tag, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine with the supplied tag unless there is already one or more coroutines running with that tag. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <param name="layer">A layer to attach to the coroutine, and to check for existing instances.</param> + /// <param name="overwrite">True will kill any pre-existing coroutines. False will only start this coroutine if none exist.</param> + /// <returns>The newly created or existing handle.</returns> + public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator<float> coroutine, Segment timing, int layer, bool overwrite = false) + { + if (coroutine == null) + return new CoroutineHandle(); + + if (overwrite) + { + KillCoroutines(layer); + } + else if (_layeredProcesses.ContainsKey(layer)) + { + var indexEnum = _layeredProcesses[layer].GetEnumerator(); + if (indexEnum.MoveNext()) + return _indexToHandle[indexEnum.Current]; + } + + return RunCoroutineInternal(coroutine, timing, layer, null, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine with the supplied tag unless there is already one or more coroutines running with that tag. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <param name="tag">A tag to attach to the coroutine, and to check for existing instances.</param> + /// <param name="overwrite">True will kill any pre-existing coroutines. False will only start this coroutine if none exist.</param> + /// <returns>The newly created or existing handle.</returns> + public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator<float> coroutine, Segment timing, string tag, bool overwrite = false) + { + if (coroutine == null) + return new CoroutineHandle(); + + if (overwrite) + { + KillCoroutines(tag); + } + else if (_taggedProcesses.ContainsKey(tag)) + { + var indexEnum = _taggedProcesses[tag].GetEnumerator(); + if (indexEnum.MoveNext()) + return _indexToHandle[indexEnum.Current]; + } + + return RunCoroutineInternal(coroutine, timing, null, tag, new CoroutineHandle(_instanceID), true); + } + + /// <summary> + /// Run a new coroutine with the supplied tag unless there is already one or more coroutines running with that tag. + /// </summary> + /// <param name="coroutine">The new coroutine's handle.</param> + /// <param name="timing">The segment that the coroutine should run in.</param> + /// <param name="layer">A layer to attach to the coroutine, and to check for existing instances.</param> + /// <param name="tag">A tag to attach to the coroutine, and to check for existing instances.</param> + /// <param name="overwrite">True will kill any pre-existing coroutines. False will only start this coroutine if none exist.</param> + /// <returns>The newly created or existing handle.</returns> + public CoroutineHandle RunCoroutineSingletonOnInstance(IEnumerator<float> coroutine, Segment timing, int layer, string tag, bool overwrite = false) + { + if (coroutine == null) + return new CoroutineHandle(); + + if (overwrite) + { + KillCoroutines(layer, tag); + return RunCoroutineInternal(coroutine, timing, layer, tag, new CoroutineHandle(_instanceID), true); + } + + if (!_taggedProcesses.ContainsKey(tag) || !_layeredProcesses.ContainsKey(layer)) + return RunCoroutineInternal(coroutine, timing, layer, tag, new CoroutineHandle(_instanceID), true); + + var matchesEnum = _taggedProcesses[tag].GetEnumerator(); + while (matchesEnum.MoveNext()) + if (_processLayers.ContainsKey(matchesEnum.Current) && _processLayers[matchesEnum.Current] == layer) + return _indexToHandle[matchesEnum.Current]; + + return RunCoroutineInternal(coroutine, timing, layer, tag, new CoroutineHandle(_instanceID), true); + } + + private CoroutineHandle RunCoroutineInternal(IEnumerator<float> coroutine, Segment timing, int? layer, string tag, CoroutineHandle handle, bool prewarm) + { + ProcessIndex slot = new ProcessIndex { seg = timing }; + + if (_handleToIndex.ContainsKey(handle)) + { + _indexToHandle.Remove(_handleToIndex[handle]); + _handleToIndex.Remove(handle); + } + + switch (timing) + { + case Segment.Update: + + if (_nextUpdateProcessSlot >= UpdateProcesses.Length) + { + IEnumerator<float>[] oldProcArray = UpdateProcesses; + bool[] oldPausedArray = UpdatePaused; + + UpdateProcesses = new IEnumerator<float>[UpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; + UpdatePaused = new bool[UpdateProcesses.Length]; + + for (int i = 0; i < oldProcArray.Length; i++) + { + UpdateProcesses[i] = oldProcArray[i]; + UpdatePaused[i] = oldPausedArray[i]; + } + } + + slot.i = _nextUpdateProcessSlot++; + UpdateProcesses[slot.i] = coroutine; + + if (null != tag) + AddTag(tag, slot); + + if (layer.HasValue) + AddLayer((int)layer, slot); + + _indexToHandle.Add(slot, handle); + _handleToIndex.Add(handle, slot); + + if (!_runningUpdate && prewarm) + { + try + { + _runningUpdate = true; + SetTimeValues(slot.seg); + + if (UpdateProcesses[slot.i] != null && !UpdateProcesses[slot.i].MoveNext()) + { + UpdateProcesses[slot.i] = null; + } + else if (UpdateProcesses[slot.i] != null && float.IsNaN(UpdateProcesses[slot.i].Current)) + { + if (ReplacementFunction == null) + { + UpdateProcesses[slot.i] = null; + } + else + { + UpdateProcesses[slot.i] = ReplacementFunction(UpdateProcesses[slot.i], this, _indexToHandle[slot]); + + ReplacementFunction = null; + + if (UpdateProcesses[slot.i] != null) + UpdateProcesses[slot.i].MoveNext(); + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + UpdateProcesses[slot.i] = null; + } + finally + { + _runningUpdate = false; + } + } + + return handle; + + case Segment.FixedUpdate: + + if (_nextFixedUpdateProcessSlot >= FixedUpdateProcesses.Length) + { + IEnumerator<float>[] oldProcArray = FixedUpdateProcesses; + bool[] oldPausedArray = FixedUpdatePaused; + + FixedUpdateProcesses = new IEnumerator<float>[FixedUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; + FixedUpdatePaused = new bool[FixedUpdateProcesses.Length]; + + for (int i = 0; i < oldProcArray.Length; i++) + { + FixedUpdateProcesses[i] = oldProcArray[i]; + FixedUpdatePaused[i] = oldPausedArray[i]; + } + } + + slot.i = _nextFixedUpdateProcessSlot++; + FixedUpdateProcesses[slot.i] = coroutine; + + if (null != tag) + AddTag(tag, slot); + + if (layer.HasValue) + AddLayer((int)layer, slot); + + _indexToHandle.Add(slot, handle); + _handleToIndex.Add(handle, slot); + + if (!_runningFixedUpdate && prewarm) + { + try + { + _runningFixedUpdate = true; + SetTimeValues(slot.seg); + + if (FixedUpdateProcesses[slot.i] != null && !FixedUpdateProcesses[slot.i].MoveNext()) + { + FixedUpdateProcesses[slot.i] = null; + } + else if (FixedUpdateProcesses[slot.i] != null && float.IsNaN(FixedUpdateProcesses[slot.i].Current)) + { + if (ReplacementFunction == null) + { + FixedUpdateProcesses[slot.i] = null; + } + else + { + FixedUpdateProcesses[slot.i] = ReplacementFunction(FixedUpdateProcesses[slot.i], this, _indexToHandle[slot]); + + ReplacementFunction = null; + + if (FixedUpdateProcesses[slot.i] != null) + FixedUpdateProcesses[slot.i].MoveNext(); + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + FixedUpdateProcesses[slot.i] = null; + } + finally + { + _runningFixedUpdate = false; + } + } + + return handle; + + case Segment.LateUpdate: + + if (_nextLateUpdateProcessSlot >= LateUpdateProcesses.Length) + { + IEnumerator<float>[] oldProcArray = LateUpdateProcesses; + bool[] oldPausedArray = LateUpdatePaused; + + LateUpdateProcesses = new IEnumerator<float>[LateUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; + LateUpdatePaused = new bool[LateUpdateProcesses.Length]; + + for (int i = 0; i < oldProcArray.Length; i++) + { + LateUpdateProcesses[i] = oldProcArray[i]; + LateUpdatePaused[i] = oldPausedArray[i]; + } + } + + slot.i = _nextLateUpdateProcessSlot++; + LateUpdateProcesses[slot.i] = coroutine; + + if (null != tag) + AddTag(tag, slot); + + if (layer.HasValue) + AddLayer((int)layer, slot); + + _indexToHandle.Add(slot, handle); + _handleToIndex.Add(handle, slot); + + if (!_runningLateUpdate && prewarm) + { + try + { + _runningLateUpdate = true; + SetTimeValues(slot.seg); + + if (LateUpdateProcesses[slot.i] != null && !LateUpdateProcesses[slot.i].MoveNext()) + { + LateUpdateProcesses[slot.i] = null; + } + else if (LateUpdateProcesses[slot.i] != null && float.IsNaN(LateUpdateProcesses[slot.i].Current)) + { + if (ReplacementFunction == null) + { + LateUpdateProcesses[slot.i] = null; + } + else + { + LateUpdateProcesses[slot.i] = ReplacementFunction(LateUpdateProcesses[slot.i], + this, _indexToHandle[slot]); + + ReplacementFunction = null; + + if (LateUpdateProcesses[slot.i] != null) + LateUpdateProcesses[slot.i].MoveNext(); + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + LateUpdateProcesses[slot.i] = null; + } + finally + { + _runningLateUpdate = false; + } + } + + return handle; + + case Segment.SlowUpdate: + + if (_nextSlowUpdateProcessSlot >= SlowUpdateProcesses.Length) + { + IEnumerator<float>[] oldProcArray = SlowUpdateProcesses; + bool[] oldPausedArray = SlowUpdatePaused; + + SlowUpdateProcesses = new IEnumerator<float>[SlowUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; + SlowUpdatePaused = new bool[SlowUpdateProcesses.Length]; + + for (int i = 0; i < oldProcArray.Length; i++) + { + SlowUpdateProcesses[i] = oldProcArray[i]; + SlowUpdatePaused[i] = oldPausedArray[i]; + } + } + + slot.i = _nextSlowUpdateProcessSlot++; + SlowUpdateProcesses[slot.i] = coroutine; + + if (null != tag) + AddTag(tag, slot); + + if (layer.HasValue) + AddLayer((int)layer, slot); + + _indexToHandle.Add(slot, handle); + _handleToIndex.Add(handle, slot); + + return handle; + + case Segment.RealtimeUpdate: + + if (_nextRealtimeUpdateProcessSlot >= RealtimeUpdateProcesses.Length) + { + IEnumerator<float>[] oldProcArray = RealtimeUpdateProcesses; + bool[] oldPausedArray = RealtimeUpdatePaused; + + RealtimeUpdateProcesses = new IEnumerator<float>[RealtimeUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; + RealtimeUpdatePaused = new bool[RealtimeUpdateProcesses.Length]; + + for (int i = 0; i < oldProcArray.Length; i++) + { + RealtimeUpdateProcesses[i] = oldProcArray[i]; + RealtimeUpdatePaused[i] = oldPausedArray[i]; + } + } + + slot.i = _nextRealtimeUpdateProcessSlot++; + RealtimeUpdateProcesses[slot.i] = coroutine; + + if (null != tag) + AddTag(tag, slot); + + if (layer.HasValue) + AddLayer((int)layer, slot); + + _indexToHandle.Add(slot, handle); + _handleToIndex.Add(handle, slot); + + if (!_runningRealtimeUpdate && prewarm) + { + try + { + _runningRealtimeUpdate = true; + SetTimeValues(slot.seg); + + if (RealtimeUpdateProcesses[slot.i] != null && !RealtimeUpdateProcesses[slot.i].MoveNext()) + { + RealtimeUpdateProcesses[slot.i] = null; + } + else if (RealtimeUpdateProcesses[slot.i] != null && float.IsNaN(RealtimeUpdateProcesses[slot.i].Current)) + { + if (ReplacementFunction == null) + { + RealtimeUpdateProcesses[slot.i] = null; + } + else + { + RealtimeUpdateProcesses[slot.i] = ReplacementFunction(RealtimeUpdateProcesses[slot.i], + this, _indexToHandle[slot]); + + ReplacementFunction = null; + + if (RealtimeUpdateProcesses[slot.i] != null) + RealtimeUpdateProcesses[slot.i].MoveNext(); + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + RealtimeUpdateProcesses[slot.i] = null; + } + finally + { + _runningRealtimeUpdate = false; + } + } + + return handle; + + case Segment.EditorUpdate: + + if (!OnEditorStart()) + return new CoroutineHandle(); + + if (_nextEditorUpdateProcessSlot >= EditorUpdateProcesses.Length) + { + IEnumerator<float>[] oldProcArray = EditorUpdateProcesses; + bool[] oldPausedArray = EditorUpdatePaused; + + EditorUpdateProcesses = new IEnumerator<float>[EditorUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; + EditorUpdatePaused = new bool[EditorUpdateProcesses.Length]; + + for (int i = 0; i < oldProcArray.Length; i++) + { + EditorUpdateProcesses[i] = oldProcArray[i]; + EditorUpdatePaused[i] = oldPausedArray[i]; + } + } + + slot.i = _nextEditorUpdateProcessSlot++; + EditorUpdateProcesses[slot.i] = coroutine; + + if (null != tag) + AddTag(tag, slot); + + if (layer.HasValue) + AddLayer((int)layer, slot); + + _indexToHandle.Add(slot, handle); + _handleToIndex.Add(handle, slot); + + if (!_runningEditorUpdate && prewarm) + { + try + { + _runningEditorUpdate = true; + SetTimeValues(slot.seg); + + if (EditorUpdateProcesses[slot.i] != null && !EditorUpdateProcesses[slot.i].MoveNext()) + { + EditorUpdateProcesses[slot.i] = null; + } + else if (EditorUpdateProcesses[slot.i] != null && float.IsNaN(EditorUpdateProcesses[slot.i].Current)) + { + if (ReplacementFunction == null) + { + EditorUpdateProcesses[slot.i] = null; + } + else + { + EditorUpdateProcesses[slot.i] = ReplacementFunction(EditorUpdateProcesses[slot.i], + this, _indexToHandle[slot]); + + ReplacementFunction = null; + + if (EditorUpdateProcesses[slot.i] != null) + EditorUpdateProcesses[slot.i].MoveNext(); + } + } + } + catch (System.Exception ex) + { + if (OnError == null) + _exceptions.Enqueue(ex); + else + OnError(ex); + + EditorUpdateProcesses[slot.i] = null; + } + finally + { + _runningEditorUpdate = false; + } + } + + return handle; + + case Segment.EditorSlowUpdate: + + if (!OnEditorStart()) + return new CoroutineHandle(); + + if (_nextEditorSlowUpdateProcessSlot >= EditorSlowUpdateProcesses.Length) + { + IEnumerator<float>[] oldProcArray = EditorSlowUpdateProcesses; + bool[] oldPausedArray = EditorSlowUpdatePaused; + + EditorSlowUpdateProcesses = new IEnumerator<float>[EditorSlowUpdateProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; + EditorSlowUpdatePaused = new bool[EditorSlowUpdateProcesses.Length]; + + for (int i = 0; i < oldProcArray.Length; i++) + { + EditorSlowUpdateProcesses[i] = oldProcArray[i]; + EditorSlowUpdatePaused[i] = oldPausedArray[i]; + } + } + + slot.i = _nextEditorSlowUpdateProcessSlot++; + EditorSlowUpdateProcesses[slot.i] = coroutine; + + if (null != tag) + AddTag(tag, slot); + + if (layer.HasValue) + AddLayer((int)layer, slot); + + _indexToHandle.Add(slot, handle); + _handleToIndex.Add(handle, slot); + + return handle; + + case Segment.EndOfFrame: + + if (_nextEndOfFrameProcessSlot >= EndOfFrameProcesses.Length) + { + IEnumerator<float>[] oldProcArray = EndOfFrameProcesses; + bool[] oldPausedArray = EndOfFramePaused; + + EndOfFrameProcesses = new IEnumerator<float>[EndOfFrameProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; + EndOfFramePaused = new bool[EndOfFrameProcesses.Length]; + + for (int i = 0; i < oldProcArray.Length; i++) + { + EndOfFrameProcesses[i] = oldProcArray[i]; + EndOfFramePaused[i] = oldPausedArray[i]; + } + } + + slot.i = _nextEndOfFrameProcessSlot++; + EndOfFrameProcesses[slot.i] = coroutine; + + if (null != tag) + AddTag(tag, slot); + + if (layer.HasValue) + AddLayer((int)layer, slot); + + _indexToHandle.Add(slot, handle); + _handleToIndex.Add(handle, slot); + + RunCoroutineSingletonOnInstance(_EOFPumpWatcher(), "MEC_EOFPumpWatcher"); + + return handle; + + case Segment.ManualTimeframe: + + if (_nextManualTimeframeProcessSlot >= ManualTimeframeProcesses.Length) + { + IEnumerator<float>[] oldProcArray = ManualTimeframeProcesses; + bool[] oldPausedArray = ManualTimeframePaused; + + ManualTimeframeProcesses = new IEnumerator<float>[ManualTimeframeProcesses.Length + (ProcessArrayChunkSize * _expansions++)]; + ManualTimeframePaused = new bool[ManualTimeframeProcesses.Length]; + + for (int i = 0; i < oldProcArray.Length; i++) + { + ManualTimeframeProcesses[i] = oldProcArray[i]; + ManualTimeframePaused[i] = oldPausedArray[i]; + } + } + + slot.i = _nextManualTimeframeProcessSlot++; + ManualTimeframeProcesses[slot.i] = coroutine; + + if (null != tag) + AddTag(tag, slot); + + if (layer.HasValue) + AddLayer((int)layer, slot); + + _indexToHandle.Add(slot, handle); + _handleToIndex.Add(handle, slot); + + return handle; + + default: + return new CoroutineHandle(); + } + } + + /// <summary> + /// This will kill all coroutines running on the main MEC instance and reset the context. + /// </summary> + /// <returns>The number of coroutines that were killed.</returns> + public static int KillCoroutines() + { + return _instance == null ? 0 : _instance.KillCoroutinesOnInstance(); + } + + /// <summary> + /// This will kill all coroutines running on the current MEC instance and reset the context. + /// </summary> + /// <returns>The number of coroutines that were killed.</returns> + public int KillCoroutinesOnInstance() + { + int retVal = _nextUpdateProcessSlot + _nextLateUpdateProcessSlot + _nextFixedUpdateProcessSlot + _nextSlowUpdateProcessSlot + + _nextRealtimeUpdateProcessSlot + _nextEditorUpdateProcessSlot + _nextEditorSlowUpdateProcessSlot + + _nextEndOfFrameProcessSlot + _nextManualTimeframeProcessSlot; + + UpdateProcesses = new IEnumerator<float>[InitialBufferSizeLarge]; + UpdatePaused = new bool[InitialBufferSizeLarge]; + UpdateCoroutines = 0; + _nextUpdateProcessSlot = 0; + + LateUpdateProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; + LateUpdatePaused = new bool[InitialBufferSizeSmall]; + LateUpdateCoroutines = 0; + _nextLateUpdateProcessSlot = 0; + + FixedUpdateProcesses = new IEnumerator<float>[InitialBufferSizeMedium]; + FixedUpdatePaused = new bool[InitialBufferSizeMedium]; + FixedUpdateCoroutines = 0; + _nextFixedUpdateProcessSlot = 0; + + SlowUpdateProcesses = new IEnumerator<float>[InitialBufferSizeMedium]; + SlowUpdatePaused = new bool[InitialBufferSizeMedium]; + SlowUpdateCoroutines = 0; + _nextSlowUpdateProcessSlot = 0; + + RealtimeUpdateProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; + RealtimeUpdatePaused = new bool[InitialBufferSizeSmall]; + RealtimeUpdateCoroutines = 0; + _nextRealtimeUpdateProcessSlot = 0; + + EditorUpdateProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; + EditorUpdatePaused = new bool[InitialBufferSizeSmall]; + EditorUpdateCoroutines = 0; + _nextEditorUpdateProcessSlot = 0; + + EditorSlowUpdateProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; + EditorSlowUpdatePaused = new bool[InitialBufferSizeSmall]; + EditorSlowUpdateCoroutines = 0; + _nextEditorSlowUpdateProcessSlot = 0; + + EndOfFrameProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; + EndOfFramePaused = new bool[InitialBufferSizeSmall]; + EndOfFrameCoroutines = 0; + _nextEndOfFrameProcessSlot = 0; + + ManualTimeframeProcesses = new IEnumerator<float>[InitialBufferSizeSmall]; + ManualTimeframePaused = new bool[InitialBufferSizeSmall]; + ManualTimeframeCoroutines = 0; + _nextManualTimeframeProcessSlot = 0; + + _processTags.Clear(); + _taggedProcesses.Clear(); + _processLayers.Clear(); + _layeredProcesses.Clear(); + _handleToIndex.Clear(); + _indexToHandle.Clear(); + _waitingTriggers.Clear(); + _expansions = (ushort)((_expansions / 2) + 1); + + ResetTimeCountOnInstance(); + +#if UNITY_EDITOR + EditorApplication.update -= OnEditorUpdate; +#endif + return retVal; + } + + /// <summary> + /// Kills all coroutines that have the given tag. + /// </summary> + /// <param name="tag">All coroutines with this tag will be killed.</param> + /// <returns>The number of coroutines that were found and killed.</returns> + public static int KillCoroutines(string tag) + { + return _instance == null ? 0 : _instance.KillCoroutinesOnInstance(tag); + } + + /// <summary> + /// Kills all coroutines that have the given tag. + /// </summary> + /// <param name="tag">All coroutines with this tag will be killed.</param> + /// <returns>The number of coroutines that were found and killed.</returns> + public int KillCoroutinesOnInstance(string tag) + { + if (tag == null) return 0; + int numberFound = 0; + + while (_taggedProcesses.ContainsKey(tag)) + { + var matchEnum = _taggedProcesses[tag].GetEnumerator(); + matchEnum.MoveNext(); + + if (CoindexKill(matchEnum.Current)) + { + if (_waitingTriggers.ContainsKey(_indexToHandle[matchEnum.Current])) + CloseWaitingProcess(_indexToHandle[matchEnum.Current]); + + numberFound++; + } + + RemoveGraffiti(matchEnum.Current); + } + + return numberFound; + } + + /// <summary> + /// Kills all coroutines on the given layer. + /// </summary> + /// <param name="layer">All coroutines on this layer will be killed.</param> + /// <returns>The number of coroutines that were found and killed.</returns> + public static int KillCoroutines(int layer) + { + return _instance == null ? 0 : _instance.KillCoroutinesOnInstance(layer); + } + + /// <summary> + /// Kills all coroutines on the given layer. + /// </summary> + /// <param name="layer">All coroutines on this layer will be killed.</param> + /// <returns>The number of coroutines that were found and killed.</returns> + public int KillCoroutinesOnInstance(int layer) + { + int numberFound = 0; + + while (_layeredProcesses.ContainsKey(layer)) + { + var matchEnum = _layeredProcesses[layer].GetEnumerator(); + matchEnum.MoveNext(); + + if (CoindexKill(matchEnum.Current)) + { + if (_waitingTriggers.ContainsKey(_indexToHandle[matchEnum.Current])) + CloseWaitingProcess(_indexToHandle[matchEnum.Current]); + + numberFound++; + } + + RemoveGraffiti(matchEnum.Current); + } + + return numberFound; + } + + /// <summary> + /// Kills all coroutines with the given tag on the given layer. + /// </summary> + /// <param name="layer">All coroutines on this layer with the given tag will be killed.</param> + /// <param name="tag">All coroutines with this tag on the given layer will be killed.</param> + /// <returns>The number of coroutines that were found and killed.</returns> + public static int KillCoroutines(int layer, string tag) + { + return _instance == null ? 0 : _instance.KillCoroutinesOnInstance(layer, tag); + } + + /// <summary> + /// Kills all coroutines with the given tag on the given layer. + /// </summary> + /// <param name="layer">All coroutines on this layer with the given tag will be killed.</param> + /// <param name="tag">All coroutines with this tag on the given layer will be killed.</param> + /// <returns>The number of coroutines that were found and killed.</returns> + public int KillCoroutinesOnInstance(int layer, string tag) + { + if (tag == null) + return KillCoroutinesOnInstance(layer); + if (!_layeredProcesses.ContainsKey(layer) || !_taggedProcesses.ContainsKey(tag)) + return 0; + int count = 0; + + var indexesEnum = _taggedProcesses[tag].GetEnumerator(); + while (indexesEnum.MoveNext()) + { + if (CoindexIsNull(indexesEnum.Current) || !_layeredProcesses[layer].Contains(indexesEnum.Current) || !CoindexKill(indexesEnum.Current)) + continue; + + if (_waitingTriggers.ContainsKey(_indexToHandle[indexesEnum.Current])) + CloseWaitingProcess(_indexToHandle[indexesEnum.Current]); + + count++; + RemoveGraffiti(indexesEnum.Current); + + if (!_taggedProcesses.ContainsKey(tag) || !_layeredProcesses.ContainsKey(layer)) + break; + + indexesEnum = _taggedProcesses[tag].GetEnumerator(); + } + + return count; + } + + /// <summary> + /// Kills the instance of the coroutine handle if it exists. + /// </summary> + /// <param name="handle">The handle of the coroutine to kill.</param> + /// <returns>The number of coroutines that were found and killed (0 or 1).</returns> + public static int KillCoroutines(CoroutineHandle handle) + { + return ActiveInstances.ContainsKey(handle.Key) ? GetInstance(handle.Key).KillCoroutinesOnInstance(handle) : 0; + } + + /// <summary> + /// Kills the instance of the coroutine handle on this Timing instance if it exists. + /// </summary> + /// <param name="handle">The handle of the coroutine to kill.</param> + /// <returns>The number of coroutines that were found and killed (0 or 1).</returns> + public int KillCoroutinesOnInstance(CoroutineHandle handle) + { + bool foundOne = false; + + if (_handleToIndex.ContainsKey(handle)) + { + if (_waitingTriggers.ContainsKey(handle)) + CloseWaitingProcess(handle); + + foundOne = CoindexExtract(_handleToIndex[handle]) != null; + RemoveGraffiti(_handleToIndex[handle]); + } + + return foundOne ? 1 : 0; + } + + /// <summary> + /// Use "yield return Timing.WaitForSeconds(time);" to wait for the specified number of seconds. + /// </summary> + /// <param name="waitTime">Number of seconds to wait.</param> + public static float WaitForSeconds(float waitTime) + { + if (float.IsNaN(waitTime)) waitTime = 0f; + return LocalTime + waitTime; + } + + /// <summary> + /// Use "yield return timingInstance.WaitForSecondsOnInstance(time);" to wait for the specified number of seconds. + /// </summary> + /// <param name="waitTime">Number of seconds to wait.</param> + public float WaitForSecondsOnInstance(float waitTime) + { + if (float.IsNaN(waitTime)) waitTime = 0f; + return (float)localTime + waitTime; + } + + private void UpdateTimeValues(Segment segment) + { + switch (segment) + { + case Segment.Update: + deltaTime = Time.deltaTime; + _lastUpdateTime += deltaTime; + localTime = _lastUpdateTime; + return; + case Segment.LateUpdate: + deltaTime = Time.deltaTime; + _lastLateUpdateTime += deltaTime; + localTime = _lastLateUpdateTime; + return; + case Segment.FixedUpdate: + deltaTime = Time.deltaTime; + _lastFixedUpdateTime += deltaTime; + localTime = _lastFixedUpdateTime; + return; + case Segment.SlowUpdate: + if (_lastSlowUpdateTime == 0d) + deltaTime = TimeBetweenSlowUpdateCalls; + else + deltaTime = ThreadSafeElapsedTime.GetElapsedSecondsSinceStartUp() - (float)_lastSlowUpdateTime; + + localTime = _lastSlowUpdateTime = ThreadSafeElapsedTime.GetElapsedSecondsSinceStartUp(); + return; + case Segment.RealtimeUpdate: + deltaTime = Time.unscaledDeltaTime; + _lastRealtimeUpdateTime += deltaTime; + localTime = _lastRealtimeUpdateTime; + return; +#if UNITY_EDITOR + case Segment.EditorUpdate: + deltaTime = (float)(EditorApplication.timeSinceStartup - _lastEditorUpdateTime); + + if (deltaTime > Time.maximumDeltaTime) + deltaTime = Time.maximumDeltaTime; + + localTime = _lastEditorUpdateTime = EditorApplication.timeSinceStartup; + return; + case Segment.EditorSlowUpdate: + deltaTime = (float)(EditorApplication.timeSinceStartup - _lastEditorSlowUpdateTime); + localTime = _lastEditorSlowUpdateTime = EditorApplication.timeSinceStartup; + return; +#endif + case Segment.EndOfFrame: + deltaTime = Time.deltaTime; + localTime = _lastUpdateTime; + return; + case Segment.ManualTimeframe: + localTime = SetManualTimeframeTime == null ? Time.time : SetManualTimeframeTime(_lastManualTimeframeTime); + deltaTime = (float)(localTime - _lastManualTimeframeTime); + + if (deltaTime > Time.maximumDeltaTime) + deltaTime = Time.maximumDeltaTime; + + _lastManualTimeframeTime = localTime; + return; + } + } + + private void SetTimeValues(Segment segment) + { + switch (segment) + { + case Segment.Update: + deltaTime = Time.deltaTime; + localTime = _lastUpdateTime; + return; + case Segment.LateUpdate: + deltaTime = Time.deltaTime; + localTime = _lastLateUpdateTime; + return; + case Segment.FixedUpdate: + deltaTime = Time.deltaTime; + localTime = _lastFixedUpdateTime; + return; + case Segment.SlowUpdate: + deltaTime = ThreadSafeElapsedTime.GetElapsedSecondsSinceStartUp() - (float)_lastSlowUpdateTime; + localTime = _lastSlowUpdateTime; + return; + case Segment.RealtimeUpdate: + deltaTime = Time.unscaledDeltaTime; + localTime = _lastRealtimeUpdateTime; + return; +#if UNITY_EDITOR + case Segment.EditorUpdate: + deltaTime = (float)(EditorApplication.timeSinceStartup - _lastEditorUpdateTime); + + if (deltaTime > Time.maximumDeltaTime) + deltaTime = Time.maximumDeltaTime; + + localTime = _lastEditorUpdateTime; + return; + case Segment.EditorSlowUpdate: + deltaTime = (float)(EditorApplication.timeSinceStartup - _lastEditorSlowUpdateTime); + localTime = _lastEditorSlowUpdateTime; + return; +#endif + case Segment.EndOfFrame: + deltaTime = Time.deltaTime; + localTime = _lastUpdateTime; + return; + case Segment.ManualTimeframe: + deltaTime = Time.deltaTime; + localTime = _lastManualTimeframeTime; + return; + } + } + + private double GetSegmentTime(Segment segment) + { + switch (segment) + { + case Segment.Update: + return _lastUpdateTime; + case Segment.LateUpdate: + return _lastLateUpdateTime; + case Segment.FixedUpdate: + return _lastFixedUpdateTime; + case Segment.SlowUpdate: + return _lastSlowUpdateTime; + case Segment.RealtimeUpdate: + return _lastRealtimeUpdateTime; + case Segment.EditorUpdate: + return _lastEditorUpdateTime; + case Segment.EditorSlowUpdate: + return _lastEditorSlowUpdateTime; + case Segment.EndOfFrame: + return _lastUpdateTime; + case Segment.ManualTimeframe: + return _lastManualTimeframeTime; + default: + return 0d; + } + } + + /// <summary> + /// Resets the value of LocalTime to zero (only for the Update, LateUpdate, FixedUpdate, and RealtimeUpdate loops). + /// </summary> + public void ResetTimeCountOnInstance() + { + localTime = 0d; + + _lastUpdateTime = 0d; + _lastLateUpdateTime = 0d; + _lastFixedUpdateTime = 0d; + _lastRealtimeUpdateTime = 0d; + + _EOFPumpRan = false; + } + + /// <summary> + /// This will pause all coroutines running on the current MEC instance until ResumeCoroutines is called. + /// </summary> + /// <returns>The number of coroutines that were paused.</returns> + public static int PauseCoroutines() + { + return _instance == null ? 0 : _instance.PauseCoroutinesOnInstance(); + } + + /// <summary> + /// This will pause all coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. + /// </summary> + /// <returns>The number of coroutines that were paused.</returns> + public int PauseCoroutinesOnInstance() + { + int count = 0; + int i; + for (i = 0; i < _nextUpdateProcessSlot; i++) + { + if (!UpdatePaused[i] && UpdateProcesses[i] != null) + { + UpdatePaused[i] = true; + count++; + } + } + + for (i = 0; i < _nextLateUpdateProcessSlot; i++) + { + if (!LateUpdatePaused[i] && LateUpdateProcesses[i] != null) + { + LateUpdatePaused[i] = true; + count++; + } + } + + for (i = 0; i < _nextFixedUpdateProcessSlot; i++) + { + if (!FixedUpdatePaused[i] && FixedUpdateProcesses[i] != null) + { + FixedUpdatePaused[i] = true; + count++; + } + } + + for (i = 0; i < _nextSlowUpdateProcessSlot; i++) + { + if (!SlowUpdatePaused[i] && SlowUpdateProcesses[i] != null) + { + SlowUpdatePaused[i] = true; + count++; + } + } + + for (i = 0; i < _nextRealtimeUpdateProcessSlot; i++) + { + if (!RealtimeUpdatePaused[i] && RealtimeUpdateProcesses[i] != null) + { + RealtimeUpdatePaused[i] = true; + count++; + } + } + + for (i = 0; i < _nextEditorUpdateProcessSlot; i++) + { + if (!EditorUpdatePaused[i] && EditorUpdateProcesses[i] != null) + { + EditorUpdatePaused[i] = true; + count++; + } + } + + for (i = 0; i < _nextEditorSlowUpdateProcessSlot; i++) + { + if (!EditorSlowUpdatePaused[i] && EditorSlowUpdateProcesses[i] != null) + { + EditorSlowUpdatePaused[i] = true; + count++; + } + } + + for (i = 0; i < _nextEndOfFrameProcessSlot; i++) + { + if (!EndOfFramePaused[i] && EndOfFrameProcesses[i] != null) + { + EndOfFramePaused[i] = true; + count++; + } + } + + for (i = 0; i < _nextManualTimeframeProcessSlot; i++) + { + if (!ManualTimeframePaused[i] && ManualTimeframeProcesses[i] != null) + { + ManualTimeframePaused[i] = true; + count++; + } + } + + return count; + } + + /// <summary> + /// This will pause any matching coroutines running on the current MEC instance until ResumeCoroutines is called. + /// </summary> + /// <param name="tag">Any coroutines with a matching tag will be paused.</param> + /// <returns>The number of coroutines that were paused.</returns> + public static int PauseCoroutines(string tag) + { + return _instance == null ? 0 : _instance.PauseCoroutinesOnInstance(tag); + } + + /// <summary> + /// This will pause any matching coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. + /// </summary> + /// <param name="tag">Any coroutines with a matching tag will be paused.</param> + /// <returns>The number of coroutines that were paused.</returns> + public int PauseCoroutinesOnInstance(string tag) + { + if (tag == null || !_taggedProcesses.ContainsKey(tag)) + return 0; + + int count = 0; + var matchesEnum = _taggedProcesses[tag].GetEnumerator(); + + while (matchesEnum.MoveNext()) + if (!CoindexIsNull(matchesEnum.Current) && !CoindexSetPause(matchesEnum.Current)) + count++; + + return count; + } + + /// <summary> + /// This will pause any matching coroutines running on the current MEC instance until ResumeCoroutines is called. + /// </summary> + /// <param name="layer">Any coroutines on the matching layer will be paused.</param> + /// <returns>The number of coroutines that were paused.</returns> + public static int PauseCoroutines(int layer) + { + return _instance == null ? 0 : _instance.PauseCoroutinesOnInstance(layer); + } + + /// <summary> + /// This will pause any matching coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. + /// </summary> + /// <param name="layer">Any coroutines on the matching layer will be paused.</param> + /// <returns>The number of coroutines that were paused.</returns> + public int PauseCoroutinesOnInstance(int layer) + { + if (!_layeredProcesses.ContainsKey(layer)) + return 0; + + int count = 0; + var matchesEnum = _layeredProcesses[layer].GetEnumerator(); + + while (matchesEnum.MoveNext()) + if (!CoindexIsNull(matchesEnum.Current) && !CoindexSetPause(matchesEnum.Current)) + count++; + + return count; + } + + /// <summary> + /// This will pause any matching coroutines running on the current MEC instance until ResumeCoroutines is called. + /// </summary> + /// <param name="layer">Any coroutines on the matching layer will be paused.</param> + /// <param name="tag">Any coroutines with a matching tag will be paused.</param> + /// <returns>The number of coroutines that were paused.</returns> + public static int PauseCoroutines(int layer, string tag) + { + return _instance == null ? 0 : _instance.PauseCoroutinesOnInstance(layer, tag); + } + + /// <summary> + /// This will pause any matching coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. + /// </summary> + /// <param name="layer">Any coroutines on the matching layer will be paused.</param> + /// <param name="tag">Any coroutines with a matching tag will be paused.</param> + /// <returns>The number of coroutines that were paused.</returns> + public int PauseCoroutinesOnInstance(int layer, string tag) + { + if (tag == null) + return PauseCoroutinesOnInstance(layer); + + if (!_taggedProcesses.ContainsKey(tag) || !_layeredProcesses.ContainsKey(layer)) + return 0; + + int count = 0; + var matchesEnum = _taggedProcesses[tag].GetEnumerator(); + + while (matchesEnum.MoveNext()) + if (_processLayers.ContainsKey(matchesEnum.Current) && _processLayers[matchesEnum.Current] == layer + && !CoindexIsNull(matchesEnum.Current) && !CoindexSetPause(matchesEnum.Current)) + count++; + + return count; + } + + /// <summary> + /// This will pause any matching coroutines until ResumeCoroutines is called. + /// </summary> + /// <param name="handle">The handle of the coroutine to pause.</param> + /// <returns>The number of coroutines that were paused (0 or 1).</returns> + public static int PauseCoroutines(CoroutineHandle handle) + { + return ActiveInstances.ContainsKey(handle.Key) ? GetInstance(handle.Key).PauseCoroutinesOnInstance(handle) : 0; + } + + /// <summary> + /// This will pause any matching coroutines running on this MEC instance until ResumeCoroutinesOnInstance is called. + /// </summary> + /// <param name="handle">The handle of the coroutine to pause.</param> + /// <returns>The number of coroutines that were paused (0 or 1).</returns> + public int PauseCoroutinesOnInstance(CoroutineHandle handle) + { + return _handleToIndex.ContainsKey(handle) && !CoindexIsNull(_handleToIndex[handle]) && !CoindexSetPause(_handleToIndex[handle]) ? 1 : 0; + } + + /// <summary> + /// This resumes all coroutines on the current MEC instance if they are currently paused, otherwise it has + /// no effect. + /// </summary> + /// <returns>The number of coroutines that were resumed.</returns> + public static int ResumeCoroutines() + { + return _instance == null ? 0 : _instance.ResumeCoroutinesOnInstance(); + } + + /// <summary> + /// This resumes all coroutines on this MEC instance if they are currently paused, otherwise it has no effect. + /// </summary> + /// <returns>The number of coroutines that were resumed.</returns> + public int ResumeCoroutinesOnInstance() + { + int count = 0; + int i; + for (i = 0; i < _nextUpdateProcessSlot; i++) + { + if (UpdatePaused[i] && UpdateProcesses[i] != null) + { + UpdatePaused[i] = false; + count++; + } + } + + for (i = 0; i < _nextLateUpdateProcessSlot; i++) + { + if (LateUpdatePaused[i] && LateUpdateProcesses[i] != null) + { + LateUpdatePaused[i] = false; + count++; + } + } + + for (i = 0; i < _nextFixedUpdateProcessSlot; i++) + { + if (FixedUpdatePaused[i] && FixedUpdateProcesses[i] != null) + { + FixedUpdatePaused[i] = false; + count++; + } + } + + for (i = 0; i < _nextSlowUpdateProcessSlot; i++) + { + if (SlowUpdatePaused[i] && SlowUpdateProcesses[i] != null) + { + SlowUpdatePaused[i] = false; + count++; + } + } + + for (i = 0; i < _nextRealtimeUpdateProcessSlot; i++) + { + if (RealtimeUpdatePaused[i] && RealtimeUpdateProcesses[i] != null) + { + RealtimeUpdatePaused[i] = false; + count++; + } + } + + for (i = 0; i < _nextEditorUpdateProcessSlot; i++) + { + if (EditorUpdatePaused[i] && EditorUpdateProcesses[i] != null) + { + EditorUpdatePaused[i] = false; + count++; + } + } + + for (i = 0; i < _nextEditorSlowUpdateProcessSlot; i++) + { + if (EditorSlowUpdatePaused[i] && EditorSlowUpdateProcesses[i] != null) + { + EditorSlowUpdatePaused[i] = false; + count++; + } + } + + for (i = 0; i < _nextEndOfFrameProcessSlot; i++) + { + if (EndOfFramePaused[i] && EndOfFrameProcesses[i] != null) + { + EndOfFramePaused[i] = false; + count++; + } + } + + for (i = 0; i < _nextManualTimeframeProcessSlot; i++) + { + if (ManualTimeframePaused[i] && ManualTimeframeProcesses[i] != null) + { + ManualTimeframePaused[i] = false; + count++; + } + } + + var waitingEnum = _waitingTriggers.GetEnumerator(); + while (waitingEnum.MoveNext()) + { + int listCount = 0; + var pausedList = waitingEnum.Current.Value.GetEnumerator(); + + while (pausedList.MoveNext()) + { + if (_handleToIndex.ContainsKey(pausedList.Current.Handle) && !CoindexIsNull(_handleToIndex[pausedList.Current.Handle])) + { + CoindexSetPause(_handleToIndex[pausedList.Current.Handle]); + listCount++; + } + else + { + waitingEnum.Current.Value.Remove(pausedList.Current); + listCount = 0; + pausedList = waitingEnum.Current.Value.GetEnumerator(); + } + } + + count -= listCount; + } + + return count; + } + + /// <summary> + /// This resumes any matching coroutines on the current MEC instance if they are currently paused, otherwise it has no effect. + /// </summary> + /// <param name="tag">Any coroutines previously paused with a matching tag will be resumend.</param> + /// <returns>The number of coroutines that were resumed.</returns> + public static int ResumeCoroutines(string tag) + { + return _instance == null ? 0 : _instance.ResumeCoroutinesOnInstance(tag); + } + + /// <summary> + /// This resumes any matching coroutines on this MEC instance if they are currently paused, otherwise it has no effect. + /// </summary> + /// <param name="tag">Any coroutines previously paused with a matching tag will be resumend.</param> + /// <returns>The number of coroutines that were resumed.</returns> + public int ResumeCoroutinesOnInstance(string tag) + { + if (tag == null || !_taggedProcesses.ContainsKey(tag)) + return 0; + int count = 0; + + var indexesEnum = _taggedProcesses[tag].GetEnumerator(); + while (indexesEnum.MoveNext()) + if (!CoindexIsNull(indexesEnum.Current) && CoindexSetPause(indexesEnum.Current, false)) + count++; + + var waitingEnum = _waitingTriggers.GetEnumerator(); + while (waitingEnum.MoveNext()) + { + var pausedList = waitingEnum.Current.Value.GetEnumerator(); + while (pausedList.MoveNext()) + { + if (_handleToIndex.ContainsKey(pausedList.Current.Handle) && !CoindexIsNull(_handleToIndex[pausedList.Current.Handle]) + && !CoindexSetPause(_handleToIndex[pausedList.Current.Handle])) + count--; + } + } + + return count; + } + + /// <summary> + /// This resumes any matching coroutines on the current MEC instance if they are currently paused, otherwise it has + /// no effect. + /// </summary> + /// <param name="layer">Any coroutines previously paused on the matching layer will be resumend.</param> + /// <returns>The number of coroutines that were resumed.</returns> + public static int ResumeCoroutines(int layer) + { + return _instance == null ? 0 : _instance.ResumeCoroutinesOnInstance(layer); + } + + /// <summary> + /// This resumes any matching coroutines on this MEC instance if they are currently paused, otherwise it has no effect. + /// </summary> + /// <param name="layer">Any coroutines previously paused on the matching layer will be resumend.</param> + /// <returns>The number of coroutines that were resumed.</returns> + public int ResumeCoroutinesOnInstance(int layer) + { + if (!_layeredProcesses.ContainsKey(layer)) + return 0; + int count = 0; + + var indexesEnum = _layeredProcesses[layer].GetEnumerator(); + while (indexesEnum.MoveNext()) + if (!CoindexIsNull(indexesEnum.Current) && CoindexSetPause(indexesEnum.Current, false)) + count++; + + var waitingEnum = _waitingTriggers.GetEnumerator(); + while (waitingEnum.MoveNext()) + { + var pausedList = waitingEnum.Current.Value.GetEnumerator(); + while (pausedList.MoveNext()) + { + if (_handleToIndex.ContainsKey(pausedList.Current.Handle) && !CoindexIsNull(_handleToIndex[pausedList.Current.Handle]) + && !CoindexSetPause(_handleToIndex[pausedList.Current.Handle])) + count--; + } + } + + return count; + } + + /// <summary> + /// This resumes any matching coroutines on the current MEC instance if they are currently paused, otherwise it has + /// no effect. + /// </summary> + /// <param name="layer">Any coroutines previously paused on the matching layer will be resumend.</param> + /// <param name="tag">Any coroutines previously paused with a matching tag will be resumend.</param> + /// <returns>The number of coroutines that were resumed.</returns> + public static int ResumeCoroutines(int layer, string tag) + { + return _instance == null ? 0 : _instance.ResumeCoroutinesOnInstance(layer, tag); + } + + /// <summary> + /// This resumes any matching coroutines on this MEC instance if they are currently paused, otherwise it has no effect. + /// </summary> + /// <param name="layer">Any coroutines previously paused on the matching layer will be resumend.</param> + /// <param name="tag">Any coroutines previously paused with a matching tag will be resumend.</param> + /// <returns>The number of coroutines that were resumed.</returns> + public int ResumeCoroutinesOnInstance(int layer, string tag) + { + if (tag == null) + return ResumeCoroutinesOnInstance(layer); + if (!_layeredProcesses.ContainsKey(layer) || !_taggedProcesses.ContainsKey(tag)) + return 0; + int count = 0; + + var indexesEnum = _taggedProcesses[tag].GetEnumerator(); + while (indexesEnum.MoveNext()) + if (!CoindexIsNull(indexesEnum.Current) && _layeredProcesses[layer].Contains(indexesEnum.Current) && + CoindexSetPause(indexesEnum.Current, false)) + count++; + + var waitingEnum = _waitingTriggers.GetEnumerator(); + while (waitingEnum.MoveNext()) + { + var pausedList = waitingEnum.Current.Value.GetEnumerator(); + while (pausedList.MoveNext()) + { + if (_handleToIndex.ContainsKey(pausedList.Current.Handle) && !CoindexIsNull(_handleToIndex[pausedList.Current.Handle]) + && !CoindexSetPause(_handleToIndex[pausedList.Current.Handle])) + count--; + } + } + + return count; + } + + /// <summary> + /// This will resume any matching coroutines. + /// </summary> + /// <param name="handle">The handle of the coroutine to resume.</param> + /// <returns>The number of coroutines that were resumed (0 or 1).</returns> + public static int ResumeCoroutines(CoroutineHandle handle) + { + return ActiveInstances.ContainsKey(handle.Key) ? GetInstance(handle.Key).ResumeCoroutinesOnInstance(handle) : 0; + } + + /// <summary> + /// This will resume any matching coroutines running on this MEC instance. + /// </summary> + /// <param name="handle">The handle of the coroutine to resume.</param> + /// <returns>The number of coroutines that were resumed (0 or 1).</returns> + public int ResumeCoroutinesOnInstance(CoroutineHandle handle) + { + var waitingEnum = _waitingTriggers.GetEnumerator(); + while (waitingEnum.MoveNext()) + { + var pausedList = waitingEnum.Current.Value.GetEnumerator(); + while (pausedList.MoveNext()) + if (pausedList.Current.Handle == handle) + return 0; + } + + return _handleToIndex.ContainsKey(handle) && + !CoindexIsNull(_handleToIndex[handle]) && CoindexSetPause(_handleToIndex[handle], false) ? 1 : 0; + } + + /// <summary> + /// Returns the tag associated with the coroutine that the given handle points to, if it is running. + /// </summary> + /// <param name="handle">The handle to the coroutine.</param> + /// <returns>The coroutine's tag, or null if there is no matching tag.</returns> + public static string GetTag(CoroutineHandle handle) + { + Timing inst = GetInstance(handle.Key); + return inst != null && inst._handleToIndex.ContainsKey(handle) && inst._processTags.ContainsKey(inst._handleToIndex[handle]) + ? inst._processTags[inst._handleToIndex[handle]] : null; + } + + /// <summary> + /// Returns the layer associated with the coroutine that the given handle points to, if it is running. + /// </summary> + /// <param name="handle">The handle to the coroutine.</param> + /// <returns>The coroutine's layer as a nullable integer, or null if there is no matching layer.</returns> + public static int? GetLayer(CoroutineHandle handle) + { + Timing inst = GetInstance(handle.Key); + return inst != null && inst._handleToIndex.ContainsKey(handle) && inst._processLayers.ContainsKey(inst._handleToIndex[handle]) + ? inst._processLayers[inst._handleToIndex[handle]] : (int?)null; + } + + /// <summary> + /// Returns the segment that the coroutine with the given handle is running on. + /// </summary> + /// <param name="handle">The handle to the coroutine.</param> + /// <returns>The coroutine's segment, or Segment.Invalid if it's not found.</returns> + public static Segment GetSegment(CoroutineHandle handle) + { + Timing inst = GetInstance(handle.Key); + return inst != null && inst._handleToIndex.ContainsKey(handle) ? inst._handleToIndex[handle].seg : Segment.Invalid; + } + + /// <summary> + /// Sets the coroutine that the handle points to to have the given tag. + /// </summary> + /// <param name="handle">The handle to the coroutine.</param> + /// <param name="newTag">The new tag to assign, or null to clear the tag.</param> + /// <param name="overwriteExisting">If set to false then the tag will not be changed if the coroutine has an existing tag.</param> + /// <returns>Whether the tag was set successfully.</returns> + public static bool SetTag(CoroutineHandle handle, string newTag, bool overwriteExisting = true) + { + Timing inst = GetInstance(handle.Key); + if (inst == null || !inst._handleToIndex.ContainsKey(handle) || inst.CoindexIsNull(inst._handleToIndex[handle]) + || (!overwriteExisting && inst._processTags.ContainsKey(inst._handleToIndex[handle]))) + return false; + + if (newTag == null) + { + inst.RemoveTag(inst._handleToIndex[handle]); + return true; + } + + if (inst._processTags.ContainsKey(inst._handleToIndex[handle])) + { + if (inst._taggedProcesses[inst._processTags[inst._handleToIndex[handle]]].Count <= 1) + inst._taggedProcesses.Remove(inst._processTags[inst._handleToIndex[handle]]); + else + inst._taggedProcesses[inst._processTags[inst._handleToIndex[handle]]].Remove(inst._handleToIndex[handle]); + + inst._processTags[inst._handleToIndex[handle]] = newTag; + } + else + { + inst._processTags.Add(inst._handleToIndex[handle], newTag); + } + + if (inst._taggedProcesses.ContainsKey(newTag)) + inst._taggedProcesses[newTag].Add(inst._handleToIndex[handle]); + else + inst._taggedProcesses.Add(newTag, new HashSet<ProcessIndex> { inst._handleToIndex[handle] }); + + return true; + } + + /// <summary> + /// Sets the coroutine that the handle points to to have the given layer. + /// </summary> + /// <param name="handle">The handle to the coroutine.</param> + /// <param name="newLayer">The new tag to assign.</param> + /// <param name="overwriteExisting">If set to false then the tag will not be changed if the coroutine has an existing tag.</param> + /// <returns>Whether the layer was set successfully.</returns> + public static bool SetLayer(CoroutineHandle handle, int newLayer, bool overwriteExisting = true) + { + Timing inst = GetInstance(handle.Key); + if (inst == null || !inst._handleToIndex.ContainsKey(handle) || inst.CoindexIsNull(inst._handleToIndex[handle]) + || (!overwriteExisting && inst._processLayers.ContainsKey(inst._handleToIndex[handle]))) + return false; + + if (inst._processLayers.ContainsKey(inst._handleToIndex[handle])) + { + if (inst._layeredProcesses[inst._processLayers[inst._handleToIndex[handle]]].Count <= 1) + inst._layeredProcesses.Remove(inst._processLayers[inst._handleToIndex[handle]]); + else + inst._layeredProcesses[inst._processLayers[inst._handleToIndex[handle]]].Remove(inst._handleToIndex[handle]); + + inst._processLayers[inst._handleToIndex[handle]] = newLayer; + } + else + { + inst._processLayers.Add(inst._handleToIndex[handle], newLayer); + } + + if (inst._layeredProcesses.ContainsKey(newLayer)) + inst._layeredProcesses[newLayer].Add(inst._handleToIndex[handle]); + else + inst._layeredProcesses.Add(newLayer, new HashSet<ProcessIndex> { inst._handleToIndex[handle] }); + + return true; + } + + /// <summary> + /// Sets the segment for the coroutine with the given handle. + /// </summary> + /// <param name="handle">The handle to the coroutine.</param> + /// <param name="newSegment">The new segment to run the coroutine in.</param> + /// <returns>Whether the segment was set successfully.</returns> + public static bool SetSegment(CoroutineHandle handle, Segment newSegment) + { + Timing inst = GetInstance(handle.Key); + if (inst == null || !inst._handleToIndex.ContainsKey(handle) || inst.CoindexIsNull(inst._handleToIndex[handle])) + return false; + + inst.RunCoroutineInternal(inst.CoindexExtract(inst._handleToIndex[handle]), newSegment, inst._processLayers.ContainsKey(inst._handleToIndex[handle]) + ? inst._processLayers[inst._handleToIndex[handle]] : (int?)null, inst._processTags.ContainsKey(inst._handleToIndex[handle]) + ? inst._processTags[inst._handleToIndex[handle]] : null, handle, false); + + return true; + } + + /// <summary> + /// Sets the coroutine that the handle points to to have the given tag. + /// </summary> + /// <param name="handle">The handle to the coroutine.</param> + /// <returns>Whether the tag was removed successfully.</returns> + public static bool RemoveTag(CoroutineHandle handle) + { + return SetTag(handle, null); + } + + /// <summary> + /// Sets the coroutine that the handle points to to have the given layer. + /// </summary> + /// <param name="handle">The handle to the coroutine.</param> + /// <returns>Whether the layer was removed successfully.</returns> + public static bool RemoveLayer(CoroutineHandle handle) + { + Timing inst = GetInstance(handle.Key); + if (inst == null || !inst._handleToIndex.ContainsKey(handle) || inst.CoindexIsNull(inst._handleToIndex[handle])) + return false; + + inst.RemoveLayer(inst._handleToIndex[handle]); + + return true; + } + + /// <summary> + /// Tests to see if the handle you have points to a valid coroutine. + /// </summary> + /// <param name="handle">The handle to test.</param> + /// <returns>Whether it's a valid coroutine.</returns> + public static bool IsRunning(CoroutineHandle handle) + { + Timing inst = GetInstance(handle.Key); + return inst != null && inst._handleToIndex.ContainsKey(handle) && !inst.CoindexIsNull(inst._handleToIndex[handle]); + } + + /// <summary> + /// Tests to see if the handle you have points to a paused coroutine. + /// </summary> + /// <param name="handle">The handle to test.</param> + /// <returns>Whether it's a paused coroutine.</returns> + public static bool IsPaused(CoroutineHandle handle) + { + Timing inst = GetInstance(handle.Key); + return inst != null && inst._handleToIndex.ContainsKey(handle) && !inst.CoindexIsNull(inst._handleToIndex[handle]) && + !inst.CoindexIsPaused(inst._handleToIndex[handle]); + } + + private void AddTag(string tag, ProcessIndex coindex) + { + _processTags.Add(coindex, tag); + + if (_taggedProcesses.ContainsKey(tag)) + _taggedProcesses[tag].Add(coindex); + else + _taggedProcesses.Add(tag, new HashSet<ProcessIndex> { coindex }); + } + + private void AddLayer(int layer, ProcessIndex coindex) + { + _processLayers.Add(coindex, layer); + + if (_layeredProcesses.ContainsKey(layer)) + _layeredProcesses[layer].Add(coindex); + else + _layeredProcesses.Add(layer, new HashSet<ProcessIndex> { coindex }); + } + + private void RemoveTag(ProcessIndex coindex) + { + if (_processTags.ContainsKey(coindex)) + { + if (_taggedProcesses[_processTags[coindex]].Count > 1) + _taggedProcesses[_processTags[coindex]].Remove(coindex); + else + _taggedProcesses.Remove(_processTags[coindex]); + + _processTags.Remove(coindex); + } + } + + private void RemoveLayer(ProcessIndex coindex) + { + if (_processLayers.ContainsKey(coindex)) + { + if (_layeredProcesses[_processLayers[coindex]].Count > 1) + _layeredProcesses[_processLayers[coindex]].Remove(coindex); + else + _layeredProcesses.Remove(_processLayers[coindex]); + + _processLayers.Remove(coindex); + } + } + + private void RemoveGraffiti(ProcessIndex coindex) + { + if (_processLayers.ContainsKey(coindex)) + { + if (_layeredProcesses[_processLayers[coindex]].Count > 1) + _layeredProcesses[_processLayers[coindex]].Remove(coindex); + else + _layeredProcesses.Remove(_processLayers[coindex]); + + _processLayers.Remove(coindex); + } + + if (_processTags.ContainsKey(coindex)) + { + if (_taggedProcesses[_processTags[coindex]].Count > 1) + _taggedProcesses[_processTags[coindex]].Remove(coindex); + else + _taggedProcesses.Remove(_processTags[coindex]); + + _processTags.Remove(coindex); + } + + if (_indexToHandle.ContainsKey(coindex)) + { + _handleToIndex.Remove(_indexToHandle[coindex]); + _indexToHandle.Remove(coindex); + } + } + + private void MoveGraffiti(ProcessIndex coindexFrom, ProcessIndex coindexTo) + { + RemoveGraffiti(coindexTo); + + int layer; + if (_processLayers.TryGetValue(coindexFrom, out layer)) + { + _layeredProcesses[layer].Remove(coindexFrom); + _layeredProcesses[layer].Add(coindexTo); + + _processLayers.Add(coindexTo, layer); + _processLayers.Remove(coindexFrom); + } + + string tag; + if (_processTags.TryGetValue(coindexFrom, out tag)) + { + _taggedProcesses[tag].Remove(coindexFrom); + _taggedProcesses[tag].Add(coindexTo); + + _processTags.Add(coindexTo, tag); + _processTags.Remove(coindexFrom); + } + + _handleToIndex[_indexToHandle[coindexFrom]] = coindexTo; + _indexToHandle.Add(coindexTo, _indexToHandle[coindexFrom]); + _indexToHandle.Remove(coindexFrom); + } + + private IEnumerator<float> CoindexExtract(ProcessIndex coindex) + { + IEnumerator<float> retVal; + + switch (coindex.seg) + { + case Segment.Update: + retVal = UpdateProcesses[coindex.i]; + UpdateProcesses[coindex.i] = null; + return retVal; + case Segment.FixedUpdate: + retVal = FixedUpdateProcesses[coindex.i]; + FixedUpdateProcesses[coindex.i] = null; + return retVal; + case Segment.LateUpdate: + retVal = LateUpdateProcesses[coindex.i]; + LateUpdateProcesses[coindex.i] = null; + return retVal; + case Segment.SlowUpdate: + retVal = SlowUpdateProcesses[coindex.i]; + SlowUpdateProcesses[coindex.i] = null; + return retVal; + case Segment.RealtimeUpdate: + retVal = RealtimeUpdateProcesses[coindex.i]; + RealtimeUpdateProcesses[coindex.i] = null; + return retVal; + case Segment.EditorUpdate: + retVal = EditorUpdateProcesses[coindex.i]; + EditorUpdateProcesses[coindex.i] = null; + return retVal; + case Segment.EditorSlowUpdate: + retVal = EditorSlowUpdateProcesses[coindex.i]; + EditorSlowUpdateProcesses[coindex.i] = null; + return retVal; + case Segment.EndOfFrame: + retVal = EndOfFrameProcesses[coindex.i]; + EndOfFrameProcesses[coindex.i] = null; + return retVal; + case Segment.ManualTimeframe: + retVal = ManualTimeframeProcesses[coindex.i]; + ManualTimeframeProcesses[coindex.i] = null; + return retVal; + default: + return null; + } + } + + private bool CoindexIsNull(ProcessIndex coindex) + { + switch (coindex.seg) + { + case Segment.Update: + return UpdateProcesses[coindex.i] == null; + case Segment.FixedUpdate: + return FixedUpdateProcesses[coindex.i] == null; + case Segment.LateUpdate: + return LateUpdateProcesses[coindex.i] == null; + case Segment.SlowUpdate: + return SlowUpdateProcesses[coindex.i] == null; + case Segment.RealtimeUpdate: + return RealtimeUpdateProcesses[coindex.i] == null; + case Segment.EditorUpdate: + return EditorUpdateProcesses[coindex.i] == null; + case Segment.EditorSlowUpdate: + return EditorSlowUpdateProcesses[coindex.i] == null; + case Segment.EndOfFrame: + return EndOfFrameProcesses[coindex.i] == null; + case Segment.ManualTimeframe: + return ManualTimeframeProcesses[coindex.i] == null; + default: + return true; + } + } + + private IEnumerator<float> CoindexPeek(ProcessIndex coindex) + { + switch (coindex.seg) + { + case Segment.Update: + return UpdateProcesses[coindex.i]; + case Segment.FixedUpdate: + return FixedUpdateProcesses[coindex.i]; + case Segment.LateUpdate: + return LateUpdateProcesses[coindex.i]; + case Segment.SlowUpdate: + return SlowUpdateProcesses[coindex.i]; + case Segment.RealtimeUpdate: + return RealtimeUpdateProcesses[coindex.i]; + case Segment.EditorUpdate: + return EditorUpdateProcesses[coindex.i]; + case Segment.EditorSlowUpdate: + return EditorSlowUpdateProcesses[coindex.i]; + case Segment.EndOfFrame: + return EndOfFrameProcesses[coindex.i]; + case Segment.ManualTimeframe: + return ManualTimeframeProcesses[coindex.i]; + default: + return null; + } + } + + private bool CoindexKill(ProcessIndex coindex) + { + bool retVal; + + switch (coindex.seg) + { + case Segment.Update: + retVal = UpdateProcesses[coindex.i] != null; + UpdateProcesses[coindex.i] = null; + return retVal; + case Segment.FixedUpdate: + retVal = FixedUpdateProcesses[coindex.i] != null; + FixedUpdateProcesses[coindex.i] = null; + return retVal; + case Segment.LateUpdate: + retVal = LateUpdateProcesses[coindex.i] != null; + LateUpdateProcesses[coindex.i] = null; + return retVal; + case Segment.SlowUpdate: + retVal = SlowUpdateProcesses[coindex.i] != null; + SlowUpdateProcesses[coindex.i] = null; + return retVal; + case Segment.RealtimeUpdate: + retVal = RealtimeUpdateProcesses[coindex.i] != null; + RealtimeUpdateProcesses[coindex.i] = null; + return retVal; + case Segment.EditorUpdate: + retVal = UpdateProcesses[coindex.i] != null; + EditorUpdateProcesses[coindex.i] = null; + return retVal; + case Segment.EditorSlowUpdate: + retVal = EditorSlowUpdateProcesses[coindex.i] != null; + EditorSlowUpdateProcesses[coindex.i] = null; + return retVal; + case Segment.EndOfFrame: + retVal = EndOfFrameProcesses[coindex.i] != null; + EndOfFrameProcesses[coindex.i] = null; + return retVal; + case Segment.ManualTimeframe: + retVal = ManualTimeframeProcesses[coindex.i] != null; + ManualTimeframeProcesses[coindex.i] = null; + return retVal; + default: + return false; + } + } + + private bool CoindexSetPause(ProcessIndex coindex, bool newPausedState = true) + { + bool isPaused; + + switch (coindex.seg) + { + case Segment.Update: + isPaused = UpdatePaused[coindex.i]; + UpdatePaused[coindex.i] = newPausedState; + return isPaused; + case Segment.FixedUpdate: + isPaused = FixedUpdatePaused[coindex.i]; + FixedUpdatePaused[coindex.i] = newPausedState; + return isPaused; + case Segment.LateUpdate: + isPaused = LateUpdatePaused[coindex.i]; + LateUpdatePaused[coindex.i] = newPausedState; + return isPaused; + case Segment.SlowUpdate: + isPaused = SlowUpdatePaused[coindex.i]; + SlowUpdatePaused[coindex.i] = newPausedState; + return isPaused; + case Segment.RealtimeUpdate: + isPaused = RealtimeUpdatePaused[coindex.i]; + RealtimeUpdatePaused[coindex.i] = newPausedState; + return isPaused; + case Segment.EditorUpdate: + isPaused = EditorUpdatePaused[coindex.i]; + EditorUpdatePaused[coindex.i] = newPausedState; + return isPaused; + case Segment.EditorSlowUpdate: + isPaused = EditorSlowUpdatePaused[coindex.i]; + EditorSlowUpdatePaused[coindex.i] = newPausedState; + return isPaused; + case Segment.EndOfFrame: + isPaused = EndOfFramePaused[coindex.i]; + EndOfFramePaused[coindex.i] = newPausedState; + return isPaused; + case Segment.ManualTimeframe: + isPaused = ManualTimeframePaused[coindex.i]; + ManualTimeframePaused[coindex.i] = newPausedState; + return isPaused; + default: + return false; + } + } + + private bool CoindexIsPaused(ProcessIndex coindex) + { + switch (coindex.seg) + { + case Segment.Update: + return UpdatePaused[coindex.i]; + case Segment.FixedUpdate: + return FixedUpdatePaused[coindex.i]; + case Segment.LateUpdate: + return LateUpdatePaused[coindex.i]; + case Segment.SlowUpdate: + return SlowUpdatePaused[coindex.i]; + case Segment.RealtimeUpdate: + return RealtimeUpdatePaused[coindex.i]; + case Segment.EditorUpdate: + return EditorUpdatePaused[coindex.i]; + case Segment.EditorSlowUpdate: + return EditorSlowUpdatePaused[coindex.i]; + case Segment.EndOfFrame: + return EndOfFramePaused[coindex.i]; + case Segment.ManualTimeframe: + return ManualTimeframePaused[coindex.i]; + default: + return false; + } + } + + private void CoindexReplace(ProcessIndex coindex, IEnumerator<float> replacement) + { + switch (coindex.seg) + { + case Segment.Update: + UpdateProcesses[coindex.i] = replacement; + return; + case Segment.FixedUpdate: + FixedUpdateProcesses[coindex.i] = replacement; + return; + case Segment.LateUpdate: + LateUpdateProcesses[coindex.i] = replacement; + return; + case Segment.SlowUpdate: + SlowUpdateProcesses[coindex.i] = replacement; + return; + case Segment.RealtimeUpdate: + RealtimeUpdateProcesses[coindex.i] = replacement; + return; + case Segment.EditorUpdate: + EditorUpdateProcesses[coindex.i] = replacement; + return; + case Segment.EditorSlowUpdate: + EditorSlowUpdateProcesses[coindex.i] = replacement; + return; + case Segment.EndOfFrame: + EndOfFrameProcesses[coindex.i] = replacement; + return; + case Segment.ManualTimeframe: + ManualTimeframeProcesses[coindex.i] = replacement; + return; + } + } + + /// <summary> + /// Use the command "yield return Timing.WaitUntilDone(otherCoroutine);" to pause the current + /// coroutine until otherCoroutine is done. + /// </summary> + /// <param name="otherCoroutine">The coroutine to pause for.</param> + //public static float WaitUntilDone(CoroutineHandle otherCoroutine) + //{ + // return Instance.WaitUntilDoneOnInstance(otherCoroutine); + //} + + ///// <summary> + ///// Use the command "yield return Timing.WaitUntilDone(otherCoroutine);" to pause the current + ///// coroutine until otherCoroutine is done. + ///// </summary> + ///// <param name="otherCoroutine">The coroutine to pause for.</param> + ///// <param name="warnOnIssue">Post a warning to the console if no hold action was actually performed.</param> + //public static float WaitUntilDone(CoroutineHandle otherCoroutine, bool warnOnIssue) + //{ + // return Instance.WaitUntilDoneOnInstance(otherCoroutine, warnOnIssue); + //} + + ///// <summary> + ///// Use the command "yield return timingInstance.WaitUntilDoneOnInstance(otherCoroutine);" to pause the current + ///// coroutine until the otherCoroutine is done. + ///// </summary> + ///// <param name="otherCoroutine">The coroutine to pause for.</param> + ///// <param name="warnOnIssue">Post a warning to the console if no hold action was actually performed.</param> + //public float WaitUntilDoneOnInstance(CoroutineHandle otherCoroutine, bool warnOnIssue = true) + //{ + // if (_handleToIndex.ContainsKey(otherCoroutine)) + // { + // if (CoindexIsNull(_handleToIndex[otherCoroutine])) + // return 0f; + + // if (!_waitingTriggers.ContainsKey(otherCoroutine)) + // { + // CoindexReplace(_handleToIndex[otherCoroutine], _StartWhenDone(otherCoroutine, CoindexPeek(_handleToIndex[otherCoroutine]))); + // _waitingTriggers.Add(otherCoroutine, new HashSet<ProcessData>()); + // } + + // ReplacementFunction = (coptr, instance, handle) => + // { + // if (handle == otherCoroutine) + // { + // if (warnOnIssue) + // Debug.LogWarning("A coroutine attempted to wait for itself."); + + // return coptr; + // } + + // _waitingTriggers[otherCoroutine].Add(new ProcessData + // { + // Handle = handle, + // PauseTime = coptr.Current > GetSegmentTime(_handleToIndex[handle].seg) + // ? coptr.Current - (float)GetSegmentTime(_handleToIndex[handle].seg) : 0f + // }); + + // CoindexSetPause(_handleToIndex[handle]); + + // return coptr; + // }; + + // return float.NaN; + // } + + // if (warnOnIssue) + // Debug.LogWarning("WaitUntilDone cannot hold: The coroutine handle that was passed in is invalid.\n" + otherCoroutine); + + // return 0f; + //} + + private IEnumerator<float> _StartWhenDone(CoroutineHandle handle, IEnumerator<float> proc) + { + if (!_waitingTriggers.ContainsKey(handle)) yield break; + + try + { + if (proc.Current > localTime) + yield return proc.Current; + + while (proc.MoveNext()) + yield return proc.Current; + } + finally + { + CloseWaitingProcess(handle); + } + } + + private void CloseWaitingProcess(CoroutineHandle handle) + { + if (!_waitingTriggers.ContainsKey(handle)) return; + + var tasksEnum = _waitingTriggers[handle].GetEnumerator(); + _waitingTriggers.Remove(handle); + + while (tasksEnum.MoveNext()) + { + if (_handleToIndex.ContainsKey(tasksEnum.Current.Handle)) + { + ProcessIndex coIndex = _handleToIndex[tasksEnum.Current.Handle]; + + //if (tasksEnum.Current.PauseTime > 0d) + //CoindexReplace(coIndex, _InjectDelay(CoindexPeek(coIndex), (float)(GetSegmentTime(coIndex.seg) + tasksEnum.Current.PauseTime))); + + CoindexSetPause(coIndex, false); + } + } + } + + private static IEnumerator<float> _InjectDelay(IEnumerator<float> proc, float returnAt) + { + yield return returnAt; + + ReplacementFunction = delegate { return proc; }; + yield return float.NaN; + } + + /// <summary> + /// Use the command "yield return Timing.SwitchCoroutine(segment);" to switch this coroutine to + /// the given segment on the default instance. + /// </summary> + /// <param name="newSegment">The new segment to run in.</param> + public static float SwitchCoroutine(Segment newSegment) + { + ReplacementFunction = (coptr, instance, handle) => + { + ProcessIndex index = instance._handleToIndex[handle]; + instance.RunCoroutineInternal(coptr, newSegment, instance._processLayers.ContainsKey(index) ? instance._processLayers[index] : (int?)null, + instance._processTags.ContainsKey(index) ? instance._processTags[index] : null, handle, false); + return null; + }; + + return float.NaN; + } + + /// <summary> + /// Use the command "yield return Timing.SwitchCoroutine(segment, tag);" to switch this coroutine to + /// the given values. + /// </summary> + /// <param name="newSegment">The new segment to run in.</param> + /// <param name="newTag">The new tag to apply, or null to remove this coroutine's tag.</param> + public static float SwitchCoroutine(Segment newSegment, string newTag) + { + ReplacementFunction = (coptr, instance, handle) => + { + ProcessIndex index = instance._handleToIndex[handle]; + instance.RunCoroutineInternal(coptr, newSegment, + instance._processLayers.ContainsKey(index) ? instance._processLayers[index] : (int?)null, newTag, handle, false); + return null; + }; + + return float.NaN; + } + + /// <summary> + /// Use the command "yield return Timing.SwitchCoroutine(segment, layer);" to switch this coroutine to + /// the given values. + /// </summary> + /// <param name="newSegment">The new segment to run in.</param> + /// <param name="newLayer">The new layer to apply.</param> + public static float SwitchCoroutine(Segment newSegment, int newLayer) + { + ReplacementFunction = (coptr, instance, handle) => + { + ProcessIndex index = instance._handleToIndex[handle]; + instance.RunCoroutineInternal(coptr, newSegment, newLayer, + instance._processTags.ContainsKey(index) ? instance._processTags[index] : null, handle, false); + return null; + }; + + return float.NaN; + } + + /// <summary> + /// Use the command "yield return Timing.SwitchCoroutine(segment, layer, tag);" to switch this coroutine to + /// the given values. + /// </summary> + /// <param name="newSegment">The new segment to run in.</param> + /// <param name="newLayer">The new layer to apply.</param> + /// <param name="newTag">The new tag to apply, or null to remove this coroutine's tag.</param> + public static float SwitchCoroutine(Segment newSegment, int newLayer, string newTag) + { + ReplacementFunction = (coptr, instance, handle) => + { + instance.RunCoroutineInternal(coptr, newSegment, newLayer, newTag, handle, false); + return null; + }; + + return float.NaN; + } + + /// <summary> + /// Use the command "yield return Timing.SwitchCoroutine(tag);" to switch this coroutine to + /// the given tag. + /// </summary> + /// <param name="newTag">The new tag to apply, or null to remove this coroutine's tag.</param> + public static float SwitchCoroutine(string newTag) + { + ReplacementFunction = (coptr, instance, handle) => + { + instance.RemoveTag(instance._handleToIndex[handle]); + if (newTag != null) + instance.AddTag(newTag, instance._handleToIndex[handle]); + return coptr; + }; + return float.NaN; + } + + /// <summary> + /// Use the command "yield return Timing.SwitchCoroutine(layer);" to switch this coroutine to + /// the given layer. + /// </summary> + /// <param name="newLayer">The new layer to apply.</param> + public static float SwitchCoroutine(int newLayer) + { + ReplacementFunction = (coptr, instance, handle) => + { + instance.RemoveLayer(instance._handleToIndex[handle]); + instance.AddLayer(newLayer, instance._handleToIndex[handle]); + return coptr; + }; + + return float.NaN; + } + + /// <summary> + /// Use the command "yield return Timing.SwitchCoroutine(layer, tag);" to switch this coroutine to + /// the given tag. + /// </summary> + /// <param name="newLayer">The new layer to apply.</param> + /// <param name="newTag">The new tag to apply, or null to remove this coroutine's tag.</param> + public static float SwitchCoroutine(int newLayer, string newTag) + { + ReplacementFunction = (coptr, instance, handle) => + { + instance.RemoveLayer(instance._handleToIndex[handle]); + instance.AddLayer(newLayer, instance._handleToIndex[handle]); + instance.RemoveTag(instance._handleToIndex[handle]); + if (newTag != null) + instance.AddTag(newTag, instance._handleToIndex[handle]); + return coptr; + }; + + return float.NaN; + } + + ///// <summary> + ///// Use the command "yield return Timing.WaitUntilDone(wwwObject);" to pause the current + ///// coroutine until the wwwObject is done. + ///// </summary> + ///// <param name="wwwObject">The www object to pause for.</param> + //public static float WaitUntilDone(WWW wwwObject) + //{ + // if(wwwObject == null || wwwObject.isDone) return 0f; + // ReplacementFunction = (coptr, instance, handle) => _StartWhenDone(wwwObject, coptr); + + // return float.NaN; + //} + + private static IEnumerator<float> _StartWhenDone(WWW wwwObject, IEnumerator<float> pausedProc) + { + while (!wwwObject.isDone) + yield return 0f; + + ReplacementFunction = delegate { return pausedProc; }; + yield return float.NaN; + } + + ///// <summary> + ///// Use the command "yield return Timing.WaitUntilDone(operation);" to pause the current + ///// coroutine until the operation is done. + ///// </summary> + ///// <param name="operation">The operation variable returned.</param> + //public static float WaitUntilDone(AsyncOperation operation) + //{ + // if (operation == null || operation.isDone) return 0f; + // ReplacementFunction = (coptr, instance, handle) => _StartWhenDone(operation, coptr); + // return float.NaN; + //} + + private static IEnumerator<float> _StartWhenDone(AsyncOperation operation, IEnumerator<float> pausedProc) + { + while (!operation.isDone) + yield return 0f; + + ReplacementFunction = delegate { return pausedProc; }; + yield return float.NaN; + } + +#if !UNITY_4_6 && !UNITY_4_7 && !UNITY_5_0 && !UNITY_5_1 && !UNITY_5_2 + /// <summary> + /// Use the command "yield return Timing.WaitUntilDone(operation);" to pause the current + /// coroutine until the operation is done. + /// </summary> + /// <param name="operation">The operation variable returned.</param> + //public static float WaitUntilDone(CustomYieldInstruction operation) + //{ + // if (operation == null || !operation.keepWaiting) return 0f; + // ReplacementFunction = (coptr, instance, handle) => _StartWhenDone(operation, coptr); + // return float.NaN; + //} + + private static IEnumerator<float> _StartWhenDone(CustomYieldInstruction operation, IEnumerator<float> pausedProc) + { + while (operation.keepWaiting) + yield return 0f; + + ReplacementFunction = delegate { return pausedProc; }; + yield return float.NaN; + } +#endif + + /// <summary> + /// Use the command "yield return Timing.WaitUntilTrue(evaluatorFunc);" to pause the current + /// coroutine until the evaluator function returns true. + /// </summary> + /// <param name="evaluatorFunc">The evaluator function.</param> + public static float WaitUntilTrue(System.Func<bool> evaluatorFunc) + { + if (evaluatorFunc == null || evaluatorFunc()) return 0f; + ReplacementFunction = (coptr, instance, handle) => _StartWhenDone(evaluatorFunc, false, coptr); + return float.NaN; + } + + /// <summary> + /// Use the command "yield return Timing.WaitUntilFalse(evaluatorFunc);" to pause the current + /// coroutine until the evaluator function returns false. + /// </summary> + /// <param name="evaluatorFunc">The evaluator function.</param> + public static float WaitUntilFalse(System.Func<bool> evaluatorFunc) + { + if (evaluatorFunc == null || !evaluatorFunc()) return 0f; + ReplacementFunction = (coptr, instance, handle) => _StartWhenDone(evaluatorFunc, true, coptr); + return float.NaN; + } + + private static IEnumerator<float> _StartWhenDone(System.Func<bool> evaluatorFunc, bool continueOn, IEnumerator<float> pausedProc) + { + while (evaluatorFunc() == continueOn) + yield return 0f; + + ReplacementFunction = delegate { return pausedProc; }; + yield return float.NaN; + } + + + + + /// <summary> + /// Calls the specified action after a specified number of seconds. + /// </summary> + /// <param name="delay">The number of seconds to wait before calling the action.</param> + /// <param name="action">The action to call.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public static CoroutineHandle CallDelayed(float delay, System.Action action) + { + return action == null ? new CoroutineHandle() : RunCoroutine(Instance._DelayedCall(delay, action)); + } + + /// <summary> + /// Calls the specified action after a specified number of seconds. + /// </summary> + /// <param name="delay">The number of seconds to wait before calling the action.</param> + /// <param name="action">The action to call.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public CoroutineHandle CallDelayedOnInstance(float delay, System.Action action) + { + return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_DelayedCall(delay, action)); + } + + private IEnumerator<float> _DelayedCall(float delay, System.Action action) + { + yield return WaitForSecondsOnInstance(delay); + + action(); + } + + /// <summary> + /// Calls the supplied action at the given rate for a given number of seconds. + /// </summary> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="period">The amount of time between calls.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public static CoroutineHandle CallPeriodically(float timeframe, float period, System.Action action, System.Action onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(timeframe, period, action, onDone), Segment.Update); + } + + /// <summary> + /// Calls the supplied action at the given rate for a given number of seconds. + /// </summary> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="period">The amount of time between calls.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public CoroutineHandle CallPeriodicallyOnInstance(float timeframe, float period, System.Action action, System.Action onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(timeframe, period, action, onDone), Segment.Update); + } + + /// <summary> + /// Calls the supplied action at the given rate for a given number of seconds. + /// </summary> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="period">The amount of time between calls.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="timing">The timing segment to run in.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public static CoroutineHandle CallPeriodically(float timeframe, float period, System.Action action, Segment timing, System.Action onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(timeframe, period, action, onDone), timing); + } + + /// <summary> + /// Calls the supplied action at the given rate for a given number of seconds. + /// </summary> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="period">The amount of time between calls.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="timing">The timing segment to run in.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public CoroutineHandle CallPeriodicallyOnInstance(float timeframe, float period, System.Action action, Segment timing, System.Action onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(timeframe, period, action, onDone), timing); + } + + /// <summary> + /// Calls the supplied action at the given rate for a given number of seconds. + /// </summary> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public static CoroutineHandle CallContinuously(float timeframe, System.Action action, System.Action onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(timeframe, 0f, action, onDone), Segment.Update); + } + + /// <summary> + /// Calls the supplied action at the given rate for a given number of seconds. + /// </summary> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public CoroutineHandle CallContinuouslyOnInstance(float timeframe, System.Action action, System.Action onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(timeframe, 0f, action, onDone), Segment.Update); + } + + /// <summary> + /// Calls the supplied action every frame for a given number of seconds. + /// </summary> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="timing">The timing segment to run in.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public static CoroutineHandle CallContinuously(float timeframe, System.Action action, Segment timing, System.Action onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(timeframe, 0f, action, onDone), timing); + } + + /// <summary> + /// Calls the supplied action every frame for a given number of seconds. + /// </summary> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="timing">The timing segment to run in.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public CoroutineHandle CallContinuouslyOnInstance(float timeframe, System.Action action, Segment timing, System.Action onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(timeframe, 0f, action, onDone), timing); + } + + private IEnumerator<float> _CallContinuously(float timeframe, float period, System.Action action, System.Action onDone) + { + double startTime = localTime; + while (localTime <= startTime + timeframe) + { + yield return WaitForSecondsOnInstance(period); + + action(); + } + + if (onDone != null) + onDone(); + } + + /// <summary> + /// Calls the supplied action at the given rate for a given number of seconds. + /// </summary> + /// <param name="reference">A value that will be passed in to the supplied action each period.</param> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="period">The amount of time between calls.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public static CoroutineHandle CallPeriodically<T>(T reference, float timeframe, float period, + System.Action<T> action, System.Action<T> onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(reference, timeframe, period, action, onDone), Segment.Update); + } + + /// <summary> + /// Calls the supplied action at the given rate for a given number of seconds. + /// </summary> + /// <param name="reference">A value that will be passed in to the supplied action each period.</param> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="period">The amount of time between calls.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public CoroutineHandle CallPeriodicallyOnInstance<T>(T reference, float timeframe, float period, + System.Action<T> action, System.Action<T> onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(reference, timeframe, period, action, onDone), Segment.Update); + } + + /// <summary> + /// Calls the supplied action at the given rate for a given number of seconds. + /// </summary> + /// <param name="reference">A value that will be passed in to the supplied action each period.</param> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="period">The amount of time between calls.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="timing">The timing segment to run in.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public static CoroutineHandle CallPeriodically<T>(T reference, float timeframe, float period, System.Action<T> action, + Segment timing, System.Action<T> onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(reference, timeframe, period, action, onDone), timing); + } + + /// <summary> + /// Calls the supplied action at the given rate for a given number of seconds. + /// </summary> + /// <param name="reference">A value that will be passed in to the supplied action each period.</param> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="period">The amount of time between calls.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="timing">The timing segment to run in.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public CoroutineHandle CallPeriodicallyOnInstance<T>(T reference, float timeframe, float period, System.Action<T> action, + Segment timing, System.Action<T> onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(reference, timeframe, period, action, onDone), timing); + } + + /// <summary> + /// Calls the supplied action every frame for a given number of seconds. + /// </summary> + /// <param name="reference">A value that will be passed in to the supplied action each frame.</param> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public static CoroutineHandle CallContinuously<T>(T reference, float timeframe, System.Action<T> action, System.Action<T> onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(reference, timeframe, 0f, action, onDone), Segment.Update); + } + + /// <summary> + /// Calls the supplied action every frame for a given number of seconds. + /// </summary> + /// <param name="reference">A value that will be passed in to the supplied action each frame.</param> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public CoroutineHandle CallContinuouslyOnInstance<T>(T reference, float timeframe, System.Action<T> action, System.Action<T> onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(reference, timeframe, 0f, action, onDone), Segment.Update); + } + + /// <summary> + /// Calls the supplied action every frame for a given number of seconds. + /// </summary> + /// <param name="reference">A value that will be passed in to the supplied action each frame.</param> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="timing">The timing segment to run in.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public static CoroutineHandle CallContinuously<T>(T reference, float timeframe, System.Action<T> action, + Segment timing, System.Action<T> onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutine(Instance._CallContinuously(reference, timeframe, 0f, action, onDone), timing); + } + + /// <summary> + /// Calls the supplied action every frame for a given number of seconds. + /// </summary> + /// <param name="reference">A value that will be passed in to the supplied action each frame.</param> + /// <param name="timeframe">The number of seconds that this function should run.</param> + /// <param name="action">The action to call every frame.</param> + /// <param name="timing">The timing segment to run in.</param> + /// <param name="onDone">An optional action to call when this function finishes.</param> + /// <returns>The handle to the coroutine that is started by this function.</returns> + public CoroutineHandle CallContinuouslyOnInstance<T>(T reference, float timeframe, System.Action<T> action, + Segment timing, System.Action<T> onDone = null) + { + return action == null ? new CoroutineHandle() : RunCoroutineOnInstance(_CallContinuously(reference, timeframe, 0f, action, onDone), timing); + } + + private IEnumerator<float> _CallContinuously<T>(T reference, float timeframe, float period, + System.Action<T> action, System.Action<T> onDone = null) + { + double startTime = localTime; + while (localTime <= startTime + timeframe) + { + yield return WaitForSecondsOnInstance(period); + + action(reference); + } + + if (onDone != null) + onDone(reference); + } + + private struct ProcessData : System.IEquatable<ProcessData> + { + public CoroutineHandle Handle; + //public float PauseTime; + + public bool Equals(ProcessData other) + { + return Handle == other.Handle; + } + + public override bool Equals(object other) + { + if (other is ProcessData) + return Equals((ProcessData)other); + return false; + } + + public override int GetHashCode() + { + return Handle.GetHashCode(); + } + } + + private struct ProcessIndex : System.IEquatable<ProcessIndex> + { + public Segment seg; + public int i; + + public bool Equals(ProcessIndex other) + { + return seg == other.seg && i == other.i; + } + + public override bool Equals(object other) + { + if (other is ProcessIndex) + return Equals((ProcessIndex)other); + return false; + } + + public static bool operator ==(ProcessIndex a, ProcessIndex b) + { + return a.seg == b.seg && a.i == b.i; + } + + public static bool operator !=(ProcessIndex a, ProcessIndex b) + { + return a.seg != b.seg || a.i != b.i; + } + + public override int GetHashCode() + { + return (((int)seg - 4) * (int.MaxValue / 7)) + i; + } + } + + } + + public enum Segment + { + Invalid = -1, + Update, + FixedUpdate, + LateUpdate, + SlowUpdate, + RealtimeUpdate, + EditorUpdate, + EditorSlowUpdate, + EndOfFrame, + ManualTimeframe + } + + /// <summary> + /// A handle for a MEC coroutine. + /// </summary> + public struct CoroutineHandle : System.IEquatable<CoroutineHandle> + { + private static int _nextIndex; + private readonly int _id; + + public byte Key { get { return (byte)(_id & 0x1F); } } + + public CoroutineHandle(byte ind) + { + if (ind > 0x1F) + ind -= 0x1F; + + _id = _nextIndex + ind; + _nextIndex += 0x20; + } + + public bool Equals(CoroutineHandle other) + { + return _id == other._id; + } + + public override bool Equals(object other) + { + if (other is CoroutineHandle) + return Equals((CoroutineHandle)other); + return false; + } + + public static bool operator ==(CoroutineHandle a, CoroutineHandle b) + { + return a._id == b._id; + } + + public static bool operator !=(CoroutineHandle a, CoroutineHandle b) + { + return a._id != b._id; + } + + public override int GetHashCode() + { + return _id; + } + + /// <summary> + /// Get or set the corrosponding coroutine's tag. Null removes the tag or represents no tag assigned. + /// </summary> + public string Tag + { + get { return Timing.GetTag(this); } + set { Timing.SetTag(this, value); } + } + + /// <summary> + /// Get or set the corrosponding coroutine's layer. Null removes the layer or represents no layer assigned. + /// </summary> + public int? Layer + { + get { return Timing.GetLayer(this); } + set + { + if (value == null) + Timing.RemoveLayer(this); + else + Timing.SetLayer(this, (int)value); + } + } + + /// <summary> + /// Get or set the coorsponding coroutine's segment. + /// </summary> + public Segment Segment + { + get { return Timing.GetSegment(this); } + set { Timing.SetSegment(this, value); } + } + + /// <summary> + /// Is true until the coroutine function ends or is killed. Setting this to false will kill the coroutine. + /// </summary> + public bool IsRunning + { + get { return Timing.IsRunning(this); } + set { if (!value) Timing.KillCoroutines(this); } + } + + /// <summary> + /// Is true while the coroutine is paused (but not in a WaitUntilDone holding pattern). Setting this will pause and resume the coroutine. + /// </summary> + public bool IsPaused + { + get { return Timing.IsPaused(this); } + set { if (value) Timing.PauseCoroutines(this); else Timing.ResumeCoroutines(this); } + } + } +} + +public static class MECExtensionMethods +{ + /// <summary> + /// Adds a delay to the beginning of this coroutine. + /// </summary> + /// <param name="coroutine">The coroutine handle to act upon.</param> + /// <param name="timeToDelay">The number of seconds to delay this coroutine.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> Delay(this IEnumerator<float> coroutine, float timeToDelay) + { + yield return MovementEffects.Timing.WaitForSeconds(timeToDelay); + + while (coroutine.MoveNext()) + yield return coroutine.Current; + } + + /// <summary> + /// Adds a delay to the beginning of this coroutine until a function returns true. + /// </summary> + /// <param name="coroutine">The coroutine handle to act upon.</param> + /// <param name="condition">The coroutine will be paused until this function returns true.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> Delay(this IEnumerator<float> coroutine, System.Func<bool> condition) + { + while (!condition()) + yield return 0f; + + while (coroutine.MoveNext()) + yield return coroutine.Current; + } + + /// <summary> + /// Adds a delay to the beginning of this coroutine until a function returns true. + /// </summary> + /// <param name="coroutine">The coroutine handle to act upon.</param> + /// <param name="data">A variable that will be passed into the condition function each time it is tested.</param> + /// <param name="condition">The coroutine will be paused until this function returns true.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> Delay<T>(this IEnumerator<float> coroutine, T data, System.Func<T, bool> condition) + { + while (!condition(data)) + yield return 0f; + + while (coroutine.MoveNext()) + yield return coroutine.Current; + } + + /// <summary> + /// Cancels this coroutine when the supplied game object is destroyed or made inactive. + /// </summary> + /// <param name="coroutine">The coroutine handle to act upon.</param> + /// <param name="gameObject">The GameObject to test.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> CancelWith(this IEnumerator<float> coroutine, GameObject gameObject) + { + while (gameObject && gameObject.activeInHierarchy && coroutine.MoveNext()) + yield return coroutine.Current; + } + + /// <summary> + /// Cancels this coroutine when the supplied game objects are destroyed or made inactive. + /// </summary> + /// <param name="coroutine">The coroutine handle to act upon.</param> + /// <param name="gameObject1">The first GameObject to test.</param> + /// <param name="gameObject2">The second GameObject to test</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> CancelWith(this IEnumerator<float> coroutine, GameObject gameObject1, GameObject gameObject2) + { + while (gameObject1 && gameObject1.activeInHierarchy && gameObject2 && gameObject2.activeInHierarchy && coroutine.MoveNext()) + yield return coroutine.Current; + } + + /// <summary> + /// Cancels this coroutine when the supplied game objects are destroyed or made inactive. + /// </summary> + /// <param name="coroutine">The coroutine handle to act upon.</param> + /// <param name="gameObject1">The first GameObject to test.</param> + /// <param name="gameObject2">The second GameObject to test</param> + /// <param name="gameObject3">The third GameObject to test.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> CancelWith(this IEnumerator<float> coroutine, + GameObject gameObject1, GameObject gameObject2, GameObject gameObject3) + { + while (gameObject1 && gameObject1.activeInHierarchy && gameObject2 && gameObject2.activeInHierarchy && + gameObject3 && gameObject3.activeInHierarchy && coroutine.MoveNext()) + yield return coroutine.Current; + } + + /// <summary> + /// Cancels this coroutine when the supplied function returns false. + /// </summary> + /// <param name="coroutine">The coroutine handle to act upon.</param> + /// <param name="condition">The test function. True for continue, false to stop.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> CancelWith(this IEnumerator<float> coroutine, System.Func<bool> condition) + { + if (condition == null) yield break; + + while (condition() && coroutine.MoveNext()) + yield return coroutine.Current; + } + + /// <summary> + /// Runs the supplied coroutine immediately after this one. + /// </summary> + /// <param name="coroutine">The coroutine handle to act upon.</param> + /// <param name="nextCoroutine">The coroutine to run next.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> Append(this IEnumerator<float> coroutine, IEnumerator<float> nextCoroutine) + { + while (coroutine.MoveNext()) + yield return coroutine.Current; + + if (nextCoroutine != null) + while (nextCoroutine.MoveNext()) + yield return nextCoroutine.Current; + } + + /// <summary> + /// Runs the supplied function immediately after this coroutine finishes. + /// </summary> + /// <param name="coroutine">The coroutine handle to act upon.</param> + /// <param name="onDone">The action to run after this coroutine finishes.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> Append(this IEnumerator<float> coroutine, System.Action onDone) + { + while (coroutine.MoveNext()) + yield return coroutine.Current; + + if (onDone != null) + onDone(); + } + + /// <summary> + /// Runs the supplied coroutine immediately before this one. + /// </summary> + /// <param name="coroutine">The coroutine handle to act upon.</param> + /// <param name="lastCoroutine">The coroutine to run first.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> Prepend(this IEnumerator<float> coroutine, IEnumerator<float> lastCoroutine) + { + if (lastCoroutine != null) + while (lastCoroutine.MoveNext()) + yield return lastCoroutine.Current; + + while (coroutine.MoveNext()) + yield return coroutine.Current; + } + + /// <summary> + /// Runs the supplied function immediately before this coroutine starts. + /// </summary> + /// <param name="coroutine">The coroutine handle to act upon.</param> + /// <param name="onStart">The action to run before this coroutine starts.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> Prepend(this IEnumerator<float> coroutine, System.Action onStart) + { + if (onStart != null) + onStart(); + + while (coroutine.MoveNext()) + yield return coroutine.Current; + } + + /// <summary> + /// Combines the this coroutine with another and runs them in a combined handle. + /// </summary> + /// <param name="coroutineA">The coroutine handle to act upon.</param> + /// <param name="coroutineB">The coroutine handle to combine.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> Superimpose(this IEnumerator<float> coroutineA, IEnumerator<float> coroutineB) + { + return Superimpose(coroutineA, coroutineB, MovementEffects.Timing.Instance); + } + + /// <summary> + /// Combines the this coroutine with another and runs them in a combined handle. + /// </summary> + /// <param name="coroutineA">The coroutine handle to act upon.</param> + /// <param name="coroutineB">The coroutine handle to combine.</param> + /// <param name="instance">The timing instance that this will be run in, if not the default instance.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> Superimpose(this IEnumerator<float> coroutineA, IEnumerator<float> coroutineB, MovementEffects.Timing instance) + { + while (coroutineA != null || coroutineB != null) + { + if (coroutineA != null && !(instance.localTime < coroutineA.Current) && !coroutineA.MoveNext()) + coroutineA = null; + + if (coroutineB != null && !(instance.localTime < coroutineB.Current) && !coroutineB.MoveNext()) + coroutineB = null; + + if ((coroutineA != null && float.IsNaN(coroutineA.Current)) || (coroutineB != null && float.IsNaN(coroutineB.Current))) + yield return float.NaN; + else if (coroutineA != null && coroutineB != null) + yield return coroutineA.Current < coroutineB.Current ? coroutineA.Current : coroutineB.Current; + else if (coroutineA == null && coroutineB != null) + yield return coroutineB.Current; + else if (coroutineA != null) + yield return coroutineA.Current; + } + } + + /// <summary> + /// Uses the passed in function to change the return values of this coroutine. + /// </summary> + /// <param name="coroutine">The coroutine handle to act upon.</param> + /// <param name="newReturn">A function that takes the current return value and returns the new return.</param> + /// <returns>The modified coroutine handle.</returns> + public static IEnumerator<float> Hijack(this IEnumerator<float> coroutine, System.Func<float, float> newReturn) + { + if (newReturn == null) yield break; + + while (coroutine.MoveNext()) + yield return newReturn(coroutine.Current); + } +} + diff --git a/WorldlineKeepers/Assets/Plugins/Trinary Software/Timing.cs.meta b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Timing.cs.meta index 21e6c1b..21e6c1b 100644 --- a/WorldlineKeepers/Assets/Plugins/Trinary Software/Timing.cs.meta +++ b/WorldlineKeepers/Assets/Scripts/Tools/Trinary Software/Timing.cs.meta diff --git a/WorldlineKeepers/Assets/Scripts/Tools/UnityObjectPool.cs b/WorldlineKeepers/Assets/Scripts/Tools/UnityObjectPool.cs new file mode 100644 index 0000000..3005b27 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/UnityObjectPool.cs @@ -0,0 +1,128 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace WK +{ + + public class UnityObjectPool<T> : System.IDisposable where T : UnityEngine.Object + { + public interface IPooledInstanceInitializer + { + void InitPooledInstance(T instance); + void DestroyPooledInstance(T instance); + } + + private T _sourceObject; + private Transform _poolParent; + private int _initialSize; + + private IPooledInstanceInitializer _initalizer; + + private List<T> _usedInstances; + private Stack<T> _freeInstances; + + public UnityObjectPool(T sourceObject, Transform poolParent, IPooledInstanceInitializer initializer, int initialSize = 0) + { + Debug.Assert(sourceObject != null); + Debug.Assert(poolParent != null); + Debug.Assert(initializer != null); + Debug.Assert(initialSize >= 0); + + _sourceObject = sourceObject; + _poolParent = poolParent; + _initalizer = initializer; + _initialSize = initialSize; + + _usedInstances = new List<T>(); + _freeInstances = new Stack<T>(initialSize); + + for (int i = 0; i < _initialSize; ++i) + { + T instance = CreateInstance(); + _freeInstances.Push(instance); + } + } + + public T Acquire() + { + T instance; + if (_freeInstances.Count > 0) + { + instance = _freeInstances.Pop(); + } + else + { + instance = CreateInstance(); + } + + _usedInstances.Add(instance); + + return instance; + } + + public void Release(T instance) + { + Debug.Assert(instance != null); + Debug.Assert(!_freeInstances.Contains(instance)); + + bool res = _usedInstances.Remove(instance); + Debug.Assert(res); + _freeInstances.Push(instance); + } + + private T CreateInstance() + { + T instance = Object.Instantiate(_sourceObject, _poolParent); + + // Let the client code initialize the object however it want to + _initalizer.InitPooledInstance(instance); + + return instance; + } + + #region IDisposable Support + private bool _disposed = false; + + protected virtual void Dispose(bool disposing) + { + if (Application.isPlaying) + { + Debug.Assert(!_disposed, "Trying to dispose an pool twice!"); + Debug.Assert(_usedInstances.Count == 0, "Disposing pool before releasing all objects!"); + } + + foreach (T instance in _freeInstances) + { + // We can't destroy a UnityEngine.Object without knowing if it's a Gameobject or Component. Calling Destroy(instance) directly here will give not destroy the Gameobject if it's a component + _initalizer.DestroyPooledInstance(instance); + } + _freeInstances.Clear(); + + _sourceObject = null; + _poolParent = null; + _initalizer = null; + + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + System.GC.SuppressFinalize(this); + } + +#if ENABLE_ASSERTS + ~UnityObjectPool() + { + MainThreadDispatcher.BeginInvoke(() => // Can't call Application.isPlaying in Unity's finalizer thread + { + if (Application.isPlaying) + { + DebugTools.Assert(false, string.Format("Failed to dispose a pool of type {0}!", typeof(T))); + } + }); + } +#endif + #endregion + } +} diff --git a/WorldlineKeepers/Assets/Scripts/Tools/UnityObjectPool.cs.meta b/WorldlineKeepers/Assets/Scripts/Tools/UnityObjectPool.cs.meta new file mode 100644 index 0000000..b5da7d0 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Tools/UnityObjectPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d3e19936e347184b9b193d4a0688a73 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/UI/PanelBase.cs b/WorldlineKeepers/Assets/Scripts/UI/PanelBase.cs new file mode 100644 index 0000000..f0b25f7 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/UI/PanelBase.cs @@ -0,0 +1,16 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace WK +{ + + /// <summary> + /// + /// </summary> + public class PanelBase : MonoBehaviour + { + + } + +} diff --git a/WorldlineKeepers/Assets/Scripts/UI/PanelBase.cs.meta b/WorldlineKeepers/Assets/Scripts/UI/PanelBase.cs.meta new file mode 100644 index 0000000..fbb8228 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/UI/PanelBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f1b7648b8de812a4b8c1fd40a9747c64 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/Assets/Scripts/Utils/LogHelper.cs b/WorldlineKeepers/Assets/Scripts/Utils/LogHelper.cs index 5ffed1e..e019695 100644 --- a/WorldlineKeepers/Assets/Scripts/Utils/LogHelper.cs +++ b/WorldlineKeepers/Assets/Scripts/Utils/LogHelper.cs @@ -6,10 +6,18 @@ using UnityEngine; namespace WK { - public class LogHelper + public static class LogHelper { + public static void LogError(object msg) + { + Debug.LogError(msg); + } + public static void Log(object msg) + { + Debug.Log(msg); + } } diff --git a/WorldlineKeepers/Assets/Scripts/Utils/TransformUtils.cs b/WorldlineKeepers/Assets/Scripts/Utils/TransformUtils.cs new file mode 100644 index 0000000..dcfb8c9 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Utils/TransformUtils.cs @@ -0,0 +1,18 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace WK +{ + + public static class TransformUtils + { + public static void ResetLocal(this Transform transform) + { + transform.localPosition = Vector3.zero; + transform.localRotation = Quaternion.identity; + transform.localScale = Vector3.one; + } + } + +} diff --git a/WorldlineKeepers/Assets/Scripts/Utils/TransformUtils.cs.meta b/WorldlineKeepers/Assets/Scripts/Utils/TransformUtils.cs.meta new file mode 100644 index 0000000..ef5cea8 --- /dev/null +++ b/WorldlineKeepers/Assets/Scripts/Utils/TransformUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 644de0d155567684eb770eafd8e91ac4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WorldlineKeepers/ProjectSettings/EditorBuildSettings.asset b/WorldlineKeepers/ProjectSettings/EditorBuildSettings.asset index 263d68c..ad91e94 100644 --- a/WorldlineKeepers/ProjectSettings/EditorBuildSettings.asset +++ b/WorldlineKeepers/ProjectSettings/EditorBuildSettings.asset @@ -6,6 +6,18 @@ EditorBuildSettings: serializedVersion: 2 m_Scenes: - enabled: 1 - path: Assets/Scenes/Test.unity + path: Assets/Scenes/0_Test.unity guid: 6091fb2bc6977fe4da28c51e3e925b9e + - enabled: 1 + path: Assets/Scenes/1_Entry.unity + guid: 112e175ba5ba1114ebabf31b55e3bdd5 + - enabled: 1 + path: Assets/Scenes/2_Lobby.unity + guid: ecdcfd6e9a5201c48b99a4fa4d0f9fd8 + - enabled: 1 + path: Assets/Scenes/3_Dojo.unity + guid: 43d444c8630a4cf43a5d69b05d63e08e + - enabled: 1 + path: Assets/Scenes/4_Stage.unity + guid: ccc90f7a55ca31e4a820bc889ba35fec m_configObjects: {} |