/// <summary> /// Creates a Fiber and runs it. /// </summary> public Routine RunFiber(MonoBehaviour inHost, IEnumerator inStart) { if (inStart == null) { return(Routine.Null); } if (!m_Initialized) { Initialize(); } if (ReferenceEquals(inHost, null)) { inHost = Host; } #if DEVELOPMENT if (inStart.GetType().TypeHandle.Value == TYPEHANDLE_DECORATOR) { RoutineDecorator decorator = (RoutineDecorator)inStart; if ((decorator.Flags & RoutineDecoratorFlag.Inline) != 0) { UnityEngine.Debug.LogWarning("[BeauRoutine] Starting a BeauRoutine with an Inlined coroutine. This is not supported, and the coroutine will not execute immediately."); } } #endif // DEVELOPMENT Routine handle; Fiber fiber = CreateFiber(inHost, inStart, false, out handle); Fibers.AddActiveFiber(fiber); return(handle); }
// HACK: We need to pass a parent fiber into nested fibers // in order for timescale to work appropriately during a yield update. private void CheckForNesting(IEnumerator inEnumerator, bool inbCheckDecorator) { INestedFiberContainer container = inEnumerator as INestedFiberContainer; if (container != null) { container.SetParentFiber(this); } else if (inbCheckDecorator) { RoutineDecorator decorator = inEnumerator as RoutineDecorator; if (decorator != null && decorator.Enumerator != null) { CheckForNesting(decorator.Enumerator, true); } } }
/// <summary> /// Runs the Fiber one frame. /// Will dispose itself if requested. /// Returns if still running. /// </summary> public bool Update(YieldPhase inYieldUpdate = YieldPhase.None) { // We don't support recursive Fiber updates if (m_Executing) { return(true); } if (m_StackPosition < 0) { return(false); } if (m_Disposing || (!m_HostedByManager && !m_Host)) { Dispose(); return(false); } if (inYieldUpdate != YieldPhase.None && m_YieldFrameDelay > 0) { --m_YieldFrameDelay; return(true); } if (IsPaused() || m_UnityWait != null) { return(true); } if (m_YieldPhase != inYieldUpdate) { return(true); } if (inYieldUpdate != YieldPhase.None) { Manager.Fibers.RemoveFiberFromYieldList(this, m_YieldPhase); m_YieldPhase = YieldPhase.None; // If we're in a manual update, don't proceed from here if (m_UpdatePhase == RoutinePhase.Manual) { return(true); } } // If we're a chained routine, just accept // the parent's delta time. if (!m_Chained) { ApplyDeltaTime(); } else if (inYieldUpdate != YieldPhase.None) { m_RootFiber.ApplyDeltaTime(); } if (m_WaitTime > 0) { m_WaitTime -= Manager.Frame.DeltaTime; if (m_WaitTime > 0) { return(true); } // We don't modify delta time in the fixed update phase, // to preserve a consistent delta time within fixed update. if (m_UpdatePhase != RoutinePhase.FixedUpdate && inYieldUpdate != YieldPhase.WaitForFixedUpdate) { Manager.Frame.DeltaTime += m_WaitTime; } m_WaitTime = 0; } bool bExecuteStack = true; while (bExecuteStack) { bExecuteStack = false; // Set this flag to prevent updating this routine mid-execution m_Executing = true; IEnumerator current = m_Stack[m_StackPosition]; bool bMovedNext = false; if (Manager.HandleExceptions || m_HandleExceptions || (m_Chained && m_RootFiber.m_HandleExceptions)) { try { bMovedNext = current.MoveNext(); } catch (Exception e) { Debug.LogException(e); Routine.ExceptionHandler exceptionHandler = m_OnException; if (m_Chained) { exceptionHandler = m_RootFiber.m_OnException; m_RootFiber.m_Disposing = true; } if (exceptionHandler != null) { exceptionHandler(e); } m_Disposing = true; } } else { bMovedNext = current.MoveNext(); } m_Executing = false; // Don't check for the object being destroyed. // Destroy won't go into effect until after // all the Fibers are done processing anyways. if (m_Disposing) { Dispose(); return(false); } if (bMovedNext) { object result = current.Current; if (result == null) { return(true); } IntPtr resultType = result.GetType().TypeHandle.Value; // Check all the easy-to-identify result types if (resultType == TYPEHANDLE_INT) { m_WaitTime = (int)result; return(true); } if (resultType == TYPEHANDLE_FLOAT) { m_WaitTime = (float)result; return(true); } if (resultType == TYPEHANDLE_DOUBLE) { m_WaitTime = (float)(double)result; return(true); } if (resultType == TYPEHANDLE_ROUTINE) { IEnumerator waitSequence = ((Routine)result).Wait(); if (waitSequence != null) { if (m_StackPosition == m_StackSize - 1) { Array.Resize(ref m_Stack, m_StackSize *= 2); } m_Stack[++m_StackPosition] = waitSequence; } return(true); } if (resultType == TYPEHANDLE_WWW) { // Disable obsolete WWW warning #pragma warning disable 612, 618 m_UnityWait = Manager.Host.StartCoroutine(UnityWait((WWW)result)); return(true); // Restore obsolete WWW warning #pragma warning restore 612, 618 } if (resultType == TYPEHANDLE_COMMAND) { Routine.Command c = (Routine.Command)result; switch (c) { case Routine.Command.Pause: Pause(); return(true); case Routine.Command.Stop: Stop(); Dispose(); return(false); case Routine.Command.BreakAndResume: m_Stack[m_StackPosition--] = null; ((IDisposable)current).Dispose(); if (m_StackPosition < 0) { Dispose(); return(false); } bExecuteStack = true; break; case Routine.Command.Continue: bExecuteStack = true; break; } if (!bExecuteStack) { return(true); } continue; } if (resultType == TYPEHANDLE_DECORATOR) { RoutineDecorator decorator = (RoutineDecorator)result; IEnumerator decoratedEnumerator = decorator.Enumerator; bExecuteStack = (decorator.Flags & RoutineDecoratorFlag.Inline) != 0; if (decoratedEnumerator != null) { // Check if we need to resize the stack if (m_StackPosition == m_StackSize - 1) { Array.Resize(ref m_Stack, m_StackSize *= 2); } m_Stack[++m_StackPosition] = decorator; CheckForNesting(decoratedEnumerator); } if (!bExecuteStack) { return(true); } continue; } // Check for yield timing changes if (m_UpdatePhase != RoutinePhase.Manual) { bool bApplyYieldDelay = !Manager.IsUpdating(RoutinePhase.Manual) && inYieldUpdate == YieldPhase.None; if (resultType == TYPEHANDLE_ROUTINEPHASE) { RoutinePhase phase = (RoutinePhase)result; switch (phase) { case RoutinePhase.FixedUpdate: { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForFixedUpdate); Manager.Host.WaitForFixedUpdate(); m_YieldPhase = YieldPhase.WaitForFixedUpdate; m_YieldFrameDelay = bApplyYieldDelay && m_UpdatePhase == RoutinePhase.FixedUpdate && Manager.IsUpdating(RoutinePhase.FixedUpdate) ? 1 : 0; return(true); } case RoutinePhase.LateUpdate: { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForLateUpdate); m_YieldPhase = YieldPhase.WaitForLateUpdate; m_YieldFrameDelay = bApplyYieldDelay && m_UpdatePhase == RoutinePhase.LateUpdate && Manager.IsUpdating(RoutinePhase.LateUpdate) ? 1 : 0; return(true); } case RoutinePhase.Update: { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForUpdate); m_YieldPhase = YieldPhase.WaitForUpdate; m_YieldFrameDelay = bApplyYieldDelay && m_UpdatePhase == RoutinePhase.Update && Manager.IsUpdating(RoutinePhase.Update) ? 1 : 0; return(true); } case RoutinePhase.CustomUpdate: { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForCustomUpdate); m_YieldPhase = YieldPhase.WaitForCustomUpdate; m_YieldFrameDelay = bApplyYieldDelay && m_UpdatePhase == RoutinePhase.CustomUpdate && Manager.IsUpdating(RoutinePhase.CustomUpdate) ? 1 : 0; return(true); } case RoutinePhase.ThinkUpdate: { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForThinkUpdate); m_YieldPhase = YieldPhase.WaitForThinkUpdate; m_YieldFrameDelay = bApplyYieldDelay && m_UpdatePhase == RoutinePhase.ThinkUpdate && Manager.IsUpdating(RoutinePhase.ThinkUpdate) ? 1 : 0; return(true); } case RoutinePhase.RealtimeUpdate: { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForRealtimeUpdate); m_YieldPhase = YieldPhase.WaitForRealtimeUpdate; m_YieldFrameDelay = bApplyYieldDelay && m_UpdatePhase == RoutinePhase.RealtimeUpdate && Manager.IsUpdating(RoutinePhase.RealtimeUpdate) ? 1 : 0; return(true); } case RoutinePhase.Manual: default: { // Yielding a manual will not do anything return(true); } } } if (resultType == TYPEHANDLE_WAITFORENDOFFRAME) { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForEndOfFrame); m_YieldPhase = YieldPhase.WaitForEndOfFrame; m_YieldFrameDelay = 0; return(true); } if (resultType == TYPEHANDLE_WAITFORFIXEDUPDATE) { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForFixedUpdate); Manager.Host.WaitForFixedUpdate(); m_YieldPhase = YieldPhase.WaitForFixedUpdate; m_YieldFrameDelay = bApplyYieldDelay && m_UpdatePhase == RoutinePhase.FixedUpdate && Manager.IsUpdating(RoutinePhase.FixedUpdate) ? 1 : 0; return(true); } if (resultType == TYPEHANDLE_WAITFORLATEUPDATE) { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForLateUpdate); m_YieldPhase = YieldPhase.WaitForLateUpdate; m_YieldFrameDelay = bApplyYieldDelay && m_UpdatePhase == RoutinePhase.LateUpdate && Manager.IsUpdating(RoutinePhase.LateUpdate) ? 1 : 0; return(true); } if (resultType == TYPEHANDLE_WAITFORUPDATE) { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForUpdate); m_YieldPhase = YieldPhase.WaitForUpdate; m_YieldFrameDelay = bApplyYieldDelay && m_UpdatePhase == RoutinePhase.Update && Manager.IsUpdating(RoutinePhase.Update) ? 1 : 0; return(true); } if (resultType == TYPEHANDLE_WAITFORCUSTOMUPDATE) { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForCustomUpdate); m_YieldPhase = YieldPhase.WaitForCustomUpdate; m_YieldFrameDelay = bApplyYieldDelay && m_UpdatePhase == RoutinePhase.CustomUpdate && Manager.IsUpdating(RoutinePhase.CustomUpdate) ? 1 : 0; return(true); } if (resultType == TYPEHANDLE_WAITFORTHINKUPDATE) { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForThinkUpdate); m_YieldPhase = YieldPhase.WaitForThinkUpdate; m_YieldFrameDelay = bApplyYieldDelay && m_UpdatePhase == RoutinePhase.ThinkUpdate && Manager.IsUpdating(RoutinePhase.ThinkUpdate) ? 1 : 0; return(true); } if (resultType == TYPEHANDLE_WAITFORREALTIMEUPDATE) { Manager.Fibers.AddFiberToYieldList(this, YieldPhase.WaitForRealtimeUpdate); m_YieldPhase = YieldPhase.WaitForRealtimeUpdate; m_YieldFrameDelay = bApplyYieldDelay && m_UpdatePhase == RoutinePhase.RealtimeUpdate && Manager.IsUpdating(RoutinePhase.RealtimeUpdate) ? 1 : 0; return(true); } } if (WAITFORSECONDS_BYPASS && resultType == TYPEHANDLE_WAITFORSECONDS) { m_WaitTime = (float)FIELD_WAITFORSECONDS_SECONDS.GetValue(result); return(true); } // Check for the subclassable types #if SUPPORTS_CUSTOMYIELDINSTRUCTION CustomYieldInstruction customInstruction = result as CustomYieldInstruction; if (customInstruction != null) { m_UnityWait = Manager.Host.StartCoroutine(UnityWait(customInstruction)); return(true); } #endif YieldInstruction instruction = result as YieldInstruction; if (instruction != null) { m_UnityWait = Manager.Host.StartCoroutine(UnityWait(instruction)); return(true); } IFuture future = result as IFuture; if (future != null) { if (!future.IsDone()) { IEnumerator waitSequence = future.Wait(); if (waitSequence != null) { if (m_StackPosition == m_StackSize - 1) { Array.Resize(ref m_Stack, m_StackSize *= 2); } m_Stack[++m_StackPosition] = waitSequence; } } return(true); } IEnumerator enumerator = result as IEnumerator; if (enumerator != null) { // Check if we need to resize the stack if (m_StackPosition == m_StackSize - 1) { Array.Resize(ref m_Stack, m_StackSize *= 2); } m_Stack[++m_StackPosition] = enumerator; CheckForNesting(enumerator); return(true); } } else { bExecuteStack = current is RoutineDecorator && (((RoutineDecorator)current).Flags & RoutineDecoratorFlag.Inline) != 0; m_Stack[m_StackPosition--] = null; ((IDisposable)current).Dispose(); if (m_StackPosition < 0) { Dispose(); return(false); } } } return(true); }