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

using IPA.Config;
using IPA.Updating.BeatMods;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using static IPA.Updating.BeatMods.Updater;
namespace BSIPA_ModList
{
internal class DownloadObject
{
public enum States
{
ToDownload, Downloading, Installing, Failed, Completed
}
public DependencyObject Mod;
public Sprite Icon;
public States State = States.ToDownload;
public double Progress = 0;
}
internal class DownloadController : MonoBehaviour
{
private static DownloadController _instance;
public static DownloadController Instance
{
get
{
if (_instance == null)
_instance = Create();
return _instance;
}
}
public static DownloadController Create()
{
var inst = new GameObject("BSIPA Modlist Download Controller").AddComponent<DownloadController>();
if (SelfConfig.SelfConfigRef.Value.Updates.AutoCheckUpdates)
inst.StartCoroutine(inst.StartUpdateCheck());
return inst;
}
private IEnumerator StartUpdateCheck()
{
yield return null;
CheckForUpdates();
}
private readonly List<DownloadObject> downloads = new List<DownloadObject>();
private readonly Dictionary<DependencyObject, DownloadObject> lookup = new Dictionary<DependencyObject, DownloadObject>();
internal IReadOnlyList<DownloadObject> Downloads => downloads;
public event Action OnCheckForUpdates;
public event Action<int> OnCheckForUpdatesComplete;
public event Action OnDownloadStateChanged;
public event Action OnDownloaderListChanged;
private enum States
{
Start, Checking, UpdatesFound, Downloading, Done, DoneWithNoUpdates
}
private States _state = States.Start;
private States State
{
get => _state;
set
{
_state = value;
OnDownloadStateChanged?.Invoke();
}
}
public bool CanCheck => State == States.Start || IsDone;
public bool CanDownload => State == States.UpdatesFound;
public bool CanReset => State == States.UpdatesFound;
public bool IsChecking => State == States.Checking;
public bool IsDownloading => State == States.Downloading;
public bool IsDone => State == States.Done || State == States.DoneWithNoUpdates;
public bool HadUpdates => State == States.Done;
public void Awake() => DontDestroyOnLoad(this);
public void CheckForUpdates()
{
if (!CanCheck)
throw new InvalidOperationException("Invalid state for CheckForUpdates to be called");
State = States.Checking;
OnCheckForUpdates?.Invoke();
Updater.Instance.CheckForUpdates(UpdateCheckComplete);
}
public void ResetCheck(bool resetCache = false)
{
if (!CanReset)
throw new InvalidOperationException("Invalid state for ResetCheck to be called");
Clear();
State = States.Start;
if (resetCache)
ResetRequestCache();
}
private void Clear()
{
downloads.Clear();
lookup.Clear();
OnDownloaderListChanged?.Invoke();
}
private void Add(DownloadObject obj)
{
downloads.Add(obj);
lookup.Add(obj.Mod, obj);
}
private void Remove(DependencyObject obj)
{
downloads.Remove(lookup[obj]);
lookup.Remove(obj);
OnDownloaderListChanged?.Invoke();
}
private void UpdateCheckComplete(List<DependencyObject> found)
{
State = States.UpdatesFound;
OnCheckForUpdatesComplete?.Invoke(found.Count);
foreach (var dep in found)
Add(new DownloadObject
{
Mod = dep,
Icon = dep.IsLegacy ? Utilities.DefaultIPAIcon : Utilities.GetIcon(dep.LocalPluginMeta?.Metadata),
State = DownloadObject.States.ToDownload,
Progress = 0
});
OnDownloaderListChanged?.Invoke();
if (downloads.Count == 0)
OnAllDownloadsCompleted(false);
else if (SelfConfig.SelfConfigRef.Value.Updates.AutoUpdate)
StartDownloads();
}
public void StartDownloads()
{
if (!CanDownload)
throw new InvalidOperationException("Invalid state for StartDownloads to be called");
State = States.Downloading;
Updater.Instance.StartDownload(downloads.Select(d => d.Mod), _DownloadStart, _DownloadProgress,
_DownloadFailed, _DownloadFinished, _InstallFailed, _InstallFinished);
}
private void _DownloadStart(DependencyObject obj)
{
var dl = lookup[obj];
dl.Progress = 0;
dl.State = DownloadObject.States.Downloading;
}
private void _DownloadProgress(DependencyObject obj, long totalBytes, long currentBytes, double progress)
{
lookup[obj].Progress = progress;
}
private void _DownloadFailed(DependencyObject obj, string error)
{
lookup[obj].State = DownloadObject.States.Failed;
}
private void _DownloadFinished(DependencyObject obj)
{
lookup[obj].State = DownloadObject.States.Installing;
}
private void _InstallFailed(DependencyObject obj, Exception error)
{
lookup[obj].State = DownloadObject.States.Failed;
}
private void _InstallFinished(DependencyObject obj, bool didError)
{
if (!didError)
lookup[obj].State = DownloadObject.States.Completed;
StartCoroutine(RemoveModFromList(obj));
}
private IEnumerator RemoveModFromList(DependencyObject obj)
{
yield return new WaitForSeconds(0.25f);
Remove(obj);
if (downloads.Count == 0)
OnAllDownloadsCompleted(true);
}
private void OnAllDownloadsCompleted(bool hadUpdates)
{
State = hadUpdates ? States.Done : States.DoneWithNoUpdates;
}
}
}