using IPA.Loader; using IPA.Utilities; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; using UnityEngine.SceneManagement; namespace BSIPA_ModList.UI { internal struct WarningEntry { public string ModName; public string[] MissingDependencies; public string[] DisabledDependencies; public WarningEntry(string modName, string[] missingDependencies, string[] disabledDependencies) { ModName = modName; MissingDependencies = missingDependencies; DisabledDependencies = disabledDependencies; } } internal class WarningUI : MonoBehaviour { internal static WarningUI Instance; internal static bool firstShow = true; public void Awake() { DontDestroyOnLoad(gameObject); SceneManager.activeSceneChanged += this.SceneManager_activeSceneChanged; } private void SceneManager_activeSceneChanged(Scene from, Scene to) { if (to.name == "EmptyTransition") { if (Instance != null) { Instance.StopAllCoroutines(); Destroy(Instance.gameObject); _mainFlow = null; } Instance = null; } } public void Init() { Instance = this; Logger.log.Debug("Warning UI Awake"); if (firstShow) { firstShow = false; StartCoroutine(LookForUnmetDependencies()); } } private static MainFlowCoordinator _mainFlow; private static SimpleDialogPromptViewController _warningDialog; private static Queue _warningsQueue = new Queue(); private static IEnumerator LookForUnmetDependencies() { Logger.log.Debug("Waiting for MainFlowCoordinator to appear..."); yield return new WaitWhile(() => FindObjectOfType() == null); Logger.log.Debug("Looking for unmet dependencies..."); lock (Instance) { if (_mainFlow == null) { _mainFlow = FindObjectOfType(); _warningDialog = _mainFlow.GetPrivateField("_simpleDialogPromptViewController"); } _warningsQueue.Clear(); Dictionary loadedPlugins = PluginManager.AllPlugins.Select(x => x.Metadata).Concat(PluginManager.DisabledPlugins).Concat(PluginLoader.ignoredPlugins).ToDictionary(x => x.Id, y => y.Version); foreach (var meta in PluginManager.AllPlugins.Select(x => x.Metadata).Concat(PluginManager.DisabledPlugins).Concat(PluginLoader.ignoredPlugins)) { List disabledDependencies = new List(); List missingDependencies = new List(); foreach (var dep in meta.Manifest.Dependencies) { #if DEBUG Logger.log.Debug($"Looking for dependency {dep.Key} with version range {dep.Value.Intersect(new SemVer.Range("*.*.*"))}"); #endif if (loadedPlugins.ContainsKey(dep.Key) && dep.Value.IsSatisfied(loadedPlugins[dep.Key])) continue; if (loadedPlugins.ContainsKey(dep.Key) && dep.Value.IsSatisfied(loadedPlugins[dep.Key])) { Logger.log.Debug($"Dependency {dep.Key} was found, but disabled."); disabledDependencies.Add($"{dep.Key}@{dep.Value.ToString()}"); } else { Logger.log.Debug($"{meta.Name} is missing dependency {dep.Key}@{dep.Value}"); missingDependencies.Add($"{dep.Key}@{dep.Value.ToString()}"); } } if(disabledDependencies.Count > 0 || missingDependencies.Count > 0) { _warningsQueue.Enqueue(new WarningEntry(meta.Name, missingDependencies.ToArray(), disabledDependencies.ToArray())); } } if (_warningsQueue.Count > 0) { yield return new WaitWhile(() => !_mainFlow.isActivated); ShowWarningDialog(); } yield break; } } private static void ShowWarningDialog() { WarningEntry warning = _warningsQueue.Dequeue(); _warningDialog.Init("Unmet Dependencies", $"Mod {warning.ModName} has unmet dependencies!" + (warning.MissingDependencies.Length > 0 ? $"\nMissing:\n{string.Join("\n", warning.MissingDependencies)}" : "") + (warning.DisabledDependencies.Length > 0 ? $"\nDisabled:\n{string.Join("\n", warning.DisabledDependencies)}" : "") , "Okay", WarningDialogDidFinish); _mainFlow.InvokePrivateMethod("PresentViewController", _warningDialog, null, true); } private static void WarningDialogDidFinish(int button) { _mainFlow.InvokePrivateMethod("DismissViewController", _warningDialog, null, (_warningsQueue.Count > 0)); if (_warningsQueue.Count > 0) { ShowWarningDialog(); } } } }