diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj index 33fa27fe..cd5b9ac6 100644 --- a/IPA.Loader/IPA.Loader.csproj +++ b/IPA.Loader/IPA.Loader.csproj @@ -62,6 +62,7 @@ + diff --git a/IPA.Loader/Loader/Features/AddInFeature.cs b/IPA.Loader/Loader/Features/AddInFeature.cs new file mode 100644 index 00000000..47cc9294 --- /dev/null +++ b/IPA.Loader/Loader/Features/AddInFeature.cs @@ -0,0 +1,21 @@ +namespace IPA.Loader.Features +{ + internal class AddInFeature : Feature + { + private PluginLoader.PluginMetadata selfMeta; + + public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters) + { + selfMeta = meta; + + RequireLoaded(meta); + + return true; + } + + public override bool BeforeLoad(PluginLoader.PluginMetadata plugin) + { + return plugin != selfMeta; + } + } +} diff --git a/IPA.Loader/Loader/Features/DefineFeature.cs b/IPA.Loader/Loader/Features/DefineFeature.cs index c2b30511..15fffad0 100644 --- a/IPA.Loader/Loader/Features/DefineFeature.cs +++ b/IPA.Loader/Loader/Features/DefineFeature.cs @@ -7,6 +7,8 @@ namespace IPA.Loader.Features { public static bool NewFeature = true; + internal override bool StoreOnPlugin => false; + public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters) { // parameters should be (name, fully qualified type) if (parameters.Length != 2) diff --git a/IPA.Loader/Loader/Features/Feature.cs b/IPA.Loader/Loader/Features/Feature.cs index 7f707732..ec84ec20 100644 --- a/IPA.Loader/Loader/Features/Feature.cs +++ b/IPA.Loader/Loader/Features/Feature.cs @@ -25,6 +25,14 @@ namespace IPA.Loader.Features /// if the feature is valid for the plugin, otherwise public abstract bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters); + /// + /// Evaluates the Feature for use in conditional meta-Features. This should be re-calculated on every call, unless it can be proven to not change. + /// + /// This will be called on every feature that returns from + /// + /// the truthiness of the Feature. + public virtual bool Evaluate() => true; + /// /// The message to be logged when the feature is not valid for a plugin. /// This should also be set whenever either or returns false. @@ -61,11 +69,15 @@ namespace IPA.Loader.Features /// the plugin to ensure is loaded. protected void RequireLoaded(PluginLoader.PluginMetadata plugin) => PluginLoader.Load(plugin); + internal virtual bool StoreOnPlugin => true; + private static readonly Dictionary featureTypes = new Dictionary { { "define-feature", typeof(DefineFeature) } }; + internal static bool HasFeature(string name) => featureTypes.ContainsKey(name); + internal static bool RegisterFeature(string name, Type type) { if (!typeof(Feature).IsAssignableFrom(type)) @@ -103,7 +115,7 @@ namespace IPA.Loader.Features var parameters = new List(); bool escape = false; - bool readingParams = false; + int parens = 0; bool removeWhitespace = true; foreach (var chr in featureString) { @@ -119,18 +131,20 @@ namespace IPA.Loader.Features case '\\': escape = true; break; - case '(' when !readingParams: + case '(': + parens++; + if (parens != 1) goto default; removeWhitespace = true; - readingParams = true; name = builder.ToString(); builder.Clear(); break; - case ')' when readingParams: - readingParams = false; + case ')': + parens--; + if (parens != 0) goto default; goto case ','; case ',': + if (parens > 1) goto default; parameters.Add(builder.ToString()); - if (!readingParams) break; builder.Clear(); removeWhitespace = true; break; @@ -149,7 +163,7 @@ namespace IPA.Loader.Features parsed = new FeatureParse(name, parameters.ToArray()); - if (readingParams) + if (parens != 0) { failException = new Exception("Malformed feature definition"); return false; diff --git a/IPA.Loader/Loader/Features/PrintFeature.cs b/IPA.Loader/Loader/Features/PrintFeature.cs index 52b03316..401a8e6f 100644 --- a/IPA.Loader/Loader/Features/PrintFeature.cs +++ b/IPA.Loader/Loader/Features/PrintFeature.cs @@ -11,4 +11,22 @@ namespace IPA.Loader.Features return true; } } + + internal class DebugFeature : Feature + { + public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters) + { + Logger.features.Debug($"{meta.Name}: {string.Join(" ", parameters)}"); + return true; + } + } + + internal class WarnFeature : Feature + { + public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters) + { + Logger.features.Debug($"{meta.Name}: {string.Join(" ", parameters)}"); + return true; + } + } } diff --git a/IPA.Loader/Loader/PluginLoader.cs b/IPA.Loader/Loader/PluginLoader.cs index 57e58dc6..62baf403 100644 --- a/IPA.Loader/Loader/PluginLoader.cs +++ b/IPA.Loader/Loader/PluginLoader.cs @@ -322,9 +322,9 @@ namespace IPA.Loader feature.Item2.Value = parsed; else if (success) { - if (valid) + if (valid && featureObj.StoreOnPlugin) plugin.Item1.InternalFeatures.Add(featureObj); - else + else if (!valid) Logger.features.Warn( $"Feature not valid on {plugin.Item1.Name}: {featureObj.InvalidMessage}"); plugin.Item2.RemoveAt(i--); @@ -336,6 +336,10 @@ namespace IPA.Loader plugin.Item2.RemoveAt(i--); } } + + foreach (var plugin in PluginsMetadata) + foreach (var feature in plugin.Features) + feature.Evaluate(); } foreach (var plugin in parsedFeatures) diff --git a/IPA.Loader/Loader/manifest.json b/IPA.Loader/Loader/manifest.json index 029b5189..26c2f1da 100644 --- a/IPA.Loader/Loader/manifest.json +++ b/IPA.Loader/Loader/manifest.json @@ -8,7 +8,10 @@ "version": "3.12.0", "features": [ "define-feature(print, IPA.Loader.Features.PrintFeature)", + "define-feature(debug, IPA.Loader.Features.DebugFeature)", + "define-feature(warn, IPA.Loader.Features.WarnFeature)", "define-feature(no-update, IPA.Loader.Features.NoUpdateFeature)", + "define-feature(add-in, IPA.Loader.Features.AddInFeature)", "print(YO! Howz it goin\\, its ya boi desinc here)" ] } \ No newline at end of file