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.

216 lines
6.5 KiB

  1. using IPA.Config;
  2. using IPA.Updating.BeatMods;
  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 static IPA.Updating.BeatMods.Updater;
  11. namespace BSIPA_ModList
  12. {
  13. internal class DownloadObject
  14. {
  15. public enum States
  16. {
  17. ToDownload, Downloading, Installing, Failed, Completed
  18. }
  19. public DependencyObject Mod;
  20. public Sprite Icon;
  21. public States State = States.ToDownload;
  22. public double Progress = 0;
  23. }
  24. internal class DownloadController : MonoBehaviour
  25. {
  26. private static DownloadController _instance;
  27. public static DownloadController Instance
  28. {
  29. get
  30. {
  31. if (_instance == null)
  32. _instance = Create();
  33. return _instance;
  34. }
  35. }
  36. public static DownloadController Create()
  37. {
  38. var inst = new GameObject("BSIPA Modlist Download Controller").AddComponent<DownloadController>();
  39. if (SelfConfig.SelfConfigRef.Value.Updates.AutoCheckUpdates)
  40. inst.StartCoroutine(inst.StartUpdateCheck());
  41. return inst;
  42. }
  43. private IEnumerator StartUpdateCheck()
  44. {
  45. yield return null;
  46. CheckForUpdates();
  47. }
  48. private readonly List<DownloadObject> downloads = new List<DownloadObject>();
  49. private readonly Dictionary<DependencyObject, DownloadObject> lookup = new Dictionary<DependencyObject, DownloadObject>();
  50. internal IReadOnlyList<DownloadObject> Downloads => downloads;
  51. public event Action OnCheckForUpdates;
  52. public event Action<int> OnCheckForUpdatesComplete;
  53. public event Action OnDownloadStateChanged;
  54. public event Action OnDownloaderListChanged;
  55. private enum States
  56. {
  57. Start, Checking, UpdatesFound, Downloading, Done, DoneWithNoUpdates
  58. }
  59. private States _state = States.Start;
  60. private States State
  61. {
  62. get => _state;
  63. set
  64. {
  65. _state = value;
  66. OnDownloadStateChanged?.Invoke();
  67. }
  68. }
  69. public bool CanCheck => State == States.Start || IsDone;
  70. public bool CanDownload => State == States.UpdatesFound;
  71. public bool CanReset => State == States.UpdatesFound;
  72. public bool IsChecking => State == States.Checking;
  73. public bool IsDownloading => State == States.Downloading;
  74. public bool IsDone => State == States.Done || State == States.DoneWithNoUpdates;
  75. public bool HadUpdates => State == States.Done;
  76. public void Awake() => DontDestroyOnLoad(this);
  77. public void CheckForUpdates()
  78. {
  79. if (!CanCheck)
  80. throw new InvalidOperationException("Invalid state for CheckForUpdates to be called");
  81. State = States.Checking;
  82. OnCheckForUpdates?.Invoke();
  83. Updater.Instance.CheckForUpdates(UpdateCheckComplete);
  84. }
  85. public void ResetCheck(bool resetCache = false)
  86. {
  87. if (!CanReset)
  88. throw new InvalidOperationException("Invalid state for ResetCheck to be called");
  89. Clear();
  90. State = States.Start;
  91. if (resetCache)
  92. ResetRequestCache();
  93. }
  94. private void Clear()
  95. {
  96. downloads.Clear();
  97. lookup.Clear();
  98. OnDownloaderListChanged?.Invoke();
  99. }
  100. private void Add(DownloadObject obj)
  101. {
  102. downloads.Add(obj);
  103. lookup.Add(obj.Mod, obj);
  104. }
  105. private void Remove(DependencyObject obj)
  106. {
  107. downloads.Remove(lookup[obj]);
  108. lookup.Remove(obj);
  109. OnDownloaderListChanged?.Invoke();
  110. }
  111. private void UpdateCheckComplete(List<DependencyObject> found)
  112. {
  113. State = States.UpdatesFound;
  114. OnCheckForUpdatesComplete?.Invoke(found.Count);
  115. foreach (var dep in found)
  116. Add(new DownloadObject
  117. {
  118. Mod = dep,
  119. Icon = dep.IsLegacy ? Utilities.DefaultIPAIcon : Utilities.GetIcon(dep.LocalPluginMeta?.Metadata),
  120. State = DownloadObject.States.ToDownload,
  121. Progress = 0
  122. });
  123. OnDownloaderListChanged?.Invoke();
  124. if (downloads.Count == 0)
  125. OnAllDownloadsCompleted(false);
  126. else if (SelfConfig.SelfConfigRef.Value.Updates.AutoUpdate)
  127. StartDownloads();
  128. }
  129. public void StartDownloads()
  130. {
  131. if (!CanDownload)
  132. throw new InvalidOperationException("Invalid state for StartDownloads to be called");
  133. State = States.Downloading;
  134. Updater.Instance.StartDownload(downloads.Select(d => d.Mod), _DownloadStart, _DownloadProgress,
  135. _DownloadFailed, _DownloadFinished, _InstallFailed, _InstallFinished);
  136. }
  137. private void _DownloadStart(DependencyObject obj)
  138. {
  139. var dl = lookup[obj];
  140. dl.Progress = 0;
  141. dl.State = DownloadObject.States.Downloading;
  142. }
  143. private void _DownloadProgress(DependencyObject obj, long totalBytes, long currentBytes, double progress)
  144. {
  145. lookup[obj].Progress = progress;
  146. }
  147. private void _DownloadFailed(DependencyObject obj, string error)
  148. {
  149. lookup[obj].State = DownloadObject.States.Failed;
  150. }
  151. private void _DownloadFinished(DependencyObject obj)
  152. {
  153. lookup[obj].State = DownloadObject.States.Installing;
  154. }
  155. private void _InstallFailed(DependencyObject obj, Exception error)
  156. {
  157. lookup[obj].State = DownloadObject.States.Failed;
  158. }
  159. private void _InstallFinished(DependencyObject obj, bool didError)
  160. {
  161. if (!didError)
  162. lookup[obj].State = DownloadObject.States.Completed;
  163. StartCoroutine(RemoveModFromList(obj));
  164. }
  165. private IEnumerator RemoveModFromList(DependencyObject obj)
  166. {
  167. yield return new WaitForSeconds(0.25f);
  168. Remove(obj);
  169. if (downloads.Count == 0)
  170. OnAllDownloadsCompleted(true);
  171. }
  172. private void OnAllDownloadsCompleted(bool hadUpdates)
  173. {
  174. State = hadUpdates ? States.Done : States.DoneWithNoUpdates;
  175. }
  176. }
  177. }