using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.DI; using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Servers; using SPTarkov.Server.Core.Services; using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel; namespace SPTarkov.Server.Core.Utils; [Injectable(InjectionType.Singleton)] public class App { protected readonly RandomUtil _randomUtil; protected ConfigServer _configServer; protected CoreConfig _coreConfig; protected DatabaseService _databaseService; protected EncodingUtil _encodingUtil; protected HttpServer _httpServer; protected HttpServerHelper _httpServerHelper; protected LocalisationService _localisationService; protected ISptLogger _logger; protected IEnumerable _onLoad; protected IEnumerable _onUpdate; protected Dictionary _onUpdateLastRun = new(); protected Timer _timer; protected TimeUtil _timeUtil; public App( ISptLogger logger, TimeUtil timeUtil, RandomUtil randomUtil, LocalisationService localisationService, ConfigServer configServer, EncodingUtil encodingUtil, HttpServer httpServer, DatabaseService databaseService, IEnumerable onLoadComponents, IEnumerable onUpdateComponents, HttpServerHelper httpServerHelper ) { _logger = logger; _timeUtil = timeUtil; _randomUtil = randomUtil; _localisationService = localisationService; _configServer = configServer; _encodingUtil = encodingUtil; _httpServer = httpServer; _httpServerHelper = httpServerHelper; _databaseService = databaseService; _onLoad = onLoadComponents; _onUpdate = onUpdateComponents; _coreConfig = configServer.GetConfig(); } public async Task InitializeAsync() { // execute onLoad callbacks _logger.Info(_localisationService.GetText("executing_startup_callbacks")); var isAlreadyRunning = _httpServerHelper.IsAlreadyRunning(); if (isAlreadyRunning) { _logger.Critical(_localisationService.GetText("webserver_already_running")); await Task.Delay(Timeout.Infinite); } if (_logger.IsLogEnabled(LogLevel.Debug)) { _logger.Debug($"OS: {Environment.OSVersion.Version} | {Environment.OSVersion.Platform}"); _logger.Debug($"Ran as admin: {Environment.IsPrivilegedProcess}"); _logger.Debug($"CPU cores: {Environment.ProcessorCount}"); _logger.Debug($"PATH: {_encodingUtil.ToBase64(Environment.ProcessPath ?? "null returned")}"); _logger.Debug($"Server: {ProgramStatics.SPT_VERSION() ?? _coreConfig.SptVersion}"); // _logger.Debug($"RAM: {(os.totalmem() / 1024 / 1024 / 1024).toFixed(2)}GB"); if (ProgramStatics.BUILD_TIME() is not null) { _logger.Debug($"Date: {ProgramStatics.BUILD_TIME()}"); } if (ProgramStatics.COMMIT() is not null) { _logger.Debug($"Commit: {ProgramStatics.COMMIT()}"); } } foreach (var onLoad in _onLoad) { await onLoad.OnLoad(); } _timer = new Timer(_ => Update(_onUpdate), null, TimeSpan.Zero, TimeSpan.FromMilliseconds(5000)); } public async Task StartAsync() { if(!_httpServer.IsStarted()) { _logger.Success(_localisationService.GetText("started_webserver_success", _httpServer.ListeningUrl())); _logger.Success(_localisationService.GetText("websocket-started", _httpServer.ListeningUrl().Replace("https://", "wss://"))); } _logger.Success(GetRandomisedStartMessage()); await _httpServer.StartAsync(); } protected string GetRandomisedStartMessage() { if (_randomUtil.GetInt(1, 1000) > 999) { return _localisationService.GetRandomTextThatMatchesPartialKey("server_start_meme_"); } return _localisationService.GetText("server_start_success"); } protected void Update(IEnumerable onUpdateComponents) { try { // If the server has failed to start, skip any update calls if (!_httpServer.IsStarted() || !_databaseService.IsDatabaseValid()) { return; } foreach (var updateable in onUpdateComponents) { var success = false; if (!_onUpdateLastRun.TryGetValue(updateable.GetRoute(), out var lastRunTimeTimestamp)) { lastRunTimeTimestamp = 0; } var secondsSinceLastRun = _timeUtil.GetTimeStamp() - lastRunTimeTimestamp; try { success = updateable.OnUpdate(secondsSinceLastRun); } catch (Exception err) { LogUpdateException(err, updateable); } if (success) { _onUpdateLastRun[updateable.GetRoute()] = _timeUtil.GetTimeStamp(); } else { /* temporary for debug */ const int warnTime = 20 * 60; if (secondsSinceLastRun % warnTime == 0) { if (_logger.IsLogEnabled(LogLevel.Debug)) { _logger.Debug(_localisationService.GetText("route_onupdate_no_response", updateable.GetRoute())); } } } } } catch (Exception e) { Console.WriteLine(e); throw; } } protected void LogUpdateException(Exception err, IOnUpdate updateable) { _logger.Error(_localisationService.GetText("scheduled_event_failed_to_run", updateable.GetRoute())); _logger.Error(err.ToString()); } }