summaryrefslogtreecommitdiff
path: root/WorldlineKeepers/Assets/Plugins/Trinary Software/Timing.cs
diff options
context:
space:
mode:
Diffstat (limited to 'WorldlineKeepers/Assets/Plugins/Trinary Software/Timing.cs')
-rw-r--r--WorldlineKeepers/Assets/Plugins/Trinary Software/Timing.cs2738
1 files changed, 2738 insertions, 0 deletions
diff --git a/WorldlineKeepers/Assets/Plugins/Trinary Software/Timing.cs b/WorldlineKeepers/Assets/Plugins/Trinary Software/Timing.cs
new file mode 100644
index 0000000..c61b93e
--- /dev/null
+++ b/WorldlineKeepers/Assets/Plugins/Trinary Software/Timing.cs
@@ -0,0 +1,2738 @@
+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;
+ }
+}