From c600e51164e49df2700543cc205cb651e454c475 Mon Sep 17 00:00:00 2001 From: snixtho Date: Thu, 29 Jun 2023 11:33:49 +0200 Subject: [PATCH 01/22] begin startup overhaul --- .../Attributes/CliCommandAttribute.cs | 6 - .../Attributes/CliOptionAttribute.cs | 23 --- .../Attributes/RequiredFeaturesAttribute.cs | 26 +++ src/EvoSC.CLI/BaseStartup.cs | 188 ++++++++++++++++++ src/EvoSC.CLI/CliCommandContext.cs | 9 - src/EvoSC.CLI/CliHandler.cs | 88 -------- src/EvoSC.CLI/CliManager.cs | 134 +++++++++++++ src/EvoSC.CLI/EvoSC.CLI.csproj | 3 + .../Exceptions/CliCommandAttributeNotFound.cs | 9 - src/EvoSC.CLI/Exceptions/EvoSCCliException.cs | 21 -- src/EvoSC.CLI/Interfaces/ICliCommand.cs | 12 -- src/EvoSC.CLI/Interfaces/ICliCommandInfo.cs | 15 ++ src/EvoSC.CLI/Interfaces/ICliManager.cs | 9 + src/EvoSC.CLI/Models/CliCommandInfo.cs | 15 ++ src/EvoSC.CLI/Models/CliEvoScConfig.cs | 8 + src/EvoSC.CLI/Models/CliEvoScConfigBinder.cs | 15 ++ src/EvoSC.Common/Application/AppFeature.cs | 24 +++ .../Application/ServicesBuilder.cs | 11 + .../Application/StartupPipeline.cs | 44 ++++ .../Config/ConfigServiceExtensions.cs | 41 ---- src/EvoSC.Common/Config/Configuration.cs | 37 ++++ ...TomlMappingManaager.cs => ConfigMapper.cs} | 6 +- .../Config/Stores/EvoSCBaseConfigStore.cs | 47 +++++ .../Config/Stores/StoreExtensions.cs | 6 +- .../Config/Stores/TomlConfigStore.cs | 1 + .../Extensions/DatabaseServiceExtensions.cs | 7 +- .../Interfaces/IEvoSCApplication.cs | 2 + .../Interfaces/IStartupPipeline.cs | 25 +++ src/EvoSC/Application.cs | 59 ++++-- src/EvoSC/CliCommands/RunCommand.cs | 20 +- src/EvoSC/EvoSC.csproj | 4 + src/EvoSC/Program.cs | 8 +- tests/EvoSC.CLI.Tests/CliHandlerTests.cs | 40 ---- .../TestFiles/ConfigTests.cs | 13 -- 34 files changed, 675 insertions(+), 301 deletions(-) delete mode 100644 src/EvoSC.CLI/Attributes/CliOptionAttribute.cs create mode 100644 src/EvoSC.CLI/Attributes/RequiredFeaturesAttribute.cs create mode 100644 src/EvoSC.CLI/BaseStartup.cs delete mode 100644 src/EvoSC.CLI/CliCommandContext.cs delete mode 100644 src/EvoSC.CLI/CliHandler.cs create mode 100644 src/EvoSC.CLI/CliManager.cs delete mode 100644 src/EvoSC.CLI/Exceptions/CliCommandAttributeNotFound.cs delete mode 100644 src/EvoSC.CLI/Exceptions/EvoSCCliException.cs delete mode 100644 src/EvoSC.CLI/Interfaces/ICliCommand.cs create mode 100644 src/EvoSC.CLI/Interfaces/ICliCommandInfo.cs create mode 100644 src/EvoSC.CLI/Interfaces/ICliManager.cs create mode 100644 src/EvoSC.CLI/Models/CliCommandInfo.cs create mode 100644 src/EvoSC.CLI/Models/CliEvoScConfig.cs create mode 100644 src/EvoSC.CLI/Models/CliEvoScConfigBinder.cs create mode 100644 src/EvoSC.Common/Application/AppFeature.cs create mode 100644 src/EvoSC.Common/Application/ServicesBuilder.cs create mode 100644 src/EvoSC.Common/Application/StartupPipeline.cs delete mode 100644 src/EvoSC.Common/Config/ConfigServiceExtensions.cs create mode 100644 src/EvoSC.Common/Config/Configuration.cs rename src/EvoSC.Common/Config/Mapping/Toml/{TomlMappingManaager.cs => ConfigMapper.cs} (61%) create mode 100644 src/EvoSC.Common/Config/Stores/EvoSCBaseConfigStore.cs create mode 100644 src/EvoSC.Common/Interfaces/IStartupPipeline.cs diff --git a/src/EvoSC.CLI/Attributes/CliCommandAttribute.cs b/src/EvoSC.CLI/Attributes/CliCommandAttribute.cs index b7c454dcc..014b7d3c6 100644 --- a/src/EvoSC.CLI/Attributes/CliCommandAttribute.cs +++ b/src/EvoSC.CLI/Attributes/CliCommandAttribute.cs @@ -3,12 +3,6 @@ [AttributeUsage(AttributeTargets.Class)] public class CliCommandAttribute : Attribute { - /// - /// The name of the command. - /// public required string Name { get; init; } - /// - /// Description of the command. - /// public required string Description { get; init; } } diff --git a/src/EvoSC.CLI/Attributes/CliOptionAttribute.cs b/src/EvoSC.CLI/Attributes/CliOptionAttribute.cs deleted file mode 100644 index c26761295..000000000 --- a/src/EvoSC.CLI/Attributes/CliOptionAttribute.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace EvoSC.CLI.Attributes; - -[AttributeUsage(AttributeTargets.Class)] -public class CliOptionAttribute : Attribute -{ - public string[] Aliases { get; } - public string Description { get; } - public Type TOption { get; } - - public CliOptionAttribute(Type tOption, string name, string description) - { - Aliases = new[] {name}; - TOption = tOption; - Description = description; - } - - public CliOptionAttribute(Type tOption, string description, params string[] aliases) - { - Aliases = aliases; - Description = description; - TOption = tOption; - } -} diff --git a/src/EvoSC.CLI/Attributes/RequiredFeaturesAttribute.cs b/src/EvoSC.CLI/Attributes/RequiredFeaturesAttribute.cs new file mode 100644 index 000000000..c459c7921 --- /dev/null +++ b/src/EvoSC.CLI/Attributes/RequiredFeaturesAttribute.cs @@ -0,0 +1,26 @@ +using EvoSC.Common.Application; + +namespace EvoSC.CLI.Attributes; + +[AttributeUsage(AttributeTargets.Class)] +public class RequiredFeaturesAttribute : Attribute +{ + public AppFeature[] Features { get; init; } + + public RequiredFeaturesAttribute(AppFeature feature, params AppFeature[] features) + { + var requiredFeatures = new List(); + + if (feature == AppFeature.All) + { + requiredFeatures.AddRange(Enum.GetValues().Where(f => f != AppFeature.All)); + } + else + { + requiredFeatures.Add(feature); + requiredFeatures.AddRange(features); + } + + Features = requiredFeatures.ToArray(); + } +} diff --git a/src/EvoSC.CLI/BaseStartup.cs b/src/EvoSC.CLI/BaseStartup.cs new file mode 100644 index 000000000..aef2e89ce --- /dev/null +++ b/src/EvoSC.CLI/BaseStartup.cs @@ -0,0 +1,188 @@ +using EvoSC.Commands; +using EvoSC.Commands.Interfaces; +using EvoSC.Common.Application; +using EvoSC.Common.Config.Models; +using EvoSC.Common.Controllers; +using EvoSC.Common.Database; +using EvoSC.Common.Database.Extensions; +using EvoSC.Common.Events; +using EvoSC.Common.Interfaces; +using EvoSC.Common.Interfaces.Controllers; +using EvoSC.Common.Interfaces.Middleware; +using EvoSC.Common.Interfaces.Services; +using EvoSC.Common.Logging; +using EvoSC.Common.Middleware; +using EvoSC.Common.Permissions; +using EvoSC.Common.Remote; +using EvoSC.Common.Services; +using EvoSC.Manialinks; +using EvoSC.Manialinks.Interfaces; +using EvoSC.Modules.Extensions; +using EvoSC.Modules.Interfaces; +using EvoSC.Modules.Util; +using SimpleInjector; + +namespace EvoSC.CLI; + +public static class BaseStartup +{ + public static void SetupBasePipeline(this IStartupPipeline pipeline, IEvoScBaseConfig config) + { + pipeline + .Services(AppFeature.Logging, s => s.AddEvoScLogging(config.Logging)) + + .Services(AppFeature.DatabaseMigrations, s => s.AddEvoScMigrations()) + + .Services(AppFeature.Database, s => s + .DependsOn("DatabaseMigrations") + .AddEvoScDatabase(config.Database) + ) + + .Services(AppFeature.Events, s => s.AddEvoScEvents()) + + .Services(AppFeature.GbxRemoteClient, s => s.AddGbxRemoteClient()) + + .Services(AppFeature.Modules, s => s.AddEvoScModules()) + + .Services(AppFeature.ControllerManager, s => s.AddEvoScControllers()) + + .Services(AppFeature.PlayerManager, s => s + .Register(Lifestyle.Transient) + ) + + .Services(AppFeature.MapsManager, s => s + .Register(Lifestyle.Transient) + ) + + .Services(AppFeature.PlayerCache, s => s + .RegisterSingleton() + ) + + .Services(AppFeature.MatchSettings, s => s + .Register(Lifestyle.Transient) + ) + + .Services(AppFeature.Auditing, s => s + .Register(Lifestyle.Transient) + ) + + .Services(AppFeature.ServicesManager, s => s + .RegisterSingleton() + ) + + .Services(AppFeature.ChatCommands, s => s.AddEvoScChatCommands()) + + .Services(AppFeature.ActionPipelines, s => s.AddEvoScMiddlewarePipelines()) + + .Services(AppFeature.Permissions, s => s.AddEvoScPermissions()) + + .Services(AppFeature.Manialinks, s => s.AddEvoScManialinks()) + + .Action("ActionMigrateDatabase", MigrateDatabase) + + .Action("ActionSetupControllerManager", SetupControllerManager) + + .ActionAsync("ActionSetupModules", SetupModules) + + .Action("ActionInitializeEventManager", s => s + .DependsOn("Events") + .GetInstance() + ) + + .Action("ActionInitializePlayerCache", s => s + .DependsOn("PlayerCache") + .GetInstance() + ) + + .Action("ActionInitializeManialinkInteractionHandler", s => s + .DependsOn("ActionInitializeEventManager", "ActionInitializePlayerCache") + ) + + .ActionAsync("InitializeGbxRemoteConnection", SetupGbxRemoteConnection) + + .ActionAsync("ActionEnableModules", EnableModules) + + .ActionAsync("ActionInitializeTemplates", InitializeTemplates); + } + + private static async Task InitializeTemplates(ServicesBuilder s) + { + s.DependsOn("Manialinks"); + + var maniaLinks = s.GetInstance(); + await maniaLinks.PreprocessAllAsync(); + } + + private static async Task EnableModules(ServicesBuilder s) + { + await s.GetInstance().AddDefaultTemplatesAsync(); + await s.GetInstance().EnableModulesAsync(); + } + + private static void MigrateDatabase(ServicesBuilder s) + { + s.DependsOn("DatabaseMigrations"); + using var scope = new Scope(s); + var manager = scope.GetInstance(); + + // main migrations + manager.MigrateFromAssembly(typeof(MigrationManager).Assembly); + + // internal modules + // manager.RunInternalModuleMigrations(); + } + + private static void SetupControllerManager(ServicesBuilder s) + { + s.DependsOn("ControllerManager", "ActionPipelines"); + + var controllers = s.GetInstance(); + controllers.AddControllerActionRegistry(s.GetInstance()); + controllers.AddControllerActionRegistry(s.GetInstance()); + controllers.AddControllerActionRegistry(s.GetInstance()); + + var pipelineManager = s.GetInstance(); + pipelineManager.UseEvoScCommands(s); + pipelineManager.UseEvoScManialinks(s); + } + + private static async Task SetupModules(ServicesBuilder s) + { + s.DependsOn("Modules", "Config"); + + var modules = s.GetInstance(); + var config = s.GetInstance(); + + // await modules.LoadInternalModulesAsync(); + + var dirs = config.Modules.ModuleDirectories; + var externalModules = new SortedModuleCollection(); + foreach (var dir in dirs) + { + if (!Directory.Exists(dir)) + { + continue; + } + + ModuleDirectoryUtils.FindModulesFromDirectory(dir, externalModules); + } + + externalModules.SetIgnoredDependencies(modules.LoadedModules.Select(m => m.ModuleInfo.Name)); + await modules.LoadAsync(externalModules); + } + + private static async Task SetupGbxRemoteConnection(ServicesBuilder s) + { + s.DependsOn( + "ActionInitializeEventManager", + "ActionInitializePlayerCache", + "ActionInitializeManialinkInteractionHandler", + "GbxRemoteClient" + ); + + var serverClient = s.GetInstance(); + s.GetInstance(); + s.GetInstance(); + await serverClient.StartAsync(CancellationToken.None); + } +} diff --git a/src/EvoSC.CLI/CliCommandContext.cs b/src/EvoSC.CLI/CliCommandContext.cs deleted file mode 100644 index 3c1ca1bf1..000000000 --- a/src/EvoSC.CLI/CliCommandContext.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.CommandLine.Invocation; - -namespace EvoSC.CLI; - -public class CliCommandContext -{ - public required InvocationContext InvocationContext { get; init; } - public required string[] Args { get; init; } -} diff --git a/src/EvoSC.CLI/CliHandler.cs b/src/EvoSC.CLI/CliHandler.cs deleted file mode 100644 index 50704edef..000000000 --- a/src/EvoSC.CLI/CliHandler.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.CommandLine; -using System.Reflection; -using EvoSC.CLI.Attributes; -using EvoSC.CLI.Exceptions; -using EvoSC.CLI.Interfaces; - -namespace EvoSC.CLI; - -public class CliHandler -{ - private readonly RootCommand _rootCommand; - private readonly string[] _args; - - public CliHandler(string[] args) - { - _args = args; - _rootCommand = new RootCommand("EvoSC# TrackMania Server Controller."); - } - - /// - /// Register a command to the command handler. - /// - /// An instance that implements the command. - /// - public CliHandler RegisterCommand(ICliCommand cliCmd) - { - var cmdAttr = GetCommandInfo(cliCmd); - var cmd = new Command(cmdAttr.Name, cmdAttr.Description); - - var options = GetCommandOptions(cliCmd); - foreach (var option in options) - { - cmd.AddOption(option); - } - - cmd.SetHandler((invocationContext) => - { - var context = new CliCommandContext {InvocationContext = invocationContext, Args = _args}; - cliCmd.ExecuteAsync(CancellationToken.None, context).GetAwaiter().GetResult(); - }); - - _rootCommand.AddCommand(cmd); - return this; - } - - /// - /// Check user input and run the appropriate commands if possible. - /// - /// - public Task HandleAsync() - { - return _rootCommand.InvokeAsync(_args); - } - - private IEnumerable