You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
6.9 KiB

  1. using IPA.Loader;
  2. using IPA.Utilities;
  3. using System;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using UnityEngine;
  10. using UnityEngine.SceneManagement;
  11. namespace BSIPA_ModList.UI
  12. {
  13. internal struct WarningEntry
  14. {
  15. public string ModName;
  16. public string[] MissingDependencies;
  17. public string[] IgnoredDependencies;
  18. public string[] DisabledDependencies;
  19. public WarningEntry(string modName, string[] missingDependencies, string[] ignoredDependencies, string[] disabledDependencies)
  20. {
  21. ModName = modName;
  22. MissingDependencies = missingDependencies;
  23. IgnoredDependencies = ignoredDependencies;
  24. DisabledDependencies = disabledDependencies;
  25. }
  26. }
  27. internal class WarningUI : MonoBehaviour
  28. { // TODO: rework this to just use disable/ignore reason
  29. internal static WarningUI Instance;
  30. internal static bool firstShow = true;
  31. public void Awake()
  32. {
  33. DontDestroyOnLoad(gameObject);
  34. SceneManager.activeSceneChanged += this.SceneManager_activeSceneChanged;
  35. }
  36. private void SceneManager_activeSceneChanged(Scene from, Scene to)
  37. {
  38. if (to.name == "EmptyTransition")
  39. {
  40. if (Instance != null)
  41. {
  42. Instance.StopAllCoroutines();
  43. Destroy(Instance.gameObject);
  44. _mainFlow = null;
  45. }
  46. Instance = null;
  47. }
  48. }
  49. public void Init()
  50. {
  51. Instance = this;
  52. Logger.log.Debug("Warning UI Awake");
  53. if (firstShow)
  54. {
  55. firstShow = false;
  56. StartCoroutine(LookForUnmetDependencies());
  57. }
  58. }
  59. private static MainFlowCoordinator _mainFlow;
  60. private static SimpleDialogPromptViewController _warningDialog;
  61. private static Queue<WarningEntry> _warningsQueue = new Queue<WarningEntry>();
  62. private static IEnumerator LookForUnmetDependencies()
  63. {
  64. Logger.log.Debug("Waiting for MainFlowCoordinator to appear...");
  65. yield return new WaitWhile(() => FindObjectOfType<MainFlowCoordinator>() == null);
  66. Logger.log.Debug("Looking for unmet dependencies...");
  67. lock (Instance)
  68. {
  69. if (_mainFlow == null)
  70. {
  71. _mainFlow = FindObjectOfType<MainFlowCoordinator>();
  72. _warningDialog = _mainFlow.GetPrivateField<SimpleDialogPromptViewController>("_simpleDialogPromptViewController");
  73. }
  74. _warningsQueue.Clear();
  75. var enabledPlugins = PluginManager.AllPlugins.Select(p => p.Metadata).Where(x => x.Id != null).ToDictionary(x => x.Id, y => y.Version);
  76. var ignoredPlugins = PluginLoader.ignoredPlugins.Where(x => x.Id != null).ToDictionary(x => x.Id, y => y.Version);
  77. var disabledPlugins = PluginManager.DisabledPlugins.Where(x => x.Id != null).ToDictionary(x => x.Id, y => y.Version);
  78. // iterate only disabled and ignored, as thats where missing deps can end up
  79. foreach (var meta in PluginManager.DisabledPlugins.Concat(PluginLoader.ignoredPlugins))
  80. {
  81. List<string> disabledDependencies = new List<string>();
  82. List<string> ignoredDependencies = new List<string>();
  83. List<string> missingDependencies = new List<string>();
  84. foreach (var dep in meta.Manifest.Dependencies)
  85. {
  86. #if DEBUG
  87. Logger.log.Debug($"Looking for dependency {dep.Key} with version range {dep.Value.Intersect(new SemVer.Range("*.*.*"))}");
  88. #endif
  89. if (disabledPlugins.TryGetValue(dep.Key, out var version) && dep.Value.IsSatisfied(version))
  90. {
  91. Logger.log.Debug($"Dependency {dep.Key} was found, but disabled.");
  92. disabledDependencies.Add($"{dep.Key} {dep.Value.ToString()}");
  93. }
  94. else if (ignoredPlugins.TryGetValue(dep.Key, out version) && dep.Value.IsSatisfied(version))
  95. {
  96. Logger.log.Debug($"Dependency {dep.Key} was found, but was ignored, likely due to a missing dependency.");
  97. ignoredDependencies.Add($"{dep.Key} {dep.Value.ToString()}");
  98. }
  99. else if (enabledPlugins.TryGetValue(dep.Key, out version) && dep.Value.IsSatisfied(version))
  100. {
  101. // do nothing, this was probably user disabled
  102. }
  103. else
  104. {
  105. Logger.log.Debug($"{meta.Name} is missing dependency {dep.Key} {dep.Value}");
  106. missingDependencies.Add($"{dep.Key} {dep.Value.ToString()}");
  107. }
  108. }
  109. if(disabledDependencies.Count > 0 || ignoredDependencies.Count > 0 || missingDependencies.Count > 0)
  110. _warningsQueue.Enqueue(new WarningEntry(meta.Name, missingDependencies.ToArray(), ignoredDependencies.ToArray(), disabledDependencies.ToArray()));
  111. }
  112. if (_warningsQueue.Count > 0)
  113. {
  114. yield return new WaitWhile(() => !_mainFlow.isActivated);
  115. ShowWarningDialog();
  116. }
  117. yield break;
  118. }
  119. }
  120. private static void ShowWarningDialog()
  121. {
  122. WarningEntry warning = _warningsQueue.Dequeue();
  123. _warningDialog.Init("Unmet Dependencies", $"Mod <b>{warning.ModName}</b> has unmet dependencies!" +
  124. (warning.MissingDependencies.Length > 0 ? $"\nMissing:\n<color=red>{string.Join("\n", warning.MissingDependencies)}</color>" : "") +
  125. (warning.IgnoredDependencies.Length > 0 ? $"\nIgnored:\n<color=#C2B2B2>{string.Join("\n", warning.IgnoredDependencies)}</color>" : "") +
  126. (warning.DisabledDependencies.Length > 0 ? $"\nDisabled:\n<color=#C2C2C2>{string.Join("\n", warning.DisabledDependencies)}</color>" : "")
  127. , "Okay", WarningDialogDidFinish);
  128. _mainFlow.InvokePrivateMethod("PresentViewController", _warningDialog, null, true);
  129. }
  130. private static void WarningDialogDidFinish(int button)
  131. {
  132. _mainFlow.InvokePrivateMethod("DismissViewController", _warningDialog, null, (_warningsQueue.Count > 0));
  133. if (_warningsQueue.Count > 0)
  134. {
  135. ShowWarningDialog();
  136. }
  137. }
  138. }
  139. }