using BetterAmongUs.Commands; using BetterAmongUs.Modules.AntiCheat; using System.Reflection; using System.Runtime.Serialization; namespace BetterAmongUs.Attributes; /// /// Base attribute class for automatically discovering and registering instances of specific types through reflection. /// internal abstract class AutoRegisterAttribute : Attribute { /// /// Scans the entire assembly and registers all instances of classes marked with subclasses. /// internal static void Initialize() { var types = ModInfo.Assembly.GetTypes(); foreach (var type in types) { if (type.IsAbstract || !type.IsSealed) continue; if (!typeof(AutoRegisterAttribute).IsAssignableFrom(type)) continue; var tempAttribute = (AutoRegisterAttribute)FormatterServices.GetUninitializedObject(type); tempAttribute.Register(); } } /// /// When implemented in a derived class, registers individual instances. /// protected abstract void Register(); } /// /// Generic attribute for automatically registering static instances of a specified base type or interface. /// /// The base type or interface that attributed classes must implement. [AttributeUsage(AttributeTargets.Class)] internal abstract class AutoRegisterAttribute : AutoRegisterAttribute where T : class { /// /// The collection of all discovered and registered instances of type . /// protected static readonly List _instances = []; /// /// Gets a read-only collection of all registered instances of type . /// internal static IReadOnlyList Instances => _instances.AsReadOnly(); /// /// Retrieves a specific instance by its concrete type. /// /// The concrete type of the instance to retrieve. Must inherit from or implement . /// The instance of type if found, otherwise null. internal static J? GetInstance() where J : T => (J?)_instances.FirstOrDefault(instance => instance.GetType() == typeof(J)); /// protected override void Register() { var types = ModInfo.Assembly.GetTypes(); foreach (var type in types) { if (type.GetCustomAttribute(GetType()) == null) continue; if (type.IsAbstract || type.IsInterface) continue; if (typeof(T).IsAssignableFrom(type)) { var constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Type.EmptyTypes, null); if (constructor != null) { if (constructor.Invoke(null) is T instance) { _instances.Add(instance); } } } } } } // Class instances internal sealed class RegisterCommandAttribute : AutoRegisterAttribute { } internal sealed class RegisterRPCHandlerAttribute : AutoRegisterAttribute { }