From d2a060b76eae206e5a75b560e1bbe40eebb69f0b Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 5 Mar 2022 22:49:52 +1000 Subject: [PATCH] Create module subsystem under TShockAPI.Modules This should allow for TShock.cs to be split up to mini-modules, or "micro-plugins" --- TShockAPI/Modules/Module.cs | 34 ++++++++++ TShockAPI/Modules/ModuleManager.cs | 101 +++++++++++++++++++++++++++++ TShockAPI/TShock.cs | 7 ++ 3 files changed, 142 insertions(+) create mode 100644 TShockAPI/Modules/Module.cs create mode 100644 TShockAPI/Modules/ModuleManager.cs diff --git a/TShockAPI/Modules/Module.cs b/TShockAPI/Modules/Module.cs new file mode 100644 index 00000000..20bef23b --- /dev/null +++ b/TShockAPI/Modules/Module.cs @@ -0,0 +1,34 @@ +/* +TShock, a server mod for Terraria +Copyright (C) 2011-2019 Pryaxis & TShock Contributors + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +using System; +namespace TShockAPI.Modules +{ + public abstract class Module : IDisposable + { + /// + /// Entry point of the module + /// + public abstract void Initialise(); + + /// + /// Resource cleanup, e.g. hooks and events + /// + public virtual void Dispose() { } + } +} + diff --git a/TShockAPI/Modules/ModuleManager.cs b/TShockAPI/Modules/ModuleManager.cs new file mode 100644 index 00000000..c1458fb6 --- /dev/null +++ b/TShockAPI/Modules/ModuleManager.cs @@ -0,0 +1,101 @@ +/* +TShock, a server mod for Terraria +Copyright (C) 2011-2019 Pryaxis & TShock Contributors + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace TShockAPI.Modules +{ + public class ModuleManager : IDisposable + { + private List _modules = new(); + + /// + /// Discovers derived classes from across the assembly + /// + /// Type definitions of the modules that can be created + IEnumerable CollectModules() => Assembly.GetExecutingAssembly() + .GetTypes() + .Where(t => typeof(Module).IsAssignableFrom(t) && !t.IsAbstract) + ; + + /// + /// Initialises derived classes defined across the assembly + /// + /// Additional constructor arguments allowed for modules + public void Initialise(object[] parameters) + { + foreach (var moduleType in CollectModules()) + InitialiseModule(moduleType, parameters); + } + + /// + /// Initialises a module by its type definition + /// + /// The type of the module + /// Additional constructor arguments allowed for modules + public void InitialiseModule(Type moduleType, object[] parameters) + { + if (!typeof(Module).IsAssignableFrom(moduleType)) + throw new NotSupportedException($"Cannot load module {moduleType.FullName} as it does not derive from {typeof(Module).FullName}"); + + var args = new List(); + ConstructorInfo constructor = null; + + foreach (var ctor in moduleType.GetConstructors()) + { + args.Clear(); + var ctorParams = ctor.GetParameters(); + + foreach (var prm in ctorParams) + { + var matching_objects = parameters.Where(p => prm.ParameterType.IsAssignableFrom(p.GetType())); + if (matching_objects.Count() == 1) + args.Add(matching_objects.Single()); + else + { + // skip this ctor since we cannot find a suitable parameter for it. + break; + } + } + + if (args.Count() == ctorParams.Length) + constructor = ctor; + } + + if (constructor is not null) + { + var module = (Module)constructor.Invoke(args.ToArray()); + _modules.Add(module); + module.Initialise(); + } + } + + /// + /// Disposes of the module and the manager instance + /// + public void Dispose() + { + foreach (var module in _modules) + module.Dispose(); + _modules.Clear(); + } + } +} + diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 27e170d2..798b5a5b 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -46,6 +46,7 @@ using TShockAPI.Localization; using TShockAPI.Configuration; using Terraria.GameContent.Creative; using System.Runtime.InteropServices; +using TShockAPI.Modules; namespace TShockAPI { @@ -149,6 +150,8 @@ namespace TShockAPI /// public static event Action Initialized; + public static ModuleManager ModuleManager { get; } = new ModuleManager(); + /// Version - The version required by the TerrariaAPI to be passed back for checking & loading the plugin. /// value - The version number specified in the Assembly, based on the VersionNum variable set in this class. public override Version Version @@ -421,6 +424,8 @@ namespace TShockAPI EnglishLanguage.Initialize(); + ModuleManager.Initialise(new object[] { this }); + if (Config.Settings.RestApiEnabled) RestApi.Start(); @@ -463,6 +468,8 @@ namespace TShockAPI } SaveManager.Instance.Dispose(); + ModuleManager.Dispose(); + ServerApi.Hooks.GamePostInitialize.Deregister(this, OnPostInit); ServerApi.Hooks.GameUpdate.Deregister(this, OnUpdate); ServerApi.Hooks.GameHardmodeTileUpdate.Deregister(this, OnHardUpdate);