Browse Source

Added mod download queue

Added DownloadController
Added FloatingNotification
pull/46/head
Anairkoen Schno 5 years ago
parent
commit
5a10beec40
14 changed files with 795 additions and 64 deletions
  1. +5
    -0
      BSIPA-ModList/BSIPA-ModList.csproj
  2. +216
    -0
      BSIPA-ModList/DownloadController.cs
  3. +3
    -0
      BSIPA-ModList/Plugin.cs
  4. +1
    -4
      BSIPA-ModList/UI/ButtonUI.cs
  5. +98
    -0
      BSIPA-ModList/UI/DownloadProgressCell.cs
  6. +218
    -0
      BSIPA-ModList/UI/FloatingNotification.cs
  7. +9
    -4
      BSIPA-ModList/UI/ModListFlowCoordinator.cs
  8. +150
    -0
      BSIPA-ModList/UI/ViewControllers/DownloadProgressViewController.cs
  9. +4
    -51
      BSIPA-ModList/UI/ViewControllers/ModCells.cs
  10. +79
    -0
      BSIPA-ModList/Utilities.cs
  11. +9
    -2
      IPA.Loader/Updating/BeatMods/Updater.cs
  12. +1
    -1
      IPA/IPA.csproj
  13. +1
    -1
      IPA/app.config
  14. +1
    -1
      IPA/obj/Debug/IPA.csproj.CoreCompileInputs.cache

+ 5
- 0
BSIPA-ModList/BSIPA-ModList.csproj View File

@ -67,14 +67,19 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="DownloadController.cs" />
<Compile Include="Plugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UI\ButtonUI.cs" />
<Compile Include="UI\DownloadProgressCell.cs" />
<Compile Include="UI\ModListFlowCoordinator.cs" />
<Compile Include="UI\ViewControllers\BackButtonNavigationController.cs" />
<Compile Include="UI\ViewControllers\DownloadProgressViewController.cs" />
<Compile Include="UI\FloatingNotification.cs" />
<Compile Include="UI\ViewControllers\ModCells.cs" />
<Compile Include="UI\ViewControllers\ModInfoViewController.cs" />
<Compile Include="UI\ViewControllers\ModListController.cs" />
<Compile Include="Utilities.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IPA.Loader\IPA.Loader.csproj">


+ 216
- 0
BSIPA-ModList/DownloadController.cs View File

@ -0,0 +1,216 @@
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
}
private States _state = States.Start;
private States State
{
get => _state;
set
{
_state = value;
OnDownloadStateChanged?.Invoke();
}
}
public bool CanCheck => State == States.Start || State == States.Done;
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;
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 = Utilities.GetIcon(dep.LocalPluginMeta?.Metadata),
State = DownloadObject.States.ToDownload,
Progress = 0
});
OnDownloaderListChanged?.Invoke();
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);
if (downloads.Count == 0)
OnAllDownloadsCompleted();
}
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();
}
private void OnAllDownloadsCompleted()
{
State = States.Done;
}
}
}

+ 3
- 0
BSIPA-ModList/Plugin.cs View File

@ -31,6 +31,7 @@ namespace BSIPA_ModList
public void OnApplicationStart()
{
}
public void OnFixedUpdate()
@ -41,6 +42,8 @@ namespace BSIPA_ModList
{
if (scene.name == "MenuCore")
{
FloatingNotification.Create();
if (ButtonUI.Instance == null)
{
Logger.log.Debug("Creating Menu");


+ 1
- 4
BSIPA-ModList/UI/ButtonUI.cs View File

@ -46,7 +46,6 @@ namespace BSIPA_ModList.UI
StartCoroutine(AddModListButton());
}
private static MainFlowCoordinator mainFlow;
private static ModListFlowCoordinator menuFlow;
private static readonly WaitUntil _bottomPanelExists = new WaitUntil(() => GameObject.Find(ControllerPanel) != null);
@ -65,8 +64,6 @@ namespace BSIPA_ModList.UI
lock (Instance)
{
if (mainFlow == null)
mainFlow = Resources.FindObjectsOfTypeAll<MainFlowCoordinator>().First();
if (menuFlow == null)
menuFlow = new GameObject("BSIPA Mod List Flow Controller").AddComponent<ModListFlowCoordinator>();
if (panel == null)
@ -77,7 +74,7 @@ namespace BSIPA_ModList.UI
button = BeatSaberUI.CreateUIButton(panel, CopyButton, () =>
{
Logger.log.Debug("Presenting own flow controller");
menuFlow.PresentOn(mainFlow);
menuFlow.Present();
}, "Mod List");
panel.Find(CopyButton).SetAsLastSibling();


+ 98
- 0
BSIPA-ModList/UI/DownloadProgressCell.cs View File

@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TMPro;
using System.Threading.Tasks;
using IPA.Updating.BeatMods;
using UnityEngine;
namespace BSIPA_ModList.UI
{
// originally ripped verbatim from Andruzz's BeatSaverDownloader
internal class DownloadProgressCell : LevelListTableCell
{
private DownloadObject mod;
protected override void Awake()
{
base.Awake();
}
public void Init(DownloadObject mod)
{
Destroy(GetComponent<LevelListTableCell>());
reuseIdentifier = "DownloadCell";
this.mod = mod;
_authorText = GetComponentsInChildren<TextMeshProUGUI>().First(x => x.name == "Author");
_authorText.enableWordWrapping = false;
_authorText.overflowMode = TextOverflowModes.Overflow;
_songNameText = GetComponentsInChildren<TextMeshProUGUI>().First(x => x.name == "SongName");
_songNameText.enableWordWrapping = false;
_songNameText.overflowMode = TextOverflowModes.Overflow;
_coverImage = GetComponentsInChildren<UnityEngine.UI.Image>().First(x => x.name == "CoverImage");
_bgImage = GetComponentsInChildren<UnityEngine.UI.Image>().First(x => x.name == "BG");
_highlightImage = GetComponentsInChildren<UnityEngine.UI.Image>().First(x => x.name == "Highlight");
_beatmapCharacteristicAlphas = new float[0];
_beatmapCharacteristicImages = new UnityEngine.UI.Image[0];
_bought = true;
foreach (var icon in GetComponentsInChildren<UnityEngine.UI.Image>().Where(x => x.name.StartsWith("LevelTypeIcon")))
Destroy(icon.gameObject);
_songNameText.text = $"{mod.Mod.Name} <size=60%>v{mod.Mod.ResolvedVersion}</size>";
_authorText.text = "";
_coverImage.sprite = mod.Icon;
_bgImage.enabled = true;
_bgImage.sprite = Sprite.Create(new Texture2D(1, 1), new Rect(0, 0, 1, 1), Vector2.one / 2f);
_bgImage.type = UnityEngine.UI.Image.Type.Filled;
_bgImage.fillMethod = UnityEngine.UI.Image.FillMethod.Horizontal;
Update();
}
public void Update()
{
_bgImage.enabled = true;
switch (mod.State)
{
case DownloadObject.States.ToDownload:
{
_bgImage.color = new Color(1f, 1f, 1f, 0.35f);
_bgImage.fillAmount = 0;
}
break;
case DownloadObject.States.Downloading:
{
_bgImage.color = new Color(1f, 1f, 1f, 0.35f);
_bgImage.fillAmount = (float)mod.Progress;
}
break;
case DownloadObject.States.Installing:
{
_bgImage.color = new Color(0f, 1f, 1f, 0.35f);
_bgImage.fillAmount = 1f;
}
break;
case DownloadObject.States.Completed:
{
_bgImage.color = new Color(0f, 1f, 0f, 0.35f);
_bgImage.fillAmount = 1f;
}
break;
case DownloadObject.States.Failed:
{
_bgImage.color = new Color(1f, 0f, 0f, 0.35f);
_bgImage.fillAmount = 1f;
}
break;
}
}
}
}

+ 218
- 0
BSIPA-ModList/UI/FloatingNotification.cs View File

@ -0,0 +1,218 @@
using CustomUI.BeatSaber;
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace BSIPA_ModList.UI
{
internal class FloatingNotification : MonoBehaviour
{
private Canvas _canvas;
private TMP_Text _authorNameText;
private TMP_Text _pluginNameText;
private TMP_Text _headerText;
private Image _loadingBackg;
private Image _loadingBar;
private static readonly Vector3 Position = new Vector3(2.3f, 2.3f, 1.35f);
private static readonly Vector3 Rotation = new Vector3(0, 60, 0);
private static readonly Vector3 Scale = new Vector3(0.01f, 0.01f, 0.01f);
private static readonly Vector2 CanvasSize = new Vector2(100, 50);
private const string AuthorNameText = "BSIPA";
private const float AuthorNameFontSize = 7f;
private static readonly Vector2 AuthorNamePosition = new Vector2(10, 31);
private const string PluginNameText = "Mod Updater";
private const float PluginNameFontSize = 9f;
private static readonly Vector2 PluginNamePosition = new Vector2(10, 23);
private static readonly Vector2 HeaderPosition = new Vector2(10, 15);
private static readonly Vector2 HeaderSize = new Vector2(100, 20);
private const string HeaderText = "Checking for updates...";
private const float HeaderFontSize = 15f;
private static readonly Vector2 LoadingBarSize = new Vector2(100, 10);
private static readonly Color BackgroundColor = new Color(0, 0, 0, 0.2f);
private bool _showingMessage;
public static FloatingNotification Create()
{
return new GameObject("Mod List Floating Notification").AddComponent<FloatingNotification>();
}
public void ShowMessage(string message, float time)
{
StopAllCoroutines();
_showingMessage = true;
_headerText.text = message;
_loadingBar.enabled = false;
_loadingBackg.enabled = false;
_canvas.enabled = true;
StartCoroutine(DisableCanvasRoutine(time));
}
public void ShowMessage(string message)
{
StopAllCoroutines();
_showingMessage = true;
_headerText.text = message;
_loadingBar.enabled = false;
_loadingBackg.enabled = false;
_canvas.enabled = true;
}
protected void OnEnable()
{
SceneManager.activeSceneChanged += SceneManagerOnActiveSceneChanged;
DownloadController.Instance.OnDownloadStateChanged += DownloaderStateChanged;
DownloadController.Instance.OnCheckForUpdates += CheckForUpdatesStart;
DownloadController.Instance.OnCheckForUpdatesComplete += CheckForUpdatesDone;
}
protected void OnDisable()
{
SceneManager.activeSceneChanged -= SceneManagerOnActiveSceneChanged;
DownloadController.Instance.OnDownloadStateChanged -= DownloaderStateChanged;
DownloadController.Instance.OnCheckForUpdates -= CheckForUpdatesStart;
DownloadController.Instance.OnCheckForUpdatesComplete -= CheckForUpdatesDone;
}
private void CheckForUpdatesStart()
{
_showingMessage = false;
_headerText.text = HeaderText;
_loadingBar.enabled = false;
_loadingBackg.enabled = false;
_canvas.enabled = true;
}
private void CheckForUpdatesDone(int count)
{
_showingMessage = false;
_headerText.text = $"{count} updates found";
_loadingBar.enabled = false;
_loadingBackg.enabled = false;
_canvas.enabled = true;
StartCoroutine(DisableCanvasRoutine(5f));
}
private void SceneManagerOnActiveSceneChanged(Scene oldScene, Scene newScene)
{
if (newScene.name == "MenuCore")
{
if (_showingMessage)
{
_canvas.enabled = true;
}
}
else
{
_canvas.enabled = false;
}
}
private void DownloaderStateChanged()
{
if (DownloadController.Instance.IsDownloading)
{
StopAllCoroutines();
_showingMessage = false;
_headerText.text = "Downloading updates...";
_loadingBar.enabled = false;
_loadingBackg.enabled = false;
_canvas.enabled = true;
}
if (DownloadController.Instance.IsDone)
{
_showingMessage = false;
_headerText.text = "Updates complete";
_loadingBar.enabled = false;
_loadingBackg.enabled = false;
StartCoroutine(DisableCanvasRoutine(5f));
}
}
private IEnumerator DisableCanvasRoutine(float time)
{
yield return new WaitForSecondsRealtime(time);
_canvas.enabled = false;
_showingMessage = false;
}
private static FloatingNotification instance;
protected void Awake()
{
if (instance != null)
{
Destroy(this);
return;
}
instance = this;
gameObject.transform.position = Position;
gameObject.transform.eulerAngles = Rotation;
gameObject.transform.localScale = Scale;
_canvas = gameObject.AddComponent<Canvas>();
_canvas.renderMode = RenderMode.WorldSpace;
_canvas.enabled = false;
var rectTransform = _canvas.transform as RectTransform;
rectTransform.sizeDelta = CanvasSize;
_authorNameText = BeatSaberUI.CreateText(_canvas.transform as RectTransform, AuthorNameText, AuthorNamePosition);
rectTransform = _authorNameText.transform as RectTransform;
rectTransform.SetParent(_canvas.transform, false);
rectTransform.anchoredPosition = AuthorNamePosition;
rectTransform.sizeDelta = HeaderSize;
_authorNameText.text = AuthorNameText;
_authorNameText.fontSize = AuthorNameFontSize;
_pluginNameText = BeatSaberUI.CreateText(_canvas.transform as RectTransform, PluginNameText, PluginNamePosition);
rectTransform = _pluginNameText.transform as RectTransform;
rectTransform.SetParent(_canvas.transform, false);
rectTransform.sizeDelta = HeaderSize;
rectTransform.anchoredPosition = PluginNamePosition;
_pluginNameText.text = PluginNameText;
_pluginNameText.fontSize = PluginNameFontSize;
_headerText = BeatSaberUI.CreateText(_canvas.transform as RectTransform, HeaderText, HeaderPosition);
rectTransform = _headerText.transform as RectTransform;
rectTransform.SetParent(_canvas.transform, false);
rectTransform.anchoredPosition = HeaderPosition;
rectTransform.sizeDelta = HeaderSize;
_headerText.text = HeaderText;
_headerText.fontSize = HeaderFontSize;
_loadingBackg = new GameObject("Background").AddComponent<Image>();
rectTransform = _loadingBackg.transform as RectTransform;
rectTransform.SetParent(_canvas.transform, false);
rectTransform.sizeDelta = LoadingBarSize;
_loadingBackg.color = BackgroundColor;
_loadingBar = new GameObject("Loading Bar").AddComponent<Image>();
rectTransform = _loadingBar.transform as RectTransform;
rectTransform.SetParent(_canvas.transform, false);
rectTransform.sizeDelta = LoadingBarSize;
var tex = Texture2D.whiteTexture;
var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), Vector2.one * 0.5f, 100, 1);
_loadingBar.sprite = sprite;
_loadingBar.type = Image.Type.Filled;
_loadingBar.fillMethod = Image.FillMethod.Horizontal;
_loadingBar.color = new Color(1, 1, 1, 0.5f);
DontDestroyOnLoad(gameObject);
}
/*private void Update()
{
if (!_canvas.enabled) return;
_loadingBar.fillAmount = SongLoader.LoadingProgress;
}*/
}
}

+ 9
- 4
BSIPA-ModList/UI/ModListFlowCoordinator.cs View File

@ -1,4 +1,5 @@
using CustomUI.BeatSaber;
using BSIPA_ModList.UI.ViewControllers;
using CustomUI.BeatSaber;
using CustomUI.Utilities;
using IPA.Loader;
using System;
@ -13,6 +14,7 @@ namespace BSIPA_ModList.UI
{
private BackButtonNavigationController navigationController;
private ModListController modList;
private DownloadProgressViewController downloads;
#pragma warning disable CS0618
protected override void DidActivate(bool firstActivation, ActivationType activationType)
@ -27,17 +29,19 @@ namespace BSIPA_ModList.UI
modList = BeatSaberUI.CreateViewController<ModListController>();
modList.Init(this, PluginManager.AllPlugins, PluginLoader.ignoredPlugins, PluginManager.Plugins);
downloads = BeatSaberUI.CreateViewController<DownloadProgressViewController>();
PushViewControllerToNavigationController(navigationController, modList);
}
ProvideInitialViewControllers(navigationController);
ProvideInitialViewControllers(navigationController, rightViewController: downloads);
}
#pragma warning restore
private delegate void PresentFlowCoordDel(FlowCoordinator self, FlowCoordinator newF, Action finished, bool immediate, bool replaceTop);
private static PresentFlowCoordDel presentFlow;
public void PresentOn(FlowCoordinator main, Action finished = null, bool immediate = false, bool replaceTop = false)
public void Present(Action finished = null, bool immediate = false, bool replaceTop = false)
{
if (presentFlow == null)
{
@ -46,7 +50,8 @@ namespace BSIPA_ModList.UI
presentFlow = (PresentFlowCoordDel)Delegate.CreateDelegate(typeof(PresentFlowCoordDel), m);
}
presentFlow(main, this, finished, immediate, replaceTop);
MainFlowCoordinator mainFlow = Resources.FindObjectsOfTypeAll<MainFlowCoordinator>().First();
presentFlow(mainFlow, this, finished, immediate, replaceTop);
}
public bool HasSelected { get; private set; } = false;


+ 150
- 0
BSIPA-ModList/UI/ViewControllers/DownloadProgressViewController.cs View File

@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CustomUI.BeatSaber;
using CustomUI.Utilities;
using HMUI;
using IPA.Updating.BeatMods;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using VRUI;
namespace BSIPA_ModList.UI.ViewControllers
{
// originally ripped verbatim from Andruzz's BeatSaverDownloader
internal class DownloadProgressViewController : VRUIViewController, TableView.IDataSource
{
private TextMeshProUGUI _titleText;
private Button _checkForUpdates;
private Button _downloadUpdates;
private TableView _currentlyUpdatingTableView;
private LevelListTableCell _songListTableCellInstance;
private Button _pageUpButton;
private Button _pageDownButton;
protected override void DidActivate(bool firstActivation, ActivationType type)
{
if (firstActivation && type == ActivationType.AddedToHierarchy)
{
DownloadController.Instance.OnDownloaderListChanged -= Refresh;
DownloadController.Instance.OnDownloadStateChanged -= DownloaderStateChanged;
_songListTableCellInstance = Resources.FindObjectsOfTypeAll<LevelListTableCell>().First(x => (x.name == "LevelListTableCell"));
_titleText = BeatSaberUI.CreateText(rectTransform, "DOWNLOAD QUEUE", new Vector2(0f, 35f));
_titleText.alignment = TextAlignmentOptions.Top;
_titleText.fontSize = 6f;
_pageUpButton = Instantiate(Resources.FindObjectsOfTypeAll<Button>().Last(x => (x.name == "PageUpButton")), rectTransform, false);
(_pageUpButton.transform as RectTransform).anchorMin = new Vector2(0.5f, 1f);
(_pageUpButton.transform as RectTransform).anchorMax = new Vector2(0.5f, 1f);
(_pageUpButton.transform as RectTransform).anchoredPosition = new Vector2(0f, -14f);
(_pageUpButton.transform as RectTransform).sizeDelta = new Vector2(40f, 10f);
_pageUpButton.interactable = true;
_pageUpButton.onClick.AddListener(delegate ()
{
_currentlyUpdatingTableView.PageScrollUp();
});
_pageDownButton = Instantiate(Resources.FindObjectsOfTypeAll<Button>().First(x => (x.name == "PageDownButton")), rectTransform, false);
(_pageDownButton.transform as RectTransform).anchorMin = new Vector2(0.5f, 0f);
(_pageDownButton.transform as RectTransform).anchorMax = new Vector2(0.5f, 0f);
(_pageDownButton.transform as RectTransform).anchoredPosition = new Vector2(0f, 8f);
(_pageDownButton.transform as RectTransform).sizeDelta = new Vector2(40f, 10f);
_pageDownButton.interactable = true;
_pageDownButton.onClick.AddListener(delegate ()
{
_currentlyUpdatingTableView.PageScrollDown();
});
var gobj = new GameObject("DownloadTable");
gobj.SetActive(false);
_currentlyUpdatingTableView = gobj.AddComponent<TableView>();
_currentlyUpdatingTableView.transform.SetParent(rectTransform, false);
_currentlyUpdatingTableView.SetPrivateField("_isInitialized", false);
_currentlyUpdatingTableView.SetPrivateField("_preallocatedCells", new TableView.CellsGroup[0]);
_currentlyUpdatingTableView.Init();
RectMask2D viewportMask = Instantiate(Resources.FindObjectsOfTypeAll<RectMask2D>().First(), _currentlyUpdatingTableView.transform, false);
viewportMask.transform.DetachChildren();
_currentlyUpdatingTableView.GetComponentsInChildren<RectTransform>().First(x => x.name == "Content").transform.SetParent(viewportMask.rectTransform, false);
(_currentlyUpdatingTableView.transform as RectTransform).anchorMin = new Vector2(0.3f, 0.5f);
(_currentlyUpdatingTableView.transform as RectTransform).anchorMax = new Vector2(0.7f, 0.5f);
(_currentlyUpdatingTableView.transform as RectTransform).sizeDelta = new Vector2(0f, 60f);
(_currentlyUpdatingTableView.transform as RectTransform).anchoredPosition = new Vector3(0f, -3f);
ReflectionUtil.SetPrivateField(_currentlyUpdatingTableView, "_pageUpButton", _pageUpButton);
ReflectionUtil.SetPrivateField(_currentlyUpdatingTableView, "_pageDownButton", _pageDownButton);
_currentlyUpdatingTableView.selectionType = TableViewSelectionType.None;
_currentlyUpdatingTableView.dataSource = this;
gobj.SetActive(true);
_checkForUpdates = BeatSaberUI.CreateUIButton(rectTransform, "CreditsButton", new Vector2(36f, -30f), new Vector2(20f, 10f), CheckUpdates, "Check for updates");
_checkForUpdates.interactable = DownloadController.Instance.CanCheck || DownloadController.Instance.CanReset;
_checkForUpdates.ToggleWordWrapping(false);
_downloadUpdates = BeatSaberUI.CreateUIButton(rectTransform, "CreditsButton", new Vector2(36f, -15f), new Vector2(20f, 10f), DownloadUpdates, "Download Updates");
_downloadUpdates.interactable = DownloadController.Instance.CanDownload;
_downloadUpdates.ToggleWordWrapping(false);
DownloadController.Instance.OnDownloaderListChanged += Refresh;
DownloadController.Instance.OnDownloadStateChanged += DownloaderStateChanged;
}
}
private void DownloadUpdates()
{
if (DownloadController.Instance.CanDownload)
DownloadController.Instance.StartDownloads();
}
private void CheckUpdates()
{
Updater.ResetRequestCache();
if (DownloadController.Instance.CanReset)
DownloadController.Instance.ResetCheck(false);
if (DownloadController.Instance.CanCheck)
DownloadController.Instance.CheckForUpdates();
}
private void DownloaderStateChanged()
{
_checkForUpdates.interactable = DownloadController.Instance.CanCheck || DownloadController.Instance.CanReset;
_downloadUpdates.interactable = DownloadController.Instance.CanDownload;
}
protected override void DidDeactivate(DeactivationType type)
{
DownloadController.Instance.OnDownloaderListChanged -= Refresh;
DownloadController.Instance.OnDownloadStateChanged -= DownloaderStateChanged;
}
private void Refresh()
{
_currentlyUpdatingTableView.ReloadData();
}
public float CellSize()
{
return 10f;
}
public int NumberOfCells()
{
return DownloadController.Instance.Downloads.Count;
}
public TableCell CellForIdx(int row)
{
LevelListTableCell _tableCell = Instantiate(_songListTableCellInstance);
DownloadProgressCell _queueCell = _tableCell.gameObject.AddComponent<DownloadProgressCell>();
_queueCell.Init(DownloadController.Instance.Downloads[row]);
return _queueCell;
}
}
}

+ 4
- 51
BSIPA-ModList/UI/ViewControllers/ModCells.cs View File

@ -19,17 +19,6 @@ namespace BSIPA_ModList.UI.ViewControllers
internal class BSIPAModCell : CustomCellInfo, IClickableCell
{
private static Sprite _defaultIcon;
public static Sprite DefaultIcon
{
get
{
if (_defaultIcon == null)
_defaultIcon = UIUtilities.LoadSpriteFromResources("BSIPA_ModList.Icons.mod_bsipa.png");
return _defaultIcon;
}
}
internal PluginLoader.PluginInfo Plugin;
private ModListController list;
@ -42,21 +31,7 @@ namespace BSIPA_ModList.UI.ViewControllers
if (string.IsNullOrWhiteSpace(subtext))
subtext = "<color=#BFBFBF><i>Unspecified Author</i>";
if (plugin.Metadata.Manifest.IconPath != null)
{
try
{
icon = UIUtilities.LoadSpriteRaw(UIUtilities.GetResource(plugin.Metadata.Assembly, plugin.Metadata.Manifest.IconPath));
}
catch (Exception e)
{
Logger.log.Error($"Error loading icon for {plugin.Metadata.Name}");
Logger.log.Error(e);
}
}
if (icon == null)
icon = DefaultIcon;
icon = plugin.Metadata.GetIcon();
Logger.log.Debug($"BSIPAModCell {plugin.Metadata.Name} {plugin.Metadata.Version}");
}
@ -93,7 +68,7 @@ namespace BSIPA_ModList.UI.ViewControllers
private string authorText;
public BSIPAIgnoredModCell(ModListController list, PluginLoader.PluginMetadata plugin)
: base($"<color=#878787>{plugin.Name} <size=60%>v{plugin.Version}", "", BSIPAModCell.DefaultIcon)
: base($"<color=#878787>{plugin.Name} <size=60%>v{plugin.Version}", "", Utilities.DefaultBSIPAIcon)
{
Plugin = plugin;
this.list = list;
@ -130,17 +105,6 @@ namespace BSIPA_ModList.UI.ViewControllers
}
internal class LibraryModCell : CustomCellInfo, IClickableCell
{
private static Sprite _defaultIcon;
public static Sprite DefaultIcon
{
get
{
if (_defaultIcon == null)
_defaultIcon = UIUtilities.LoadSpriteFromResources("BSIPA_ModList.Icons.library.png");
return _defaultIcon;
}
}
internal PluginLoader.PluginInfo Plugin;
private ModListController list;
@ -153,7 +117,7 @@ namespace BSIPA_ModList.UI.ViewControllers
if (string.IsNullOrWhiteSpace(subtext))
subtext = "<color=#BFBFBF><i>Unspecified Author</i>";
icon = DefaultIcon;
icon = Utilities.DefaultLibraryIcon;
Logger.log.Debug($"LibraryModCell {plugin.Metadata.Name} {plugin.Metadata.Version}");
}
@ -183,22 +147,11 @@ namespace BSIPA_ModList.UI.ViewControllers
#pragma warning disable CS0618
internal class IPAModCell : CustomCellInfo, IClickableCell
{
private static Sprite _defaultIcon;
public static Sprite DefaultIcon
{
get
{
if (_defaultIcon == null)
_defaultIcon = UIUtilities.LoadSpriteFromResources("BSIPA_ModList.Icons.mod_ipa.png");
return _defaultIcon;
}
}
internal IPlugin Plugin;
private ModListController list;
public IPAModCell(ModListController list, IPlugin plugin)
: base($"{plugin.Name} <size=60%>{plugin.Version}", "<color=#BFBFBF><i>Legacy</i>", DefaultIcon)
: base($"{plugin.Name} <size=60%>{plugin.Version}", "<color=#BFBFBF><i>Legacy</i>", Utilities.DefaultIPAIcon)
{
Plugin = plugin;
this.list = list;


+ 79
- 0
BSIPA-ModList/Utilities.cs View File

@ -0,0 +1,79 @@
using CustomUI.Utilities;
using IPA.Loader;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace BSIPA_ModList
{
internal static class Utilities
{
private static Sprite _defaultBsipaIcon;
public static Sprite DefaultBSIPAIcon
{
get
{
if (_defaultBsipaIcon == null)
_defaultBsipaIcon = UIUtilities.LoadSpriteFromResources("BSIPA_ModList.Icons.mod_bsipa.png");
return _defaultBsipaIcon;
}
}
private static Sprite _defaultLibraryIcon;
public static Sprite DefaultLibraryIcon
{
get
{
if (_defaultLibraryIcon == null)
_defaultLibraryIcon = UIUtilities.LoadSpriteFromResources("BSIPA_ModList.Icons.library.png");
return _defaultLibraryIcon;
}
}
private static Sprite _defaultIpaIcon;
public static Sprite DefaultIPAIcon
{
get
{
if (_defaultIpaIcon == null)
_defaultIpaIcon = UIUtilities.LoadSpriteFromResources("BSIPA_ModList.Icons.mod_ipa.png");
return _defaultIpaIcon;
}
}
public static Sprite GetIcon(this PluginLoader.PluginMetadata meta)
{
if (meta == null) return DefaultBSIPAIcon;
if (meta.IsBare) return DefaultLibraryIcon;
else return GetEmbeddedIcon(meta) ?? DefaultBSIPAIcon;
}
private static Dictionary<PluginLoader.PluginMetadata, Sprite> embeddedIcons = new Dictionary<PluginLoader.PluginMetadata, Sprite>();
public static Sprite GetEmbeddedIcon(this PluginLoader.PluginMetadata meta)
{
if (embeddedIcons.TryGetValue(meta, out var sprite)) return sprite;
var icon = GetEmbeddedIconNoCache(meta);
embeddedIcons.Add(meta, icon);
return icon;
}
private static Sprite GetEmbeddedIconNoCache(PluginLoader.PluginMetadata meta)
{
if (meta.Assembly == null) return null;
if (meta.Manifest.IconPath == null) return null;
try
{
return UIUtilities.LoadSpriteRaw(UIUtilities.GetResource(meta.Assembly, meta.Manifest.IconPath));
}
catch (Exception e)
{
Logger.log.Error($"Error loading icon for {meta.Name}");
Logger.log.Error(e);
return null;
}
}
}
}

+ 9
- 2
IPA.Loader/Updating/BeatMods/Updater.cs View File

@ -76,6 +76,13 @@ namespace IPA.Updating.BeatMods
}
}
public static void ResetRequestCache()
{
requestCache.Clear();
modCache.Clear();
modVersionsCache.Clear();
}
private static readonly Dictionary<string, string> requestCache = new Dictionary<string, string>();
private static IEnumerator GetBeatModsEndpoint(string url, Ref<string> result)
{
@ -241,7 +248,7 @@ namespace IPA.Updating.BeatMods
onComplete?.Invoke(depList);
if (!ModListPresent && SelfConfig.SelfConfigRef.Value.Updates.AutoUpdate)
StartDownload(depList);
StartDownload(depList.Value);
}
internal IEnumerator ResolveDependencyRanges(Ref<List<DependencyObject>> list)
@ -388,7 +395,7 @@ namespace IPA.Updating.BeatMods
/// <param name="error"></param>
internal delegate void InstallFailed(DependencyObject obj, Exception error);
internal void StartDownload(List<DependencyObject> download, DownloadStart downloadStart = null,
internal void StartDownload(IEnumerable<DependencyObject> download, DownloadStart downloadStart = null,
DownloadProgress downloadProgress = null, DownloadFailed downloadFail = null, DownloadFinish downloadFinish = null,
InstallFailed installFail = null, InstallFinish installFinish = null)
{


+ 1
- 1
IPA/IPA.csproj View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>IPA</RootNamespace>
<AssemblyName>IPA</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile>
</TargetFrameworkProfile>


+ 1
- 1
IPA/app.config View File

@ -1,3 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup></configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/></startup></configuration>

+ 1
- 1
IPA/obj/Debug/IPA.csproj.CoreCompileInputs.cache View File

@ -1 +1 @@
b1078c6b90a14bd4bd5a5e7f04507fbb89e2b3a7
2ad64d7c1386fd6d6aca4a6ef5b1ea0e603afd9c

Loading…
Cancel
Save