using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; using UnityEditor.PackageManager; using UnityEditor.PackageManager.Requests; using UnityEngine.XR.Management; namespace UnityEditor.XR.Management.Metadata { /// /// Provide access to the metadata store. Currently only usable as a way to assign and remove loaders /// to/from an instance. /// [InitializeOnLoad] public class XRPackageMetadataStore { const string k_WaitingPackmanQuery = "XRMGT Waiting Packman Query."; const string k_RebuildCache = "XRMGT Rebuilding Cache."; const string k_InstallingPackage = "XRMGT Installing XR Package."; const string k_AssigningPackage = "XRMGT Assigning XR Package."; const string k_UninstallingPackage = "XRMGT Uninstalling XR Package."; const string k_CachedMDStoreKey = "XR Metadata Store"; static float k_TimeOutDelta = 30f; [Serializable] struct KnownPackageInfo { public string packageId; public string verifiedVersion; } [Serializable] struct CachedMDStoreInformation { public bool hasAlreadyRequestedData; public KnownPackageInfo[] knownPackageInfos; public string[] installedPackages; public string[] installablePackages; } static CachedMDStoreInformation s_CachedMDStoreInformation = new CachedMDStoreInformation() { hasAlreadyRequestedData = false, knownPackageInfos = { }, installedPackages = { }, installablePackages = { }, }; static void LoadCachedMDStoreInformation() { string data = SessionState.GetString(k_CachedMDStoreKey, "{}"); s_CachedMDStoreInformation = JsonUtility.FromJson(data); } static void StoreCachedMDStoreInformation() { SessionState.EraseString(k_CachedMDStoreKey); string data = JsonUtility.ToJson(s_CachedMDStoreInformation, true); SessionState.SetString(k_CachedMDStoreKey, data); } enum InstallationState { New, RebuildInstalledCache, StartInstallation, Installing, Assigning, Complete, Uninstalling, Log } enum LogLevel { Info, Warning, Error } [Serializable] struct LoaderAssignmentRequest { [SerializeField] public string packageId; [SerializeField] public string loaderType; [SerializeField] public BuildTargetGroup buildTargetGroup; [SerializeField] public bool needsAddRequest; [SerializeField] public ListRequest packageListRequest; [SerializeField] public AddRequest packageAddRequest; [SerializeField] #pragma warning disable CS0649 public RemoveRequest packageRemoveRequest; #pragma warning disable CS0649 [SerializeField] public float timeOut; [SerializeField] public InstallationState installationState; [SerializeField] public string logMessage; [SerializeField] public LogLevel logLevel; } [Serializable] struct LoaderAssignmentRequests { [SerializeField] public List activeRequests; } static Dictionary s_Packages = null; static SearchRequest s_SearchRequest = null; static Dictionary packages { get { if(s_Packages == null) { s_Packages = new Dictionary(); InitKnownPluginPackages(); XRPackageInitializationBootstrap.BeginPackageInitialization(); } return s_Packages; } } const string k_DefaultSessionStateString = "DEADBEEF"; static bool SessionStateHasStoredData(string queueName) { return SessionState.GetString(queueName, k_DefaultSessionStateString) != XRPackageMetadataStore.k_DefaultSessionStateString; } internal static bool isCheckingInstallationRequirements => XRPackageMetadataStore.SessionStateHasStoredData(k_WaitingPackmanQuery); internal static bool isRebuildingCache => XRPackageMetadataStore.SessionStateHasStoredData(k_RebuildCache); internal static bool isInstallingPackages => XRPackageMetadataStore.SessionStateHasStoredData(k_InstallingPackage); internal static bool isUninstallingPackages => XRPackageMetadataStore.SessionStateHasStoredData(k_UninstallingPackage); internal static bool isAssigningLoaders => XRPackageMetadataStore.SessionStateHasStoredData(k_AssigningPackage); internal static bool isDoingQueueProcessing { get { return isCheckingInstallationRequirements || isRebuildingCache || isInstallingPackages || isUninstallingPackages || isAssigningLoaders; } } internal struct LoaderBuildTargetQueryResult { public string packageName; public string packageId; public string loaderName; public string loaderType; } internal static void MoveMockInListToEnd(List loaderList) { int index = loaderList.FindIndex((x) => { return String.Compare(x.loaderType, KnownPackages.k_KnownPackageMockHMDLoader) == 0; }); if (index >= 0) { var mock = loaderList[index]; loaderList.RemoveAt(index); loaderList.Add(mock); } } internal static List GetAllLoadersForBuildTarget(BuildTargetGroup buildTarget) { var ret = from pm in (from p in packages.Values select p.metadata) from lm in pm.loaderMetadata where lm.supportedBuildTargets.Contains(buildTarget) orderby lm.loaderName select new LoaderBuildTargetQueryResult() { packageName = pm.packageName, packageId = pm.packageId, loaderName = lm.loaderName, loaderType = lm.loaderType }; var retList = ret.Distinct().ToList(); MoveMockInListToEnd(retList); return retList; } internal static List GetLoadersForBuildTarget(BuildTargetGroup buildTargetGroup) { var ret = from pm in (from p in packages.Values select p.metadata) from lm in pm.loaderMetadata where lm.supportedBuildTargets.Contains(buildTargetGroup) orderby lm.loaderName select new LoaderBuildTargetQueryResult() { packageName = pm.packageName, packageId = pm.packageId, loaderName = lm.loaderName, loaderType = lm.loaderType }; var retList = ret.ToList(); MoveMockInListToEnd(retList); return retList; } /// /// Return a read only list of all package metadata information currently known. /// /// Read only list of . public static IReadOnlyList GetAllPackageMetadata() { return packages.Values.ToList().AsReadOnly(); } /// /// Return a read only list of all package metadata information currently known that has loaders that support the given build. /// /// Read only list of . public static IReadOnlyList GetAllPackageMetadataForBuildTarget(BuildTargetGroup buildTargetGroup) { HashSet ret = new HashSet(); foreach (var p in packages.Values) { foreach (var lm in p.metadata.loaderMetadata) { if (lm.supportedBuildTargets.Contains(buildTargetGroup)) { ret.Add(p); break; } } } return ret.ToList().AsReadOnly(); } /// /// Given a package id, return the metadata for that package. /// /// The package id to check for. /// An instance of if the package has metadata or null. public static IXRPackageMetadata GetMetadataForPackage(string packageId) { return packages.Values. Select(x => x.metadata). FirstOrDefault(xmd => String.Compare(xmd.packageId, packageId) == 0); } internal static bool HasInstallablePackageData() { return s_CachedMDStoreInformation.installablePackages?.Any() ?? false; } internal static bool IsPackageInstalled(string package) { return (s_CachedMDStoreInformation.installedPackages?.Contains(package) ?? false) && File.Exists($"Packages/{package}/package.json"); } internal static bool IsPackageInstallable(string package) { return s_CachedMDStoreInformation.installablePackages?.Contains(package) ?? false; } /// /// Given a loader type and a build target group will return whether or not that loader /// is currently assigned to be active for that build target. /// /// Loader type to check. /// Build target group to check for assignment in. /// public static bool IsLoaderAssigned(string loaderTypeName, BuildTargetGroup buildTargetGroup) { var settings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(buildTargetGroup); if (settings == null) return false; foreach (var loader in settings.AssignedSettings.activeLoaders) { if (loader != null && String.Compare(loader.GetType().FullName, loaderTypeName) == 0) return true; } return false; } internal static bool IsLoaderAssigned(XRManagerSettings settings, string loaderTypeName) { if (settings == null) return false; foreach (var l in settings.activeLoaders) { if (l != null && String.Compare(l.GetType().FullName, loaderTypeName) == 0) return true; } return false; } internal static void InstallPackageAndAssignLoaderForBuildTarget(string package, string loaderType, BuildTargetGroup buildTargetGroup) { var req = new LoaderAssignmentRequest(); req.packageId = package; req.loaderType = loaderType; req.buildTargetGroup = buildTargetGroup; req.installationState = InstallationState.New; QueueLoaderRequest(req); } /// /// Assigns a loader of type loaderTypeName to the settings instance. Will instantiate an /// instance if one can't be found in the users project folder before assigning it. /// /// An instance of to add the loader to. /// The full type name for the loader instance to assign to settings. /// The build target group being assigned to. /// True if assignment succeeds, false if not. public static bool AssignLoader(XRManagerSettings settings, string loaderTypeName, BuildTargetGroup buildTargetGroup) { if (EditorApplication.isPlaying || EditorApplication.isPaused) { Debug.LogError($"Attempt to add {loaderTypeName} for {buildTargetGroup} while in Play mode. XR Plug-in Management can not make changes to the loader list when running."); return false; } var instance = EditorUtilities.GetInstanceOfTypeWithNameFromAssetDatabase(loaderTypeName); if (instance == null || !(instance is XRLoader)) { instance = EditorUtilities.CreateScriptableObjectInstance(loaderTypeName, EditorUtilities.GetAssetPathForComponents(EditorUtilities.s_DefaultLoaderPath)); if (instance == null) return false; } XRLoader newLoader = instance as XRLoader; if (settings.TryAddLoader(newLoader)) { var assignedLoaders = settings.activeLoaders; var orderedLoaders = new List(); var allLoaders = GetAllLoadersForBuildTarget(buildTargetGroup); foreach (var ldr in allLoaders) { var newInstance = EditorUtilities.GetInstanceOfTypeWithNameFromAssetDatabase(ldr.loaderType) as XRLoader; if (newInstance != null && assignedLoaders.Contains(newInstance)) { orderedLoaders.Add(newInstance); #if UNITY_EDITOR var loaderHelper = newLoader as XRLoaderHelper; loaderHelper?.WasAssignedToBuildTarget(buildTargetGroup); #endif } } if (!settings.TrySetLoaders(orderedLoaders)) return false; EditorUtility.SetDirty(settings); AssetDatabase.SaveAssets(); } return true; } /// /// Remove a previously assigned loader from settings. If the loader type is unknown or /// an instance of the loader can't be found in the project folder no action is taken. /// /// Removal will not delete the instance from the project folder. /// /// An instance of to add the loader to. /// The full type name for the loader instance to remove from settings. /// The build target group being removed from. /// True if removal succeeds, false if not. public static bool RemoveLoader(XRManagerSettings settings, string loaderTypeName, BuildTargetGroup buildTargetGroup) { if (EditorApplication.isPlaying || EditorApplication.isPaused) { Debug.LogError($"Attempt to remove {loaderTypeName} for {buildTargetGroup} while in Play mode. XR Plug-in Management can not make changes to the loader list when running."); return false; } var instance = EditorUtilities.GetInstanceOfTypeWithNameFromAssetDatabase(loaderTypeName); if (instance == null || !(instance is XRLoader)) return false; XRLoader loader = instance as XRLoader; if (settings.TryRemoveLoader(loader)) { EditorUtility.SetDirty(settings); AssetDatabase.SaveAssets(); #if UNITY_EDITOR var loaderHelper = loader as XRLoaderHelper; loaderHelper?.WasUnassignedFromBuildTarget(buildTargetGroup); #endif return true; } return false; } internal static IXRPackage GetPackageForSettingsTypeNamed(string settingsTypeName) { var ret = packages.Values. Where((p => String.Compare(p.metadata.settingsType, settingsTypeName, true) == 0)). Select((p) => p); return ret.Any() ? ret.First() : null; } internal static string GetCurrentStatusDisplayText() { if (XRPackageMetadataStore.isCheckingInstallationRequirements) { return "Checking installation requirements for packages..."; } else if (XRPackageMetadataStore.isRebuildingCache) { return "Querying Package Manager for currently installed packages..."; } else if (XRPackageMetadataStore.isInstallingPackages) { return "Installing packages..."; } else if (XRPackageMetadataStore.isUninstallingPackages) { return "Uninstalling packages..."; } else if (XRPackageMetadataStore.isAssigningLoaders) { return "Assigning all requested loaders..."; } return ""; } internal static void AddPluginPackage(IXRPackage package) { if (s_CachedMDStoreInformation.installedPackages != null && !s_CachedMDStoreInformation.installedPackages.Contains(package.metadata.packageId)) { List installedPackages = s_CachedMDStoreInformation.installedPackages.ToList(); installedPackages.Add(package.metadata.packageId); s_CachedMDStoreInformation.installedPackages = installedPackages.ToArray(); StoreCachedMDStoreInformation(); } InternalAddPluginPackage(package); } static void InternalAddPluginPackage(IXRPackage package) { packages[package.metadata.packageId] = package; } internal static void InitKnownPluginPackages() { foreach (var knownPackage in KnownPackages.Packages) { InternalAddPluginPackage(knownPackage); } } static XRPackageMetadataStore() { EditorApplication.playModeStateChanged -= PlayModeStateChanged; EditorApplication.playModeStateChanged += PlayModeStateChanged; if (IsEditorInPlayMode()) return; AssemblyReloadEvents.afterAssemblyReload += AssemblyReloadEvents_afterAssemblyReload; } static void AssemblyReloadEvents_afterAssemblyReload() { LoadCachedMDStoreInformation(); if (!IsEditorInPlayMode()) { if (!s_CachedMDStoreInformation.hasAlreadyRequestedData) { s_SearchRequest = Client.SearchAll(true); } RebuildInstalledCache(); StartAllQueues(); } } static bool IsEditorInPlayMode() { return EditorApplication.isPlayingOrWillChangePlaymode || EditorApplication.isPlaying || EditorApplication.isPaused; } static void PlayModeStateChanged(PlayModeStateChange state) { switch (state) { case PlayModeStateChange.ExitingEditMode: StopAllQueues(); StoreCachedMDStoreInformation(); break; case PlayModeStateChange.EnteredPlayMode: break; case PlayModeStateChange.EnteredEditMode: LoadCachedMDStoreInformation(); StartAllQueues(); break; } } static void StopAllQueues() { EditorApplication.update -= UpdateInstallablePackages; EditorApplication.update -= WaitingOnSearchQuery; EditorApplication.update -= MonitorPackageInstallation; EditorApplication.update -= MonitorPackageUninstall; EditorApplication.update -= AssignAnyRequestedLoadersUpdate; EditorApplication.update -= RebuildCache; } static void StartAllQueues() { EditorApplication.update += UpdateInstallablePackages; EditorApplication.update += WaitingOnSearchQuery; EditorApplication.update += MonitorPackageInstallation; EditorApplication.update += MonitorPackageUninstall; EditorApplication.update += AssignAnyRequestedLoadersUpdate; EditorApplication.update += RebuildCache; } static void UpdateInstallablePackages() { EditorApplication.update -= UpdateInstallablePackages; if (s_SearchRequest == null || IsEditorInPlayMode() || s_CachedMDStoreInformation.hasAlreadyRequestedData) { return; } if (s_SearchRequest.IsCompleted && s_SearchRequest.Error != null) { Debug.LogError($"Error retrieving package information from Package Manager: {s_SearchRequest.Error.message}."); s_SearchRequest = null; return; } if (!s_SearchRequest.IsCompleted || s_SearchRequest.Result == null) { EditorApplication.update += UpdateInstallablePackages; return; } var installablePackages = new List(); var knownPackageInfos = new List(); foreach (var package in s_SearchRequest.Result) { if (packages.ContainsKey(package.name)) { var kpi = new KnownPackageInfo(); kpi.packageId = package.name; kpi.verifiedVersion = package.versions.verified; if (string.IsNullOrEmpty(kpi.verifiedVersion)) kpi.verifiedVersion = package.versions.latestCompatible; knownPackageInfos.Add(kpi); installablePackages.Add(package.name); } } s_CachedMDStoreInformation.knownPackageInfos = knownPackageInfos.ToArray(); s_CachedMDStoreInformation.installablePackages = installablePackages.ToArray(); s_CachedMDStoreInformation.hasAlreadyRequestedData = true; s_SearchRequest = null; StoreCachedMDStoreInformation(); } static void AddRequestToQueue(LoaderAssignmentRequest request, string queueName) { LoaderAssignmentRequests reqs; if (XRPackageMetadataStore.SessionStateHasStoredData(queueName)) { string fromJson = SessionState.GetString(queueName, k_DefaultSessionStateString); reqs = JsonUtility.FromJson(fromJson); } else { reqs = new LoaderAssignmentRequests(); reqs.activeRequests = new List(); } reqs.activeRequests.Add(request); string json = JsonUtility.ToJson(reqs); SessionState.SetString(queueName, json); } static void SetRequestsInQueue(LoaderAssignmentRequests reqs, string queueName) { string json = JsonUtility.ToJson(reqs); SessionState.SetString(queueName, json); } static LoaderAssignmentRequests GetAllRequestsInQueue(string queueName) { var reqs = new LoaderAssignmentRequests(); reqs.activeRequests = new List(); if (XRPackageMetadataStore.SessionStateHasStoredData(queueName)) { string fromJson = SessionState.GetString(queueName, k_DefaultSessionStateString); reqs = JsonUtility.FromJson(fromJson); SessionState.EraseString(queueName); } return reqs; } internal static void RebuildInstalledCache() { if (isRebuildingCache) return; var req = new LoaderAssignmentRequest(); req.packageListRequest = Client.List(true, false); req.installationState = InstallationState.RebuildInstalledCache; req.timeOut = Time.realtimeSinceStartup + k_TimeOutDelta; QueueLoaderRequest(req); } static void RebuildCache() { EditorApplication.update -= RebuildCache; if (IsEditorInPlayMode()) { return; // Use the cached data that should have been passed in the play state change. } LoaderAssignmentRequests reqs = GetAllRequestsInQueue(k_RebuildCache); if (reqs.activeRequests == null || reqs.activeRequests.Count == 0) { return; } var req = reqs.activeRequests[0]; reqs.activeRequests.Remove(req); if (req.timeOut < Time.realtimeSinceStartup) { req.logMessage = $"Timeout trying to get package list after {k_TimeOutDelta}s."; req.logLevel = LogLevel.Warning; req.installationState = InstallationState.Log; QueueLoaderRequest(req); } else if (req.packageListRequest.IsCompleted) { if (req.packageListRequest.Status == StatusCode.Success) { var installedPackages = new List(); foreach (var packageInfo in req.packageListRequest.Result) { installedPackages.Add(packageInfo.name); } var packageIds = packages.Values. Where((p) => installedPackages.Contains(p.metadata.packageId)). Select((p) => p.metadata.packageId); s_CachedMDStoreInformation.installedPackages = packageIds.ToArray(); } StoreCachedMDStoreInformation(); } else if (!req.packageListRequest.IsCompleted) { QueueLoaderRequest(req); } else { req.logMessage = $"Unable to rebuild installed package cache. Some state may be missing or incorrect."; req.logLevel = LogLevel.Warning; req.installationState = InstallationState.Log; QueueLoaderRequest(req); } if (reqs.activeRequests.Count > 0) { SetRequestsInQueue(reqs, k_RebuildCache); EditorApplication.update += RebuildCache; } } static void ResetManagerUiIfAvailable() { if (XRSettingsManager.Instance != null) XRSettingsManager.Instance.ResetUi = true; } static void AssignAnyRequestedLoadersUpdate() { EditorApplication.update -= AssignAnyRequestedLoadersUpdate; LoaderAssignmentRequests reqs = GetAllRequestsInQueue(k_AssigningPackage); if (reqs.activeRequests == null || reqs.activeRequests.Count == 0) return; while (reqs.activeRequests.Count > 0) { var req = reqs.activeRequests[0]; reqs.activeRequests.RemoveAt(0); var settings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(req.buildTargetGroup); if (settings == null) continue; if (settings.AssignedSettings == null) { var assignedSettings = ScriptableObject.CreateInstance() as XRManagerSettings; settings.AssignedSettings = assignedSettings; EditorUtility.SetDirty(settings); } if (!XRPackageMetadataStore.AssignLoader(settings.AssignedSettings, req.loaderType, req.buildTargetGroup)) { req.installationState = InstallationState.Log; req.logMessage = $"Unable to assign {req.packageId} for build target {req.buildTargetGroup}."; req.logLevel = LogLevel.Error; QueueLoaderRequest(req); } } ResetManagerUiIfAvailable(); } internal static void AssignAnyRequestedLoaders() { EditorApplication.update += AssignAnyRequestedLoadersUpdate; } static void MonitorPackageInstallation() { EditorApplication.update -= MonitorPackageInstallation; LoaderAssignmentRequests reqs = GetAllRequestsInQueue(k_InstallingPackage); if (reqs.activeRequests.Count > 0) { var request = reqs.activeRequests[0]; reqs.activeRequests.RemoveAt(0); if (request.needsAddRequest) { if (s_CachedMDStoreInformation.knownPackageInfos == null) { request.logMessage = $"No package information to query against. Unable to load package {request.packageId}."; request.logLevel = LogLevel.Error; request.installationState = InstallationState.Log; QueueLoaderRequest(request); } else { var versionToInstallQ = s_CachedMDStoreInformation.knownPackageInfos. Where((kpi) => String.Compare(request.packageId, kpi.packageId) == 0). Select((kpi) => kpi.verifiedVersion); var versionToInstall = versionToInstallQ.FirstOrDefault(); var packageToInstall = String.IsNullOrEmpty(versionToInstall) ? request.packageId : $"{request.packageId}@{versionToInstall}"; request.packageAddRequest = Client.Add(packageToInstall); request.needsAddRequest = false; request.installationState = InstallationState.Installing; s_CachedMDStoreInformation.hasAlreadyRequestedData = true; StoreCachedMDStoreInformation(); QueueLoaderRequest(request); } } else if (request.packageAddRequest.IsCompleted && File.Exists($"Packages/{request.packageId}/package.json")) { if (request.packageAddRequest.Status == StatusCode.Success) { if (!String.IsNullOrEmpty(request.loaderType)) { request.packageAddRequest = null; request.installationState = InstallationState.Assigning; QueueLoaderRequest(request); } else { request.logMessage = $"Missing loader type. Unable to assign loader."; request.logLevel = LogLevel.Error; request.installationState = InstallationState.Log; QueueLoaderRequest(request); } } } else if (request.packageAddRequest.IsCompleted && request.packageAddRequest.Status != StatusCode.Success) { if (String.IsNullOrEmpty(request.packageId)) { request.logMessage = $"Error installing package with no package id."; } else { request.logMessage = $"Error Message: {request.packageAddRequest?.Error?.message ?? "UNKNOWN" }.\nError installing package {request.packageId ?? "UNKNOWN PACKAGE ID" }."; } request.logLevel = LogLevel.Error; request.installationState = InstallationState.Log; QueueLoaderRequest(request); } else if (request.timeOut < Time.realtimeSinceStartup) { if (String.IsNullOrEmpty(request.packageId)) { request.logMessage = $"Time out while installing pacakge with no package id."; } else { request.logMessage = $"Error installing package {request.packageId}. Package installation timed out. Check Package Manager UI to see if the package is installed and/or retry your operation."; } request.logLevel = LogLevel.Error; if (request.packageAddRequest.IsCompleted) { request.logMessage += $" Error message: {request.packageAddRequest.Error.message}"; } request.installationState = InstallationState.Log; QueueLoaderRequest(request); } else { QueueLoaderRequest(request); } } } static void WaitingOnSearchQuery() { EditorApplication.update -= WaitingOnSearchQuery; if (s_SearchRequest != null) { if (s_SearchRequest.IsCompleted) EditorApplication.update += UpdateInstallablePackages; else EditorApplication.update += WaitingOnSearchQuery; return; } LoaderAssignmentRequests reqs = GetAllRequestsInQueue(k_WaitingPackmanQuery); if (reqs.activeRequests.Count > 0) { for (int i = 0; i < reqs.activeRequests.Count; i++) { var req = reqs.activeRequests[i]; req.installationState = IsPackageInstalled(req.packageId) ? InstallationState.Assigning : InstallationState.StartInstallation; req.timeOut = Time.realtimeSinceStartup + k_TimeOutDelta; QueueLoaderRequest(req); } } } static void MonitorPackageUninstall() { EditorApplication.update -= MonitorPackageUninstall; LoaderAssignmentRequests reqs = GetAllRequestsInQueue(k_UninstallingPackage); if (reqs.activeRequests.Count > 0) { for (int i = 0; i < reqs.activeRequests.Count; i++) { var req = reqs.activeRequests[i]; if (!req.packageRemoveRequest.IsCompleted) QueueLoaderRequest(req); if (req.packageRemoveRequest.Status == StatusCode.Failure) { req.installationState = InstallationState.Log; req.logMessage = req.packageRemoveRequest.Error.message; req.logLevel = LogLevel.Warning; QueueLoaderRequest(req); } } } } static void QueueLoaderRequest(LoaderAssignmentRequest req) { switch (req.installationState) { case InstallationState.New: if (!s_CachedMDStoreInformation.hasAlreadyRequestedData && !HasInstallablePackageData() && s_SearchRequest == null) { s_SearchRequest = Client.SearchAll(false); EditorApplication.update += UpdateInstallablePackages; } AddRequestToQueue(req, k_WaitingPackmanQuery); EditorApplication.update += WaitingOnSearchQuery; break; case InstallationState.RebuildInstalledCache: AddRequestToQueue(req, k_RebuildCache); EditorApplication.update += RebuildCache; break; case InstallationState.StartInstallation: req.needsAddRequest = true; req.packageAddRequest = null; req.timeOut = Time.realtimeSinceStartup + k_TimeOutDelta; AddRequestToQueue(req, k_InstallingPackage); EditorApplication.update += MonitorPackageInstallation; break; case InstallationState.Installing: AddRequestToQueue(req, k_InstallingPackage); EditorApplication.update += MonitorPackageInstallation; break; case InstallationState.Assigning: AddRequestToQueue(req, k_AssigningPackage); EditorApplication.update += AssignAnyRequestedLoadersUpdate; break; case InstallationState.Uninstalling: AddRequestToQueue(req, k_UninstallingPackage); EditorApplication.update += MonitorPackageUninstall; break; case InstallationState.Log: const string header = "XR Plug-in Management"; switch(req.logLevel) { case LogLevel.Info: Debug.Log($"{header}: {req.logMessage}"); break; case LogLevel.Warning: Debug.LogWarning($"{header} Warning: {req.logMessage}"); break; case LogLevel.Error: Debug.LogError($"{header} error. Failure reason: {req.logMessage}.\n Check if there are any other errors in the console and make sure they are corrected before trying again."); break; } ResetManagerUiIfAvailable(); break; } } } }