using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace SUISS.Cloud { public class CloudRequestRunner { private class CoroutineWapper : CustomYieldInstruction { private bool _isRunning; private IEnumerator _innerRoutine; private CloudRequestRunner _scheduler; private MonoBehaviour _executor; public override bool keepWaiting => _isRunning; public CoroutineWapper(IEnumerator routine, CloudRequestRunner scheduler) { _scheduler = scheduler; _executor = _scheduler.Executor; _innerRoutine = routine; if (_executor != null) { _isRunning = true; _executor.StartCoroutine(Routine(routine)); } } private IEnumerator Routine(IEnumerator routine) { while (routine.MoveNext()) { object yielded = routine.Current; if (yielded != null && !_scheduler.IsSupportedYieldable(yielded)) { UnityEngine.Debug.LogWarning(string.Format("The coroutine '{0}' yielded '{1}' which is not supported by '{2}'!", _innerRoutine, yielded ?? "null", _scheduler)); } yield return yielded; } _scheduler.OnRoutineCompleted(_innerRoutine); _isRunning = false; } } private class CloudRequestRunnerMonoBehaviour : MonoBehaviour { } private HashSet _runningRoutines = new HashSet(); private MonoBehaviour _executor; private string _executorName; private static CloudRequestRunner _instance; private MonoBehaviour Executor { get { if (_executor == null) { UnityEngine.Debug.LogError($"{this} is going to create a second GameObject to run coroutines! (Is the app quitting?) Any coroutines running before this may not have been completed! I hope you never see this error! :("); CreateScheduleExecutor(); _runningRoutines.Clear(); } return _executor; } } private CloudRequestRunner() { _executorName = $"__CloudRequestRunner{UnityEngine.Random.Range(1000, 9999)}"; CreateScheduleExecutor(); } public static CustomYieldInstruction StartCoroutine(IEnumerator routine) { if (_instance == null) { _instance = new CloudRequestRunner(); } return _instance.StartRoutine(routine); } private CustomYieldInstruction StartRoutine(IEnumerator routine) { if (_executor == null) { CreateScheduleExecutor(); } if (_executor != null) { if (_runningRoutines.Contains(routine)) { throw new Exception($"You are calling StartRoutine on a routine '{routine}' that was already started."); } _runningRoutines.Add(routine); return new CoroutineWapper(routine, this); } UnityEngine.Debug.LogError($"The schedule executor backing this scheduler is null! Can't start {routine}"); return null; } private bool IsSupportedYieldable(object yieldable) { return yieldable == null || yieldable is WWW || yieldable is YieldInstruction || yieldable is CustomYieldInstruction; } private void CreateScheduleExecutor() { if (_executor == null) { GameObject gameObject = GameObject.Find(_executorName); if (gameObject == null) { gameObject = new GameObject(); gameObject.name = _executorName; UnityEngine.Object.DontDestroyOnLoad(gameObject); } _executor = gameObject.GetComponent(); if (_executor == null) { _executor = gameObject.AddComponent(); } } } private void OnRoutineCompleted(IEnumerator routine) { _runningRoutines.Remove(routine); } } }