using System.Diagnostics; using Core.Annotations; using Core.DI; using Core.Models.Enums; using Core.Models.Spt.Config; using Core.Servers; using Core.Services; using ILogger = Core.Models.Utils.ILogger; namespace Core.Utils; [Injectable(InjectionType.Singleton)] public class App { protected Dictionary _onUpdateLastRun; protected CoreConfig _coreConfig; private ILogger _logger; private TimeUtil _timeUtil; private LocalisationService _localisationService; private ConfigServer _configServer; private EncodingUtil _encodingUtil; private HttpServer _httpServer; private DatabaseService _databaseService; private IEnumerable _onLoad; private IEnumerable _onUpdate; public App( ILogger logger, TimeUtil timeUtil, LocalisationService localisationService, ConfigServer configServer, EncodingUtil encodingUtil, HttpServer httpServer, DatabaseService databaseService, IEnumerable onLoadComponents, IEnumerable onUpdateComponents ) { _logger = logger; _timeUtil = timeUtil; _localisationService = localisationService; _configServer = configServer; _encodingUtil = encodingUtil; _httpServer = httpServer; _databaseService = databaseService; _onLoad = onLoadComponents; _onUpdate = onUpdateComponents; _coreConfig = configServer.GetConfig(ConfigTypes.CORE); } public async Task Run() { // execute onLoad callbacks _logger.Info(_localisationService.GetText("executing_startup_callbacks")); /* _logger.Debug($"OS: {os.arch()} | {os.version()} | {process.platform}"); _logger.Debug($"CPU: {os.cpus()[0]?.model} cores: {os.cpus().length}"); _logger.Debug($"RAM: {(os.totalmem() / 1024 / 1024 / 1024).toFixed(2)}GB"); _logger.Debug($"PATH: {this.encodingUtil.toBase64(process.argv[0])}"); _logger.Debug($"PATH: {this.encodingUtil.toBase64(process.execPath)}"); _logger.Debug($"Server: {ProgramStatics.SPT_VERSION || this.coreConfig.sptVersion}"); const nodeVersion = process.version.replace(/^v/, ""); if (ProgramStatics.EXPECTED_NODE && nodeVersion !== ProgramStatics.EXPECTED_NODE) { this.logger.error( "Node version mismatch. Required: ${ProgramStatics.EXPECTED_NODE} | Current: ${nodeVersion}", ); process.exit(1); } _logger.Debug("Node: ${nodeVersion}"); if (ProgramStatics.BUILD_TIME) { _logger.Debug("Date: ${ProgramStatics.BUILD_TIME}"); } if (ProgramStatics.COMMIT) { _logger.Debug("Commit: ${ProgramStatics.COMMIT}"); } */ foreach (var onLoad in _onLoad) await onLoad.OnLoad(); new Timer(_ => Update(_onUpdate), null, TimeSpan.Zero, TimeSpan.FromMilliseconds(5000)); } protected async Task Update(IEnumerable onUpdateComponents) { // 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 = await updateable.OnUpdate(secondsSinceLastRun); } catch (Exception err) { LogUpdateException(err, updateable); } if (success) { _onUpdateLastRun[updateable.GetRoute()] = _timeUtil.GetTimeStamp(); } else { /* temporary for debug */ var warnTime = 20 * 60; if (secondsSinceLastRun % warnTime == 0) _logger.Debug(_localisationService.GetText("route_onupdate_no_response", updateable.GetRoute())); } } } protected void LogUpdateException(Exception err, OnUpdate updateable) { _logger.Error(_localisationService.GetText("scheduled_event_failed_to_run", updateable.GetRoute())); _logger.Error(err.ToString()); } }