Browse Source

Added single plugin load function

Redid config system to be more extensible and used internally
pull/1/head
Anairkoen Schno 5 years ago
parent
commit
01e22a775a
17 changed files with 425 additions and 107 deletions
  1. +1
    -1
      Doorstop
  2. +2
    -3
      IPA.Injector/Backups/BackupUnit.cs
  3. +4
    -1
      IPA.Injector/Injector.cs
  4. +235
    -0
      IPA.Loader/Config/Config.cs
  5. +17
    -6
      IPA.Loader/Config/ConfigProviders/JsonConfigProvider.cs
  6. +5
    -1
      IPA.Loader/Config/IConfigProvider.cs
  7. +48
    -0
      IPA.Loader/Config/SelfConfig.cs
  8. +2
    -0
      IPA.Loader/IPA.Loader.csproj
  9. +3
    -3
      IPA.Loader/Loader/Composite/CompositeBSPlugin.cs
  10. +2
    -2
      IPA.Loader/Loader/Composite/CompositeIPAPlugin.cs
  11. +7
    -51
      IPA.Loader/Loader/PluginComponent.cs
  12. +73
    -10
      IPA.Loader/Loader/PluginLoader.cs
  13. +2
    -9
      IPA.Loader/Loader/PluginManager.cs
  14. +5
    -0
      IPA.Loader/Logging/Logger.cs
  15. +8
    -13
      IPA.Loader/Logging/StandardLogger.cs
  16. +7
    -7
      IPA.Loader/Updating/SelfPlugin.cs
  17. +4
    -0
      IPA.Loader/Utilities/BeatSaber.cs

+ 1
- 1
Doorstop

@ -1 +1 @@
Subproject commit 055301d4402532f60957f0e7745359aa78962a46
Subproject commit 92da7bf4fbea717040bf4b1bb2d4e07af12bf861

+ 2
- 3
IPA.Injector/Backups/BackupUnit.cs View File

@ -1,5 +1,4 @@
using IPA.Utilities;
using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -68,7 +67,7 @@ namespace IPA.Injector.Backups
/// <param name="file"></param> /// <param name="file"></param>
public void Add(FileInfo file) public void Add(FileInfo file)
{ {
var relativePath = LoneFunctions.GetRelativePath(file.FullName, Environment.CurrentDirectory);
var relativePath = Utilities.LoneFunctions.GetRelativePath(file.FullName, Environment.CurrentDirectory);
var backupPath = new FileInfo(Path.Combine(_backupPath.FullName, relativePath)); var backupPath = new FileInfo(Path.Combine(_backupPath.FullName, relativePath));
// Copy over // Copy over


+ 4
- 1
IPA.Injector/Injector.cs View File

@ -8,6 +8,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using IPA.Config;
using UnityEngine; using UnityEngine;
using static IPA.Logging.Logger; using static IPA.Logging.Logger;
using MethodAttributes = Mono.Cecil.MethodAttributes; using MethodAttributes = Mono.Cecil.MethodAttributes;
@ -34,6 +35,8 @@ namespace IPA.Injector
SetupLibraryLoading(); SetupLibraryLoading();
SelfConfig.Set();
loader.Debug("Prepping bootstrapper"); loader.Debug("Prepping bootstrapper");
InstallBootstrapPatch(); InstallBootstrapPatch();
@ -184,7 +187,7 @@ namespace IPA.Injector
pluginAsyncLoadTask.Wait(); pluginAsyncLoadTask.Wait();
log.Debug("Plugins loaded"); log.Debug("Plugins loaded");
log.Debug(string.Join(", ", PluginLoader.PluginsMetadata)); log.Debug(string.Join(", ", PluginLoader.PluginsMetadata));
//PluginComponent.Create();
PluginComponent.Create();
} }
} }
} }

+ 235
- 0
IPA.Loader/Config/Config.cs View File

@ -0,0 +1,235 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using IPA.Config.ConfigProviders;
using IPA.Utilities;
namespace IPA.Config
{
/// <summary>
/// A class to handle updating ConfigProviders automatically
/// </summary>
public static class Config
{
static Config()
{
JsonConfigProvider.RegisterConfig();
}
/// <inheritdoc />
/// <summary>
/// Defines the type of the <see cref="T:IPA.Config.IConfigProvider" />
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class TypeAttribute : Attribute
{
/// <summary>
/// The extension associated with this type, without the '.'
/// </summary>
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public string Extension { get; private set; }
/// <inheritdoc />
/// <summary>
/// Constructs the attribute with a specified extension.
/// </summary>
/// <param name="ext">the extension associated with this type, without the '.'</param>
public TypeAttribute(string ext)
{
Extension = ext;
}
}
/// <inheritdoc />
/// <summary>
/// Specifies that a particular parameter is preferred to be a specific type of <see cref="T:IPA.Config.IConfigProvider" />. If it is not available, also specifies backups. If none are available, the default is used.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public class PreferAttribute : Attribute
{
/// <summary>
/// The order of preference for the config type.
/// </summary>
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public string[] PreferenceOrder { get; private set; }
/// <inheritdoc />
/// <summary>
/// Constructs the attribute with a specific preference list. Each entry is the extension without a '.'
/// </summary>
/// <param name="preference">The preferences in order of preference.</param>
public PreferAttribute(params string[] preference)
{
PreferenceOrder = preference;
}
}
private static readonly Dictionary<string, Type> registeredProviders = new Dictionary<string, Type>();
/// <summary>
/// Registers a <see cref="IConfigProvider"/> to use for configs.
/// </summary>
/// <typeparam name="T">the type to register</typeparam>
public static void Register<T>() where T : IConfigProvider => Register(typeof(T));
/// <summary>
/// Registers a <see cref="IConfigProvider"/> to use for configs.
/// </summary>
/// <param name="type">the type to register</param>
public static void Register(Type type)
{
if (!(type.GetCustomAttribute(typeof(TypeAttribute)) is TypeAttribute ext))
throw new InvalidOperationException("Type does not have TypeAttribute");
if (!typeof(IConfigProvider).IsAssignableFrom(type))
throw new InvalidOperationException("Type not IConfigProvider");
if (registeredProviders.ContainsKey(ext.Extension))
throw new InvalidOperationException($"Extension provider for {ext.Extension} already exists");
registeredProviders.Add(ext.Extension, type);
}
private static SortedList<Ref<DateTime>, IConfigProvider> configProviders = new SortedList<Ref<DateTime>, IConfigProvider>();
/// <summary>
/// Gets an <see cref="IConfigProvider"/> using the specified list pf preferred config types.
/// </summary>
/// <param name="filename">the name of the file to associate it with</param>
/// <param name="extensions">the preferred config types to try to get</param>
/// <returns>an <see cref="IConfigProvider"/> of the requested type, or of type JSON.</returns>
public static IConfigProvider GetProviderFor(string filename, params string[] extensions)
{
var chosenExt = extensions.FirstOrDefault(s => registeredProviders.ContainsKey(s)) ?? "json";
var type = registeredProviders[chosenExt];
var provider = Activator.CreateInstance(type) as IConfigProvider;
if (provider != null)
{
provider.Filename = filename;
configProviders.Add(provider.LastModified, provider);
}
return provider;
}
/// <summary>
/// Gets an <see cref="IConfigProvider"/> using the specified list pf preferred config types.
/// </summary>
/// <param name="filename">the name of the file to associate it with</param>
/// <param name="info">the parameter info to try and get info for</param>
/// <returns>an <see cref="IConfigProvider"/> of the requested type, or of type JSON.</returns>
public static IConfigProvider GetProviderFor(string filename, ParameterInfo info)
{
var prefs = new string[0];
if (info.GetCustomAttribute(typeof(PreferAttribute)) is PreferAttribute prefer)
prefs = prefer.PreferenceOrder;
return GetProviderFor(filename, prefs);
}
private static SortedDictionary<IConfigProvider, Action> linkedProviders =
new SortedDictionary<IConfigProvider, Action>();
/// <summary>
/// Creates a linked <see cref="Ref{T}"/> for the config provider. This <see cref="Ref{T}"/> will be automatically updated whenever the file on-disk changes.
/// </summary>
/// <typeparam name="T">the type of the parsed value</typeparam>
/// <param name="config">the <see cref="IConfigProvider"/> to create a link to</param>
/// <param name="onChange">an action to perform on value change</param>
/// <returns>a <see cref="Ref{T}"/> to an ever-changing value, mirroring whatever the file contains.</returns>
public static Ref<T> MakeLink<T>(this IConfigProvider config, Action<IConfigProvider, Ref<T>> onChange = null)
{
Ref<T> @ref = config.Parse<T>();
void ChangeDelegate()
{
@ref.Value = config.Parse<T>();
onChange?.Invoke(config, @ref);
}
if (linkedProviders.ContainsKey(config))
linkedProviders[config] = (Action) Delegate.Combine(linkedProviders[config], (Action) ChangeDelegate);
else
linkedProviders.Add(config, ChangeDelegate);
ChangeDelegate();
return @ref;
}
/// <summary>
/// Removes all linked <see cref="Ref{T}"/> such that they are no longer updated.
/// </summary>
/// <param name="config">the <see cref="IConfigProvider"/> to unlink</param>
public static void RemoveLinks(this IConfigProvider config)
{
if (linkedProviders.ContainsKey(config))
linkedProviders.Remove(config);
}
internal static void Update()
{
foreach (var provider in configProviders)
{
if (provider.Value.LastModified > provider.Key.Value)
{
try
{
provider.Value.Load(); // auto reload if it changes
provider.Key.Value = provider.Value.LastModified;
}
catch (Exception e)
{
Logging.Logger.config.Error("Error when trying to load config");
Logging.Logger.config.Error(e);
}
}
if (provider.Value.HasChanged)
{
try
{
provider.Value.Save();
provider.Key.Value = DateTime.Now;
}
catch (Exception e)
{
Logging.Logger.config.Error("Error when trying to save config");
Logging.Logger.config.Error(e);
}
}
if (provider.Value.InMemoryChanged)
{
provider.Value.InMemoryChanged = false;
try
{
if (linkedProviders.ContainsKey(provider.Value))
linkedProviders[provider.Value]();
}
catch (Exception e)
{
Logging.Logger.config.Error("Error running link change events");
Logging.Logger.config.Error(e);
}
}
}
}
internal static void Save()
{
foreach (var provider in configProviders)
if (provider.Value.HasChanged)
try
{
provider.Value.Save();
}
catch (Exception e)
{
Logging.Logger.config.Error("Error when trying to save config");
Logging.Logger.config.Error(e);
}
}
}
}

+ 17
- 6
IPA.Loader/Config/ConfigProviders/JsonConfigProvider.cs View File

@ -8,14 +8,21 @@ using Newtonsoft.Json.Linq;
namespace IPA.Config.ConfigProviders namespace IPA.Config.ConfigProviders
{ {
[Config.Type("json")]
internal class JsonConfigProvider : IConfigProvider internal class JsonConfigProvider : IConfigProvider
{ {
public static void RegisterConfig()
{
Config.Register<JsonConfigProvider>();
}
private JObject jsonObj; private JObject jsonObj;
// TODO: create a wrapper that allows empty object creation // TODO: create a wrapper that allows empty object creation
public dynamic Dynamic => jsonObj; public dynamic Dynamic => jsonObj;
public bool HasChanged { get; private set; } public bool HasChanged { get; private set; }
public bool InMemoryChanged { get; set; }
public DateTime LastModified => File.GetLastWriteTime(Filename + ".json"); public DateTime LastModified => File.GetLastWriteTime(Filename + ".json");
@ -38,7 +45,7 @@ namespace IPA.Config.ConfigProviders
var fileInfo = new FileInfo(Filename + ".json"); var fileInfo = new FileInfo(Filename + ".json");
if (fileInfo.Exists) if (fileInfo.Exists)
{ {
var json = fileInfo.OpenText().ReadToEnd();
string json = File.ReadAllText(fileInfo.FullName);
try try
{ {
jsonObj = JObject.Parse(json); jsonObj = JObject.Parse(json);
@ -57,6 +64,7 @@ namespace IPA.Config.ConfigProviders
} }
SetupListeners(); SetupListeners();
InMemoryChanged = true;
} }
private void SetupListeners() private void SetupListeners()
@ -69,30 +77,32 @@ namespace IPA.Config.ConfigProviders
private void JsonObj_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) private void JsonObj_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{ {
HasChanged = true; HasChanged = true;
InMemoryChanged = true;
} }
private void JsonObj_ListChanged(object sender, ListChangedEventArgs e) private void JsonObj_ListChanged(object sender, ListChangedEventArgs e)
{ {
HasChanged = true; HasChanged = true;
InMemoryChanged = true;
} }
private void JsonObj_PropertyChanged(object sender, PropertyChangedEventArgs e) private void JsonObj_PropertyChanged(object sender, PropertyChangedEventArgs e)
{ {
HasChanged = true; HasChanged = true;
InMemoryChanged = true;
} }
public T Parse<T>() public T Parse<T>()
{ {
if (jsonObj == null)
return default(T);
return jsonObj.ToObject<T>(); return jsonObj.ToObject<T>();
} }
public void Save() public void Save()
{ {
Logger.config.Debug($"Saving file {Filename}.json"); Logger.config.Debug($"Saving file {Filename}.json");
var fileInfo = new FileInfo(Filename + ".json");
File.WriteAllText(fileInfo.FullName, JsonConvert.SerializeObject(jsonObj, Formatting.Indented));
File.WriteAllText(Filename + ".json", JsonConvert.SerializeObject(jsonObj, Formatting.Indented));
HasChanged = false; HasChanged = false;
} }
@ -102,6 +112,7 @@ namespace IPA.Config.ConfigProviders
jsonObj = JObject.FromObject(obj); jsonObj = JObject.FromObject(obj);
SetupListeners(); SetupListeners();
HasChanged = true; HasChanged = true;
InMemoryChanged = true;
} }
} }
} }

+ 5
- 1
IPA.Loader/Config/IConfigProvider.cs View File

@ -32,7 +32,11 @@ namespace IPA.Config
/// </summary> /// </summary>
bool HasChanged { get; } bool HasChanged { get; }
/// <summary> /// <summary>
/// Will be set with the filename (no extension) to save to. When saving, the implimentation should add the appropriate extension. Should error if set multiple times.
/// Returns <see langword="true"/> if the data in memory has been changed - notably including loads.
/// </summary>
bool InMemoryChanged { get; set; }
/// <summary>
/// Will be set with the filename (no extension) to save to. When saving, the implementation should add the appropriate extension. Should error if set multiple times.
/// </summary> /// </summary>
string Filename { set; } string Filename { set; }
/// <summary> /// <summary>


+ 48
- 0
IPA.Loader/Config/SelfConfig.cs View File

@ -0,0 +1,48 @@
using System.IO;
using IPA.Logging;
using IPA.Utilities;
namespace IPA.Config
{
internal class SelfConfig
{
private static IConfigProvider _loaderConfig;
public static IConfigProvider LoaderConfig
{
get => _loaderConfig;
set
{
_loaderConfig?.RemoveLinks();
value.Load();
SelfConfigRef = value.MakeLink<SelfConfig>((c, v) =>
{
var val = v.Value;
if (val.Regenerate)
c.Store(val = new SelfConfig { Regenerate = false });
StandardLogger.Configure(val);
});
_loaderConfig = value;
}
}
public static Ref<SelfConfig> SelfConfigRef;
public static void Set()
{
LoaderConfig = Config.GetProviderFor(Path.Combine("UserData", IPA_Name), "toml", "json");
}
internal const string IPA_Name = "Beat Saber IPA - Builtin manifest support";
internal const string IPA_Version = "3.12.0";
public bool Regenerate = true;
public class DebugObject
{
public bool ShowCallSource = false;
public bool ShowDebug = false;
}
public DebugObject Debug = new DebugObject();
}
}

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

@ -56,8 +56,10 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Config\Config.cs" />
<Compile Include="Config\ConfigProviders\JsonConfigProvider.cs" /> <Compile Include="Config\ConfigProviders\JsonConfigProvider.cs" />
<Compile Include="Config\IConfigProvider.cs" /> <Compile Include="Config\IConfigProvider.cs" />
<Compile Include="Config\SelfConfig.cs" />
<Compile Include="Loader\Composite\CompositeBSPlugin.cs" /> <Compile Include="Loader\Composite\CompositeBSPlugin.cs" />
<Compile Include="Loader\PluginLoader.cs" /> <Compile Include="Loader\PluginLoader.cs" />
<Compile Include="Loader\PluginManifest.cs" /> <Compile Include="Loader\PluginManifest.cs" />


+ 3
- 3
IPA.Loader/Loader/Composite/CompositeBSPlugin.cs View File

@ -54,11 +54,11 @@ namespace IPA.Loader.Composite
Invoke(plugin => plugin.OnFixedUpdate()); Invoke(plugin => plugin.OnFixedUpdate());
} }
public string Name => throw new NotImplementedException();
public string Name => throw new InvalidOperationException();
public string Version => throw new NotImplementedException();
public string Version => throw new InvalidOperationException();
public ModsaberModInfo ModInfo => throw new NotImplementedException();
public ModsaberModInfo ModInfo => throw new InvalidOperationException();
public void OnLateUpdate() { public void OnLateUpdate() {
Invoke(plugin => { Invoke(plugin => {


+ 2
- 2
IPA.Loader/Loader/Composite/CompositeIPAPlugin.cs View File

@ -43,9 +43,9 @@ namespace IPA.Loader.Composite
Invoke(plugin => plugin.OnFixedUpdate()); Invoke(plugin => plugin.OnFixedUpdate());
} }
public string Name => throw new NotImplementedException();
public string Name => throw new InvalidOperationException();
public string Version => throw new NotImplementedException();
public string Version => throw new InvalidOperationException();
public void OnLateUpdate() { public void OnLateUpdate() {
Invoke(plugin => { Invoke(plugin => {


+ 7
- 51
IPA.Loader/Loader/PluginComponent.cs View File

@ -1,5 +1,4 @@
using IPA.Loader.Composite; using IPA.Loader.Composite;
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
@ -24,9 +23,9 @@ namespace IPA.Loader
DontDestroyOnLoad(gameObject); DontDestroyOnLoad(gameObject);
bsPlugins = new CompositeBSPlugin(PluginManager.BSPlugins); bsPlugins = new CompositeBSPlugin(PluginManager.BSPlugins);
#pragma warning disable CS0618 // Type or member is obsolete
#pragma warning disable 618
ipaPlugins = new CompositeIPAPlugin(PluginManager.Plugins); ipaPlugins = new CompositeIPAPlugin(PluginManager.Plugins);
#pragma warning restore CS0618 // Type or member is obsolete
#pragma warning restore 618
gameObject.AddComponent<Updating.ModSaber.Updater>(); gameObject.AddComponent<Updating.ModSaber.Updater>();
@ -37,23 +36,15 @@ namespace IPA.Loader
SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.sceneLoaded += OnSceneLoaded;
SceneManager.sceneUnloaded += OnSceneUnloaded; SceneManager.sceneUnloaded += OnSceneUnloaded;
foreach (var provider in PluginManager.configProviders)
if (provider.Key.HasChanged)
try
{
provider.Key.Save();
}
catch (Exception e)
{
Logging.Logger.log.Error("Error when trying to save config");
Logging.Logger.log.Error(e);
}
Config.Config.Save();
} }
void Update() void Update()
{ {
bsPlugins.OnUpdate(); bsPlugins.OnUpdate();
ipaPlugins.OnUpdate(); ipaPlugins.OnUpdate();
Config.Config.Update();
} }
void LateUpdate() void LateUpdate()
@ -61,32 +52,7 @@ namespace IPA.Loader
bsPlugins.OnLateUpdate(); bsPlugins.OnLateUpdate();
ipaPlugins.OnLateUpdate(); ipaPlugins.OnLateUpdate();
foreach (var provider in PluginManager.configProviders)
{
if (provider.Key.HasChanged)
try
{
provider.Key.Save();
}
catch (Exception e)
{
Logging.Logger.log.Error("Error when trying to save config");
Logging.Logger.log.Error(e);
}
else if (provider.Key.LastModified > provider.Value.Value)
{
try
{
provider.Key.Load(); // auto reload if it changes
provider.Value.Value = provider.Key.LastModified;
}
catch (Exception e)
{
Logging.Logger.log.Error("Error when trying to load config");
Logging.Logger.log.Error(e);
}
}
}
//Config.Config.Update();
} }
void FixedUpdate() void FixedUpdate()
@ -112,17 +78,7 @@ namespace IPA.Loader
bsPlugins.OnApplicationQuit(); bsPlugins.OnApplicationQuit();
ipaPlugins.OnApplicationQuit(); ipaPlugins.OnApplicationQuit();
foreach (var provider in PluginManager.configProviders)
if (provider.Key.HasChanged)
try
{
provider.Key.Save();
}
catch (Exception e)
{
Logging.Logger.log.Error("Error when trying to save config");
Logging.Logger.log.Error(e);
}
Config.Config.Save();
quitting = true; quitting = true;
} }


+ 73
- 10
IPA.Loader/Loader/PluginLoader.cs View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using IPA.Config;
using IPA.Logging; using IPA.Logging;
using IPA.Utilities; using IPA.Utilities;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -16,17 +17,10 @@ namespace IPA.Loader
/// </summary> /// </summary>
public class PluginLoader public class PluginLoader
{ {
/// <summary>
/// The directory to load plugins from.
/// </summary>
public static string PluginsDirectory => Path.Combine(BeatSaber.InstallPath, "Plugins");
internal static Task LoadTask() => Task.Run(() => internal static Task LoadTask() => Task.Run(() =>
{ {
LoadMetadata(); LoadMetadata();
Logger.log.Debug(string.Join(", ", PluginsMetadata));
Resolve(); Resolve();
Logger.log.Debug(string.Join(", ", PluginsMetadata));
ComputeLoadOrder(); ComputeLoadOrder();
}); });
@ -81,7 +75,7 @@ namespace IPA.Loader
} }
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() => $"{Name}({Id}@{Version})({PluginType?.AssemblyQualifiedName}) from '{File.Name}'";
public override string ToString() => $"{Name}({Id}@{Version})({PluginType?.AssemblyQualifiedName}) from '{LoneFunctions.GetRelativePath(File.FullName, BeatSaber.InstallPath)}'";
} }
/// <summary> /// <summary>
@ -101,7 +95,7 @@ namespace IPA.Loader
internal static void LoadMetadata() internal static void LoadMetadata()
{ {
string[] plugins = Directory.GetFiles(PluginsDirectory, "*.dll");
string[] plugins = Directory.GetFiles(BeatSaber.PluginsPath, "*.dll");
try try
{ {
@ -273,7 +267,6 @@ namespace IPA.Loader
return 0; return 0;
}); });
Logger.log.Debug(string.Join(", ", PluginsMetadata));
var metadata = new List<PluginMetadata>(); var metadata = new List<PluginMetadata>();
var pluginsToLoad = new Dictionary<string, Version>(); var pluginsToLoad = new Dictionary<string, Version>();
@ -303,5 +296,75 @@ namespace IPA.Loader
{ {
} }
internal static PluginInfo LoadPlugin(PluginMetadata meta)
{
var info = new PluginInfo();
try
{
Logger.loader.Debug(meta.Assembly.GetName().ToString());
meta.Assembly = Assembly.Load(meta.Assembly.GetName());
var type = meta.PluginType;
var instance = (IBeatSaberPlugin)Activator.CreateInstance(type);
info.Metadata = meta;
info.Filename = meta.File.FullName;
info.Plugin = instance;
{
var init = type.GetMethod("Init", BindingFlags.Instance | BindingFlags.Public);
if (init != null)
{
var initArgs = new List<object>();
var initParams = init.GetParameters();
Logger modLogger = null;
IModPrefs modPrefs = null;
IConfigProvider cfgProvider = null;
foreach (var param in initParams)
{
var ptype = param.ParameterType;
if (ptype.IsAssignableFrom(typeof(Logger)))
{
if (modLogger == null) modLogger = new StandardLogger(meta.Name);
initArgs.Add(modLogger);
}
else if (ptype.IsAssignableFrom(typeof(IModPrefs)))
{
if (modPrefs == null) modPrefs = new ModPrefs(instance);
initArgs.Add(modPrefs);
}
else if (ptype.IsAssignableFrom(typeof(IConfigProvider)))
{
if (cfgProvider == null)
{
cfgProvider = Config.Config.GetProviderFor(Path.Combine("UserData", $"{meta.Name}"), param);
}
initArgs.Add(cfgProvider);
}
else
initArgs.Add(ptype.GetDefault());
}
init.Invoke(instance, initArgs.ToArray());
}
}
}
catch (AmbiguousMatchException)
{
Logger.loader.Error($"Only one Init allowed per plugin (ambiguous match in {meta.Name})");
return null;
}
catch (Exception e)
{
Logger.loader.Error($"Could not init plugin {meta.Name}: {e}");
return null;
}
return info;
}
} }
} }

+ 2
- 9
IPA.Loader/Loader/PluginManager.cs View File

@ -94,8 +94,6 @@ namespace IPA.Loader
internal static IConfigProvider SelfConfigProvider { get; set; } internal static IConfigProvider SelfConfigProvider { get; set; }
internal static readonly List<KeyValuePair<IConfigProvider,Ref<DateTime>>> configProviders = new List<KeyValuePair<IConfigProvider, Ref<DateTime>>>();
private static void LoadPlugins() private static void LoadPlugins()
{ {
string pluginDirectory = Path.Combine(Environment.CurrentDirectory, "Plugins"); string pluginDirectory = Path.Combine(Environment.CurrentDirectory, "Plugins");
@ -180,7 +178,7 @@ namespace IPA.Loader
Author = "DaNike", Author = "DaNike",
Features = new string[0], Features = new string[0],
Description = "", Description = "",
Version = new SemVer.Version(SelfPlugin.IPA_Version),
Version = new SemVer.Version(SelfConfig.IPA_Version),
GameVersion = BeatSaber.GameVersion, GameVersion = BeatSaber.GameVersion,
Id = "beatsaber-ipa-reloaded" Id = "beatsaber-ipa-reloaded"
}; };
@ -188,11 +186,6 @@ namespace IPA.Loader
_bsPlugins.Add(selfPlugin); _bsPlugins.Add(selfPlugin);
configProviders.Add(new KeyValuePair<IConfigProvider, Ref<DateTime>>(
SelfConfigProvider = new JsonConfigProvider {Filename = Path.Combine("UserData", SelfPlugin.IPA_Name)},
new Ref<DateTime>(SelfConfigProvider.LastModified)));
SelfConfigProvider.Load();
//Load copied plugins //Load copied plugins
string[] copiedPlugins = Directory.GetFiles(cacheDir, "*.dll"); string[] copiedPlugins = Directory.GetFiles(cacheDir, "*.dll");
foreach (string s in copiedPlugins) foreach (string s in copiedPlugins)
@ -294,7 +287,7 @@ namespace IPA.Loader
if (cfgProvider == null) if (cfgProvider == null)
{ {
cfgProvider = new JsonConfigProvider { Filename = Path.Combine("UserData", $"{bsPlugin.Name}") }; cfgProvider = new JsonConfigProvider { Filename = Path.Combine("UserData", $"{bsPlugin.Name}") };
configProviders.Add(new KeyValuePair<IConfigProvider, Ref<DateTime>>(cfgProvider, new Ref<DateTime>(cfgProvider.LastModified)));
//configProviders.Add(new KeyValuePair<IConfigProvider, Ref<DateTime>>(cfgProvider, new Ref<DateTime>(cfgProvider.LastModified)));
cfgProvider.Load(); cfgProvider.Load();
} }
initArgs.Add(cfgProvider); initArgs.Add(cfgProvider);


+ 5
- 0
IPA.Loader/Logging/Logger.cs View File

@ -107,6 +107,11 @@ namespace IPA.Logging
/// Shows all messages. /// Shows all messages.
/// </summary> /// </summary>
All = DebugOnly | InfoUp, All = DebugOnly | InfoUp,
/// <summary>
/// Used for when the level is undefined.
/// </summary>
Undefined = Byte.MaxValue
} }
/// <summary> /// <summary>


+ 8
- 13
IPA.Loader/Logging/StandardLogger.cs View File

@ -44,25 +44,25 @@ namespace IPA.Logging
}; };
private readonly string logName; private readonly string logName;
private static readonly bool showSourceClass;
private static bool showSourceClass;
/// <summary> /// <summary>
/// All levels defined by this filter will be sent to loggers. All others will be ignored. /// All levels defined by this filter will be sent to loggers. All others will be ignored.
/// </summary> /// </summary>
public static LogLevel PrintFilter { get; set; }
public static LogLevel PrintFilter { get; set; } = LogLevel.All;
private readonly List<LogPrinter> printers = new List<LogPrinter>(defaultPrinters); private readonly List<LogPrinter> printers = new List<LogPrinter>(defaultPrinters);
private readonly Dictionary<string, StandardLogger> children = new Dictionary<string, StandardLogger>(); private readonly Dictionary<string, StandardLogger> children = new Dictionary<string, StandardLogger>();
static StandardLogger()
internal static void Configure(SelfConfig cfg)
{ {
showSourceClass = ModPrefs.GetBool("IPA", "DebugShowCallSource", false, true);
PrintFilter = ModPrefs.GetBool("IPA", "PrintDebug", false, true) ? LogLevel.All : LogLevel.InfoUp;
showSourceClass = cfg.Debug.ShowCallSource;
PrintFilter = cfg.Debug.ShowDebug ? LogLevel.All : LogLevel.InfoUp;
} }
private StandardLogger(string mainName, string subName, params LogPrinter[] inherited) private StandardLogger(string mainName, string subName, params LogPrinter[] inherited)
{ {
logName = $"{mainName}/{subName}"; logName = $"{mainName}/{subName}";
printers = new List<LogPrinter>(inherited) printers = new List<LogPrinter>(inherited)
{ {
new PluginSubLogPrinter(mainName, subName) new PluginSubLogPrinter(mainName, subName)
@ -78,7 +78,6 @@ namespace IPA.Logging
internal StandardLogger(string name) internal StandardLogger(string name)
{ {
logName = name; logName = name;
printers.Add(new PluginLogFilePrinter(name)); printers.Add(new PluginLogFilePrinter(name));
if (logThread == null || !logThread.IsAlive) if (logThread == null || !logThread.IsAlive)
@ -219,13 +218,9 @@ namespace IPA.Logging
public static Logger GetChildLogger(this Logger logger, string name) public static Logger GetChildLogger(this Logger logger, string name)
{ {
if (logger is StandardLogger standardLogger) if (logger is StandardLogger standardLogger)
{
return standardLogger.GetChild(name); return standardLogger.GetChild(name);
}
else
{
throw new InvalidOperationException();
}
throw new InvalidOperationException();
} }
} }
} }

+ 7
- 7
IPA.Loader/Updating/SelfPlugin.cs View File

@ -1,21 +1,21 @@
using UnityEngine.SceneManagement;
using System;
using IPA.Config;
using UnityEngine.SceneManagement;
namespace IPA.Updating namespace IPA.Updating
{ {
[Obsolete("Only used for old updating system, replaced with a PluginMeta for teh embedded manifest")]
internal class SelfPlugin : IBeatSaberPlugin internal class SelfPlugin : IBeatSaberPlugin
{ {
internal const string IPA_Name = "Beat Saber IPA - Builtin manifest support";
internal const string IPA_Version = "3.12.0";
public static SelfPlugin Instance { get; set; } = new SelfPlugin(); public static SelfPlugin Instance { get; set; } = new SelfPlugin();
public string Name => IPA_Name;
public string Name => SelfConfig.IPA_Name;
public string Version => IPA_Version;
public string Version => SelfConfig.IPA_Version;
public ModsaberModInfo ModInfo => new ModsaberModInfo public ModsaberModInfo ModInfo => new ModsaberModInfo
{ {
CurrentVersion = IPA_Version,
CurrentVersion = SelfConfig.IPA_Version,
InternalName = "beatsaber-ipa-reloaded" InternalName = "beatsaber-ipa-reloaded"
}; };


+ 4
- 0
IPA.Loader/Utilities/BeatSaber.cs View File

@ -48,6 +48,10 @@ namespace IPA.Utilities
/// The path to the `Libs\Native` folder. Use only if necessary. /// The path to the `Libs\Native` folder. Use only if necessary.
/// </summary> /// </summary>
public static string NativeLibraryPath => Path.Combine(LibraryPath, "Native"); public static string NativeLibraryPath => Path.Combine(LibraryPath, "Native");
/// <summary>
/// The directory to load plugins from.
/// </summary>
public static string PluginsPath => Path.Combine(InstallPath, "Plugins");
private static bool FindSteamVRAsset() private static bool FindSteamVRAsset()
{ {


Loading…
Cancel
Save