Browse Source

Created API for plugin enabling and disabling

4.0.0-beta
Anairkoen Schno 4 years ago
parent
commit
a1ca6e8607
6 changed files with 92 additions and 122 deletions
  1. +1
    -0
      IPA.Loader/IPA.Loader.csproj
  2. +0
    -116
      IPA.Loader/Loader/PluginLoader.cs
  3. +10
    -1
      IPA.Loader/Loader/PluginManager.cs
  4. +0
    -5
      IPA.Loader/Loader/PluginMetadata.cs
  5. +79
    -0
      IPA.Loader/Loader/StateTransitionTransaction.cs
  6. +2
    -0
      IPA.Loader/PluginInterfaces/Attributes/PluginAttribute.cs

+ 1
- 0
IPA.Loader/IPA.Loader.csproj View File

@ -122,6 +122,7 @@
<Compile Include="Loader\PluginLoader.cs" /> <Compile Include="Loader\PluginLoader.cs" />
<Compile Include="Loader\PluginManifest.cs" /> <Compile Include="Loader\PluginManifest.cs" />
<Compile Include="Loader\PluginMetadata.cs" /> <Compile Include="Loader\PluginMetadata.cs" />
<Compile Include="Loader\StateTransitionTransaction.cs" />
<Compile Include="Logging\ConsoleWindow.cs" /> <Compile Include="Logging\ConsoleWindow.cs" />
<Compile Include="Logging\Printers\ColorlessConsolePrinter.cs" /> <Compile Include="Logging\Printers\ColorlessConsolePrinter.cs" />
<Compile Include="Logging\Printers\PluginSubLogPrinter.cs" /> <Compile Include="Logging\Printers\PluginSubLogPrinter.cs" />


+ 0
- 116
IPA.Loader/Loader/PluginLoader.cs View File

@ -177,7 +177,6 @@ namespace IPA.Loader
var rtOptionsValInt = (int)rtOptionsArg.Value; // `int` is the underlying type of RuntimeOptions var rtOptionsValInt = (int)rtOptionsArg.Value; // `int` is the underlying type of RuntimeOptions
meta.RuntimeOptions = (RuntimeOptions)rtOptionsValInt; meta.RuntimeOptions = (RuntimeOptions)rtOptionsValInt;
meta.IsAttributePlugin = true;
meta.PluginType = type; meta.PluginType = type;
return; return;
} }
@ -604,12 +603,6 @@ namespace IPA.Loader
if (meta.Manifest.GameVersion != UnityGame.GameVersion) if (meta.Manifest.GameVersion != UnityGame.GameVersion)
Logger.loader.Warn($"Mod {meta.Name} developed for game version {meta.Manifest.GameVersion}, so it may not work properly."); Logger.loader.Warn($"Mod {meta.Name} developed for game version {meta.Manifest.GameVersion}, so it may not work properly.");
if (!meta.IsAttributePlugin)
{
ignoredPlugins.Add(meta, new IgnoreReason(Reason.Unsupported) { ReasonText = "Non-attribute plugins are currently not supported" });
return null;
}
if (meta.IsSelf) if (meta.IsSelf)
return new PluginExecutor(meta, true); return new PluginExecutor(meta, true);
@ -708,115 +701,6 @@ namespace IPA.Loader
} }
return exec; return exec;
#region Interface plugin support
/*if (meta.IsSelf)
return new PluginInfo()
{
Metadata = meta,
Plugin = null
};
var info = new PluginInfo();
try
{
foreach (var dep in meta.Dependencies)
{
if (alreadyLoaded.Contains(dep)) continue;
// otherwise...
if (ignoredPlugins.TryGetValue(dep, out var reason))
{ // was added to the ignore list
ignoredPlugins.Add(meta, new IgnoreReason(Reason.Dependency)
{
ReasonText = $"Dependency was ignored at load time: {reason.ReasonText}",
RelatedTo = dep
});
}
else
{ // was not added to ignore list
ignoredPlugins.Add(meta, new IgnoreReason(Reason.Dependency)
{
ReasonText = $"Dependency was not already loaded at load time, but was also not ignored",
RelatedTo = dep
});
}
return null;
}
Load(meta);
Feature denyingFeature = null;
if (!meta.Features.All(f => (denyingFeature = f).BeforeLoad(meta)))
{
Logger.loader.Warn(
$"Feature {denyingFeature?.GetType()} denied plugin {meta.Name} from loading! {denyingFeature?.InvalidMessage}");
ignoredPlugins.Add(meta, new IgnoreReason(Reason.Feature)
{
ReasonText = $"Denied in {nameof(Feature.BeforeLoad)} of feature {denyingFeature?.GetType()}:\n\t{denyingFeature?.InvalidMessage}"
});
return null;
}
var type = meta.Assembly.GetType(meta.PluginType.FullName);
var instance = Activator.CreateInstance(type) as IPlugin;
info.Metadata = meta;
info.Plugin = instance;
var init = type.GetMethod("Init", BindingFlags.Instance | BindingFlags.Public);
if (init != null)
{
denyingFeature = null;
if (!meta.Features.All(f => (denyingFeature = f).BeforeInit(info)))
{
Logger.loader.Warn(
$"Feature {denyingFeature?.GetType()} denied plugin {meta.Name} from initializing! {denyingFeature?.InvalidMessage}");
ignoredPlugins.Add(meta, new IgnoreReason(Reason.Feature)
{
ReasonText = $"Denied in {nameof(Feature.BeforeInit)} of feature {denyingFeature?.GetType()}:\n\t{denyingFeature?.InvalidMessage}"
});
return null;
}
var args = PluginInitInjector.Inject(init.GetParameters(), meta);
init.Invoke(info.Plugin, args);
}
foreach (var feature in meta.Features)
try
{
feature.AfterInit(info, info.Plugin);
}
catch (Exception e)
{
Logger.loader.Critical($"Feature errored in {nameof(Feature.AfterInit)}: {e}");
}
}
catch (AmbiguousMatchException)
{
Logger.loader.Critical($"Only one Init allowed per plugin (ambiguous match in {meta.Name})");
Logger.loader.Critical("@Developer: you *really* should fix this");
// not adding to ignoredPlugins here because this should only happen in a development context
// if someone fucks this up on release thats on them
return null;
}
catch (Exception e)
{
Logger.loader.Error($"Could not init plugin {meta.Name}: {e}");
ignoredPlugins.Add(meta, new IgnoreReason(Reason.Error)
{
ReasonText = "Error ocurred while initializing",
Error = e
});
return null;
}
return info;*/
#endregion
} }
internal static List<PluginExecutor> LoadPlugins() internal static List<PluginExecutor> LoadPlugins()


+ 10
- 1
IPA.Loader/Loader/PluginManager.cs View File

@ -14,6 +14,7 @@ using UnityEngine;
using Logger = IPA.Logging.Logger; using Logger = IPA.Logging.Logger;
using static IPA.Loader.PluginLoader; using static IPA.Loader.PluginLoader;
using IPA.Loader.Features; using IPA.Loader.Features;
using System.Threading.Tasks;
#if NET3 #if NET3
using Net3_Proxy; using Net3_Proxy;
using Path = Net3_Proxy.Path; using Path = Net3_Proxy.Path;
@ -65,6 +66,14 @@ namespace IPA.Loader
public static PluginMetadata GetDisabledPluginFromId(string name) => public static PluginMetadata GetDisabledPluginFromId(string name) =>
DisabledPlugins.FirstOrDefault(p => p.Id == name); DisabledPlugins.FirstOrDefault(p => p.Id == name);
public static StateTransitionTransaction PluginStateTransaction()
=> new StateTransitionTransaction(AllPlugins, DisabledPlugins);
internal static Task CommitTransaction(StateTransitionTransaction transaction)
{
throw new NotImplementedException();
}
// TODO: rewrite below // TODO: rewrite below
/* /*
/// <summary> /// <summary>
@ -251,7 +260,7 @@ namespace IPA.Loader
public static event PluginDisableDelegate PluginDisabled; public static event PluginDisableDelegate PluginDisabled;
/// <summary> /// <summary>
/// Gets a list of all BSIPA plugins.
/// Gets a list of all enabled BSIPA plugins.
/// </summary> /// </summary>
/// <value>a collection of all enabled plugins as <see cref="PluginMetadata"/>s</value> /// <value>a collection of all enabled plugins as <see cref="PluginMetadata"/>s</value>
public static IEnumerable<PluginMetadata> AllPlugins => BSMetas.Select(p => p.Metadata); public static IEnumerable<PluginMetadata> AllPlugins => BSMetas.Select(p => p.Metadata);


+ 0
- 5
IPA.Loader/Loader/PluginMetadata.cs View File

@ -89,11 +89,6 @@ namespace IPA.Loader
/// The <see cref="IPA.RuntimeOptions"/> that the plugin specified in its <see cref="PluginAttribute"/>. /// The <see cref="IPA.RuntimeOptions"/> that the plugin specified in its <see cref="PluginAttribute"/>.
/// </summary> /// </summary>
public RuntimeOptions RuntimeOptions { get; internal set; } public RuntimeOptions RuntimeOptions { get; internal set; }
/// <summary>
/// Whether the plugin referred to by this metadata object is a BSIPA 4 attribute plugin.
/// </summary>
/// <value><see langword="true"/> if the plugin is a BSIPA 4 plugin, <see langword="false"/> if it is a BSIPA 3 plugin.</value>
public bool IsAttributePlugin { get; internal set; } = false;
/// <summary> /// <summary>
/// Gets all of the metadata as a readable string. /// Gets all of the metadata as a readable string.


+ 79
- 0
IPA.Loader/Loader/StateTransitionTransaction.cs View File

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IPA.Loader
{
public sealed class StateTransitionTransaction : IDisposable
{
private readonly HashSet<PluginMetadata> currentlyEnabled;
private readonly HashSet<PluginMetadata> currentlyDisabled;
private readonly HashSet<PluginMetadata> toEnable = new HashSet<PluginMetadata>();
private readonly HashSet<PluginMetadata> toDisable = new HashSet<PluginMetadata>();
internal StateTransitionTransaction(IEnumerable<PluginMetadata> enabled, IEnumerable<PluginMetadata> disabled)
{
currentlyEnabled = new HashSet<PluginMetadata>(enabled.ToArray());
currentlyDisabled = new HashSet<PluginMetadata>(disabled.ToArray());
}
public bool WillNeedRestart => toEnable.Concat(toDisable).Any(m => m.RuntimeOptions != RuntimeOptions.DynamicInit);
internal IEnumerable<PluginMetadata> ToEnable => toEnable;
internal IEnumerable<PluginMetadata> ToDisable => toDisable;
public IEnumerable<PluginMetadata> EnabledPlugins => currentlyEnabled.Except(toDisable).Concat(toEnable);
public IEnumerable<PluginMetadata> DisabledPlugins => currentlyDisabled.Except(toEnable).Concat(toDisable);
public bool IsEnabled(PluginMetadata meta)
=> ThrowIfDisposed<bool>()
|| (currentlyEnabled.Contains(meta) && !toDisable.Contains(meta))
|| toEnable.Contains(meta);
public bool IsDisabled(PluginMetadata meta)
=> ThrowIfDisposed<bool>()
|| (currentlyDisabled.Contains(meta) && !toEnable.Contains(meta))
|| toDisable.Contains(meta);
public bool Enable(PluginMetadata meta)
{ // returns whether or not state was changed
ThrowIfDisposed();
if (!currentlyEnabled.Contains(meta) && !currentlyDisabled.Contains(meta))
throw new ArgumentException(nameof(meta), "Plugin metadata does not represent a loadable plugin");
if (toEnable.Contains(meta)) return false;
if (currentlyEnabled.Contains(meta) && !toDisable.Contains(meta)) return false;
toDisable.Remove(meta);
toEnable.Add(meta);
return true;
}
public bool Disable(PluginMetadata meta)
{ // returns whether or not state was changed
ThrowIfDisposed();
if (!currentlyEnabled.Contains(meta) && !currentlyDisabled.Contains(meta))
throw new ArgumentException(nameof(meta), "Plugin metadata does not represent a ");
if (toEnable.Contains(meta)) return false;
if (currentlyEnabled.Contains(meta) && !toDisable.Contains(meta)) return false;
toDisable.Remove(meta);
toEnable.Add(meta);
return true;
}
public Task Commit() => PluginManager.CommitTransaction(this);
private void ThrowIfDisposed() => ThrowIfDisposed<byte>();
private T ThrowIfDisposed<T>()
{
if (disposed)
throw new ObjectDisposedException(nameof(StateTransitionTransaction));
return default;
}
private bool disposed = false;
public void Dispose()
=> disposed = true;
}
}

+ 2
- 0
IPA.Loader/PluginInterfaces/Attributes/PluginAttribute.cs View File

@ -50,6 +50,7 @@ namespace IPA
/// With this option set, whether or not the plugin is disabled during a given run is constant for that entire run. /// With this option set, whether or not the plugin is disabled during a given run is constant for that entire run.
/// </para> /// </para>
/// </summary> /// </summary>
// enabled exactly once and never disabled
SingleStartInit, SingleStartInit,
/// <summary> /// <summary>
/// <para> /// <para>
@ -69,6 +70,7 @@ namespace IPA
/// re-used for subsequent enables. The plugin is expected to handle this gracefully, and behave in a way that makes sense. /// re-used for subsequent enables. The plugin is expected to handle this gracefully, and behave in a way that makes sense.
/// </para> /// </para>
/// </summary> /// </summary>
// both enabled and disabled at runtime
DynamicInit DynamicInit
} }


Loading…
Cancel
Save