internal void Resume(SPComponent behaviour) { if (_state != RadicalCoroutineOperatingState.Inactive) throw new System.InvalidOperationException("Failed to start RadicalCoroutine. The Coroutine is already being processed."); if (behaviour == null) throw new System.ArgumentNullException("behaviour"); _state = RadicalCoroutineOperatingState.Active; _owner = behaviour; _token = behaviour.StartCoroutine(this); if (_stack.Count > 0 && _stack.Peek() is IPausibleYieldInstruction) (_stack.Peek() as IPausibleYieldInstruction).OnResume(); }
private void OnFinish(bool cancelled) { _stack.Clear(); _currentIEnumeratorYieldValue = null; try { _owner.StopCoroutine(this); //NOTE - due to a bug in unity, a runtime warning appears if you pass in the Coroutine token while this routine is 'WaitForSeconds' } catch (System.Exception ex) { Debug.LogException(ex); } var ev = System.EventArgs.Empty; try { if (cancelled) { _state = RadicalCoroutineOperatingState.Cancelled; if (this.OnCancelled != null) this.OnCancelled(this, ev); } else { _state = RadicalCoroutineOperatingState.Complete; if (this.OnComplete != null) this.OnComplete(this, ev); } } catch (System.Exception ex) { Debug.LogException(ex); } if (_immediatelyResumingSignal != null) { try { _immediatelyResumingSignal(this, ev); } catch (System.Exception ex) { Debug.LogException(ex); } } if (this.OnFinished != null) { try { this.OnFinished(this, System.EventArgs.Empty); } catch (System.Exception ex) { Debug.LogException(ex); } } _owner = null; _token = null; }
/// <summary> /// Starts the coroutine, one should always call this method or the StartRadicalCoroutine extension method. Never pass the RadicalCoroutine into the 'StartCoroutine' method. /// </summary> /// <param name="behaviour">A reference to the MonoBehaviour that should be handling the coroutine.</param> /// <param name="disableMode">A disableMode other than Default is only supported if the behaviour is an SPComponent.</param> /// <remarks> /// Disable modes allow you to decide how the coroutine is dealt with when the component/gameobject are disabled. Note that 'CancelOnDeactivate' is a specical case flag, /// it only takes effect if NO OTHER flag is set (it's a 0 flag actually). What this results in is that Deactivate and Disable are pausible... but a routine can only play /// through Disable, not Deactivate. This is due to the default behaviour of coroutine in unity. In default mode, coroutines continue playing when a component gets disabled, /// but when deactivated the coroutine gets cancelled. This means we can not play through a deactivation. /// </remarks> public void Start(MonoBehaviour behaviour, RadicalCoroutineDisableMode disableMode = RadicalCoroutineDisableMode.Default) { if (_state != RadicalCoroutineOperatingState.Inactive) throw new System.InvalidOperationException("Failed to start RadicalCoroutine. The Coroutine is already being processed."); if (behaviour == null) throw new System.ArgumentNullException("behaviour"); _state = RadicalCoroutineOperatingState.Active; _owner = behaviour; _token = behaviour.StartCoroutine(this); _disableMode = disableMode; if (_disableMode > RadicalCoroutineDisableMode.Default && _disableMode != RadicalCoroutineDisableMode.ResumeOnEnable) { //no point in managing the routine if it acts in default mode... a flag of 'ResumeOnEnable' is a redundant mode to default var manager = behaviour.AddOrGetComponent<RadicalCoroutineManager>(); manager.RegisterCoroutine(behaviour, this); } if (_stack.Count > 0 && _stack.Peek() is IPausibleYieldInstruction) (_stack.Peek() as IPausibleYieldInstruction).OnResume(); }
/// <summary> /// Stops the coroutine, but preserves the state of it, so that it could be resumed again later by calling start. /// </summary> /// <param name="behaviour">A reference to the MonoBehaviour that is handling the coroutine.</param> public void Stop() { if (_state == RadicalCoroutineOperatingState.Inactive) return; if (_state == RadicalCoroutineOperatingState.Cancelling || _state == RadicalCoroutineOperatingState.Completing) { this.OnFinish(_state == RadicalCoroutineOperatingState.Cancelling); } else if (_state == RadicalCoroutineOperatingState.Active) { _state = RadicalCoroutineOperatingState.Inactive; try { _owner.StopCoroutine(this); //NOTE - due to a bug in unity, a runtime warning appears if you pass in the Coroutine token while this routine is 'WaitForSeconds' } catch (System.Exception ex) { Debug.LogException(ex); } _owner = null; _token = null; if (_stack.Count > 0 && _stack.Peek() is IPausibleYieldInstruction) (_stack.Peek() as IPausibleYieldInstruction).OnPause(); } }
/// <summary> /// Stops the coroutine flagging it as finished. /// </summary> public void Cancel() { _state = RadicalCoroutineOperatingState.Cancelling; }
bool IEnumerator.MoveNext() { if (this.Cancelled) { if (_state == RadicalCoroutineOperatingState.Cancelling) { this.OnFinish(true); } return false; } else if (this.Complete) { if (_state == RadicalCoroutineOperatingState.Completing) { this.OnFinish(false); } return false; } if (_forcedTick) { _forcedTick = false; return true; } _currentIEnumeratorYieldValue = null; //actually operate while (_stack.Count > 0 && !_stack.Peek().ContinueBlocking()) { if (_stack.Peek() is IPooledYieldInstruction) (_stack.Pop() as IPooledYieldInstruction).Dispose(); else _stack.Pop(); } if (_stack.Count > 0) { if (this.Cancelled) { //routine cancelled itself if (_state == RadicalCoroutineOperatingState.Cancelling) { this.OnFinish(true); } return false; } var current = _stack.Peek().CurrentYieldObject; if (current == null) { //do nothing } else if (current is YieldInstruction) { if (current is WaitForSeconds && _disableMode.HasFlag(RadicalCoroutineDisableMode.ResumeOnEnable)) { _currentIEnumeratorYieldValue = null; _stack.Push(WaitForDuration.FromWaitForSeconds(current as WaitForSeconds)); } else { _currentIEnumeratorYieldValue = current; } } else if (current is WWW) { _currentIEnumeratorYieldValue = current; } else if (current is RadicalCoroutine) { // //v3 var rad = current as RadicalCoroutine; if (!rad.Finished) { if (rad._token != null) { _currentIEnumeratorYieldValue = rad._token; } else { _stack.Push(EnumWrapper.Create(WaitUntilDone_Routine(rad))); } } else { _currentIEnumeratorYieldValue = null; } } else if (current is IRadicalYieldInstruction) { var instruction = current as IRadicalYieldInstruction; if (instruction is IImmediatelyResumingYieldInstruction) (instruction as IImmediatelyResumingYieldInstruction).Signal += this.OnImmediatelyResumingYieldInstructionSignaled; if (instruction.ContinueBlocking()) { _currentIEnumeratorYieldValue = instruction.CurrentYieldObject; _stack.Push(instruction); } } else if (current is IEnumerable) { //yes we have to test for IEnumerable before IEnumerator. When a yield method is returned as an IEnumerator, it still needs 'GetEnumerator' called on it. var e = (current as IEnumerable).GetEnumerator(); if (e.MoveNext()) { _currentIEnumeratorYieldValue = e.Current; _stack.Push(EnumWrapper.Create(e)); } } else if (current is IEnumerator) { var e = current as IEnumerator; if (e.MoveNext()) { _currentIEnumeratorYieldValue = e.Current; _stack.Push(EnumWrapper.Create(e)); } } else if (current is RadicalCoroutineEndCommand) { var cmd = (RadicalCoroutineEndCommand)current; if (cmd.HasFlag(RadicalCoroutineEndCommand.Cancel)) { this.Cancel(); if (cmd.HasFlag(RadicalCoroutineEndCommand.StallImmediateResume)) { _currentIEnumeratorYieldValue = null; } else { this.OnFinish(true); return false; } } else { if (cmd.HasFlag(RadicalCoroutineEndCommand.StallImmediateResume)) { _state = RadicalCoroutineOperatingState.Completing; _currentIEnumeratorYieldValue = null; } else { this.OnFinish(false); return false; } } } else { _currentIEnumeratorYieldValue = current; } return true; } else { this.OnFinish(false); return false; } }
/// <summary> /// Manually step the coroutine. This is usually done from within a Update event. /// </summary> /// <param name="handle">A MonoBehaviour to use as a handle if a YieldInstruction is to be operated on.</param> /// <returns></returns> public bool ManualTick(MonoBehaviour handle) { if (_owner != null || _state != RadicalCoroutineOperatingState.Inactive) throw new System.InvalidOperationException("Can not manually operate a RadicalCoroutine that is already being operated."); if (handle == null) throw new System.ArgumentNullException("handle"); _state = RadicalCoroutineOperatingState.Active; bool result = false; try { result = (this as IEnumerator).MoveNext(); } catch(System.Exception ex) { Debug.LogException(ex, handle); } if (_state == RadicalCoroutineOperatingState.Active) _state = RadicalCoroutineOperatingState.Inactive; if (result) { var current = _currentIEnumeratorYieldValue; _currentIEnumeratorYieldValue = null; if (current is YieldInstruction || current is WWW) { var wait = ManualWaitForGeneric.Create(this, handle, current); _stack.Push(wait); wait.Start(); } } return result; }
public void Dispose() { _state = RadicalCoroutineOperatingState.Inactive; _disableMode = RadicalCoroutineDisableMode.Default; if (_stack != null) _stack.Clear(); _currentIEnumeratorYieldValue = null; _forcedTick = false; this.OnComplete = null; this.OnCancelled = null; this.OnFinished = null; //TODO - #100 - allow releasing when we've fully implemented coroutine object caching //_pool.Release(this); }
/// <summary> /// Should only be ever called from RadicalCoroutineManager. /// </summary> /// <param name="cancelledByManager"></param> internal void Cancel(bool cancelledByManager) { _state = RadicalCoroutineOperatingState.Cancelling; if (cancelledByManager) { _manager = null; } }
/// <summary> /// Should only be ever called from RadicalCoroutineManager. /// </summary> /// <param name="stalledByManager"></param> internal void Stop(bool stalledByManager) { if (_state == RadicalCoroutineOperatingState.Inactive) return; if (_state == RadicalCoroutineOperatingState.Cancelling || _state == RadicalCoroutineOperatingState.Completing) { this.OnFinish(_state == RadicalCoroutineOperatingState.Cancelling); } else if (_state == RadicalCoroutineOperatingState.Active) { if (_owner != null) { try { if (_owner != null) _owner.StopCoroutine(this);//NOTE - due to a bug in unity, a runtime warning appears if you pass in the Coroutine token while this routine is 'WaitForSeconds' } catch (System.Exception ex) { Debug.LogException(ex, _owner); } } if (!stalledByManager && _manager != null) { _manager.UnregisterCoroutine(this); _state = RadicalCoroutineOperatingState.Inactive; _owner = null; _token = null; } else { _state = RadicalCoroutineOperatingState.Paused; _token = null; } if (_stack.CurrentOperation is IPausibleYieldInstruction) (_stack.CurrentOperation as IPausibleYieldInstruction).OnPause(); } }
internal void Resume(MonoBehaviour behaviour) { if (_state != RadicalCoroutineOperatingState.Inactive && _state != RadicalCoroutineOperatingState.Paused) throw new System.InvalidOperationException("Failed to start RadicalCoroutine. The Coroutine is already being processed."); if (behaviour == null) throw new System.ArgumentNullException("behaviour"); _state = RadicalCoroutineOperatingState.Active; _owner = behaviour; if (_stack.CurrentOperation is IPausibleYieldInstruction) (_stack.CurrentOperation as IPausibleYieldInstruction).OnResume(); #if SP_LIB var manager = behaviour.AddOrGetComponent<RadicalCoroutineManager>(); #else var manager = behaviour.GetComponent<RadicalCoroutineManager>(); if (manager == null) manager = behaviour.gameObject.AddComponent<RadicalCoroutineManager>(); #endif _manager = manager; _manager.RegisterCoroutine(this); _token = behaviour.StartCoroutine(this); }
public void StartAsync(MonoBehaviour behaviour, RadicalCoroutineDisableMode disableMode = RadicalCoroutineDisableMode.Default) { if (_state != RadicalCoroutineOperatingState.Inactive) throw new System.InvalidOperationException("Failed to start RadicalCoroutine. The Coroutine is already being processed."); if (behaviour == null) throw new System.ArgumentNullException("behaviour"); _state = RadicalCoroutineOperatingState.Active; _owner = behaviour; _disableMode = disableMode; if (_stack.CurrentOperation is IPausibleYieldInstruction) (_stack.CurrentOperation as IPausibleYieldInstruction).OnResume(); _stack.Push(com.spacepuppy.Async.RadicalTask.Create(this)); //we start the task as an async operation #if SP_LIB var manager = behaviour.AddOrGetComponent<RadicalCoroutineManager>(); #else var manager = behaviour.GetComponent<RadicalCoroutineManager>(); if (manager == null) manager = behaviour.gameObject.AddComponent<RadicalCoroutineManager>(); #endif _manager = manager; _manager.RegisterCoroutine(this); _token = behaviour.StartCoroutine(this); }
bool IEnumerator.MoveNext() { if (_state == RadicalCoroutineOperatingState.Inactive) return false; if (this.Cancelled) { if (_state == RadicalCoroutineOperatingState.Cancelling) { this.OnFinish(true); } return false; } else if (this.Complete) { if (_state == RadicalCoroutineOperatingState.Completing) { this.OnFinish(false); } return false; } if (_forcedTick) { _forcedTick = false; return true; } _currentIEnumeratorYieldValue = null; //clear completed entries while(_stack.Count > 0) { try { if(_stack.Peek().IsComplete) { this.PopStack(); } else { break; } } catch(System.Exception ex) { Debug.LogException(ex); this.PopStack(); } } if (_stack.Count > 0) { //operate object current; var r = _stack.Peek(); if (!r.Tick(out current)) { //the tick may have forced a tick, which could have popped this yieldinstruction already, this usually means it was an IImmediatelyResumingYieldInstruction //deal with accordingly if (_stack.Count > 0 && _stack.Peek() == r) this.PopStack(); if(_forcedTick) { _forcedTick = false; if (this.Cancelled) { if (_state == RadicalCoroutineOperatingState.Cancelling) { this.OnFinish(true); } return false; } else if (this.Complete) { if (_state == RadicalCoroutineOperatingState.Completing) { this.OnFinish(false); } return false; } else { return true; } } else { current = null; } } else if (this.Cancelled) { //routine cancelled itself if (_state == RadicalCoroutineOperatingState.Cancelling) { this.OnFinish(true); } return false; } //deal with the current yieldObject if (current == null) { //do nothing } else if (current is YieldInstruction) { if (current is WaitForSeconds && _disableMode.HasFlag(RadicalCoroutineDisableMode.ResumeOnEnable)) { _currentIEnumeratorYieldValue = null; _stack.Push(WaitForDuration.FromWaitForSeconds(current as WaitForSeconds)); } else { _currentIEnumeratorYieldValue = current; } } else if (current is WWW) { _currentIEnumeratorYieldValue = current; } else if (current is RadicalCoroutine) { // //v3 var rad = current as RadicalCoroutine; if (!rad.Finished) { if (rad._token != null) { _currentIEnumeratorYieldValue = rad._token; } else { _stack.Push(EnumWrapper.Create(WaitUntilDone_Routine(rad))); } } else { _currentIEnumeratorYieldValue = null; } } else if (current is IRadicalYieldInstruction) { var instruction = current as IRadicalYieldInstruction; if (instruction is IResettingYieldInstruction) (instruction as IResettingYieldInstruction).Reset(); if (instruction is IImmediatelyResumingYieldInstruction) (instruction as IImmediatelyResumingYieldInstruction).Signal += this.OnImmediatelyResumingYieldInstructionSignaled; object yieldObject; if (instruction.Tick(out yieldObject)) { _currentIEnumeratorYieldValue = yieldObject; _stack.Push(instruction); } } else if (current is IEnumerable) { //yes we have to test for IEnumerable before IEnumerator. When a yield method is returned as an IEnumerator, it still needs 'GetEnumerator' called on it. var e = (current as IEnumerable).GetEnumerator(); if (e.MoveNext()) { _currentIEnumeratorYieldValue = e.Current; _stack.Push(EnumWrapper.Create(e)); } } else if (current is IEnumerator) { var e = current as IEnumerator; if (e.MoveNext()) { _currentIEnumeratorYieldValue = e.Current; _stack.Push(EnumWrapper.Create(e)); } } else if (current is RadicalCoroutineEndCommand) { var cmd = (RadicalCoroutineEndCommand)current; if (cmd.HasFlag(RadicalCoroutineEndCommand.Cancel)) { this.Cancel(); if (cmd.HasFlag(RadicalCoroutineEndCommand.StallImmediateResume)) { _currentIEnumeratorYieldValue = null; } else { this.OnFinish(true); return false; } } else { if (cmd.HasFlag(RadicalCoroutineEndCommand.StallImmediateResume)) { _state = RadicalCoroutineOperatingState.Completing; _currentIEnumeratorYieldValue = null; } else { this.OnFinish(false); return false; } } } else { _currentIEnumeratorYieldValue = current; } return true; } else { this.OnFinish(false); return false; } }