From 1f97a8480c4a1af3d8a8a5500cea56d50f332690 Mon Sep 17 00:00:00 2001 From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com> Date: Wed, 30 Jul 2025 13:50:36 +0200 Subject: [PATCH] Add Auto Restart Server Timer feature to config - Introduced "Scheduled Gameserver Restart" (AutoRestartServerTimer) input in config.html for user-defined server restart scheduling. - Updated version to 5.4.32 - added AutoRestartServerTimer to persistent configuration variables. - Implemented auto-restart logic in processmanagement.go, allowing server to restart based on the specified timeframe. - Updated ServeConfigPage to include AutoRestartServerTimer in the configuration response. --- UIMod/ui/config.html | 9 +++- src/config/config.go | 5 ++- src/config/vars.go | 27 ++++++------ src/gamemgr/processmanagement.go | 75 ++++++++++++++++++++++++++++++-- src/web/http.go | 1 + 5 files changed, 97 insertions(+), 20 deletions(-) diff --git a/UIMod/ui/config.html b/UIMod/ui/config.html index fe6dcc4f..c16125ff 100644 --- a/UIMod/ui/config.html +++ b/UIMod/ui/config.html @@ -213,6 +213,13 @@

Advanced Configuration

value="{{AdditionalParams}}">
Format: CustomParam1 Value1 CustomParam2 Value2
+ +
+ + +
Timeframe in minutes to schedule an automatic gameserver restart. 0 = disabled, 1440 = 24 hours, etc.. If SSCM is enabled, you will see "Attention, server is restarting in 30/20/10/5 seconds!" messages ingame before restarting.
+
@@ -221,7 +228,7 @@

Advanced Configuration

- + diff --git a/src/config/config.go b/src/config/config.go index 3bb44843..b18a8dd5 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -11,7 +11,7 @@ import ( var ( // All configuration variables can be found in vars.go - Version = "5.4.31" + Version = "5.4.32" Branch = "release" ) @@ -63,6 +63,7 @@ type JsonConfig struct { SubsystemFilters []string `json:"subsystemFilters"` IsUpdateEnabled *bool `json:"IsUpdateEnabled"` IsSSCMEnabled *bool `json:"IsSSCMEnabled"` + AutoRestartServerTimer string `json:"AutoRestartServerTimer"` AllowPrereleaseUpdates *bool `json:"AllowPrereleaseUpdates"` AllowMajorUpdates *bool `json:"AllowMajorUpdates"` } @@ -202,7 +203,7 @@ func applyConfig(cfg *JsonConfig) { cfg.AllowMajorUpdates = &allowMajorUpdatesVal SubsystemFilters = getStringSlice(cfg.SubsystemFilters, "SUBSYSTEM_FILTERS", []string{}) - + AutoRestartServerTimer = getString(cfg.AutoRestartServerTimer, "AUTO_RESTART_SERVER_TIMER", "0") isSSCMEnabledVal := getBool(cfg.IsSSCMEnabled, "IS_SSCM_ENABLED", false) IsSSCMEnabled = isSSCMEnabledVal cfg.IsSSCMEnabled = &isSSCMEnabledVal diff --git a/src/config/vars.go b/src/config/vars.go index 861ee619..2a6a2f97 100644 --- a/src/config/vars.go +++ b/src/config/vars.go @@ -47,19 +47,20 @@ var ( // Logging, debugging and misc var ( - IsDebugMode bool //only used for pprof server, keep it like this and check the log level instead. Debug = 10 - CreateSSUILogFile bool - LogLevel int - LogMessageBuffer string - IsFirstTimeSetup bool - BufferFlushTicker *time.Ticker - SSEMessageBufferSize = 2000 - MaxSSEConnections = 20 - GameServerAppID = "600760" - ExePath string - GameBranch string - SubsystemFilters []string - GameServerUUID uuid.UUID // Assined at startup to the current instance of the server we are managing. Currently unused. + IsDebugMode bool //only used for pprof server, keep it like this and check the log level instead. Debug = 10 + CreateSSUILogFile bool + LogLevel int + LogMessageBuffer string + IsFirstTimeSetup bool + BufferFlushTicker *time.Ticker + SSEMessageBufferSize = 2000 + MaxSSEConnections = 20 + GameServerAppID = "600760" + ExePath string + GameBranch string + SubsystemFilters []string + GameServerUUID uuid.UUID // Assined at startup to the current instance of the server we are managing. Currently unused. + AutoRestartServerTimer string ) // Discord integration diff --git a/src/gamemgr/processmanagement.go b/src/gamemgr/processmanagement.go index 1c35a6b2..63efc119 100644 --- a/src/gamemgr/processmanagement.go +++ b/src/gamemgr/processmanagement.go @@ -12,16 +12,18 @@ import ( "syscall" "time" + "github.com/JacksonTheMaster/StationeersServerUI/v5/src/commandmgr" "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config" "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger" "github.com/google/uuid" ) var ( - cmd *exec.Cmd - mu sync.Mutex - logDone chan struct{} - err error + cmd *exec.Cmd + mu sync.Mutex + logDone chan struct{} + err error + autoRestartDone chan struct{} ) // InternalIsServerRunning checks if the server process is running. @@ -163,6 +165,17 @@ func InternalStartServer() error { // create a UUID for this specific run createGameServerUUID() logger.Core.Debug("Created Game Server with internal UUID: " + config.GameServerUUID.String()) + + // Start auto-restart goroutine if AutoRestartServerTimer is set greater than 0 + if config.AutoRestartServerTimer != "0" { + if autoRestartDone != nil { + close(autoRestartDone) + } + autoRestartDone = make(chan struct{}) + go startAutoRestart(config.AutoRestartServerTimer, autoRestartDone) + logger.Core.Info("Auto-restart scheduled every " + config.AutoRestartServerTimer + " minutes") + } + return nil } @@ -174,6 +187,13 @@ func InternalStopServer() error { return fmt.Errorf("server not running") } + // Stop auto-restart goroutine + if autoRestartDone != nil { + close(autoRestartDone) + autoRestartDone = nil + logger.Core.Info("Auto-restart cycle interrupted due to manaual stop") + } + // Process is running, stop it isWindows := runtime.GOOS == "windows" var killErr error @@ -247,6 +267,53 @@ func InternalStopServer() error { return nil } +// startAutoRestart runs a goroutine that restarts the server after the specified timeframe in minutes. +func startAutoRestart(minutes string, done chan struct{}) { + minutesInt, _ := strconv.Atoi(minutes) + ticker := time.NewTicker(time.Duration(minutesInt) * time.Minute) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + mu.Lock() + if !internalIsServerRunningNoLock() { + mu.Unlock() + logger.Core.Info("Auto-restart skipped: server is not running") + return + } + mu.Unlock() + + if config.IsSSCMEnabled { + commandmgr.WriteCommand("say Attention, server is restarting in 30 seconds!") + time.Sleep(10 * time.Second) + commandmgr.WriteCommand("say Attention, server is restarting in 20 seconds!") + time.Sleep(10 * time.Second) + commandmgr.WriteCommand("say Attention, server is restarting in 10 seconds!") + time.Sleep(5 * time.Second) + commandmgr.WriteCommand("say Attention, server is restarting in 5 seconds!") + time.Sleep(5 * time.Second) + } + logger.Core.Info("Auto-restart triggered: stopping server") + if err := InternalStopServer(); err != nil { + logger.Core.Error("Auto-restart failed to stop server: " + err.Error()) + return + } + + logger.Core.Info("Auto-restart: waiting 5 seconds before restarting") + time.Sleep(5 * time.Second) + + logger.Core.Info("Auto-restart: starting server") + if err := InternalStartServer(); err != nil { + logger.Core.Error("Auto-restart failed to start server: " + err.Error()) + return + } + case <-done: + return + } + } +} + func clearGameServerUUID() { config.ConfigMu.Lock() defer config.ConfigMu.Unlock() diff --git a/src/web/http.go b/src/web/http.go index a9f05e07..a075fcb6 100644 --- a/src/web/http.go +++ b/src/web/http.go @@ -172,6 +172,7 @@ func ServeConfigPage(w http.ResponseWriter, r *http.Request) { "{{UseSteamP2PFalseSelected}}": steamP2PFalseSelected, "{{ExePath}}": config.ExePath, "{{AdditionalParams}}": config.AdditionalParams, + "{{AutoRestartServerTimer}}": config.AutoRestartServerTimer, } for placeholder, value := range replacements {