diff --git a/Refresh.Core/Configuration/ConfigStore.cs b/Refresh.Core/Configuration/ConfigStore.cs new file mode 100644 index 000000000..82c65e5f2 --- /dev/null +++ b/Refresh.Core/Configuration/ConfigStore.cs @@ -0,0 +1,55 @@ +using Bunkum.Core; +using Bunkum.Core.Configuration; +using NotEnoughLogs; + +namespace Refresh.Core.Configuration; + +public class ConfigStore +{ + public GameServerConfig GameServer { get; } + public DatabaseConfig Database { get; } + + public ContactInfoConfig ContactInfo { get; } + public IntegrationConfig Integration { get; } + public RichPresenceConfig RichPresence { get; } + + public DryArchiveConfig DryArchive { get; } + + private static readonly Lock ConfigLock = new(); + public ConfigStore(Logger logger) + { + lock (ConfigLock) + { + this.GameServer = Config.LoadFromJsonFile("refreshGameServer.json", logger); + this.Database = Config.LoadFromJsonFile("db.json", logger); + + this.ContactInfo = Config.LoadFromJsonFile("contactInfo.json", logger); + this.Integration = Config.LoadFromJsonFile("integrations.json", logger); + this.RichPresence = Config.LoadFromJsonFile("rpc.json", logger); + + this.DryArchive = Config.LoadFromJsonFile("dry.json", logger); + } + } + + public ConfigStore() + { + this.GameServer = new GameServerConfig(); + this.Database = new DatabaseConfig(); + + this.ContactInfo = new ContactInfoConfig(); + this.Integration = new IntegrationConfig(); + this.RichPresence = new RichPresenceConfig(); + + this.DryArchive = new DryArchiveConfig(); + } + + public void AddToBunkum(BunkumServer server) + { + server.AddConfig(this.GameServer); + server.AddConfig(this.Database); + server.AddConfig(this.ContactInfo); + server.AddConfig(this.Integration); + server.AddConfig(this.RichPresence); + server.AddConfig(this.DryArchive); + } +} \ No newline at end of file diff --git a/Refresh.Core/Storage/DryArchiveConfig.cs b/Refresh.Core/Configuration/DryArchiveConfig.cs similarity index 93% rename from Refresh.Core/Storage/DryArchiveConfig.cs rename to Refresh.Core/Configuration/DryArchiveConfig.cs index c79fbe801..8dd7f3550 100644 --- a/Refresh.Core/Storage/DryArchiveConfig.cs +++ b/Refresh.Core/Configuration/DryArchiveConfig.cs @@ -1,6 +1,6 @@ using Bunkum.Core.Configuration; -namespace Refresh.Core.Storage; +namespace Refresh.Core.Configuration; public class DryArchiveConfig : Config { diff --git a/Refresh.Core/Storage/DryDataStore.cs b/Refresh.Core/Storage/DryDataStore.cs index 9c00cb1a4..5b3ea1585 100644 --- a/Refresh.Core/Storage/DryDataStore.cs +++ b/Refresh.Core/Storage/DryDataStore.cs @@ -3,6 +3,7 @@ using Bunkum.Core.Storage; using JetBrains.Annotations; using Refresh.Common.Verification; +using Refresh.Core.Configuration; using Refresh.Core.Metrics; namespace Refresh.Core.Storage; diff --git a/Refresh.GameServer/RefreshGameServer.cs b/Refresh.GameServer/RefreshGameServer.cs index 3af37fba7..8f6fba5be 100644 --- a/Refresh.GameServer/RefreshGameServer.cs +++ b/Refresh.GameServer/RefreshGameServer.cs @@ -43,11 +43,7 @@ public class RefreshGameServer : RefreshServer protected readonly GameDatabaseProvider _databaseProvider; protected readonly IDataStore _dataStore; - protected MatchService _matchService = null!; - protected GuidCheckerService _guidCheckerService = null!; - - protected GameServerConfig? _config; - protected IntegrationConfig? _integrationConfig; + protected readonly ConfigStore _configStore; public RefreshGameServer( BunkumHttpListener? listener = null, @@ -59,34 +55,22 @@ public RefreshGameServer( dataStore ??= new FileSystemDataStore(); List dataStores = []; - DryArchiveConfig? dryConfig = null; try { - dryConfig = Config.LoadFromJsonFile("dry.json", this.Logger); - if (dryConfig.Enabled) - dataStores.Add(new DownloadingDataStore(dataStore, new DryDataStore(dryConfig))); + // ReSharper disable once VirtualMemberCallInConstructor (you can't stop me) + this._configStore = this.CreateConfigStore(); } catch (Exception ex) { - this.Logger.LogWarning(BunkumCategory.Configuration, "Failed to read dry.json: " + ex); + this.Logger.LogCritical(RefreshContext.Database, "Failed to read the configuration files: " + ex); + this.Logger.Dispose(); + Environment.Exit(1); } + + if (this._configStore.DryArchive.Enabled) + dataStores.Add(new DownloadingDataStore(dataStore, new DryDataStore(this._configStore.DryArchive))); - if (databaseProvider == null) - { - DatabaseConfig? dbConfig = null; - try - { - dbConfig = Config.LoadFromJsonFile("db.json", this.Logger); - } - catch (Exception ex) - { - this.Logger.LogCritical(RefreshContext.Database, "Failed to read the database configuration file: " + ex); - this.Logger.Dispose(); - Environment.Exit(1); - } - - databaseProvider = () => new GameDatabaseProvider(this.Logger, dbConfig); - } + databaseProvider ??= () => new GameDatabaseProvider(this.Logger, this._configStore.Database); this._databaseProvider = databaseProvider.Invoke(); this._databaseProvider.Initialize(); @@ -94,7 +78,7 @@ public RefreshGameServer( // Uncomment if you want to use production refresh as a source for assets // TODO: remove config option when test.lbpbonsai.com instance no longer needs prod assets #if DEBUG - if (dryConfig?.TemporaryWillBeRemoved_UseProductionRefreshData ?? false) + if (this._configStore.DryArchive?.TemporaryWillBeRemoved_UseProductionRefreshData ?? false) dataStores.Add(new DownloadingDataStore(dataStore, new RemoteRefreshDataStore())); #endif @@ -110,12 +94,17 @@ public RefreshGameServer( this.WorkerManager?.Stop(); - authProvider ??= new GameAuthenticationProvider(this._config!); + authProvider ??= new GameAuthenticationProvider(this._configStore.GameServer); this.InjectBaseServices(provider, authProvider, this._dataStore); }); } + protected virtual ConfigStore CreateConfigStore() + { + return new ConfigStore(this.Logger); + } + private void InjectBaseServices(GameDatabaseProvider databaseProvider, IAuthenticationProvider authProvider, IDataStore dataStore) { this.Server.UseDatabaseProvider(databaseProvider); @@ -135,28 +124,19 @@ protected override void Initialize() protected override void SetupMiddlewares() { this.Server.AddMiddleware(); - this.Server.AddMiddleware(new DeflateMiddleware(this._config!)); + this.Server.AddMiddleware(new DeflateMiddleware(this._configStore.GameServer)); this.Server.AddMiddleware(); // Digest middleware must be run before LegacyAdapterMiddleware, because digest is based on the raw route, not the fixed route - this.Server.AddMiddleware(new DigestMiddleware(this._config!)); + this.Server.AddMiddleware(new DigestMiddleware(this._configStore.GameServer)); this.Server.AddMiddleware(); this.Server.AddMiddleware(); - this.Server.AddMiddleware(new PresenceAuthenticationMiddleware(this._integrationConfig!)); + this.Server.AddMiddleware(new PresenceAuthenticationMiddleware(this._configStore.Integration!)); this.Server.AddMiddleware(); } protected override void SetupConfiguration() { - GameServerConfig config = Config.LoadFromJsonFile("refreshGameServer.json", this.Server.Logger); - this._config = config; - - IntegrationConfig integrationConfig = Config.LoadFromJsonFile("integrations.json", this.Server.Logger); - this._integrationConfig = integrationConfig; - - this.Server.AddConfig(config); - this.Server.AddConfig(integrationConfig); - this.Server.AddConfigFromJsonFile("rpc.json"); - this.Server.AddConfigFromJsonFile("contactInfo.json"); + this._configStore.AddToBunkum(this.Server); } protected override void SetupServices() @@ -164,22 +144,21 @@ protected override void SetupServices() this.Server.AddService(this.GetTimeProvider()); this.Server.AddRateLimitService(new RateLimitSettings(60, 400, 30, "global")); this.Server.AddService(); - this.Server.AddService(this._matchService = new MatchService(this.Server.Logger, this._config!)); + this.Server.AddService(new MatchService(this.Server.Logger, this._configStore.GameServer)); this.Server.AddService(); this.Server.AddService(); - this.Server.AddService(this._guidCheckerService = new GuidCheckerService(this._config!, this.Server.Logger)); - this.Server.AddAutoDiscover(serverBrand: $"{this._config!.InstanceName} (Refresh)", - baseEndpoint: GameEndpointAttribute.BaseRoute.Substring(0, GameEndpointAttribute.BaseRoute.Length - 1), + this.Server.AddService(new GuidCheckerService(this._configStore.GameServer, this.Server.Logger)); + this.Server.AddAutoDiscover(serverBrand: $"{this._configStore.GameServer.InstanceName} (Refresh)", + baseEndpoint: GameEndpointAttribute.BaseRoute[..^1], usesCustomDigestKey: true, - serverDescription: this._config.InstanceDescription, + serverDescription: this._configStore.GameServer.InstanceDescription, bannerImageUrl: "https://github.com/LittleBigRefresh/Branding/blob/main/logos/refresh_type.png?raw=true"); #pragma warning disable CA1825 #pragma warning disable CA1861 - this.Server.AddHealthCheckService(this._databaseProvider, new Type[] - { + this.Server.AddHealthCheckService(this._databaseProvider, [ // TODO: add postgres health check - }); + ]); #pragma warning restore CA1861 #pragma warning restore CA1825 @@ -191,7 +170,7 @@ protected override void SetupServices() this.Server.AddService(); this.Server.AddService(); - if(this._integrationConfig!.AipiEnabled) + if(this._configStore.Integration!.AipiEnabled) this.Server.AddService(); #if DEBUG @@ -208,9 +187,9 @@ protected virtual void SetupWorkers() { this.WorkerManager = RefreshWorkerManager.Create(this.Logger, this._dataStore, this._databaseProvider); - if ((this._integrationConfig?.DiscordWebhookEnabled ?? false) && this._config != null && this._config.PermitShowingOnlineUsers) + if (this._configStore.Integration.DiscordWebhookEnabled && this._configStore.GameServer.PermitShowingOnlineUsers) { - this.WorkerManager.AddJob(new DiscordIntegrationJob(this._integrationConfig, this._config)); + this.WorkerManager.AddJob(new DiscordIntegrationJob(this._configStore.Integration, this._configStore.GameServer)); } } @@ -220,7 +199,7 @@ public override void Start() this.Server.Start(); this.WorkerManager?.Start(); - if (this._config!.MaintenanceMode) + if (this._configStore.GameServer.MaintenanceMode) { this.Logger.LogWarning(RefreshContext.Startup, "The server is currently in maintenance mode! " + "Only administrators will be able to log in and interact with the server."); diff --git a/Refresh.WorkerManager/Program.cs b/Refresh.WorkerManager/Program.cs index bcf158956..c458fa38c 100644 --- a/Refresh.WorkerManager/Program.cs +++ b/Refresh.WorkerManager/Program.cs @@ -2,6 +2,7 @@ using Bunkum.Core.Storage; using NotEnoughLogs; using NotEnoughLogs.Behaviour; +using Refresh.Core.Configuration; using Refresh.Database; using Refresh.Database.Configuration; using Refresh.Interfaces.Workers; @@ -22,8 +23,11 @@ logger.LogInfo(BunkumCategory.Startup, "Starting up worker manager..."); logger.LogCritical(BunkumCategory.Startup, "The dedicated worker manager isn't complete yet! It will work in a debug setting, but do not use this in production yet."); -using GameDatabaseProvider database = new(logger, new EmptyDatabaseConfig()); +logger.LogInfo(BunkumCategory.Startup, "Initializing configs..."); +ConfigStore configStore = new(logger); + logger.LogInfo(BunkumCategory.Startup, "Initializing database..."); +using GameDatabaseProvider database = new(logger, configStore.Database); database.Initialize(); logger.LogInfo(BunkumCategory.Startup, "Warming up database..."); database.Warmup(); diff --git a/Refresh.Workers/WorkerManager.cs b/Refresh.Workers/WorkerManager.cs index 5e79d614a..cfcab9a8c 100644 --- a/Refresh.Workers/WorkerManager.cs +++ b/Refresh.Workers/WorkerManager.cs @@ -54,7 +54,7 @@ private void RunWorkCycle() if (!job.CanExecute()) continue; - this._logger.LogDebug(RefreshContext.Worker, $"Running work cycle for {job.GetType().Name}"); + this._logger.LogTrace(RefreshContext.Worker, $"Running work cycle for {job.GetType().Name}"); try { job.ExecuteJob(context); diff --git a/RefreshTests.GameServer/GameServer/TestRefreshGameServer.cs b/RefreshTests.GameServer/GameServer/TestRefreshGameServer.cs index 0fa4a5573..824e93ab8 100644 --- a/RefreshTests.GameServer/GameServer/TestRefreshGameServer.cs +++ b/RefreshTests.GameServer/GameServer/TestRefreshGameServer.cs @@ -24,15 +24,12 @@ public class TestRefreshGameServer : RefreshGameServer public TestRefreshGameServer(BunkumHttpListener listener, Func provider, IDataStore? dataStore = null) : base(listener, provider, null, dataStore ?? new InMemoryDataStore()) {} - protected override void SetupConfiguration() + protected override ConfigStore CreateConfigStore() { - this.Server.AddConfig(this._config = new GameServerConfig()); - this.Server.AddConfig(new RichPresenceConfig()); - this.Server.AddConfig(this._integrationConfig = new IntegrationConfig()); - this.Server.AddConfig(new ContactInfoConfig()); + return new ConfigStore(); } - public GameServerConfig GameServerConfig => this._config!; + public GameServerConfig GameServerConfig => this._configStore.GameServer; public override void Start() { @@ -71,7 +68,7 @@ protected override void SetupServices() this.Server.AddService(); this.Server.AddService(); this.Server.AddService(); - this.Server.AddService(new PresenceService(this.Logger, this._integrationConfig!)); + this.Server.AddService(new PresenceService(this.Logger, this._configStore.Integration!)); this.Server.AddService(); this.Server.AddService(); this.Server.AddService();