Browse Source

Added full support for disabling and enabling mods

Disablable plugins also fully supported
pull/12/head
Anairkoen Schno 5 years ago
parent
commit
c032d2b84e
8 changed files with 169 additions and 40 deletions
  1. +1
    -1
      BSIPA-ModList/UI/ModListFlowCoordinator.cs
  2. +15
    -15
      BSIPA-ModList/UI/ViewControllers/ModCells.cs
  3. +5
    -3
      BSIPA-ModList/UI/ViewControllers/ModListController.cs
  4. +1
    -1
      BSIPA-ModList/manifest.json
  5. +28
    -3
      IPA.Loader/Loader/PluginLoader.cs
  6. +92
    -14
      IPA.Loader/Loader/PluginManager.cs
  7. +1
    -1
      IPA.Loader/PluginInterfaces/BeatSaber/IDisablablePlugin.cs
  8. +26
    -2
      IPA.Loader/Updating/BeatMods/Updater.cs

+ 1
- 1
BSIPA-ModList/UI/ModListFlowCoordinator.cs View File

@ -28,7 +28,7 @@ namespace BSIPA_ModList.UI
navigationController.didFinishEvent += backButton_DidFinish; navigationController.didFinishEvent += backButton_DidFinish;
modList = BeatSaberUI.CreateViewController<ModListController>(); modList = BeatSaberUI.CreateViewController<ModListController>();
modList.Init(this, PluginManager.AllPlugins, PluginLoader.ignoredPlugins, PluginManager.Plugins);
modList.Init(this, PluginManager.AllPlugins.Select(p => p.Metadata).Concat(PluginManager.DisabledPlugins), PluginLoader.ignoredPlugins, PluginManager.Plugins);
settings = SettingsViewController.Create(); settings = SettingsViewController.Create();


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

@ -19,11 +19,11 @@ namespace BSIPA_ModList.UI.ViewControllers
internal class BSIPAModCell : CustomCellInfo, IClickableCell internal class BSIPAModCell : CustomCellInfo, IClickableCell
{ {
internal PluginLoader.PluginInfo Plugin;
internal PluginLoader.PluginMetadata Plugin;
private ModListController list; private ModListController list;
public BSIPAModCell(ModListController list, PluginLoader.PluginInfo plugin)
: base($"{plugin.Metadata.Name} <size=60%>v{plugin.Metadata.Version}", plugin.Metadata.Manifest.Author, null)
public BSIPAModCell(ModListController list, PluginLoader.PluginMetadata plugin)
: base($"{plugin.Name} <size=60%>v{plugin.Version}", plugin.Manifest.Author, null)
{ {
Plugin = plugin; Plugin = plugin;
this.list = list; this.list = list;
@ -31,24 +31,24 @@ namespace BSIPA_ModList.UI.ViewControllers
if (string.IsNullOrWhiteSpace(subtext)) if (string.IsNullOrWhiteSpace(subtext))
subtext = "<color=#BFBFBF><i>Unspecified Author</i>"; subtext = "<color=#BFBFBF><i>Unspecified Author</i>";
icon = plugin.Metadata.GetIcon();
icon = plugin.GetIcon();
} }
private ModInfoViewController infoView; private ModInfoViewController infoView;
public void OnSelect(ModListController cntrl) public void OnSelect(ModListController cntrl)
{ {
Logger.log.Debug($"Selected BSIPAModCell {Plugin.Metadata.Name} {Plugin.Metadata.Version}");
Logger.log.Debug($"Selected BSIPAModCell {Plugin.Name} {Plugin.Version}");
if (infoView == null) if (infoView == null)
{ {
var desc = Plugin.Metadata.Manifest.Description;
var desc = Plugin.Manifest.Description;
if (string.IsNullOrWhiteSpace(desc)) if (string.IsNullOrWhiteSpace(desc))
desc = "<color=#BFBFBF><i>No description</i>"; desc = "<color=#BFBFBF><i>No description</i>";
infoView = BeatSaberUI.CreateViewController<ModInfoViewController>(); infoView = BeatSaberUI.CreateViewController<ModInfoViewController>();
infoView.Init(icon, Plugin.Metadata.Name, "v" + Plugin.Metadata.Version.ToString(), subtext,
desc, Plugin.Metadata, Plugin.Metadata.Manifest.Links);
infoView.Init(icon, Plugin.Name, "v" + Plugin.Version.ToString(), subtext,
desc, Plugin, Plugin.Manifest.Links);
} }
list.flow.SetSelected(infoView, immediate: list.flow.HasSelected); list.flow.SetSelected(infoView, immediate: list.flow.HasSelected);
@ -99,11 +99,11 @@ namespace BSIPA_ModList.UI.ViewControllers
} }
internal class LibraryModCell : CustomCellInfo, IClickableCell internal class LibraryModCell : CustomCellInfo, IClickableCell
{ {
internal PluginLoader.PluginInfo Plugin;
internal PluginLoader.PluginMetadata Plugin;
private ModListController list; private ModListController list;
public LibraryModCell(ModListController list, PluginLoader.PluginInfo plugin)
: base($"{plugin.Metadata.Name} <size=60%>v{plugin.Metadata.Version}", plugin.Metadata.Manifest.Author, null)
public LibraryModCell(ModListController list, PluginLoader.PluginMetadata plugin)
: base($"{plugin.Name} <size=60%>v{plugin.Version}", plugin.Manifest.Author, null)
{ {
Plugin = plugin; Plugin = plugin;
this.list = list; this.list = list;
@ -118,17 +118,17 @@ namespace BSIPA_ModList.UI.ViewControllers
public void OnSelect(ModListController cntrl) public void OnSelect(ModListController cntrl)
{ {
Logger.log.Debug($"Selected LibraryModCell {Plugin.Metadata.Name} {Plugin.Metadata.Version}");
Logger.log.Debug($"Selected LibraryModCell {Plugin.Name} {Plugin.Version}");
if (infoView == null) if (infoView == null)
{ {
var desc = Plugin.Metadata.Manifest.Description;
var desc = Plugin.Manifest.Description;
if (string.IsNullOrWhiteSpace(desc)) if (string.IsNullOrWhiteSpace(desc))
desc = "<color=#BFBFBF><i>No description</i>"; desc = "<color=#BFBFBF><i>No description</i>";
infoView = BeatSaberUI.CreateViewController<ModInfoViewController>(); infoView = BeatSaberUI.CreateViewController<ModInfoViewController>();
infoView.Init(icon, Plugin.Metadata.Name, "v" + Plugin.Metadata.Version.ToString(), subtext,
desc, Plugin.Metadata, Plugin.Metadata.Manifest.Links);
infoView.Init(icon, Plugin.Name, "v" + Plugin.Version.ToString(), subtext,
desc, Plugin, Plugin.Manifest.Links);
} }
list.flow.SetSelected(infoView, immediate: list.flow.HasSelected); list.flow.SetSelected(infoView, immediate: list.flow.HasSelected);


+ 5
- 3
BSIPA-ModList/UI/ViewControllers/ModListController.cs View File

@ -28,8 +28,10 @@ namespace BSIPA_ModList.UI
internal ModListFlowCoordinator flow; internal ModListFlowCoordinator flow;
#pragma warning disable CS0618 #pragma warning disable CS0618
public void Init(ModListFlowCoordinator flow, IEnumerable<PluginLoader.PluginInfo> bsipaPlugins, IEnumerable<PluginLoader.PluginMetadata> ignoredPlugins, IEnumerable<IPlugin> ipaPlugins)
public void Init(ModListFlowCoordinator flow, IEnumerable<PluginLoader.PluginMetadata> bsipaPlugins, IEnumerable<PluginLoader.PluginMetadata> ignoredPlugins, IEnumerable<IPlugin> ipaPlugins)
{ {
Data.Clear();
Logger.log.Debug("List Controller Init"); Logger.log.Debug("List Controller Init");
DidActivateEvent = DidActivate; DidActivateEvent = DidActivate;
@ -43,11 +45,11 @@ namespace BSIPA_ModList.UI
reuseIdentifier = "BSIPAModListTableCell"; reuseIdentifier = "BSIPAModListTableCell";
foreach (var plugin in bsipaPlugins.Where(p => !p.Metadata.IsBare))
foreach (var plugin in bsipaPlugins.Where(p => !p.IsBare))
Data.Add(new BSIPAModCell(this, plugin)); Data.Add(new BSIPAModCell(this, plugin));
foreach (var plugin in ignoredPlugins) foreach (var plugin in ignoredPlugins)
Data.Add(new BSIPAIgnoredModCell(this, plugin)); Data.Add(new BSIPAIgnoredModCell(this, plugin));
foreach (var plugin in bsipaPlugins.Where(p => p.Metadata.IsBare))
foreach (var plugin in bsipaPlugins.Where(p => p.IsBare))
Data.Add(new LibraryModCell(this, plugin)); Data.Add(new LibraryModCell(this, plugin));
foreach (var plugin in ipaPlugins) foreach (var plugin in ipaPlugins)
Data.Add(new IPAModCell(this, plugin)); Data.Add(new IPAModCell(this, plugin));


+ 1
- 1
BSIPA-ModList/manifest.json View File

@ -14,7 +14,7 @@
"version": "1.2.0", "version": "1.2.0",
"icon": "BSIPA_ModList.Icons.self.png", "icon": "BSIPA_ModList.Icons.self.png",
"dependsOn": { "dependsOn": {
"BSIPA": "^3.12.16",
"BSIPA": "^3.12.18",
"CustomUI": "^1.5.4" "CustomUI": "^1.5.4"
}, },
"features": [], "features": [],


+ 28
- 3
IPA.Loader/Loader/PluginLoader.cs View File

@ -108,6 +108,7 @@ namespace IPA.Loader
} }
internal static List<PluginMetadata> PluginsMetadata = new List<PluginMetadata>(); internal static List<PluginMetadata> PluginsMetadata = new List<PluginMetadata>();
internal static List<PluginMetadata> DisabledPlugins = new List<PluginMetadata>();
private static readonly Regex embeddedTextDescriptionPattern = new Regex(@"#!\[(.+)\]", RegexOptions.Compiled | RegexOptions.Singleline); private static readonly Regex embeddedTextDescriptionPattern = new Regex(@"#!\[(.+)\]", RegexOptions.Compiled | RegexOptions.Singleline);
@ -363,7 +364,7 @@ namespace IPA.Loader
foreach (var meta in PluginsMetadata) foreach (var meta in PluginsMetadata)
{ {
if (disabled.Contains(meta.Id ?? meta.Name)) if (disabled.Contains(meta.Id ?? meta.Name))
ignoredPlugins.Add(meta);
DisabledPlugins.Add(meta);
else else
enabled.Add(meta); enabled.Add(meta);
} }
@ -519,10 +520,23 @@ namespace IPA.Loader
} }
} }
internal static void ReleaseAll()
internal static void ReleaseAll(bool full = false)
{ {
ignoredPlugins = new HashSet<PluginMetadata>();
if (full)
ignoredPlugins = new HashSet<PluginMetadata>();
else
{
foreach (var m in PluginsMetadata)
ignoredPlugins.Add(m);
foreach (var m in ignoredPlugins)
{ // clean them up so we can still use the metadata for updates
m.InternalFeatures.Clear();
m.PluginType = null;
m.Assembly = null;
}
}
PluginsMetadata = new List<PluginMetadata>(); PluginsMetadata = new List<PluginMetadata>();
DisabledPlugins = new List<PluginMetadata>();
Feature.Reset(); Feature.Reset();
GC.Collect(); GC.Collect();
} }
@ -590,6 +604,17 @@ namespace IPA.Loader
{ {
Logger.loader.Critical($"Feature errored in {nameof(Feature.AfterInit)}: {e}"); Logger.loader.Critical($"Feature errored in {nameof(Feature.AfterInit)}: {e}");
} }
if (instance is IDisablablePlugin disable)
try
{
disable.OnEnable();
}
catch (Exception e)
{
Logger.loader.Error($"Error occurred trying to enable {meta.Name}");
Logger.loader.Error(e);
}
} }
catch (AmbiguousMatchException) catch (AmbiguousMatchException)
{ {


+ 92
- 14
IPA.Loader/Loader/PluginManager.cs View File

@ -59,36 +59,114 @@ namespace IPA.Loader
} }
/// <summary> /// <summary>
/// Disables a plugin for the next time the game starts.
/// Gets a disabled plugin's metadata by its name.
/// </summary> /// </summary>
/// <param name="plugin">the plugin to disable</param>
public static void DisablePlugin(PluginInfo plugin) =>
DisablePlugin(plugin.Metadata.Id ?? plugin.Metadata.Name);
/// <param name="name">the name of the disabled plugin to get</param>
/// <returns>the metadata for the corresponding plugin</returns>
public static PluginMetadata GetDisabledPlugin(string name) =>
DisabledPlugins.FirstOrDefault(p => p.Name == name);
/// <summary> /// <summary>
/// Disables a plugin for the next time the game starts.
/// Gets a disabled plugin's metadata by its ID.
/// </summary> /// </summary>
/// <param name="pluginId">the ID, or name if the ID is null, of the plugin to disable</param>
public static void DisablePlugin(string pluginId)
/// <param name="name">the ID of the disabled plugin to get</param>
/// <returns>the metadata for the corresponding plugin</returns>
public static PluginMetadata GetDisabledPluginFromId(string name) =>
DisabledPlugins.FirstOrDefault(p => p.Id == name);
/// <summary>
/// Disables a plugin.
/// </summary>
/// <param name="plugin">the plugin to disable</param>
/// <returns>whether or not it needs a restart to enable</returns>
public static bool DisablePlugin(PluginInfo plugin)
{ {
DisabledConfig.Ref.Value.DisabledModIds.Add(pluginId);
if (plugin == null) return false;
DisabledConfig.Ref.Value.DisabledModIds.Add(plugin.Metadata.Id ?? plugin.Metadata.Name);
DisabledConfig.Provider.Store(DisabledConfig.Ref.Value);
if (plugin.Plugin is IDisablablePlugin disable)
{
try
{
disable.OnDisable();
}
catch (Exception e)
{
Logger.loader.Error($"Error occurred trying to disable {plugin.Metadata.Name}");
Logger.loader.Error(e);
}
runtimeDisabled.Add(plugin);
_bsPlugins.Remove(plugin);
return false;
}
return true;
} }
/// <summary>
/// Disables a plugin.
/// </summary>
/// <param name="pluginId">the ID, or name if the ID is null, of the plugin to disable</param>
/// <returns>whether a restart is needed to activate</returns>
public static bool DisablePlugin(string pluginId) => DisablePlugin(GetPluginFromId(pluginId) ?? GetPlugin(pluginId));
/// <summary> /// <summary>
/// Enables a plugin that had been previously disabled. /// Enables a plugin that had been previously disabled.
/// </summary> /// </summary>
/// <param name="plugin">the plugin to enable</param> /// <param name="plugin">the plugin to enable</param>
public static void EnablePlugin(PluginMetadata plugin) =>
EnablePlugin(plugin.Id ?? plugin.Name);
/// <returns>whether a restart is needed to activate</returns>
public static bool EnablePlugin(PluginMetadata plugin)
{
if (plugin == null) return false;
DisabledConfig.Ref.Value.DisabledModIds.Remove(plugin.Id ?? plugin.Name);
DisabledConfig.Provider.Store(DisabledConfig.Ref.Value);
var needsRestart = true;
var runtimeInfo = runtimeDisabled.FirstOrDefault(p => p.Metadata == plugin);
if (runtimeInfo != null && runtimeInfo.Plugin is IDisablablePlugin disable)
{
runtimeDisabled.Remove(runtimeInfo);
try
{
disable.OnEnable();
}
catch (Exception e)
{
Logger.loader.Error($"Error occurred trying to enable {plugin.Name}");
Logger.loader.Error(e);
}
needsRestart = false;
}
else
{
PluginLoader.DisabledPlugins.Remove(plugin);
runtimeInfo = InitPlugin(plugin);
}
_bsPlugins.Add(runtimeInfo);
return needsRestart;
}
/// <summary> /// <summary>
/// Enables a plugin that had been previously disabled. /// Enables a plugin that had been previously disabled.
/// </summary> /// </summary>
/// <param name="pluginId">the ID, or name if the ID is null, of the plugin to enable</param> /// <param name="pluginId">the ID, or name if the ID is null, of the plugin to enable</param>
public static void EnablePlugin(string pluginId)
{
DisabledConfig.Ref.Value.DisabledModIds.Remove(pluginId);
}
/// <returns>whether a restart is needed to activate</returns>
public static bool EnablePlugin(string pluginId) =>
EnablePlugin(GetDisabledPluginFromId(pluginId) ?? GetDisabledPlugin(pluginId));
private static readonly List<PluginInfo> runtimeDisabled = new List<PluginInfo>();
/// <summary>
/// Gets a list of disabled BSIPA plugins.
/// </summary>
public static IEnumerable<PluginMetadata> DisabledPlugins => PluginLoader.DisabledPlugins.Concat(runtimeDisabled.Select(p => p.Metadata));
/// <summary> /// <summary>
/// Gets a list of all BSIPA plugins. /// Gets a list of all BSIPA plugins.


+ 1
- 1
IPA.Loader/PluginInterfaces/BeatSaber/IDisablablePlugin.cs View File

@ -6,7 +6,7 @@
public interface IDisablablePlugin public interface IDisablablePlugin
{ {
/// <summary> /// <summary>
/// Called when a plugin is enabled. This is where you shhould set up Harmony patches and the like.
/// Called when a plugin is enabled. This is where you should set up Harmony patches and the like.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This will be called after Init, and will be called when the plugin loads normally too. /// This will be called after Init, and will be called when the plugin loads normally too.


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

@ -205,8 +205,8 @@ namespace IPA.Updating.BeatMods
} }
} }
foreach (var meta in PluginLoader.ignoredPlugins.Where(m => m.Id != null))
{
foreach (var meta in PluginLoader.ignoredPlugins)
{ // update ignored
if (meta.Id != null) if (meta.Id != null)
{ // updatable { // updatable
var dep = new DependencyObject var dep = new DependencyObject
@ -230,6 +230,30 @@ namespace IPA.Updating.BeatMods
} }
} }
foreach (var meta in DisabledPlugins)
{ // update ignored
if (meta.Id != null)
{ // updatable
var dep = new DependencyObject
{
Name = meta.Id,
Version = meta.Version,
Requirement = new Range($">={meta.Version}"),
LocalPluginMeta = new PluginLoader.PluginInfo
{
Metadata = meta,
Plugin = null
}
};
if (meta.Features.FirstOrDefault(f => f is NoUpdateFeature) != null)
{ // disable updating, by only matching self
dep.Requirement = new Range(meta.Version.ToString());
}
depList.Value.Add(dep);
}
}
#pragma warning disable CS0618 // Type or member is obsolete #pragma warning disable CS0618 // Type or member is obsolete
foreach (var plug in Plugins) foreach (var plug in Plugins)


Loading…
Cancel
Save