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 {