diff --git a/UXAssist/Common/ModFeatures/IModFeature.cs b/UXAssist/Common/ModFeatures/IModFeature.cs new file mode 100644 index 0000000..cc18519 --- /dev/null +++ b/UXAssist/Common/ModFeatures/IModFeature.cs @@ -0,0 +1,10 @@ +namespace UXAssist.Common.ModFeatures; + +public interface IModFeature +{ + void Init(); + void Start(); + void Uninit(); + void OnInputUpdate(); + void OnUpdate(); +} diff --git a/UXAssist/Common/ModFeatures/ModFeatureAttribute.cs b/UXAssist/Common/ModFeatures/ModFeatureAttribute.cs new file mode 100644 index 0000000..d934859 --- /dev/null +++ b/UXAssist/Common/ModFeatures/ModFeatureAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace UXAssist.Common.ModFeatures; + +[AttributeUsage(AttributeTargets.Class, Inherited = false)] +public sealed class ModFeatureAttribute : Attribute +{ + public string Name { get; } + public int Order { get; set; } + + public ModFeatureAttribute(string name = null) + { + Name = name; + } +} diff --git a/UXAssist/Common/ModFeatures/ModFeatureRegistry.cs b/UXAssist/Common/ModFeatures/ModFeatureRegistry.cs new file mode 100644 index 0000000..a241069 --- /dev/null +++ b/UXAssist/Common/ModFeatures/ModFeatureRegistry.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace UXAssist.Common.ModFeatures; + +public static class ModFeatureRegistry +{ + private static readonly List _staticFeatures = []; + private static readonly List _instanceFeatures = []; + private static readonly HashSet _discoveredAssemblies = []; + + public static void Discover(Assembly assembly) + { + if (!_discoveredAssemblies.Add(assembly)) return; + + var staticTypes = Util.GetTypesFiltered(assembly, t => + t.IsClass && t.IsAbstract && t.IsSealed && + Attribute.IsDefined(t, typeof(ModFeatureAttribute))); + + foreach (var type in staticTypes.OrderBy(GetOrder)) + { + if (!_staticFeatures.Contains(type)) + _staticFeatures.Add(type); + } + } + + public static void Register() where T : class, IModFeature, new() + { + var instance = new T(); + _instanceFeatures.Add(instance); + } + + public static void InitAll() + { + ForEachStatic("Init"); + foreach (var f in _instanceFeatures) f.Init(); + } + + public static void StartAll() + { + ForEachStatic("Start"); + foreach (var f in _instanceFeatures) f.Start(); + } + + public static void UninitAll() + { + ForEachStatic("Uninit"); + foreach (var f in _instanceFeatures) f.Uninit(); + } + + public static void OnInputUpdateAll() + { + ForEachStatic("OnInputUpdate"); + foreach (var f in _instanceFeatures) f.OnInputUpdate(); + } + + public static void OnUpdateAll() + { + ForEachStatic("OnUpdate"); + foreach (var f in _instanceFeatures) f.OnUpdate(); + } + + private static void ForEachStatic(string methodName) + { + foreach (var type in _staticFeatures) + { + var method = type.GetMethod(methodName, + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + method?.Invoke(null, null); + } + } + + private static int GetOrder(Type type) + { + return type.GetCustomAttribute()?.Order ?? 0; + } +} diff --git a/UXAssist/Common/Util.cs b/UXAssist/Common/Util.cs index f11f7d6..e186c9f 100644 --- a/UXAssist/Common/Util.cs +++ b/UXAssist/Common/Util.cs @@ -15,6 +15,11 @@ public static class Util public static Type[] GetTypesInNamespace(Assembly assembly, string nameSpace) => GetTypesFiltered(assembly, t => string.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)); + public static Type[] GetTypesInNamespacePrefix(Assembly assembly, string prefix) + { + return GetTypesFiltered(assembly, t => t.Namespace != null && t.Namespace.StartsWith(prefix, StringComparison.Ordinal)); + } + public static byte[] LoadEmbeddedResource(string path, Assembly assembly = null) { if (assembly == null)