diff --git a/Libraries/SPTarkov.Server.Core/Callbacks/HttpCallbacks.cs b/Libraries/SPTarkov.Server.Core/Callbacks/HttpCallbacks.cs deleted file mode 100644 index 4da8d202..00000000 --- a/Libraries/SPTarkov.Server.Core/Callbacks/HttpCallbacks.cs +++ /dev/null @@ -1,21 +0,0 @@ -using SPTarkov.DI.Annotations; -using SPTarkov.Server.Core.DI; -using SPTarkov.Server.Core.Servers; - -namespace SPTarkov.Server.Core.Callbacks; - -[Injectable(InjectionType.Singleton, TypePriority = OnLoadOrder.HttpCallbacks)] -public class HttpCallbacks(HttpServer httpServer) : IOnLoad -{ - public Task OnLoad() - { - httpServer.Load(); - - return Task.CompletedTask; - } - - public string GetImage() - { - return ""; - } -} diff --git a/Libraries/SPTarkov.Server.Core/Callbacks/NotifierCallbacks.cs b/Libraries/SPTarkov.Server.Core/Callbacks/NotifierCallbacks.cs index 77bd46c2..5efd9e83 100644 --- a/Libraries/SPTarkov.Server.Core/Callbacks/NotifierCallbacks.cs +++ b/Libraries/SPTarkov.Server.Core/Callbacks/NotifierCallbacks.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Http; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Controllers; using SPTarkov.Server.Core.Helpers; diff --git a/Libraries/SPTarkov.Server.Core/DI/ISerializer.cs b/Libraries/SPTarkov.Server.Core/DI/ISerializer.cs index 42e052ae..9b6aba28 100644 --- a/Libraries/SPTarkov.Server.Core/DI/ISerializer.cs +++ b/Libraries/SPTarkov.Server.Core/DI/ISerializer.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Http; using SPTarkov.Server.Core.Models.Common; namespace SPTarkov.Server.Core.DI; diff --git a/Libraries/SPTarkov.Server.Core/Helpers/HttpServerHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/HttpServerHelper.cs index 480a5714..0efbf267 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/HttpServerHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/HttpServerHelper.cs @@ -1,6 +1,7 @@ using System.Collections.Frozen; using System.Net; using System.Net.Sockets; +using Microsoft.AspNetCore.Http; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Servers; diff --git a/Libraries/SPTarkov.Server.Core/Migration/Migrations/ThreeTenToThreeEleven.cs b/Libraries/SPTarkov.Server.Core/Migration/Migrations/ThreeTenToThreeEleven.cs index 6c210d82..4d4b1097 100644 --- a/Libraries/SPTarkov.Server.Core/Migration/Migrations/ThreeTenToThreeEleven.cs +++ b/Libraries/SPTarkov.Server.Core/Migration/Migrations/ThreeTenToThreeEleven.cs @@ -85,8 +85,8 @@ namespace SPTarkov.Server.Core.Migration.Migrations ); //Directly injecting CreateProfileService causes a circular dependency which I can't be bothered to fix just for this - serviceProvider - .GetService()! + (serviceProvider + .GetService(typeof(CreateProfileService)) as CreateProfileService)! .AddMissingInternalContainersToProfile(profile.CharacterData.PmcData); } @@ -135,8 +135,8 @@ namespace SPTarkov.Server.Core.Migration.Migrations .ToList(); //Directly injecting RewardHelper causes a circular dependency which I can't be bothered to fix just for this - serviceProvider - .GetService()! + (serviceProvider + .GetService(typeof(RewardHelper)) as RewardHelper)! .ApplyRewards( filteredRewards, CustomisationSource.ACHIEVEMENT, diff --git a/Libraries/SPTarkov.Server.Core/Routers/HttpRouter.cs b/Libraries/SPTarkov.Server.Core/Routers/HttpRouter.cs index 101bd788..2dc5648e 100644 --- a/Libraries/SPTarkov.Server.Core/Routers/HttpRouter.cs +++ b/Libraries/SPTarkov.Server.Core/Routers/HttpRouter.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Http; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.DI; using SPTarkov.Server.Core.Models.Common; diff --git a/Libraries/SPTarkov.Server.Core/Routers/ImageRouter.cs b/Libraries/SPTarkov.Server.Core/Routers/ImageRouter.cs index 0f6dcd46..c390a9a5 100644 --- a/Libraries/SPTarkov.Server.Core/Routers/ImageRouter.cs +++ b/Libraries/SPTarkov.Server.Core/Routers/ImageRouter.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Http; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Utils; diff --git a/Libraries/SPTarkov.Server.Core/Routers/Serializers/BundleSerializer.cs b/Libraries/SPTarkov.Server.Core/Routers/Serializers/BundleSerializer.cs index 65b95c35..d5822bf9 100644 --- a/Libraries/SPTarkov.Server.Core/Routers/Serializers/BundleSerializer.cs +++ b/Libraries/SPTarkov.Server.Core/Routers/Serializers/BundleSerializer.cs @@ -1,4 +1,5 @@ -using SPTarkov.DI.Annotations; +using Microsoft.AspNetCore.Http; +using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.DI; using SPTarkov.Server.Core.Loaders; using SPTarkov.Server.Core.Models.Common; diff --git a/Libraries/SPTarkov.Server.Core/Routers/Serializers/ImageSerializer.cs b/Libraries/SPTarkov.Server.Core/Routers/Serializers/ImageSerializer.cs index ec4e832f..bc748f9d 100644 --- a/Libraries/SPTarkov.Server.Core/Routers/Serializers/ImageSerializer.cs +++ b/Libraries/SPTarkov.Server.Core/Routers/Serializers/ImageSerializer.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Http; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.DI; using SPTarkov.Server.Core.Models.Common; diff --git a/Libraries/SPTarkov.Server.Core/Routers/Serializers/NotifySerializer.cs b/Libraries/SPTarkov.Server.Core/Routers/Serializers/NotifySerializer.cs index 4eefe1ed..9d27c8bf 100644 --- a/Libraries/SPTarkov.Server.Core/Routers/Serializers/NotifySerializer.cs +++ b/Libraries/SPTarkov.Server.Core/Routers/Serializers/NotifySerializer.cs @@ -1,4 +1,5 @@ -using SPTarkov.DI.Annotations; +using Microsoft.AspNetCore.Http; +using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Controllers; using SPTarkov.Server.Core.DI; using SPTarkov.Server.Core.Helpers; diff --git a/Libraries/SPTarkov.Server.Core/SPTarkov.Server.Core.csproj b/Libraries/SPTarkov.Server.Core/SPTarkov.Server.Core.csproj index fca8dc1e..24fd504e 100644 --- a/Libraries/SPTarkov.Server.Core/SPTarkov.Server.Core.csproj +++ b/Libraries/SPTarkov.Server.Core/SPTarkov.Server.Core.csproj @@ -1,4 +1,4 @@ - + SPTarkov.Server.Core diff --git a/Libraries/SPTarkov.Server.Core/Servers/Http/IHttpListener.cs b/Libraries/SPTarkov.Server.Core/Servers/Http/IHttpListener.cs index af794c01..8fdb0425 100644 --- a/Libraries/SPTarkov.Server.Core/Servers/Http/IHttpListener.cs +++ b/Libraries/SPTarkov.Server.Core/Servers/Http/IHttpListener.cs @@ -1,4 +1,6 @@ -namespace SPTarkov.Server.Core.Servers.Http; +using Microsoft.AspNetCore.Http; + +namespace SPTarkov.Server.Core.Servers.Http; public interface IHttpListener { diff --git a/Libraries/SPTarkov.Server.Core/Servers/Http/SptHttpListener.cs b/Libraries/SPTarkov.Server.Core/Servers/Http/SptHttpListener.cs index 90b17544..7547c929 100644 --- a/Libraries/SPTarkov.Server.Core/Servers/Http/SptHttpListener.cs +++ b/Libraries/SPTarkov.Server.Core/Servers/Http/SptHttpListener.cs @@ -1,6 +1,7 @@ using System.Collections.Immutable; using System.IO.Compression; using System.Text; +using Microsoft.AspNetCore.Http; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.DI; using SPTarkov.Server.Core.Models.Enums; diff --git a/Libraries/SPTarkov.Server.Core/Servers/HttpServer.cs b/Libraries/SPTarkov.Server.Core/Servers/HttpServer.cs index 853bbbed..8a808168 100644 --- a/Libraries/SPTarkov.Server.Core/Servers/HttpServer.cs +++ b/Libraries/SPTarkov.Server.Core/Servers/HttpServer.cs @@ -1,10 +1,7 @@ -using System.Net; -using System.Security.Authentication; -using Microsoft.AspNetCore.Server.Kestrel.Https; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; using SPTarkov.Common.Extensions; using SPTarkov.DI.Annotations; -using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Servers.Http; @@ -14,82 +11,17 @@ namespace SPTarkov.Server.Core.Servers; [Injectable(InjectionType.Singleton)] public class HttpServer( - WebApplicationBuilder _builder, ISptLogger _logger, ServerLocalisationService _serverLocalisationService, ConfigServer _configServer, - CertificateHelper _certificateHelper, WebSocketServer _webSocketServer, ProfileActivityService _profileActivityService, IEnumerable _httpListeners ) { private readonly HttpConfig _httpConfig = _configServer.GetConfig(); - private bool _started; - private WebApplication? _webApplication; - /// - /// Handle server loading event - /// - /// Throws Exception when WebApplicationBuilder or WebApplication are null - public void Load() - { - if (_builder is null) - { - throw new Exception("WebApplicationBuilder is null in HttpServer.Load()"); - } - - _builder.WebHost.ConfigureKestrel(options => - { - options.Listen( - IPAddress.Parse(_httpConfig.Ip), - _httpConfig.Port, - listenOptions => - { - listenOptions.UseHttps(opts => - { - opts.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; - opts.ServerCertificate = _certificateHelper.LoadOrGenerateCertificatePfx(); - opts.ClientCertificateMode = ClientCertificateMode.NoCertificate; - }); - } - ); - }); - - _webApplication = _builder.Build(); - - if (_webApplication is null) - { - throw new Exception("WebApplication is null in HttpServer.Load()"); - } - - // Enable web socket - _webApplication.UseWebSockets( - new WebSocketOptions - { - // Every minute a heartbeat is sent to keep the connection alive. - KeepAliveInterval = TimeSpan.FromSeconds(60), - } - ); - - _webApplication.Use( - async (HttpContext req, RequestDelegate _) => - { - await HandleFallback(req); - } - ); - } - - public async Task StartAsync() - { - if (_webApplication != null && !_started) - { - _started = true; - await _webApplication.RunAsync(); - } - } - - private async Task HandleFallback(HttpContext context) + public async Task HandleRequest(HttpContext context) { if (context.WebSockets.IsWebSocketRequest) { @@ -203,11 +135,6 @@ public class HttpServer( return found; } - public bool IsStarted() - { - return _started; - } - public string ListeningUrl() { return $"https://{_httpConfig.Ip}:{_httpConfig.Port}"; diff --git a/Libraries/SPTarkov.Server.Core/Servers/WebSocketServer.cs b/Libraries/SPTarkov.Server.Core/Servers/WebSocketServer.cs index d21a1ab5..8e66d3cf 100644 --- a/Libraries/SPTarkov.Server.Core/Servers/WebSocketServer.cs +++ b/Libraries/SPTarkov.Server.Core/Servers/WebSocketServer.cs @@ -1,4 +1,5 @@ using System.Net.WebSockets; +using Microsoft.AspNetCore.Http; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Servers.Ws; diff --git a/Libraries/SPTarkov.Server.Core/Servers/Ws/IWebSocketConnectionHandler.cs b/Libraries/SPTarkov.Server.Core/Servers/Ws/IWebSocketConnectionHandler.cs index 36230428..3fe82192 100644 --- a/Libraries/SPTarkov.Server.Core/Servers/Ws/IWebSocketConnectionHandler.cs +++ b/Libraries/SPTarkov.Server.Core/Servers/Ws/IWebSocketConnectionHandler.cs @@ -1,4 +1,5 @@ using System.Net.WebSockets; +using Microsoft.AspNetCore.Http; namespace SPTarkov.Server.Core.Servers.Ws; diff --git a/Libraries/SPTarkov.Server.Core/Servers/Ws/SptWebSocketConnectionHandler.cs b/Libraries/SPTarkov.Server.Core/Servers/Ws/SptWebSocketConnectionHandler.cs index 8ef8d6a9..ec24e22d 100644 --- a/Libraries/SPTarkov.Server.Core/Servers/Ws/SptWebSocketConnectionHandler.cs +++ b/Libraries/SPTarkov.Server.Core/Servers/Ws/SptWebSocketConnectionHandler.cs @@ -1,5 +1,6 @@ using System.Net.WebSockets; using System.Text; +using Microsoft.AspNetCore.Http; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Models.Eft.Ws; diff --git a/Libraries/SPTarkov.Server.Core/Status/StatusPage.cs b/Libraries/SPTarkov.Server.Core/Status/StatusPage.cs index fe177891..659aa9ed 100644 --- a/Libraries/SPTarkov.Server.Core/Status/StatusPage.cs +++ b/Libraries/SPTarkov.Server.Core/Status/StatusPage.cs @@ -1,4 +1,5 @@ using System.Text; +using Microsoft.AspNetCore.Http; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Servers; diff --git a/Libraries/SPTarkov.Server.Core/Utils/App.cs b/Libraries/SPTarkov.Server.Core/Utils/App.cs index c352020a..2ebc205c 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/App.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/App.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Hosting; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.DI; using SPTarkov.Server.Core.Extensions; @@ -75,29 +76,21 @@ public class App( // Discard here, as this task will run indefinitely _ = Task.Run(Update); - } - public async Task StartAsync() - { - if (!_httpServer.IsStarted()) - { - _logger.Success( - _serverLocalisationService.GetText( - "started_webserver_success", - _httpServer.ListeningUrl() - ) - ); - _logger.Success( - _serverLocalisationService.GetText( - "websocket-started", - _httpServer.ListeningUrl().Replace("https://", "wss://") - ) - ); - } + _logger.Success( + _serverLocalisationService.GetText( + "started_webserver_success", + _httpServer.ListeningUrl() + ) + ); + _logger.Success( + _serverLocalisationService.GetText( + "websocket-started", + _httpServer.ListeningUrl().Replace("https://", "wss://") + ) + ); _logger.Success(GetRandomisedStartMessage()); - - await _httpServer.StartAsync(); } protected string GetRandomisedStartMessage() @@ -117,7 +110,7 @@ public class App( while (!_appLifeTime.ApplicationStopping.IsCancellationRequested) { // If the server has failed to start, skip any update calls - if (!_httpServer.IsStarted() || !_databaseService.IsDatabaseValid()) + if (!_databaseService.IsDatabaseValid()) { await Task.Delay(5000, _appLifeTime.ApplicationStopping); diff --git a/Libraries/SPTarkov.Server.Core/Utils/HttpFileUtil.cs b/Libraries/SPTarkov.Server.Core/Utils/HttpFileUtil.cs index 7c9d5639..e901be96 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/HttpFileUtil.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/HttpFileUtil.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Http; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Helpers; diff --git a/Libraries/SPTarkov.Server.Core/Utils/Json/Converters/SptJsonConverterRegistrator.cs b/Libraries/SPTarkov.Server.Core/Utils/Json/Converters/SptJsonConverterRegistrator.cs index 1491f9c5..2b085b4d 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/Json/Converters/SptJsonConverterRegistrator.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/Json/Converters/SptJsonConverterRegistrator.cs @@ -1,5 +1,6 @@ using System.Reflection; using System.Text.Json.Serialization; +using Microsoft.Extensions.Logging; using SPTarkov.DI.Annotations; namespace SPTarkov.Server.Core.Utils.Json.Converters; diff --git a/SPTarkov.Server/Program.cs b/SPTarkov.Server/Program.cs index 9f7e2119..0bb1500c 100644 --- a/SPTarkov.Server/Program.cs +++ b/SPTarkov.Server/Program.cs @@ -1,16 +1,23 @@ +using System.Net; using System.Runtime; using System.Runtime.InteropServices; +using System.Security.Authentication; using System.Text; +using Microsoft.AspNetCore.Server.Kestrel.Https; using SPTarkov.Common.Semver; using SPTarkov.Common.Semver.Implementations; using SPTarkov.DI; +using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Loaders; +using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Spt.Mod; using SPTarkov.Server.Core.Models.Utils; +using SPTarkov.Server.Core.Servers; using SPTarkov.Server.Core.Utils; using SPTarkov.Server.Core.Utils.Logger; using SPTarkov.Server.Logger; using SPTarkov.Server.Modding; +using SPTarkov.Server.Services; namespace SPTarkov.Server; @@ -60,57 +67,73 @@ public static class Program builder.Services.AddSingleton(builder); builder.Services.AddSingleton>(loadedMods); - var serviceProvider = builder.Services.BuildServiceProvider(); - var logger = serviceProvider.GetService().CreateLogger("Server"); - // Load bundles for bundle mods - if (ProgramStatics.MODS()) - { - var bundleLoader = serviceProvider.GetService(); - foreach (var mod in loadedMods) - { - if (mod.ModMetadata?.IsBundleMod == true) - { - // Convert to relative path - string relativeModPath = Path.GetRelativePath( - Directory.GetCurrentDirectory(), - mod.Directory - ) - .Replace('\\', '/'); + builder.Services.AddHostedService(); + // Configure Kestrel options + ConfigureKestrel(builder); - bundleLoader.AddBundles(relativeModPath); - } - } - } + var app = builder.Build(); + + // Configure Kestrel WS options and Handle fallback requests + ConfigureWebApp(app); + + // In case of exceptions we snatch a Server logger + var serverExceptionLogger = app.Services.GetService()!.CreateLogger("Server"); + // We need any logger instance to use as a finalizer when the app closes + var loggerFinalizer = app.Services.GetService>()!; try { SetConsoleOutputMode(); - // Get the Built app and run it - var app = serviceProvider.GetService(); - - if (app != null) - { - await app.InitializeAsync(); - - // Run garbage collection now the server is ready to start - GCSettings.LargeObjectHeapCompactionMode = - GCLargeObjectHeapCompactionMode.CompactOnce; - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); - - await app.StartAsync(); - } + await app.RunAsync(); } catch (Exception ex) { Console.WriteLine(ex); - logger.LogCritical(ex, "Critical exception, stopping server..."); + serverExceptionLogger.LogCritical(ex, "Critical exception, stopping server..."); } finally { - serviceProvider.GetService>()?.DumpAndStop(); + loggerFinalizer.DumpAndStop(); } } + private static void ConfigureWebApp(WebApplication app) + { + app.UseWebSockets( + new WebSocketOptions + { + // Every minute a heartbeat is sent to keep the connection alive. + KeepAliveInterval = TimeSpan.FromSeconds(60) + } + ); + app.Use(async (HttpContext context, RequestDelegate _) => + { + await context.RequestServices.GetService()!.HandleRequest(context); + }); + } + + private static void ConfigureKestrel(WebApplicationBuilder builder) + { + builder.WebHost.ConfigureKestrel((_, options) => + { + var httpConfig = options.ApplicationServices.GetService()?.GetConfig()!; + var certHelper = options.ApplicationServices.GetService()!; + options.Listen( + IPAddress.Parse(httpConfig.Ip), + httpConfig.Port, + listenOptions => + { + listenOptions.UseHttps(opts => + { + opts.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; + opts.ServerCertificate = certHelper.LoadOrGenerateCertificatePfx(); + opts.ClientCertificateMode = ClientCertificateMode.NoCertificate; + }); + } + ); + }); + } + private static WebApplicationBuilder CreateNewHostBuilder(string[]? args = null) { var builder = WebApplication.CreateBuilder(args); diff --git a/SPTarkov.Server/Services/SptServerBackgroundService.cs b/SPTarkov.Server/Services/SptServerBackgroundService.cs new file mode 100644 index 00000000..c5b65fe2 --- /dev/null +++ b/SPTarkov.Server/Services/SptServerBackgroundService.cs @@ -0,0 +1,33 @@ +using System.Runtime; +using SPTarkov.Server.Core.Loaders; +using SPTarkov.Server.Core.Models.Spt.Mod; +using SPTarkov.Server.Core.Utils; + +namespace SPTarkov.Server.Services; + +public class SptServerBackgroundService(IReadOnlyList loadedMods, BundleLoader bundleLoader, App app) : BackgroundService +{ + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + if (ProgramStatics.MODS()) + { + foreach (var mod in loadedMods) + { + if (mod.ModMetadata?.IsBundleMod == true) + { + // Convert to relative path + var relativeModPath = Path.GetRelativePath(Directory.GetCurrentDirectory(), mod.Directory) + .Replace('\\', '/'); + + bundleLoader.AddBundles(relativeModPath); + } + } + } + + await app.InitializeAsync(); + + // Run garbage collection now the server is ready to start + GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; + GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); + } +} diff --git a/Tools/ItemTplGenerator/ItemTplGenerator.cs b/Tools/ItemTplGenerator/ItemTplGenerator.cs index e0624ef8..ef7cbf2c 100644 --- a/Tools/ItemTplGenerator/ItemTplGenerator.cs +++ b/Tools/ItemTplGenerator/ItemTplGenerator.cs @@ -35,11 +35,6 @@ public class ItemTplGenerator( // Load all onload components, this gives us access to most of SPTs injections foreach (var onLoad in _onLoadComponents) { - if (onLoad is HttpCallbacks) - { - continue; - } - await onLoad.OnLoad(); }