diff --git a/IPA.Loader/Loader/PluginLoader.cs b/IPA.Loader/Loader/PluginLoader.cs index d1a01b33..3c9bff2a 100644 --- a/IPA.Loader/Loader/PluginLoader.cs +++ b/IPA.Loader/Loader/PluginLoader.cs @@ -40,6 +40,7 @@ namespace IPA.Loader Resolve(); ComputeLoadOrder(); FilterDisabled(); + FilterWithoutFiles(); ResolveDependencies(); }); @@ -232,6 +233,10 @@ namespace IPA.Loader metadata.Manifest = JsonConvert.DeserializeObject(File.ReadAllText(manifest)); + if (metadata.Manifest.Files.Length < 1) + Logger.loader.Warn($"Bare manifest {Path.GetFileName(manifest)} does not declare any files. " + + $"Dependency resolution and verification cannot be completed."); + Logger.loader.Debug($"Adding info for bare manifest {Path.GetFileName(manifest)}"); PluginsMetadata.Add(metadata); } @@ -286,7 +291,8 @@ namespace IPA.Loader internal enum Reason { Error, Duplicate, Conflict, Dependency, - Released, Feature, Unsupported + Released, Feature, Unsupported, + MissingFiles } internal struct IgnoreReason { @@ -401,6 +407,35 @@ namespace IPA.Loader PluginsMetadata = enabled; } + private static void FilterWithoutFiles() + { + var enabled = new List(PluginsMetadata.Count); + + foreach (var meta in PluginsMetadata) + { + var passed = true; + foreach (var file in meta.AssociatedFiles) + { + if (!file.Exists) + { + passed = false; + ignoredPlugins.Add(meta, new IgnoreReason(Reason.MissingFiles) + { + ReasonText = $"File {Utils.GetRelativePath(file.FullName, UnityGame.InstallPath)} (declared by {meta.Name}) does not exist" + }); + Logger.loader.Warn($"File {Utils.GetRelativePath(file.FullName, UnityGame.InstallPath)}" + + $" (declared by {meta.Name}) does not exist! Mod installation is incomplete, not loading it."); + break; + } + } + + if (passed) + enabled.Add(meta); + } + + PluginsMetadata = enabled; + } + internal static void ComputeLoadOrder() { #if DEBUG diff --git a/IPA.Loader/Loader/PluginManager.cs b/IPA.Loader/Loader/PluginManager.cs index d872892a..1468a954 100644 --- a/IPA.Loader/Loader/PluginManager.cs +++ b/IPA.Loader/Loader/PluginManager.cs @@ -292,16 +292,15 @@ namespace IPA.Loader else { foreach (string plugin in Directory.GetFiles(cacheDir, "*")) - { File.Delete(plugin); - } } // initialize BSIPA plugins first _bsPlugins.AddRange(PluginLoader.LoadPlugins()); var metadataPaths = PluginLoader.PluginsMetadata.Select(m => m.File.FullName).ToList(); - var ignoredPaths = PluginLoader.ignoredPlugins.Select(m => m.Key.File.FullName).ToList(); + var ignoredPaths = PluginLoader.ignoredPlugins.Select(m => m.Key.File.FullName) + .Concat(PluginLoader.ignoredPlugins.SelectMany(m => m.Key.AssociatedFiles.Select(f => f.FullName))).ToList(); var disabledPaths = DisabledPlugins.Select(m => m.File.FullName).ToList(); //Copy plugins to .cache @@ -328,8 +327,6 @@ namespace IPA.Loader { // fix type references if (@ref.FullName == "IllusionPlugin.IPlugin") @ref.Namespace = "IPA.Old"; //@ref.Name = ""; if (@ref.FullName == "IllusionPlugin.IEnhancedPlugin") @ref.Namespace = "IPA.Old"; //@ref.Name = ""; - if (@ref.FullName == "IllusionPlugin.IBeatSaberPlugin") { @ref.Namespace = "IPA"; @ref.Name = nameof(IPlugin); } - if (@ref.FullName == "IllusionPlugin.IEnhancedBeatSaberPlugin") { @ref.Namespace = "IPA"; @ref.Name = nameof(IEnhancedPlugin); } if (@ref.FullName == "IllusionPlugin.IniFile") @ref.Namespace = "IPA.Config"; //@ref.Name = ""; if (@ref.FullName == "IllusionPlugin.IModPrefs") @ref.Namespace = "IPA.Config"; //@ref.Name = ""; if (@ref.FullName == "IllusionPlugin.ModPrefs") @ref.Namespace = "IPA.Config"; //@ref.Name = ""; diff --git a/IPA.Loader/Loader/PluginManifest.cs b/IPA.Loader/Loader/PluginManifest.cs index d0eba0f4..f36f6e9d 100644 --- a/IPA.Loader/Loader/PluginManifest.cs +++ b/IPA.Loader/Loader/PluginManifest.cs @@ -51,6 +51,9 @@ namespace IPA.Loader [JsonProperty("icon", Required = Required.DisallowNull)] public string IconPath = null; + [JsonProperty("files", Required = Required.DisallowNull)] + public string[] Files = Array.Empty(); + [Serializable] public class LinksObject { diff --git a/IPA.Loader/Loader/PluginMetadata.cs b/IPA.Loader/Loader/PluginMetadata.cs index af1f31d0..fea9a182 100644 --- a/IPA.Loader/Loader/PluginMetadata.cs +++ b/IPA.Loader/Loader/PluginMetadata.cs @@ -3,6 +3,7 @@ using IPA.Utilities; using Mono.Cecil; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using Version = SemVer.Version; #if NET3 @@ -61,6 +62,12 @@ namespace IPA.Loader internal readonly List InternalFeatures = new List(); + /// + /// A list of files (that aren't ) that are associated with this plugin. + /// + /// a list of associated files + public IReadOnlyList AssociatedFiles { get; private set; } + internal bool IsSelf; /// @@ -82,6 +89,9 @@ namespace IPA.Loader Name = value.Name; Version = value.Version; Id = value.Id; + AssociatedFiles = value.Files + .Select(f => Path.Combine(UnityGame.InstallPath, f)) + .Select(p => new FileInfo(p)).ToList(); } }