diff --git a/.gitignore b/.gitignore index 0b29ae8..9a15be8 100644 --- a/.gitignore +++ b/.gitignore @@ -351,4 +351,5 @@ MigrationBackup/ .ionide/ .idea -.vscode \ No newline at end of file +.vscode +run \ No newline at end of file diff --git a/Assembly/ASMInfo.cs b/Assembly/ASMInfo.cs index 2cce9b2..4a37a74 100644 --- a/Assembly/ASMInfo.cs +++ b/Assembly/ASMInfo.cs @@ -6,7 +6,7 @@ using System.Reflection; [assembly: AssemblyCompany("Piccari Creations")] [assembly: AssemblyAlgorithmId(System.Configuration.Assemblies.AssemblyHashAlgorithm.MD5)] [assembly: AssemblyCopyright("(C) 2020 Tara Piccari")] -[assembly: AssemblyFileVersion("5.0.7.9200")] +[assembly: AssemblyFileVersion("6.0.1.0001")] [assembly: AssemblyDescription("ForgeCore Bot Server")] namespace ForgeCore.Assemble @@ -14,6 +14,6 @@ namespace ForgeCore.Assemble public class ASMInfo { public static string BotName = "ForgeCore"; - public static string BotVer = "2.0.121524.1636"; + public static string BotVer = "2.0.121724.1716"; } } diff --git a/ForgeCore.cs b/ForgeCore.cs index c9e1e47..ad6c26b 100644 --- a/ForgeCore.cs +++ b/ForgeCore.cs @@ -1,15 +1,19 @@ using System; +using System.IO; using System.Linq; +using ForgeCore.Assemble; using ForgeCoreAPI; using LibAC.Arguments; +using LibAC.NBT; +using LibAC.NBT.API; namespace ForgeCore; public class ForgeCore { + public static int Main(string[] args) { - PluginSystem.InitializeSystem(); Arguments arguments = ArgumentParser.Parse(args); if (arguments.HasArg("help") || arguments.Count == 0) @@ -18,11 +22,76 @@ public class ForgeCore ArgumentBuilder builder = new ArgumentBuilder(); builder.withVersionArgument().withHelpArgument(); + builder.withStringArgument("plugindir", "Plugins", true); + builder.withBooleanArgument("daemon", true); Arguments defaults = builder.Build(); Console.WriteLine(ArgumentHelpers.GenerateHelpMessage(defaults.GetAllArguments().ToList(), "ForgeCore")); + return 0; } + + if (arguments.HasArg("version")) + { + Console.WriteLine($"ForgeCore Version {ASMInfo.BotVer}"); + return 0; + } + + if (arguments.HasArg("plugindir")) + PluginSystem.PluginDirectory = arguments.GetArgument("plugindir").GetValue() as string; + + if (!Directory.Exists("Plugins")) + Directory.CreateDirectory("Plugins"); + + PluginSystem.InitializeSystem(PluginSystem.PluginDirectory); + + SharedSessionData sessionData = SharedSessionData.GetInstance(); + // Start the server tick loop + while (!sessionData.ShouldShutdown) + { + long tasksExecuted = 0; + if (sessionData.TotalTasksPerTick == 0 && sessionData.TotalTicks > 0) + { + sessionData.ShouldShutdown = true; + Console.WriteLine("FATAL ERROR\n\nNo plugins are loaded. This server would be doing nothing in a infinite loop with no way to exit. Aborting startup procedure. \n\n\n>> Recommendation: Add one of the standard ForgeCore Plugins which include methods of executing a shutdown"); + } + + foreach (var plugin in PluginSystem.Plugins) + { + if (plugin.enabled) + { + plugin.plugin.tick(); + tasksExecuted++; + } + } + + sessionData.TasksLastTick = tasksExecuted; + sessionData.TotalTicks++; + + if(sessionData.TotalTasksPerTick == 0) + sessionData.TotalTasksPerTick = tasksExecuted; + } + + Console.WriteLine("Preparing to shut down... Please wait..."); + Console.WriteLine("Gathering plugin settings..."); + + CompoundTag saveData = new CompoundTag(); + foreach (var pluginContainer in PluginSystem.Plugins) + { + pluginContainer.plugin.TearDown(); + + CompoundTag entry = new CompoundTag(); + NbtUtils.WriteBoolean(entry, "enabled", pluginContainer.enabled); + entry.Add("data", pluginContainer.plugin.SaveConfig()); + saveData.Add(pluginContainer.pluginName, entry); + } + + Console.WriteLine("Saving plugin data..."); + NbtIo.Write("PluginStorage.dat", saveData); + + Console.WriteLine("> Plugin Storage saved."); + Console.WriteLine("> Exiting server..."); + return 0; } diff --git a/ForgeCoreAPI/IPlugin.cs b/ForgeCoreAPI/IPlugin.cs index 0b6f53b..59734b6 100644 --- a/ForgeCoreAPI/IPlugin.cs +++ b/ForgeCoreAPI/IPlugin.cs @@ -27,6 +27,11 @@ public interface IPlugin /// Called every 5 seconds by the master server /// void tick(); + + /// + /// Called when the server is starting the shutdown process. This operation cannot be aborted, all plugin tasks should be immediately stopped. SaveConfig will follow this instruction. + /// + void TearDown(); /// /// Provides the plugin with a copy of its saved configuration data diff --git a/ForgeCoreAPI/PluginContainer.cs b/ForgeCoreAPI/PluginContainer.cs new file mode 100644 index 0000000..b37c261 --- /dev/null +++ b/ForgeCoreAPI/PluginContainer.cs @@ -0,0 +1,8 @@ +namespace ForgeCoreAPI; + +public class PluginContainer +{ + public IPlugin plugin; + public bool enabled = true; + public string pluginName; +} \ No newline at end of file diff --git a/ForgeCoreAPI/PluginSystem.cs b/ForgeCoreAPI/PluginSystem.cs index 7b9dbe0..559efbd 100644 --- a/ForgeCoreAPI/PluginSystem.cs +++ b/ForgeCoreAPI/PluginSystem.cs @@ -6,13 +6,22 @@ namespace ForgeCoreAPI; public class PluginSystem { - public static List Plugins = new List(); + public static string PluginDirectory { get; set; } = "Plugins"; + + public static List Plugins = new List(); - public static void InitializeSystem() + public static void InitializeSystem(string pluginDirectory) { - CompoundTag pluginsData = NbtIo.Read("PluginStorage.dat"); + CompoundTag pluginsData = new CompoundTag(); + if (!File.Exists("PluginStorage.dat")) + { + // We have no existing saved config for plugins, or this is the first run + // Leave pluginsData empty + } else pluginsData = NbtIo.Read("PluginStorage.dat"); + + // Search the Plugins directory for DLL files - string[] files = Directory.GetFiles("Plugins", "*.dll"); + string[] files = Directory.GetFiles(pluginDirectory, "*.dll"); // Begin loading assemblies foreach (var file in files) @@ -24,12 +33,17 @@ public class PluginSystem { if (types.GetCustomAttribute() != null) { + PluginContainer nPlugin = new PluginContainer(); IPlugin plugin = Activator.CreateInstance(types) as IPlugin; plugin.Initialize(); + nPlugin.plugin = plugin; ForgeCorePluginAttribute attrib = types.GetCustomAttribute(); Tag? pluginStore = pluginsData.Get(attrib.pluginName); + nPlugin.pluginName = attrib.pluginName; + + if (pluginStore == null) { plugin.ResetMemory(); @@ -40,11 +54,19 @@ public class PluginSystem CompoundTag dataTag = tag["data"] as CompoundTag; plugin.LoadConfig(dataTag); - if(tag.Get("enabled").AsByte() == 1) plugin.Enable(); - else plugin.Disable(); + if (NbtUtils.ReadBoolean(tag, "enabled")) + { + nPlugin.enabled = true; + plugin.Enable(); + } + else + { + plugin.Disable(); + nPlugin.enabled = false; + } } - Plugins.Add(plugin); + Plugins.Add(nPlugin); } } } diff --git a/ForgeCoreAPI/SharedSessionData.cs b/ForgeCoreAPI/SharedSessionData.cs new file mode 100644 index 0000000..2a2bf92 --- /dev/null +++ b/ForgeCoreAPI/SharedSessionData.cs @@ -0,0 +1,21 @@ +namespace ForgeCoreAPI; + +public class SharedSessionData +{ + private static readonly object _lock = new object(); + private static SharedSessionData? _instance; + + public static SharedSessionData GetInstance() + { + lock (_lock) + { + if(_instance == null) _instance = new SharedSessionData(); + return _instance; + } + } + + public bool ShouldShutdown { get; set; } = false; + public long TotalTicks { get; set; } = 0; + public long TasksLastTick { get; set; } = 0; + public long TotalTasksPerTick { get; set; } = 0; +} \ No newline at end of file diff --git a/localtest b/localtest new file mode 100755 index 0000000..221ddc3 --- /dev/null +++ b/localtest @@ -0,0 +1,18 @@ +#!/bin/bash + +rm -rf run || true + +mkdir run + +rm -rf bin +dotnet restore + +dotnet publish ForgeCore.csproj --nologo -c Release --self-contained true /p:PublishSingleFile=true +cp bin/Release/net8.0/linux-x64/publish/ForgeCore run/ + +cd run + +chmod +x ForgeCore +./ForgeCore --version +./ForgeCore --help +./ForgeCore --daemon diff --git a/test b/test new file mode 100755 index 0000000..efc81aa --- /dev/null +++ b/test @@ -0,0 +1,13 @@ +#!/bin/bash + +rm -rf run || true + +mkdir run +cd run + +wget https://ci.zontreck.com/job/Projects/job/CSharp/job/ForgeCore/job/master/lastSuccessfulBuild/artifact/bin/Release/net8.0/linux-x64/publish/ForgeCore + +chmod +x ForgeCore +./ForgeCore --version +./ForgeCore --help +./ForgeCore \ No newline at end of file