using System; using System.Collections; using UnityEngine; using UnityEngine.XR.Management; #if UNITY_EDITOR using UnityEditor; #endif namespace UnityEngine.XR.OpenXR { internal class OpenXRRestarter : MonoBehaviour { internal Action onAfterRestart; internal Action onAfterShutdown; internal Action onQuit; internal Action onAfterCoroutine; internal Action onAfterSuccessfulRestart; static OpenXRRestarter() { #if UNITY_EDITOR EditorApplication.playModeStateChanged += (state) => { if (state == PlayModeStateChange.EnteredPlayMode) m_pauseAndRestartAttempts = 0; }; #endif TimeBetweenRestartAttempts = 5.0f; DisableApplicationQuit = false; } public void ResetCallbacks() { onAfterRestart = null; onAfterSuccessfulRestart = null; onAfterShutdown = null; onAfterCoroutine = null; onQuit = null; m_pauseAndRestartAttempts = 0; } /// /// True if the restarter is currently running /// public bool isRunning => m_Coroutine != null; private static OpenXRRestarter s_Instance = null; private Coroutine m_Coroutine; private static int m_pauseAndRestartCoroutineCount = 0; private Object m_PauseAndRestartCoroutineCountLock = new Object(); private static int m_pauseAndRestartAttempts = 0; public static float TimeBetweenRestartAttempts { get; set; } public static int PauseAndRestartAttempts { get { return m_pauseAndRestartAttempts; } } internal static int PauseAndRestartCoroutineCount { get { return m_pauseAndRestartCoroutineCount; } } public static OpenXRRestarter Instance { get { if (s_Instance == null) { var go = GameObject.Find("~oxrestarter"); if (go == null) { go = new GameObject("~oxrestarter"); go.hideFlags = HideFlags.HideAndDontSave; go.AddComponent(); } s_Instance = go.GetComponent(); } return s_Instance; } } /// /// If true, disables the application quitting, even when Quit is called. /// Used in testing, which needs to monitor for Quit to be called. /// internal static bool DisableApplicationQuit { get; set; } /// /// Shutdown the the OpenXR loader and optionally quit the application /// public void Shutdown() { if (OpenXRLoader.Instance == null) return; if (m_Coroutine != null) { Debug.LogError("Only one shutdown or restart can be executed at a time"); return; } m_Coroutine = StartCoroutine(RestartCoroutine(false, true)); } /// /// Restart the OpenXR loader /// public void ShutdownAndRestart() { if (OpenXRLoader.Instance == null) return; if (m_Coroutine != null) { Debug.LogError("Only one shutdown or restart can be executed at a time"); return; } m_Coroutine = StartCoroutine(RestartCoroutine(true, true)); } /// /// Start a coroutine that will pause, then shut xr down, then re-initialize xr. /// public void PauseAndShutdownAndRestart() { if (OpenXRLoader.Instance == null) { return; } StartCoroutine(PauseAndShutdownAndRestartCoroutine(TimeBetweenRestartAttempts)); } /// /// Start a coroutine that will pause, then re-initialize xr if xr hasn't already succeeded. /// public void PauseAndRetryInitialization() { if (OpenXRLoader.Instance == null) { return; } StartCoroutine(PauseAndRetryInitializationCoroutine(TimeBetweenRestartAttempts)); } private void IncrementPauseAndRestartCoroutineCount() { lock (m_PauseAndRestartCoroutineCountLock) { m_pauseAndRestartCoroutineCount += 1; } } private void DecrementPauseAndRestartCoroutineCount() { lock (m_PauseAndRestartCoroutineCountLock) { m_pauseAndRestartCoroutineCount -= 1; } } private IEnumerator PauseAndShutdownAndRestartCoroutine(float pauseTimeInSeconds) { IncrementPauseAndRestartCoroutineCount(); try { // Wait a few seconds to add delay between restart requests in a restart loop. yield return new WaitForSeconds(pauseTimeInSeconds); yield return new WaitForRestartFinish(); m_pauseAndRestartAttempts += 1; m_Coroutine = StartCoroutine(RestartCoroutine(true, true)); } finally { onAfterCoroutine?.Invoke(); } DecrementPauseAndRestartCoroutineCount(); } private IEnumerator PauseAndRetryInitializationCoroutine(float pauseTimeInSeconds) { IncrementPauseAndRestartCoroutineCount(); try { // Wait a few seconds to add delay between restart requests in a restart loop. yield return new WaitForSeconds(pauseTimeInSeconds); yield return new WaitForRestartFinish(); bool shouldSkipRestart = XRGeneralSettings.Instance.Manager.activeLoader != null; if (!shouldSkipRestart) { m_pauseAndRestartAttempts += 1; m_Coroutine = StartCoroutine(RestartCoroutine(true, false)); } } finally { onAfterCoroutine?.Invoke(); } DecrementPauseAndRestartCoroutineCount(); } private IEnumerator RestartCoroutine(bool shouldRestart, bool shouldShutdown) { try { if (shouldShutdown) { Debug.Log("Shutting down OpenXR."); yield return null; // Always shutdown the loader XRGeneralSettings.Instance.Manager.DeinitializeLoader(); yield return null; onAfterShutdown?.Invoke(); } // Restart? if (shouldRestart && OpenXRRuntime.ShouldRestart()) { Debug.Log("Initializing OpenXR."); yield return XRGeneralSettings.Instance.Manager.InitializeLoader(); XRGeneralSettings.Instance.Manager.StartSubsystems(); if (XRGeneralSettings.Instance.Manager.activeLoader != null) { m_pauseAndRestartAttempts = 0; onAfterSuccessfulRestart?.Invoke(); } onAfterRestart?.Invoke(); } // Quit? else if (OpenXRRuntime.ShouldQuit()) { onQuit?.Invoke(); if (!DisableApplicationQuit) { #if UNITY_EDITOR if (EditorApplication.isPlaying || EditorApplication.isPaused) { EditorApplication.ExitPlaymode(); } #else Application.Quit(); #endif } } } finally { m_Coroutine = null; onAfterCoroutine?.Invoke(); } } } }