From 1f707c50350223dc77673d15f950c8282f6ebff8 Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Fri, 28 Dec 2018 19:18:03 -0600 Subject: [PATCH] Fixed up some sorting rules for plugin load ordering --- IPA.Loader/IPA.Loader.csproj | 4 +- IPA.Loader/Loader/PluginLoader.cs | 115 ++++++++++++++++++++--------- IPA.Loader/Loader/PluginManager.cs | 7 +- IPA.Loader/Loader/manifest.json | 12 +++ 4 files changed, 100 insertions(+), 38 deletions(-) create mode 100644 IPA.Loader/Loader/manifest.json diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj index 4c2551fc..dbc49238 100644 --- a/IPA.Loader/IPA.Loader.csproj +++ b/IPA.Loader/IPA.Loader.csproj @@ -108,6 +108,8 @@ 1.2.0 - + + + \ No newline at end of file diff --git a/IPA.Loader/Loader/PluginLoader.cs b/IPA.Loader/Loader/PluginLoader.cs index d7734bbb..d59d011e 100644 --- a/IPA.Loader/Loader/PluginLoader.cs +++ b/IPA.Loader/Loader/PluginLoader.cs @@ -24,7 +24,9 @@ namespace IPA.Loader internal static Task LoadTask() => Task.Run(() => { LoadMetadata(); + Logger.log.Debug(string.Join(", ", PluginsMetadata)); Resolve(); + Logger.log.Debug(string.Join(", ", PluginsMetadata)); ComputeLoadOrder(); }); @@ -79,7 +81,7 @@ namespace IPA.Loader } /// - public override string ToString() => $"{Name}({Id}@{Version})({PluginType.AssemblyQualifiedName}) from '{File.Name}'"; + public override string ToString() => $"{Name}({Id}@{Version})({PluginType?.AssemblyQualifiedName}) from '{File.Name}'"; } /// @@ -101,7 +103,32 @@ namespace IPA.Loader { string[] plugins = Directory.GetFiles(PluginsDirectory, "*.dll"); - Assembly.ReflectionOnlyLoadFrom(Assembly.GetExecutingAssembly().Location); // load self as reflection only + try + { + var selfmeta = new PluginMetadata + { + Assembly = Assembly.ReflectionOnlyLoadFrom(Assembly.GetExecutingAssembly() + .Location), // load self as reflection only + File = new FileInfo(Path.Combine(BeatSaber.InstallPath, "IPA.exe")), + PluginType = null + }; + + string manifest; + using (var manifestReader = + new StreamReader( + selfmeta.Assembly.GetManifestResourceStream(typeof(PluginLoader), "manifest.json") ?? + throw new InvalidOperationException())) + manifest = manifestReader.ReadToEnd(); + + selfmeta.Manifest = JsonConvert.DeserializeObject(manifest); + + PluginsMetadata.Add(selfmeta); + } + catch (Exception e) + { + Logger.loader.Critical("Error loading own manifest"); + Logger.loader.Critical(e); + } foreach (var plugin in plugins) { // should probably do patching first /shrug @@ -134,7 +161,7 @@ namespace IPA.Loader if (metadata.PluginType == null) { - Logger.log.Warn($"Could not find plugin type for {Path.GetFileName(plugin)}"); + Logger.loader.Warn($"Could not find plugin type for {Path.GetFileName(plugin)}"); continue; } @@ -144,13 +171,13 @@ namespace IPA.Loader metadataStream = assembly.GetManifestResourceStream(metadata.PluginType, "manifest.json"); if (metadataStream == null) { - Logger.log.Error($"manifest.json not found in plugin {Path.GetFileName(plugin)}"); + Logger.loader.Error($"manifest.json not found in plugin {Path.GetFileName(plugin)}"); continue; } } catch (FileNotFoundException) { - Logger.log.Error($"manifest.json not found in plugin {Path.GetFileName(plugin)}"); + Logger.loader.Error($"manifest.json not found in plugin {Path.GetFileName(plugin)}"); continue; } @@ -164,8 +191,8 @@ namespace IPA.Loader } catch (Exception e) { - Logger.log.Error($"Could not load data for plugin {Path.GetFileName(plugin)}"); - Logger.log.Error(e); + Logger.loader.Error($"Could not load data for plugin {Path.GetFileName(plugin)}"); + Logger.loader.Error(e); } } } @@ -173,7 +200,7 @@ namespace IPA.Loader internal static void Resolve() { // resolves duplicates and conflicts, etc PluginsMetadata.Sort((a, b) => a.Version.CompareTo(b.Version)); - + var ids = new HashSet(); var ignore = new HashSet(); var resolved = new List(PluginsMetadata.Count); @@ -183,7 +210,8 @@ namespace IPA.Loader { if (ids.Contains(meta.Id)) { - Logger.log.Warn($"Found duplicates of {meta.Id}, using newest"); + Logger.loader.Warn($"Found duplicates of {meta.Id}, using newest"); + ignore.Add(meta); continue; // because of sorted order, hightest order will always be the first one } @@ -196,27 +224,24 @@ namespace IPA.Loader processedLater = true; continue; } - if (meta2.Manifest.Conflicts.ContainsKey(meta.Id)) + + if (!meta2.Manifest.Conflicts.ContainsKey(meta.Id)) continue; + + var range = meta2.Manifest.Conflicts[meta.Id]; + if (!range.IsSatisfied(meta.Version)) continue; + + Logger.loader.Warn($"{meta.Id}@{meta.Version} conflicts with {meta2.Name}"); + + if (processedLater) + { + Logger.loader.Warn($"Ignoring {meta2.Name}"); + ignore.Add(meta2); + } + else { - var range = meta2.Manifest.Conflicts[meta.Id]; - if (range.IsSatisfied(meta.Version)) - { - //TODO: actually choose the one most depended on - - Logger.log.Warn($"{meta.Id}@{meta.Version} conflicts with {meta2.Name}"); - - if (processedLater) - { - Logger.log.Warn($"Ignoring {meta2.Name}"); - ignore.Add(meta2); - } - else - { - Logger.log.Warn($"Ignoring {meta.Name}"); - ignore.Add(meta); - break; - } - } + Logger.loader.Warn($"Ignoring {meta.Name}"); + ignore.Add(meta); + break; } } } @@ -237,17 +262,41 @@ namespace IPA.Loader if (a.Id == b.Id) return 0; if (a.Id != null) { - if (b.Manifest.Dependencies.ContainsKey(a.Id) || b.Manifest.LoadAfter.Contains(a.Id)) return 1; - if (b.Manifest.LoadBefore.Contains(a.Id)) return -1; + if (b.Manifest.Dependencies.ContainsKey(a.Id) || b.Manifest.LoadAfter.Contains(a.Id)) return -1; + if (b.Manifest.LoadBefore.Contains(a.Id)) return 1; } if (b.Id != null) { - if (a.Manifest.Dependencies.ContainsKey(b.Id) || a.Manifest.LoadAfter.Contains(b.Id)) return -1; - if (a.Manifest.LoadBefore.Contains(b.Id)) return 1; + if (a.Manifest.Dependencies.ContainsKey(b.Id) || a.Manifest.LoadAfter.Contains(b.Id)) return 1; + if (a.Manifest.LoadBefore.Contains(b.Id)) return -1; } return 0; }); + Logger.log.Debug(string.Join(", ", PluginsMetadata)); + + var metadata = new List(); + var pluginsToLoad = new Dictionary(); + foreach (var meta in PluginsMetadata) + { + bool load = true; + foreach (var dep in meta.Manifest.Dependencies) + { + if (pluginsToLoad.ContainsKey(dep.Key) && dep.Value.IsSatisfied(pluginsToLoad[dep.Key])) continue; + + load = false; + Logger.loader.Warn($"{meta.Name} is missing dependency {dep.Key}@{dep.Value}"); + } + + if (load) + { + metadata.Add(meta); + if (meta.Id != null) + pluginsToLoad.Add(meta.Id, meta.Version); + } + } + + PluginsMetadata = metadata; } internal static void LoadPlugins() diff --git a/IPA.Loader/Loader/PluginManager.cs b/IPA.Loader/Loader/PluginManager.cs index 79efd65f..d0ee686b 100644 --- a/IPA.Loader/Loader/PluginManager.cs +++ b/IPA.Loader/Loader/PluginManager.cs @@ -13,7 +13,6 @@ using IPA.Old; using IPA.Updating; using IPA.Utilities; using Mono.Cecil; -using SemVer; using UnityEngine; using Logger = IPA.Logging.Logger; using static IPA.Loader.PluginLoader; @@ -127,7 +126,7 @@ namespace IPA.Loader string[] originalPlugins = Directory.GetFiles(pluginDirectory, "*.dll"); foreach (string s in originalPlugins) { - if (PluginLoader.PluginsMetadata.Select(m => m.File.Name).Contains(s)) continue; + if (PluginsMetadata.Select(m => m.File.Name).Contains(s)) continue; string pluginCopy = Path.Combine(cacheDir, Path.GetFileName(s)); #region Fix assemblies for refactor @@ -198,7 +197,7 @@ namespace IPA.Loader string[] copiedPlugins = Directory.GetFiles(cacheDir, "*.dll"); foreach (string s in copiedPlugins) { - var result = LoadPluginsFromFile(s, exeName); + var result = LoadPluginsFromFile(s); _bsPlugins.AddRange(result.Item1); _ipaPlugins.AddRange(result.Item2); } @@ -221,7 +220,7 @@ namespace IPA.Loader Logger.log.Info("-----------------------------"); } - private static Tuple, IEnumerable> LoadPluginsFromFile(string file, string exeName) + private static Tuple, IEnumerable> LoadPluginsFromFile(string file) { List bsPlugins = new List(); List ipaPlugins = new List(); diff --git a/IPA.Loader/Loader/manifest.json b/IPA.Loader/Loader/manifest.json new file mode 100644 index 00000000..566cbd55 --- /dev/null +++ b/IPA.Loader/Loader/manifest.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://raw.githubusercontent.com/nike4613/ModSaber-MetadataFileSchema/master/Schema.json", + "author": "DaNike", + "description": "A mod loader specifically for Beat Saber", + "gameVersion": "0.12.2", + "id": "beatsaber-ipa-reloaded", + "name": "BSIPA", + "version": "3.12.0", + "features": [ + "early-load" + ] +} \ No newline at end of file