From 68ed892f711462459a3ee04df94baaec8e12b359 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Tue, 30 Sep 2025 02:39:20 +0200
Subject: [PATCH 01/93] this adds the core runfile logic from steamserverui
---
src/config/steamserverui-getters.go | 15 +
src/config/steamserverui-setter.go | 20 +
src/config/steamserverui-vars.go | 5 +
src/config/vars.go | 1 +
src/core/loader/runfile.go | 46 +++
src/logger/logger.go | 2 +
.../runfilemanager/argexample.go | 51 +++
src/steamserverui/runfilemanager/args.go | 390 ++++++++++++++++++
src/steamserverui/runfilemanager/getters.go | 112 +++++
src/web/routes.go | 10 +
src/web/runfile.go | 306 ++++++++++++++
11 files changed, 958 insertions(+)
create mode 100644 src/config/steamserverui-getters.go
create mode 100644 src/config/steamserverui-setter.go
create mode 100644 src/config/steamserverui-vars.go
create mode 100644 src/core/loader/runfile.go
create mode 100644 src/steamserverui/runfilemanager/argexample.go
create mode 100644 src/steamserverui/runfilemanager/args.go
create mode 100644 src/steamserverui/runfilemanager/getters.go
create mode 100644 src/web/runfile.go
diff --git a/src/config/steamserverui-getters.go b/src/config/steamserverui-getters.go
new file mode 100644
index 00000000..7b4138c3
--- /dev/null
+++ b/src/config/steamserverui-getters.go
@@ -0,0 +1,15 @@
+package config
+
+// GetRunFilesFolder returns the RunFilesFolder
+func GetRunFilesFolder() string {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+ return RunFilesFolder
+}
+
+// GetRunfileGame returns the RunfileGame
+func GetRunfileGame() string {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+ return RunfileGame
+}
diff --git a/src/config/steamserverui-setter.go b/src/config/steamserverui-setter.go
new file mode 100644
index 00000000..d79fde4e
--- /dev/null
+++ b/src/config/steamserverui-setter.go
@@ -0,0 +1,20 @@
+package config
+
+import (
+ "fmt"
+ "strings"
+)
+
+// SetRunfileGame sets the RunfileGame with validation
+func SetRunfileGame(value string) error {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+
+ if strings.TrimSpace(value) == "" {
+ return fmt.Errorf("runfile game cannot be empty")
+ }
+
+ RunfileGame = value
+ return nil
+ //return saveConfig()
+}
diff --git a/src/config/steamserverui-vars.go b/src/config/steamserverui-vars.go
new file mode 100644
index 00000000..e817e5a2
--- /dev/null
+++ b/src/config/steamserverui-vars.go
@@ -0,0 +1,5 @@
+package config
+
+var (
+ RunfileGame string
+)
diff --git a/src/config/vars.go b/src/config/vars.go
index a2f3a507..6db79528 100644
--- a/src/config/vars.go
+++ b/src/config/vars.go
@@ -143,6 +143,7 @@ var (
SSCMWebDir = "./UIMod/sscm/"
SSCMFilePath = "./BepInEx/plugins/SSCM/SSCM.socket"
SSCMPluginDir = "./BepInEx/plugins/SSCM/"
+ RunFilesFolder = "./UIMod/runfiles/"
)
// Bundled Assets
diff --git a/src/core/loader/runfile.go b/src/core/loader/runfile.go
new file mode 100644
index 00000000..32a21c9d
--- /dev/null
+++ b/src/core/loader/runfile.go
@@ -0,0 +1,46 @@
+package loader
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfilemanager"
+)
+
+// used via Runfile Gallery
+func InitRunfile(game string) error {
+ // Validate input
+ game = strings.TrimSpace(game)
+ if game == "" {
+ return fmt.Errorf("game cannot be empty")
+ }
+
+ logger.Runfile.Info("Updating runfile game to " + game)
+ logger.Runfile.Info("Stopping server if running")
+ gamemgr.InternalStopServer()
+ config.SetRunfileGame(game)
+
+ if err := ReloadRunfile(); err != nil {
+ return err
+ }
+
+ logger.Runfile.Info("Running SteamCMD, this may take a while...")
+ //steammgr.RunSteamCMD()
+ logger.Runfile.Warn("Steamcmd for runfile not implemented yet")
+ logger.Runfile.Info("Runfile game updated to " + game)
+
+ return nil
+}
+
+// used to only reload runfile into memory. Can be triggered from v1 UI -> Runfile Reset terminal
+func ReloadRunfile() error {
+ if err := runfilemanager.LoadRunfile(config.GetRunfileGame(), config.GetRunFilesFolder()); err != nil {
+ logger.Runfile.Warn("Failed to reload runfile: " + err.Error())
+ return err
+ }
+ logger.Runfile.Info("Runfile reloaded successfully")
+ return nil
+}
diff --git a/src/logger/logger.go b/src/logger/logger.go
index 958d3971..96ed57eb 100644
--- a/src/logger/logger.go
+++ b/src/logger/logger.go
@@ -24,6 +24,7 @@ var (
SSE = &Logger{suffix: SYS_SSE}
Security = &Logger{suffix: SYS_SECURITY}
Localization = &Logger{suffix: SYS_LOCALIZATION}
+ Runfile = &Logger{suffix: SYS_RUNFILE}
)
// Severity Levels
@@ -48,6 +49,7 @@ const (
SYS_SSE = "SSE"
SYS_SECURITY = "SECURITY"
SYS_LOCALIZATION = "LOCALIZATION"
+ SYS_RUNFILE = "RUNFILE"
)
const (
diff --git a/src/steamserverui/runfilemanager/argexample.go b/src/steamserverui/runfilemanager/argexample.go
new file mode 100644
index 00000000..63279371
--- /dev/null
+++ b/src/steamserverui/runfilemanager/argexample.go
@@ -0,0 +1,51 @@
+package runfilemanager
+
+import (
+ "fmt"
+
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+)
+
+// unused
+func Examples() {
+ // Load gameTemplate into the global CurrentRunfile
+ err := LoadRunfile("Stationeers", config.GetRunFilesFolder())
+ if err != nil {
+ panic(err)
+ }
+
+ // Discover all available arguments
+ allArgs := GetAllArgs()
+ fmt.Println("Available arguments:")
+ for _, arg := range allArgs {
+ fmt.Printf("%s (%s): %s\n", arg.Flag, arg.Type, arg.Description)
+ }
+
+ // Get arguments for UI display
+ groups := GetUIGroups()
+ for _, group := range groups {
+ fmt.Printf("\n%s Settings:\n", group)
+ for _, arg := range GetArgsByGroup(group) {
+ fmt.Printf("- %s: %s (Current: %s)\n", arg.UILabel, arg.Description, arg.RuntimeValue)
+ }
+ }
+
+ // Update a parameter
+ if err := SetArgValue("GamePort", "28015"); err != nil {
+ panic(err)
+ }
+
+ // Build final command line
+ args, err := BuildCommandArgs()
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println("\nCommand line:", args)
+
+ arg, err := GetSingleArg("someflag")
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ fmt.Println(arg.RuntimeValue) // Access the argument's properties
+}
diff --git a/src/steamserverui/runfilemanager/args.go b/src/steamserverui/runfilemanager/args.go
new file mode 100644
index 00000000..275f8861
--- /dev/null
+++ b/src/steamserverui/runfilemanager/args.go
@@ -0,0 +1,390 @@
+package runfilemanager
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+)
+
+// Package-level mutex for file operations
+var runfileMutex sync.Mutex
+
+var CurrentRunfile *RunFile
+
+// Custom error types
+type ErrRunfileNotLoaded struct{ Msg string }
+
+func (e ErrRunfileNotLoaded) Error() string { return e.Msg }
+
+type ErrArgNotFound struct{ Flag string }
+
+func (e ErrArgNotFound) Error() string { return fmt.Sprintf("argument %s not found", e.Flag) }
+
+type ErrInvalidGameName struct{ Name string }
+
+func (e ErrInvalidGameName) Error() string {
+ return fmt.Sprintf("invalid game name %q: must start with uppercase letter, no spaces, alphanumeric", e.Name)
+}
+
+type ErrUnsetIdentifier struct{ Name string }
+
+func (e ErrUnsetIdentifier) Error() string {
+ return fmt.Sprintf("undefined runfile Identifier %q: If this is a first time setup, you can safely ignore this warning and proceed to select a runfile from the Runfile Gallery on your UI", e.Name)
+}
+
+type ErrValidation struct {
+ Issues []string
+}
+
+func (e ErrValidation) Error() string {
+ return fmt.Sprintf("validation failed: %s", strings.Join(e.Issues, "; "))
+}
+
+type GameArg struct {
+ Flag string `json:"flag"`
+ DefaultValue string `json:"default"`
+ RuntimeValue string `json:"-"`
+ Required bool `json:"required"`
+ RequiresValue bool `json:"requires_value"`
+ Description string `json:"description"`
+ Type string `json:"type"`
+ Special string `json:"special,omitempty"`
+ UILabel string `json:"ui_label"`
+ UIGroup string `json:"ui_group"`
+ Weight int `json:"weight"`
+ Min int `json:"min,omitempty"`
+ Max int `json:"max,omitempty"`
+ Disabled bool `json:"disabled,omitempty"`
+}
+
+type Meta struct {
+ Name string `json:"name"` // SSUI Specific Game Identifier, must match the one in the filename.
+ Version string `json:"version"` // Runfile version
+}
+
+type RunFile struct {
+ Meta Meta `json:"meta"`
+ Architecture string `json:"architecture,omitempty"`
+ SteamAppID string `json:"steam_app_id"`
+ SteamLoginRequired bool `json:"steam_login_required,omitempty"` //unused & unsupported, will later be used in combination with some way to provide a steam login
+ WindowsExecutable string `json:"windows_executable"`
+ LinuxExecutable string `json:"linux_executable"`
+ Args map[string][]GameArg `json:"args"`
+}
+
+// Validate checks the RunFile state
+func (rf *RunFile) Validate() error {
+ var issues []string
+
+ // Validate SteamAppID: non-empty, numeric
+ if rf.SteamAppID == "" {
+ issues = append(issues, "SteamAppID is required")
+ } else if _, err := strconv.Atoi(rf.SteamAppID); err != nil {
+ issues = append(issues, fmt.Sprintf("SteamAppID must be numeric, got %s", rf.SteamAppID))
+ }
+
+ // Validate WindowsExecutable: if non-empty, must end with .exe
+ if rf.WindowsExecutable != "" && !strings.HasSuffix(strings.ToLower(rf.WindowsExecutable), ".exe") {
+ issues = append(issues, fmt.Sprintf("WindowsExecutable must end with .exe, got %s", rf.WindowsExecutable))
+ }
+
+ // Validate LinuxExecutable: if non-empty, must not end with .exe
+ if rf.LinuxExecutable != "" && strings.HasSuffix(strings.ToLower(rf.LinuxExecutable), ".exe") {
+ issues = append(issues, fmt.Sprintf("LinuxExecutable must not end with .exe, got %s", rf.LinuxExecutable))
+ }
+
+ // Validate Meta: ensure Name is non-empty
+ if rf.Meta.Name == "" {
+ issues = append(issues, "Meta.Name is required")
+ }
+
+ // Validate args
+ for _, arg := range rf.getAllArgs() {
+ if arg.Disabled {
+ continue
+ }
+ if arg.Required && arg.RequiresValue && arg.RuntimeValue == "" {
+ issues = append(issues, fmt.Sprintf("required argument %s has no value", arg.Flag))
+ }
+ switch arg.Type {
+ case "int":
+ if arg.RuntimeValue != "" {
+ if _, err := strconv.Atoi(arg.RuntimeValue); err != nil {
+ issues = append(issues, fmt.Sprintf("invalid integer value for %s: %s", arg.Flag, arg.RuntimeValue))
+ }
+ }
+ case "bool":
+ if arg.RuntimeValue != "" && arg.RuntimeValue != "true" && arg.RuntimeValue != "false" {
+ issues = append(issues, fmt.Sprintf("invalid boolean value for %s: %s", arg.Flag, arg.RuntimeValue))
+ }
+ }
+ }
+
+ if len(issues) > 0 {
+ return ErrValidation{Issues: issues}
+ }
+ return nil
+}
+
+// getAllArgs returns all GameArgs (internal method for validation)
+func (rf *RunFile) getAllArgs() []GameArg {
+ var allArgs []GameArg
+ for _, category := range []string{"basic", "network", "advanced"} {
+ if args, exists := rf.Args[category]; exists {
+ allArgs = append(allArgs, args...)
+ }
+ }
+ return allArgs
+}
+
+// LoadRunfile loads the runfile and stores it in CurrentRunfile
+func LoadRunfile(gameName, runFilesFolder string) error {
+ runfileMutex.Lock()
+ defer runfileMutex.Unlock()
+
+ // check if gameName is set to empty string
+ if gameName == "" {
+ err := ErrUnsetIdentifier{Name: gameName}
+ return err
+ }
+
+ // Edge case: empty runFilesFolder Setting
+ if runFilesFolder == "" {
+ err := fmt.Errorf("runFilesFolder cannot be empty")
+ logger.Runfile.Error(err.Error())
+ return err
+ }
+
+ // Edge case: validate gameName (uppercase first letter, no spaces, alphanumeric)
+ if gameName == "" || !regexp.MustCompile(`^[A-Z][a-zA-Z0-9]*$`).MatchString(gameName) {
+ err := ErrInvalidGameName{Name: gameName}
+ logger.Runfile.Error(err.Error())
+ return err
+ }
+
+ filePath := filepath.Join(runFilesFolder, fmt.Sprintf("run%s.ssui", gameName))
+ logger.Runfile.Debug(fmt.Sprintf("loading runfile: path=%s", filePath))
+
+ fileData, err := os.ReadFile(filePath)
+ if err != nil {
+ logger.Runfile.Error(fmt.Sprintf("failed to read runfile: path=%s, error=%v", filePath, err))
+ return fmt.Errorf("failed to read runfile: %w", err)
+ }
+
+ var runfile RunFile
+ if err := json.Unmarshal(fileData, &runfile); err != nil {
+ logger.Runfile.Error(fmt.Sprintf("failed to parse runfile: path=%s, error=%v", filePath, err))
+ return fmt.Errorf("failed to parse runfile: %w", err)
+ }
+
+ // Check executable availability
+ if _, err := runfile.GetExecutable(); err != nil {
+ logger.Runfile.Debug(fmt.Sprintf("executable validation failed: error=%v", err))
+ return err
+ }
+
+ // Initialize runtime values *before* validation
+ for category := range runfile.Args {
+ for i := range runfile.Args[category] {
+ runfile.Args[category][i].RuntimeValue = runfile.Args[category][i].DefaultValue
+ logger.Runfile.Debug(fmt.Sprintf("initialized arg: flag=%s, default=%s, runtime=%s",
+ runfile.Args[category][i].Flag,
+ runfile.Args[category][i].DefaultValue,
+ runfile.Args[category][i].RuntimeValue))
+ }
+ }
+
+ // Validate runfile
+ if err := runfile.Validate(); err != nil {
+ logger.Runfile.Error(fmt.Sprintf("runfile validation failed: path=%s, error=%v", filePath, err))
+ CurrentRunfile = nil // Ensure no partial state
+ return err
+ }
+
+ CurrentRunfile = &runfile
+ logger.Runfile.Info(fmt.Sprintf("runfile loaded: path=%s", filePath))
+ return nil
+}
+
+// SaveRunfile persists the current RunFile to disk
+func SaveRunfile() error {
+ runfileMutex.Lock()
+ defer runfileMutex.Unlock()
+
+ if CurrentRunfile == nil {
+ err := ErrRunfileNotLoaded{Msg: "runfile not loaded"}
+ logger.Runfile.Error(err.Error())
+ return err
+ }
+
+ // Build filepath
+ filePath := filepath.Join(config.GetRunFilesFolder(), fmt.Sprintf("run%s.ssui", config.GetRunfileGame()))
+ logger.Runfile.Debug(fmt.Sprintf("saving runfile: path=%s", filePath))
+
+ // Update DefaultValue from RuntimeValue
+ for category := range CurrentRunfile.Args {
+ for i := range CurrentRunfile.Args[category] {
+ CurrentRunfile.Args[category][i].DefaultValue = CurrentRunfile.Args[category][i].RuntimeValue
+ }
+ }
+
+ // Validate state
+ if err := CurrentRunfile.Validate(); err != nil {
+ logger.Runfile.Error(fmt.Sprintf("runfile validation failed: path=%s, error=%v", filePath, err))
+ return err
+ }
+
+ // Serialize to JSON
+ data, err := json.MarshalIndent(CurrentRunfile, "", " ")
+ if err != nil {
+ logger.Runfile.Error(fmt.Sprintf("failed to serialize runfile: path=%s, error=%v", filePath, err))
+ return fmt.Errorf("failed to serialize runfile: %w", err)
+ }
+
+ // Write to file with retries
+ const maxRetries = 3
+ for attempt := 1; attempt <= maxRetries; attempt++ {
+ if err := os.WriteFile(filePath, data, 0644); err != nil {
+ logger.Runfile.Warn(fmt.Sprintf("failed to write runfile: path=%s, attempt=%d, error=%v", filePath, attempt, err))
+ if attempt == maxRetries {
+ logger.Runfile.Error(fmt.Sprintf("failed to write runfile after %d attempts: path=%s, error=%v", maxRetries, filePath, err))
+ return fmt.Errorf("failed to write runfile after %d attempts: %w", maxRetries, err)
+ }
+ time.Sleep(100 * time.Millisecond)
+ continue
+ }
+ break
+ }
+
+ logger.Runfile.Info(fmt.Sprintf("runfile saved: path=%s", filePath))
+ return nil
+}
+
+// SetArgValue updates an argument's runtime value and saves the runfile
+func SetArgValue(flag string, value string) error {
+ if CurrentRunfile == nil {
+ err := ErrRunfileNotLoaded{Msg: "runfile not loaded"}
+ logger.Runfile.Error(err.Error())
+ return err
+ }
+
+ for category := range CurrentRunfile.Args {
+ for i := range CurrentRunfile.Args[category] {
+ if CurrentRunfile.Args[category][i].Flag != flag {
+ continue
+ }
+
+ // Validate value
+ arg := CurrentRunfile.Args[category][i]
+ switch arg.Type {
+ case "int":
+ if _, err := strconv.Atoi(value); err != nil {
+ err := ErrValidation{Issues: []string{fmt.Sprintf("invalid integer value for %s: %s", flag, value)}}
+ logger.Runfile.Error(fmt.Sprintf("validation failed: flag=%s, value=%s, error=%v", flag, value, err))
+ return err
+ }
+ case "bool":
+ if value != "true" && value != "false" {
+ err := ErrValidation{Issues: []string{fmt.Sprintf("invalid boolean value for %s: %s", flag, value)}}
+ logger.Runfile.Error(fmt.Sprintf("validation failed: flag=%s, value=%s, error=%v", flag, value, err))
+ return err
+ }
+ }
+
+ // Transactional update
+ originalValue := arg.RuntimeValue // Clone state
+ CurrentRunfile.Args[category][i].RuntimeValue = value
+ if err := SaveRunfile(); err != nil {
+ // Rollback on failure
+ CurrentRunfile.Args[category][i].RuntimeValue = originalValue
+ logger.Runfile.Error(fmt.Sprintf("failed to save runfile: flag=%s, value=%s, error=%v", flag, value, err))
+ return fmt.Errorf("failed to save runfile: %w", err)
+ }
+
+ logger.Runfile.Debug(fmt.Sprintf("set arg: flag=%s, value=%s", flag, value))
+ return nil
+ }
+ }
+
+ err := ErrArgNotFound{Flag: flag}
+ logger.Runfile.Error(fmt.Sprintf("arg not found: flag=%s", flag))
+ return err
+}
+
+// BuildCommandArgs builds the command-line arguments
+func BuildCommandArgs() ([]string, error) {
+ if CurrentRunfile == nil {
+ err := ErrRunfileNotLoaded{Msg: "no runfile is currently loaded"}
+ logger.Runfile.Error(err.Error())
+ return nil, err
+ }
+
+ // Validate before building
+ if err := CurrentRunfile.Validate(); err != nil {
+ logger.Runfile.Error(fmt.Sprintf("runfile validation failed: error=%v", err))
+ return nil, err
+ }
+
+ var args []string
+ allArgs := CurrentRunfile.getAllArgs()
+
+ // Sort by weight (primary) and UIGroup (secondary)
+ sort.Slice(allArgs, func(i, j int) bool {
+ if allArgs[i].Weight != allArgs[j].Weight {
+ return allArgs[i].Weight < allArgs[j].Weight
+ }
+ return switchCategoryWeight(allArgs[i].UIGroup) < switchCategoryWeight(allArgs[j].UIGroup)
+ })
+
+ for _, arg := range allArgs {
+ if arg.Disabled {
+ continue
+ }
+ if !arg.Required && arg.RequiresValue && arg.RuntimeValue == "" {
+ continue
+ }
+ args = append(args, arg.Flag)
+
+ // Special handling
+ if arg.Special == "space_delimited" {
+ parts := strings.Split(arg.RuntimeValue, " ")
+ for _, part := range parts {
+ if part != "" {
+ args = append(args, part)
+ }
+ }
+ continue
+ }
+
+ // Only add value if the argument requires one
+ if arg.RequiresValue && arg.RuntimeValue != "" {
+ args = append(args, arg.RuntimeValue)
+ }
+ }
+
+ return args, nil
+}
+
+// switchCategoryWeight maps UIGroup to a weight for sorting
+func switchCategoryWeight(group string) int {
+ switch group {
+ case "Basic":
+ return 0
+ case "Network":
+ return 1
+ case "Advanced":
+ return 2
+ default:
+ return 3
+ }
+}
diff --git a/src/steamserverui/runfilemanager/getters.go b/src/steamserverui/runfilemanager/getters.go
new file mode 100644
index 00000000..6fabab6d
--- /dev/null
+++ b/src/steamserverui/runfilemanager/getters.go
@@ -0,0 +1,112 @@
+package runfilemanager
+
+import (
+ "fmt"
+ "runtime"
+ "strings"
+
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+)
+
+// GetAllArgs returns all GameArgs from the runfile
+func GetAllArgs() []GameArg {
+ if CurrentRunfile == nil {
+ logger.Runfile.Error("runfile not loaded")
+ return nil
+ }
+ return CurrentRunfile.getAllArgs()
+}
+
+func GetUIGroups() []string {
+ if CurrentRunfile == nil {
+ logger.Runfile.Error("runfile not loaded")
+ return nil
+ }
+
+ groups := make(map[string]bool)
+ for _, arg := range CurrentRunfile.getAllArgs() {
+ groups[arg.UIGroup] = true
+ }
+
+ var result []string
+ for group := range groups {
+ result = append(result, group)
+ }
+ return result
+}
+
+func GetArgsByGroup(group string) []GameArg {
+ if CurrentRunfile == nil {
+ logger.Runfile.Error("runfile not loaded")
+ return nil
+ }
+
+ var result []GameArg
+ for _, arg := range CurrentRunfile.getAllArgs() {
+ if arg.UIGroup == group {
+ result = append(result, arg)
+ }
+ }
+ return result
+}
+
+// GetSingleArg retrieves a specific GameArg by its flag
+func GetSingleArg(flag string) (*GameArg, error) {
+ if CurrentRunfile == nil {
+ err := ErrRunfileNotLoaded{Msg: "runfile not loaded"}
+ logger.Runfile.Error(err.Error())
+ return nil, err
+ }
+
+ for _, arg := range CurrentRunfile.getAllArgs() {
+ if arg.Flag == flag {
+ return &arg, nil
+ }
+ }
+
+ err := ErrArgNotFound{Flag: flag}
+ logger.Runfile.Error(fmt.Sprintf("arg not found: flag=%s", flag))
+ return nil, err
+}
+
+// GetExecutable returns the appropriate executable based on GOOS or Architecture
+func (rf *RunFile) GetExecutable() (string, error) {
+ goos := strings.ToLower(runtime.GOOS)
+
+ // If Architecture is not all, use it exclusively
+ if rf.Architecture != "all" && rf.Architecture != "" {
+ arch := strings.ToLower(rf.Architecture)
+ if arch != "windows" && arch != "linux" {
+ return "", fmt.Errorf("invalid architecture in runfile: %s", rf.Architecture)
+ }
+ if arch != goos {
+ return "", fmt.Errorf("runfile architecture %s does not match current OS %s", arch, goos)
+ }
+ if arch == "windows" {
+ if rf.WindowsExecutable == "" {
+ return "", fmt.Errorf("WindowsExecutable is required when architecture is set to windows")
+ }
+ return rf.WindowsExecutable, nil
+ }
+ if rf.LinuxExecutable == "" {
+ return "", fmt.Errorf("LinuxExecutable is required when architecture is set to linux")
+ }
+ return rf.LinuxExecutable, nil
+ }
+
+ // If Architecture is all or empty, select based on GOOS
+
+ if goos == "windows" {
+ if rf.WindowsExecutable == "" {
+ return "", fmt.Errorf("WindowsExecutable is required for windows OS")
+ }
+ return rf.WindowsExecutable, nil
+ }
+ if goos == "linux" {
+ if rf.LinuxExecutable == "" {
+ return "", fmt.Errorf("LinuxExecutable is required for linux OS")
+ }
+ return rf.LinuxExecutable, nil
+ }
+ return "", fmt.Errorf("unsupported OS: %s", goos)
+}
diff --git a/src/web/routes.go b/src/web/routes.go
index 9b7a45eb..9c4f02a3 100644
--- a/src/web/routes.go
+++ b/src/web/routes.go
@@ -79,5 +79,15 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
protectedMux.HandleFunc("/api/v2/auth/setup/register", RegisterUserHandler) // user registration
protectedMux.HandleFunc("/api/v2/auth/setup/finalize", SetupFinalizeHandler)
+ // SteamServerUI
+
+ protectedMux.HandleFunc("/api/v2/runfile/groups", HandleRunfileGroups)
+ protectedMux.HandleFunc("/api/v2/runfile/args", HandleRunfileArgs)
+ protectedMux.HandleFunc("/api/v2/runfile/args/update", HandleRunfileArgUpdate)
+ protectedMux.HandleFunc("/api/v2/runfile", HandleRunfile)
+ protectedMux.HandleFunc("/api/v2/runfile/save", HandleRunfileSave)
+ protectedMux.HandleFunc("/api/v2/runfile/hardreset", HandleSetRunfileGame)
+ protectedMux.HandleFunc("/api/v2/loader/reloadrunfile", HandleReloadRunfile)
+
return mux, protectedMux
}
diff --git a/src/web/runfile.go b/src/web/runfile.go
new file mode 100644
index 00000000..3c7eea67
--- /dev/null
+++ b/src/web/runfile.go
@@ -0,0 +1,306 @@
+package web
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/loader"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfilemanager"
+)
+
+// APIGameArg is a DTO for GameArg, including RuntimeValue and all fields
+type APIGameArg struct {
+ Flag string `json:"flag"`
+ DefaultValue string `json:"default"`
+ RuntimeValue string `json:"runtime_value"`
+ Required bool `json:"required"`
+ RequiresValue bool `json:"requires_value"`
+ Description string `json:"description"`
+ Type string `json:"type"`
+ Special string `json:"special,omitempty"`
+ UILabel string `json:"ui_label"`
+ UIGroup string `json:"ui_group"`
+ Weight int `json:"weight"`
+ Min int `json:"min,omitempty"`
+ Max int `json:"max,omitempty"`
+ Disabled bool `json:"disabled"`
+}
+
+// APIMeta mirrors runfilemanager.Meta for API responses
+type APIMeta struct {
+ Name string `json:"name"`
+ Version string `json:"version"`
+}
+
+// APIRunFile is a DTO for RunFile, using APIGameArg and APIMeta
+type APIRunFile struct {
+ Meta APIMeta `json:"meta"`
+ Architecture string `json:"architecture,omitempty"`
+ SteamAppID string `json:"steam_app_id"`
+ WindowsExecutable string `json:"windows_executable"`
+ LinuxExecutable string `json:"linux_executable"`
+ Args map[string][]APIGameArg `json:"args"`
+}
+
+// apiResponse is the standard JSON response format
+type apiResponse struct {
+ Data interface{} `json:"data"`
+ Error string `json:"error,omitempty"`
+}
+
+// toAPIGameArg converts runfilemanager.GameArg to APIGameArg
+func toAPIGameArg(arg runfilemanager.GameArg) APIGameArg {
+ return APIGameArg{
+ Flag: arg.Flag,
+ DefaultValue: arg.DefaultValue,
+ RuntimeValue: arg.RuntimeValue,
+ Required: arg.Required,
+ RequiresValue: arg.RequiresValue,
+ Description: arg.Description,
+ Type: arg.Type,
+ Special: arg.Special,
+ UILabel: arg.UILabel,
+ UIGroup: arg.UIGroup,
+ Weight: arg.Weight,
+ Min: arg.Min,
+ Max: arg.Max,
+ Disabled: arg.Disabled,
+ }
+}
+
+// toAPIRunFile converts runfilemanager.RunFile to APIRunFile
+func toAPIRunFile(rf *runfilemanager.RunFile) APIRunFile {
+ apiArgs := make(map[string][]APIGameArg)
+ for category, args := range rf.Args {
+ for _, arg := range args {
+ apiArgs[category] = append(apiArgs[category], toAPIGameArg(arg))
+ }
+ }
+ return APIRunFile{
+ Meta: APIMeta{
+ Name: rf.Meta.Name,
+ Version: rf.Meta.Version,
+ },
+ Architecture: rf.Architecture,
+ SteamAppID: rf.SteamAppID,
+ WindowsExecutable: rf.WindowsExecutable,
+ LinuxExecutable: rf.LinuxExecutable,
+ Args: apiArgs,
+ }
+}
+
+// writeJSONResponse writes a JSON response with the given status code
+func writeJSONResponse(w http.ResponseWriter, status int, data interface{}, errMsg string) {
+ resp := apiResponse{Data: data, Error: errMsg}
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(status)
+ if err := json.NewEncoder(w).Encode(resp); err != nil {
+ logger.Runfile.Error(fmt.Sprintf("failed to write JSON response: %v", err))
+ }
+}
+
+// HandleRunfileGroups handles GET /api/v2/runfile/groups
+func HandleRunfileGroups(w http.ResponseWriter, r *http.Request) {
+ logger.Runfile.Debug("GET /api/v2/runfile/groups")
+ if runfilemanager.CurrentRunfile == nil {
+ logger.Runfile.Error("runfile not loaded")
+ writeJSONResponse(w, http.StatusInternalServerError, nil, "runfile not loaded")
+ return
+ }
+
+ groups := runfilemanager.GetUIGroups()
+ logger.Runfile.Info("fetched UI groups")
+ writeJSONResponse(w, http.StatusOK, groups, "")
+}
+
+// HandleRunfileArgs handles GET /api/v2/runfile/args
+func HandleRunfileArgs(w http.ResponseWriter, r *http.Request) {
+ group := r.URL.Query().Get("group")
+ logger.Runfile.Debug(fmt.Sprintf("GET /api/v2/runfile/args group=%s", group))
+
+ if runfilemanager.CurrentRunfile == nil {
+ logger.Runfile.Error("runfile not loaded")
+ writeJSONResponse(w, http.StatusInternalServerError, nil, "runfile not loaded")
+ return
+ }
+
+ var args []runfilemanager.GameArg
+ if group != "" {
+ // Validate group
+ validGroups := runfilemanager.GetUIGroups()
+ valid := false
+ for _, g := range validGroups {
+ if g == group {
+ valid = true
+ break
+ }
+ }
+ if !valid {
+ logger.Runfile.Error(fmt.Sprintf("invalid group: %s", group))
+ writeJSONResponse(w, http.StatusBadRequest, nil, fmt.Sprintf("invalid group: %s", group))
+ return
+ }
+ args = runfilemanager.GetArgsByGroup(group)
+ } else {
+ args = runfilemanager.GetAllArgs()
+ }
+
+ // Convert to APIGameArg
+ apiArgs := make([]APIGameArg, len(args))
+ for i, arg := range args {
+ apiArgs[i] = toAPIGameArg(arg)
+ }
+
+ logger.Runfile.Info(fmt.Sprintf("fetched args for group=%s", group))
+ writeJSONResponse(w, http.StatusOK, apiArgs, "")
+}
+
+// HandleRunfileArgUpdate handles POST /api/v2/runfile/args/update
+func HandleRunfileArgUpdate(w http.ResponseWriter, r *http.Request) {
+ logger.Runfile.Debug("POST /api/v2/runfile/args")
+
+ if runfilemanager.CurrentRunfile == nil {
+ logger.Runfile.Error("runfile not loaded")
+ writeJSONResponse(w, http.StatusInternalServerError, nil, "runfile not loaded")
+ return
+ }
+
+ var req struct {
+ Flag string `json:"flag"`
+ Value string `json:"value"`
+ }
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ logger.Runfile.Error(fmt.Sprintf("invalid request body: %v", err))
+ writeJSONResponse(w, http.StatusBadRequest, nil, "invalid request body")
+ return
+ }
+
+ if req.Flag == "" {
+ logger.Runfile.Error("flag is required")
+ writeJSONResponse(w, http.StatusBadRequest, nil, "flag is required")
+ return
+ }
+
+ if err := runfilemanager.SetArgValue(req.Flag, req.Value); err != nil {
+ logger.Runfile.Error(fmt.Sprintf("failed to set arg %s: %v", req.Flag, err))
+ writeJSONResponse(w, http.StatusBadRequest, nil, fmt.Sprintf("failed to set arg: %v", err))
+ return
+ }
+
+ logger.Runfile.Info(fmt.Sprintf("updated arg %s to %s", req.Flag, req.Value))
+ writeJSONResponse(w, http.StatusOK, map[string]string{"flag": req.Flag, "value": req.Value}, "")
+}
+
+// HandleRunfile handles GET /api/v2/runfile
+func HandleRunfile(w http.ResponseWriter, r *http.Request) {
+ logger.Runfile.Debug("GET /api/v2/runfile")
+
+ if runfilemanager.CurrentRunfile == nil {
+ logger.Runfile.Error("runfile not loaded")
+ writeJSONResponse(w, http.StatusInternalServerError, nil, "runfile not loaded")
+ return
+ }
+
+ apiRunfile := toAPIRunFile(runfilemanager.CurrentRunfile)
+ logger.Runfile.Info("fetched runfile")
+ writeJSONResponse(w, http.StatusOK, apiRunfile, "")
+}
+
+// HandleRunfileSave handles POST /api/v2/runfile/save
+func HandleRunfileSave(w http.ResponseWriter, r *http.Request) {
+ logger.Runfile.Debug("POST /api/v2/runfile/save")
+
+ if runfilemanager.CurrentRunfile == nil {
+ logger.Runfile.Error("runfile not loaded")
+ writeJSONResponse(w, http.StatusInternalServerError, nil, "runfile not loaded")
+ return
+ }
+
+ if err := runfilemanager.SaveRunfile(); err != nil {
+ logger.Runfile.Error(fmt.Sprintf("failed to save runfile: %v", err))
+ writeJSONResponse(w, http.StatusInternalServerError, nil, fmt.Sprintf("failed to save runfile: %v", err))
+ return
+ }
+
+ logger.Runfile.Info("runfile saved")
+ writeJSONResponse(w, http.StatusOK, "runfile saved", "")
+}
+
+// HandleSetRunfileGame reloads the runfile and restarts most of the server. It can also be used to reload the runfile from Disk as a hard reset.
+func HandleSetRunfileGame(w http.ResponseWriter, r *http.Request) {
+
+ reloadMu.Lock()
+ defer reloadMu.Unlock()
+
+ // Restrict to POST method
+ if r.Method != http.MethodPost {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ // Read and validate request body
+ var request struct {
+ Game string `json:"game"`
+ }
+
+ if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
+ http.Error(w, "Invalid request body", http.StatusBadRequest)
+ return
+ }
+
+ // Validate input
+ game := strings.TrimSpace(request.Game)
+ if game == "" {
+ http.Error(w, "Game cannot be empty", http.StatusBadRequest)
+ return
+ }
+
+ // Call InitRunfile to handle the runfile update
+ if err := loader.InitRunfile(game); err != nil {
+ logger.Core.Debug("Failed to initialize runfile: " + err.Error())
+ http.Error(w, "Failed to initialize runfile: "+err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ logger.Core.Info("Runfile game updated successfully to " + game)
+
+ // Prepare response
+ response := struct {
+ Message string `json:"message"`
+ Game string `json:"game"`
+ }{
+ Message: "Monitor console for update status",
+ Game: game,
+ }
+
+ // Set response headers and write JSON response
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusAccepted)
+ if err := json.NewEncoder(w).Encode(response); err != nil {
+ http.Error(w, "Failed to write response", http.StatusInternalServerError)
+ return
+ }
+}
+
+func HandleReloadRunfile(w http.ResponseWriter, r *http.Request) {
+ logger.Web.Debug("Received reloadrunfile request from API")
+ reloadMu.Lock()
+ defer reloadMu.Unlock()
+ // accept only GET requests
+ if r.Method != http.MethodGet {
+ http.Error(w, "Only GET requests are allowed", http.StatusMethodNotAllowed)
+ return
+ }
+ loader.ReloadRunfile()
+
+ // Set response headers and write JSON response
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusAccepted)
+ if err := json.NewEncoder(w).Encode(map[string]string{"status": "OK"}); err != nil {
+ http.Error(w, "Failed to write response", http.StatusInternalServerError)
+ return
+ }
+}
From e47d89864f8d2e627a35611e624a75efe9185dce Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Tue, 30 Sep 2025 02:55:47 +0200
Subject: [PATCH 02/93] this adds the settings implementaiton from
SteamServerUI
---
src/steamserverui/settings/retrieve.go | 345 +++++++++++++++++++++++++
src/steamserverui/settings/save.go | 326 +++++++++++++++++++++++
src/web/routes.go | 3 +
3 files changed, 674 insertions(+)
create mode 100644 src/steamserverui/settings/retrieve.go
create mode 100644 src/steamserverui/settings/save.go
diff --git a/src/steamserverui/settings/retrieve.go b/src/steamserverui/settings/retrieve.go
new file mode 100644
index 00000000..8cf82c71
--- /dev/null
+++ b/src/steamserverui/settings/retrieve.go
@@ -0,0 +1,345 @@
+package settings
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+)
+
+// package settings handles API communication with the config values in package config via getter /setter functions.
+
+// ConfigSetting represents metadata for a configuration setting
+type ConfigSetting struct {
+ Name string `json:"name"`
+ Type string `json:"type"`
+ Group string `json:"group"`
+ Description string `json:"description"`
+ Value interface{} `json:"value"`
+ Min *int `json:"min,omitempty"`
+ Max *int `json:"max,omitempty"`
+ Required bool `json:"required"`
+}
+
+// ConfigSettingsResponse represents the API response
+type ConfigSettingsResponse struct {
+ Data []ConfigSetting `json:"data"`
+ Error string `json:"error,omitempty"`
+}
+
+func RetrieveSettings(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
+ return
+ }
+
+ settings := []ConfigSetting{
+ {
+ Name: "IsDebugMode",
+ Type: "bool",
+ Group: "System Settings",
+ Description: "Enable pprof server",
+ Value: config.GetIsDebugMode(),
+ },
+ {
+ Name: "CreateSSUILogFile",
+ Type: "bool",
+ Group: "System Settings",
+ Description: "Create SSUI log files",
+ Value: config.GetCreateSSUILogFile(),
+ },
+ {
+ Name: "LogLevel",
+ Type: "int",
+ Group: "System Settings",
+ Description: "Logging verbosity level",
+ Value: config.GetLogLevel(),
+ Min: intPtr(0),
+ },
+ //{
+ // Name: "BackendEndpointIP",
+ // Type: "string",
+ // Group: "System Settings",
+ // Description: "IP address for backend endpoint",
+ // Value: config.GetBackendEndpointIP(),
+ // Required: true,
+ //},
+ {
+ Name: "BackendEndpointPort",
+ Type: "string",
+ Group: "System Settings",
+ Description: "Port for backend endpoint",
+ Value: config.GetSSUIWebPort(), // CHANGE VAR NAME TO MATCH (Get)BackendEndpointPort
+ Required: true,
+ },
+ //{
+ // Name: "BackupKeepLastN",
+ // Type: "int",
+ // Group: "Advanced Settings",
+ // Description: "Number of recent backups to keep",
+ // Value: config.GetBackupKeepLastN(),
+ // Min: intPtr(0),
+ //},
+ //{
+ // Name: "IsCleanupEnabled",
+ // Type: "bool",
+ // Group: "Advanced Settings",
+ // Description: "Enable backup cleanup",
+ // Value: config.GetIsCleanupEnabled(),
+ //},
+ //{
+ // Name: "BackupKeepDailyFor",
+ // Type: "int",
+ // Group: "Advanced Settings",
+ // Description: "Hours to keep daily backups",
+ // Value: int(config.GetBackupKeepDailyFor() / time.Hour),
+ // Min: intPtr(0),
+ //},
+ //{
+ // Name: "BackupKeepWeeklyFor",
+ // Type: "int",
+ // Group: "Advanced Settings",
+ // Description: "Hours to keep weekly backups",
+ // Value: int(config.GetBackupKeepWeeklyFor() / time.Hour),
+ // Min: intPtr(0),
+ //},
+ //{
+ // Name: "BackupKeepMonthlyFor",
+ // Type: "int",
+ // Group: "Advanced Settings",
+ // Description: "Hours to keep monthly backups",
+ // Value: int(config.GetBackupKeepMonthlyFor() / time.Hour),
+ // Min: intPtr(0),
+ //},
+ //{
+ // Name: "BackupCleanupInterval",
+ // Type: "int",
+ // Group: "Advanced Settings",
+ // Description: "Hours between backup cleanup runs",
+ // Value: int(config.GetBackupCleanupInterval() / time.Hour),
+ // Min: intPtr(0),
+ //},
+ //{
+ // Name: "BackupWaitTime",
+ // Type: "int",
+ // Group: "Advanced Settings",
+ // Description: "Seconds to wait before backup",
+ // Value: int(config.GetBackupWaitTime() / time.Second),
+ // Min: intPtr(0),
+ //},
+ {
+ Name: "GameBranch",
+ Type: "string",
+ Group: "Update Settings",
+ Description: "Game branch for updates (Restart Required)",
+ Value: config.GetGameBranch(),
+ },
+ //{
+ // Name: "Users",
+ // Type: "map",
+ // Group: "Advanced Settings",
+ // Description: "User authentication mappings",
+ // Value: config.GetUsers(),
+ //},
+ {
+ Name: "AuthEnabled",
+ Type: "bool",
+ Group: "System Settings",
+ Description: "Enable authentication",
+ Value: config.GetAuthEnabled(),
+ },
+ {
+ Name: "JwtKey",
+ Type: "string",
+ Group: "Security Settings",
+ Description: "Encryption key for Authentication",
+ Value: config.GetJwtKey(),
+ },
+ {
+ Name: "AuthTokenLifetime",
+ Type: "int",
+ Group: "Security Settings",
+ Description: "Token lifetime in seconds",
+ Value: config.GetAuthTokenLifetime(),
+ Min: intPtr(0),
+ },
+ {
+ Name: "IsUpdateEnabled",
+ Type: "bool",
+ Group: "System Settings",
+ Description: "Enable automatic updates",
+ Value: config.GetIsUpdateEnabled(),
+ },
+ //{
+ // Name: "IsSSCMEnabled",
+ // Type: "bool",
+ // Group: "Advanced Settings",
+ // Description: "Enable SSCM integration",
+ // Value: config.GetIsSSCMEnabled(),
+ //},
+ //{
+ // Name: "IsCodeServerEnabled",
+ // Type: "bool",
+ // Group: "System Settings",
+ // Description: "Enables the Code Server integration. Linux only. Attention: Does NOT have an updater at the moment. Be careful with this feature.",
+ // Value: config.GetIsCodeServerEnabled(),
+ //},
+ {
+ Name: "AllowPrereleaseUpdates",
+ Type: "bool",
+ Group: "Update Settings",
+ Description: "Allow prerelease updates",
+ Value: config.GetAllowPrereleaseUpdates(),
+ },
+ {
+ Name: "AllowMajorUpdates",
+ Type: "bool",
+ Group: "Update Settings",
+ Description: "Allow major version updates",
+ Value: config.GetAllowMajorUpdates(),
+ },
+ // Discord Settings
+ {
+ Name: "IsDiscordEnabled",
+ Type: "bool",
+ Group: "Discord Settings",
+ Description: "Enable Discord integration",
+ Value: config.GetIsDiscordEnabled(),
+ },
+ {
+ Name: "DiscordToken",
+ Type: "string",
+ Group: "Discord Settings",
+ Description: "Discord bot token",
+ Value: config.GetDiscordToken(),
+ },
+ {
+ Name: "ControlChannelID",
+ Type: "string",
+ Group: "Discord Settings",
+ Description: "Control channel ID",
+ Value: config.GetControlChannelID(),
+ },
+ {
+ Name: "StatusChannelID",
+ Type: "string",
+ Group: "Discord Settings",
+ Description: "Status channel ID",
+ Value: config.GetStatusChannelID(),
+ },
+ {
+ Name: "ConnectionListChannelID",
+ Type: "string",
+ Group: "Discord Settings",
+ Description: "Connection list channel ID",
+ Value: config.GetConnectionListChannelID(),
+ },
+ {
+ Name: "LogChannelID",
+ Type: "string",
+ Group: "Discord Settings",
+ Description: "Log channel ID",
+ Value: config.GetLogChannelID(),
+ },
+ {
+ Name: "SaveChannelID",
+ Type: "string",
+ Group: "Discord Settings",
+ Description: "Save channel ID",
+ Value: config.GetSaveChannelID(),
+ },
+ {
+ Name: "ControlPanelChannelID",
+ Type: "string",
+ Group: "Discord Settings",
+ Description: "Control panel channel ID",
+ Value: config.GetControlPanelChannelID(),
+ },
+ {
+ Name: "DiscordCharBufferSize",
+ Type: "int",
+ Group: "Discord Settings",
+ Description: "Discord character buffer size",
+ Value: config.GetDiscordCharBufferSize(),
+ },
+ {
+ Name: "ErrorChannelID",
+ Type: "string",
+ Group: "Discord Settings",
+ Description: "Error channel ID",
+ Value: config.GetErrorChannelID(),
+ },
+ //{
+ // Name: "BackupContentDir",
+ // Type: "string",
+ // Group: "Backup Settings",
+ // Description: "Backup content directory",
+ // Value: config.GetBackupContentDir(),
+ //},
+ //{
+ // Name: "BackupsStoreDir",
+ // Type: "string",
+ // Group: "Backup Settings",
+ // Description: "Backup stored backups directory",
+ // Value: config.GetBackupsStoreDir(),
+ //},
+ //{
+ // Name: "BackupLoopInterval",
+ // Type: "string",
+ // Group: "Backup Settings",
+ // Description: "Backup loop interval",
+ // Value: config.GetBackupLoopInterval().String(),
+ //},
+ //{
+ // Name: "BackupMode",
+ // Type: "string",
+ // Group: "Backup Settings",
+ // Description: "Backup mode",
+ // Value: config.GetBackupMode(),
+ //},
+ //{
+ // Name: "BackupMaxFileSize",
+ // Type: "int",
+ // Group: "Backup Settings",
+ // Description: "Max file size",
+ // Value: config.GetBackupMaxFileSize(),
+ //},
+ //{
+ // Name: "BackupUseCompression",
+ // Type: "bool",
+ // Group: "Backup Settings",
+ // Description: "Use compression",
+ // Value: config.GetBackupUseCompression(),
+ //},
+ //{
+ // Name: "BackupKeepSnapshot",
+ // Type: "bool",
+ // Group: "Backup Settings",
+ // Description: "Keep snapshot",
+ // Value: config.GetBackupKeepSnapshot(),
+ //},
+ {
+ Name: "IsConsoleEnabled",
+ Type: "bool",
+ Group: "System Settings",
+ Description: "Expose various actions directly in the command line (Restart Required)",
+ Value: config.GetIsConsoleEnabled(),
+ },
+ }
+
+ response := ConfigSettingsResponse{
+ Data: settings,
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ if err := json.NewEncoder(w).Encode(response); err != nil {
+ http.Error(w, fmt.Sprintf("Error encoding response: %v", err), http.StatusInternalServerError)
+ return
+ }
+}
+
+// intPtr creates a pointer to an int
+func intPtr(i int) *int {
+ return &i
+}
diff --git a/src/steamserverui/settings/save.go b/src/steamserverui/settings/save.go
new file mode 100644
index 00000000..ca857c27
--- /dev/null
+++ b/src/steamserverui/settings/save.go
@@ -0,0 +1,326 @@
+package settings
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+)
+
+// package settings handles API communication with the config values in package config via getter /setter functions.
+
+// setterFunc defines the signature for setter functions
+type setterFunc func(interface{}) error
+
+// setterMap maps JSON keys (global variable names) to setter functions with type checking
+var setterMap = map[string]setterFunc{
+ //"BackendEndpointIP": func(v interface{}) error {
+ // if str, ok := v.(string); ok {
+ // return config.SetBackendEndpointIP(str)
+ // }
+ // return fmt.Errorf("invalid type for BackendEndpointIP: expected string")
+ //},
+ //"BackendEndpointPort": func(v interface{}) error {
+ // if str, ok := v.(string); ok {
+ // return config.SetBackendEndpointPort(str)
+ // }
+ // return fmt.Errorf("invalid type for BackendEndpointPort: expected string")
+ //},
+ "DiscordToken": func(v interface{}) error {
+ if str, ok := v.(string); ok {
+ return config.SetDiscordToken(str)
+ }
+ return fmt.Errorf("invalid type for DiscordToken: expected string")
+ },
+ "ControlChannelID": func(v interface{}) error {
+ if str, ok := v.(string); ok {
+ return config.SetControlChannelID(str)
+ }
+ return fmt.Errorf("invalid type for ControlChannelID: expected string")
+ },
+ "StatusChannelID": func(v interface{}) error {
+ if str, ok := v.(string); ok {
+ return config.SetStatusChannelID(str)
+ }
+ return fmt.Errorf("invalid type for StatusChannelID: expected string")
+ },
+ "ConnectionListChannelID": func(v interface{}) error {
+ if str, ok := v.(string); ok {
+ return config.SetConnectionListChannelID(str)
+ }
+ return fmt.Errorf("invalid type for ConnectionListChannelID: expected string")
+ },
+ "LogChannelID": func(v interface{}) error {
+ if str, ok := v.(string); ok {
+ return config.SetLogChannelID(str)
+ }
+ return fmt.Errorf("invalid type for LogChannelID: expected string")
+ },
+ "SaveChannelID": func(v interface{}) error {
+ if str, ok := v.(string); ok {
+ return config.SetSaveChannelID(str)
+ }
+ return fmt.Errorf("invalid type for SaveChannelID: expected string")
+ },
+ "ControlPanelChannelID": func(v interface{}) error {
+ if str, ok := v.(string); ok {
+ return config.SetControlPanelChannelID(str)
+ }
+ return fmt.Errorf("invalid type for ControlPanelChannelID: expected string")
+ },
+ "DiscordCharBufferSize": func(v interface{}) error {
+ if f, ok := v.(float64); ok {
+ return config.SetDiscordCharBufferSize(int(f))
+ }
+ return fmt.Errorf("invalid type for DiscordCharBufferSize: expected number")
+ },
+ "BlackListFilePath": func(v interface{}) error {
+ if str, ok := v.(string); ok {
+ return config.SetBlackListFilePath(str)
+ }
+ return fmt.Errorf("invalid type for BlackListFilePath: expected string")
+ },
+ "IsDiscordEnabled": func(v interface{}) error {
+ if b, ok := v.(bool); ok {
+ return config.SetIsDiscordEnabled(b)
+ }
+ return fmt.Errorf("invalid type for IsDiscordEnabled: expected bool")
+ },
+ "ErrorChannelID": func(v interface{}) error {
+ if str, ok := v.(string); ok {
+ return config.SetErrorChannelID(str)
+ }
+ return fmt.Errorf("invalid type for ErrorChannelID: expected string")
+ },
+ "GameBranch": func(v interface{}) error {
+ if str, ok := v.(string); ok {
+ return config.SetGameBranch(str)
+ }
+ return fmt.Errorf("invalid type for GameBranch: expected string")
+ },
+ "Users": func(v interface{}) error {
+ if m, ok := v.(map[string]interface{}); ok {
+ users := make(map[string]string)
+ for k, val := range m {
+ if strVal, ok := val.(string); ok {
+ users[k] = strVal
+ } else {
+ return fmt.Errorf("invalid value type for Users: expected string")
+ }
+ }
+ return config.SetUsers(users)
+ }
+ return fmt.Errorf("invalid type for Users: expected map[string]string")
+ },
+ "AuthEnabled": func(v interface{}) error {
+ if b, ok := v.(bool); ok {
+ return config.SetAuthEnabled(b)
+ }
+ return fmt.Errorf("invalid type for AuthEnabled: expected bool")
+ },
+ "JwtKey": func(v interface{}) error {
+ if str, ok := v.(string); ok {
+ return config.SetJwtKey(str)
+ }
+ return fmt.Errorf("invalid type for JwtKey: expected string")
+ },
+ "AuthTokenLifetime": func(v interface{}) error {
+ if f, ok := v.(float64); ok {
+ return config.SetAuthTokenLifetime(int(f))
+ }
+ return fmt.Errorf("invalid type for AuthTokenLifetime: expected number")
+ },
+ "IsDebugMode": func(v interface{}) error {
+ if b, ok := v.(bool); ok {
+ return config.SetIsDebugMode(b)
+ }
+ return fmt.Errorf("invalid type for IsDebugMode: expected bool")
+ },
+ "CreateSSUILogFile": func(v interface{}) error {
+ if b, ok := v.(bool); ok {
+ return config.SetCreateSSUILogFile(b)
+ }
+ return fmt.Errorf("invalid type for CreateSSUILogFile: expected bool")
+ },
+ "LogLevel": func(v interface{}) error {
+ if f, ok := v.(float64); ok {
+ return config.SetLogLevel(int(f))
+ }
+ return fmt.Errorf("invalid type for LogLevel: expected number")
+ },
+ "SubsystemFilters": func(v interface{}) error {
+ if arr, ok := v.([]interface{}); ok {
+ filters := make([]string, 0, len(arr))
+ for _, val := range arr {
+ if strVal, ok := val.(string); ok {
+ filters = append(filters, strVal)
+ } else {
+ return fmt.Errorf("invalid value type for SubsystemFilters: expected string")
+ }
+ }
+ return config.SetSubsystemFilters(filters)
+ }
+ return fmt.Errorf("invalid type for SubsystemFilters: expected array of strings")
+ },
+ "IsUpdateEnabled": func(v interface{}) error {
+ if b, ok := v.(bool); ok {
+ return config.SetIsUpdateEnabled(b)
+ }
+ return fmt.Errorf("invalid type for IsUpdateEnabled: expected bool")
+ },
+ "IsSSCMEnabled": func(v interface{}) error {
+ if b, ok := v.(bool); ok {
+ return config.SetIsSSCMEnabled(b)
+ }
+ return fmt.Errorf("invalid type for IsSSCMEnabled: expected bool")
+ },
+ "AllowPrereleaseUpdates": func(v interface{}) error {
+ if b, ok := v.(bool); ok {
+ return config.SetAllowPrereleaseUpdates(b)
+ }
+ return fmt.Errorf("invalid type for AllowPrereleaseUpdates: expected bool")
+ },
+ "AllowMajorUpdates": func(v interface{}) error {
+ if b, ok := v.(bool); ok {
+ return config.SetAllowMajorUpdates(b)
+ }
+ return fmt.Errorf("invalid type for AllowMajorUpdates: expected bool")
+ },
+ //"IsCodeServerEnabled": func(v interface{}) error {
+ // if b, ok := v.(bool); ok {
+ // return config.SetIsCodeServerEnabled(b)
+ // }
+ // return fmt.Errorf("invalid type for IsCodeServerEnabled: expected bool")
+ //},
+ //"BackupContentDir": func(v interface{}) error {
+ // if str, ok := v.(string); ok {
+ // return config.SetBackupContentDir(str)
+ // }
+ // return fmt.Errorf("invalid type for BackupContentDir: expected string")
+ //},
+ //"BackupsStoreDir": func(v interface{}) error {
+ // if str, ok := v.(string); ok {
+ // return config.SetBackupsStoreDir(str)
+ // }
+ // return fmt.Errorf("invalid type for BackupsStoreDir: expected string")
+ //},
+ //"BackupLoopInterval": func(v interface{}) error {
+ // if str, ok := v.(string); ok {
+ // interval, err := time.ParseDuration(str)
+ // if err != nil {
+ // return err
+ // }
+ // return config.SetBackupLoopInterval(interval)
+ // }
+ // return fmt.Errorf("invalid type for BackupLoopInterval: expected string")
+ //},
+ //"BackupMode": func(v interface{}) error {
+ // if str, ok := v.(string); ok {
+ // return config.SetBackupMode(str)
+ // }
+ // return fmt.Errorf("invalid type for BackupMode: expected string")
+ //},
+ //"BackupMaxFileSize": func(v interface{}) error {
+ // if f, ok := v.(float64); ok {
+ // return config.SetBackupMaxFileSize(int64(f))
+ // }
+ // return fmt.Errorf("invalid type for BackupMaxFileSize: expected number")
+ //},
+ //"BackupUseCompression": func(v interface{}) error {
+ // if b, ok := v.(bool); ok {
+ // return config.SetBackupUseCompression(b)
+ // }
+ // return fmt.Errorf("invalid type for BackupUseCompression: expected bool")
+ //},
+ //"BackupKeepSnapshot": func(v interface{}) error {
+ // if b, ok := v.(bool); ok {
+ // return config.SetBackupKeepSnapshot(b)
+ // }
+ // return fmt.Errorf("invalid type for BackupKeepSnapshot: expected bool")
+ //},
+ //"IsTelemetryEnabled": func(v interface{}) error {
+ // //Accepts both bool and an empty string as a valid value for true.
+ // var expectedtype string
+ // if _, ok := v.(string); ok {
+ // expectedtype = "string"
+ // return config.SetIsTelemetryEnabled(true)
+ // }
+ // if _, ok := v.(bool); ok {
+ // expectedtype = "bool"
+ // return config.SetIsTelemetryEnabled(v.(bool))
+ // }
+ // return fmt.Errorf("invalid type for IsTelemetryEnabled: expected %s", expectedtype)
+ //},
+ "IsConsoleEnabled": func(v interface{}) error {
+ if b, ok := v.(bool); ok {
+ return config.SetIsConsoleEnabled(b)
+ }
+ return fmt.Errorf("invalid type for IsConsoleEnabled: expected bool")
+ },
+}
+
+// SaveSetting handles RESTful requests to update a single configuration setting
+func SaveSetting(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
+ return
+ }
+
+ // Read request body
+ body, err := io.ReadAll(r.Body)
+ if err != nil {
+ http.Error(w, fmt.Sprintf("Error reading request body: %v", err), http.StatusInternalServerError)
+ return
+ }
+ defer r.Body.Close()
+
+ // Parse JSON into a map
+ var requestData map[string]interface{}
+ if err := json.Unmarshal(body, &requestData); err != nil {
+ http.Error(w, fmt.Sprintf("Error parsing JSON: %v", err), http.StatusBadRequest)
+ return
+ }
+
+ // Ensure exactly one key-value pair
+ if len(requestData) != 1 {
+ http.Error(w, "Request must contain exactly one key-value pair", http.StatusBadRequest)
+ return
+ }
+
+ // Get the single key and value
+ var key string
+ var value interface{}
+ for k, v := range requestData {
+ key = k
+ value = v
+ break
+ }
+
+ // Look up the setter
+ setter, exists := setterMap[key]
+ if !exists {
+ http.Error(w, fmt.Sprintf("Unknown configuration key: %s", key), http.StatusBadRequest)
+ return
+ }
+
+ // Call the setter
+ if err := setter(value); err != nil {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusBadRequest)
+ json.NewEncoder(w).Encode(map[string]string{
+ "status": "error",
+ "message": err.Error(),
+ })
+ return
+ }
+
+ // Success response
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(map[string]string{
+ "status": "success",
+ "message": "Configuration updated successfully",
+ })
+}
diff --git a/src/web/routes.go b/src/web/routes.go
index 9c4f02a3..c24f40df 100644
--- a/src/web/routes.go
+++ b/src/web/routes.go
@@ -8,6 +8,7 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/config/configchanger"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/backupmgr"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/detectionmgr"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/settings"
)
func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
@@ -88,6 +89,8 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
protectedMux.HandleFunc("/api/v2/runfile/save", HandleRunfileSave)
protectedMux.HandleFunc("/api/v2/runfile/hardreset", HandleSetRunfileGame)
protectedMux.HandleFunc("/api/v2/loader/reloadrunfile", HandleReloadRunfile)
+ protectedMux.HandleFunc("/api/v2/settings/save", settings.SaveSetting)
+ protectedMux.HandleFunc("/api/v2/settings", settings.RetrieveSettings)
return mux, protectedMux
}
From fa0517757875d0f0c2d479e415d0edaf1435c76f Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Tue, 30 Sep 2025 22:31:53 +0200
Subject: [PATCH 03/93] added config interfaces from SteamServerUI
---
.vscode/launch.json | 11 +
frontend/package.json | 2 +-
.../components/settings/AppSettings.svelte | 505 ++++++++++++++++++
.../settings/BackendSettings.svelte | 452 ++++++++++++++++
.../settings/RunfileSettings.svelte | 494 +++++++++++++++++
.../components/settings/SettingsView.svelte | 33 +-
6 files changed, 1492 insertions(+), 5 deletions(-)
create mode 100644 frontend/src/components/settings/AppSettings.svelte
create mode 100644 frontend/src/components/settings/BackendSettings.svelte
create mode 100644 frontend/src/components/settings/RunfileSettings.svelte
diff --git a/.vscode/launch.json b/.vscode/launch.json
index b5d9d441..2498df4a 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -29,6 +29,17 @@
"console": "integratedTerminal",
"showLog": false, // Hides some Go Debugger(Delve) log stuff that is not useful for debugging atm
"args": ["--NoSteamCMD"]
+ },
+ {
+ "name": "Debug Go Server noSteamCMD BUT svelte",
+ "type": "go",
+ "request": "launch",
+ "mode": "debug",
+ "program": "${workspaceFolder}/server.go",
+ "console": "integratedTerminal",
+ "preLaunchTask": "npm: build",
+ "showLog": false, // Hides some Go Debugger(Delve) log stuff that is not useful for debugging atm
+ "args": ["--NoSteamCMD"]
}
]
}
\ No newline at end of file
diff --git a/frontend/package.json b/frontend/package.json
index 7abeceef..eb5f3888 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -2,7 +2,7 @@
"name": "steamserverui",
"main": "main.cjs",
"private": true,
- "version": "v5.6.4",
+ "version": "v5.7.1",
"description": "Svelte UI Interface for Steam Server UI (SSUI) Backend",
"author": {
"name": "JacksonTheMaster",
diff --git a/frontend/src/components/settings/AppSettings.svelte b/frontend/src/components/settings/AppSettings.svelte
new file mode 100644
index 00000000..2c120bbd
--- /dev/null
+++ b/frontend/src/components/settings/AppSettings.svelte
@@ -0,0 +1,505 @@
+
+
+
+ {#if activeSidebarTab === 'General' || activeSidebarTab === 'SSUI Settings'}
+
+
SSUI Settings
+
+
+ Configure general application settings. Changes will be applied immediately.
+
+
+ {#if settingsGroups.length > 0}
+
+ {#each settingsGroups as group}
+ selectSettingsGroup(group)}>
+ {group}
+
+ {/each}
+
+
+ {#each settingsGroups as group}
+ {#if activeSettingsGroup === group}
+
+
{group}
+
+ {#each settingsData.filter(s => s.group === group) as setting}
+
+ {/each}
+
+
+ {/if}
+ {/each}
+
+ {#if statusMessage}
+
+ {isError ? '⚠️' : '✓'}
+ {statusMessage}
+ statusMessage = ''}>×
+
+ {/if}
+ {:else}
+
+
+
Loading settings...
+
+ {/if}
+
+{/if}
+
+
\ No newline at end of file
diff --git a/frontend/src/components/settings/BackendSettings.svelte b/frontend/src/components/settings/BackendSettings.svelte
new file mode 100644
index 00000000..36354a92
--- /dev/null
+++ b/frontend/src/components/settings/BackendSettings.svelte
@@ -0,0 +1,452 @@
+
+
+
+
+
+
+
+
+
Configured Backends
+
+ {#if activeBackendStatus.status === 'online'}
+ 🟢 Online
+ {:else if activeBackendStatus.status === 'offline'}
+ 🔴 Offline
+ {:else if activeBackendStatus.status === 'error'}
+ ⚠️ Error
+ {:else}
+ ⚪ Unknown
+ {/if}
+ {#if activeBackendStatus.lastChecked}
+
+ (Last checked: {new Date(activeBackendStatus.lastChecked).toLocaleTimeString()})
+
+ {/if}
+
+
testBackend(activeBackend)}
+ disabled={testing}
+ >
+ {testing ? 'Testing...' : 'Test Active Backend'}
+
+
+
+
+ {#each backends as backendId}
+
+
+
{backendId}
+
{currentConfig.backends[backendId].url}
+
+
+
+
+ {backendId === activeBackend ? 'Active' : 'Set Active'}
+
+
+
+ {#if backendId !== 'default'}
+ removeBackend(backendId)}>Remove
+ {/if}
+
+
+ {/each}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/settings/RunfileSettings.svelte b/frontend/src/components/settings/RunfileSettings.svelte
new file mode 100644
index 00000000..8adcc00b
--- /dev/null
+++ b/frontend/src/components/settings/RunfileSettings.svelte
@@ -0,0 +1,494 @@
+
+
+
+
+
Game Settings
+
+
+ Configure command line arguments that will be used when launching the game server.
+
+
+ {#if isLoading && runfileGroups.length === 0}
+
+
+
Loading runfile configuration...
+
+ {:else if runfileGroups.length === 0}
+
+
No Runfile Groups Available
+
Select a runfile from the Runfile Gallery to get started!
+
+ {:else}
+
+
+ {#each runfileGroups as group}
+ selectRunfileGroup(group)}>
+ {group}
+
+ {/each}
+
+
+ {#if isLoading && activeRunfileGroup}
+
+
+
Loading {activeRunfileGroup} settings...
+
+ {:else if activeRunfileGroup}
+
+
{activeRunfileGroup} GameServer Settings
+
+ {#if runfileArgs.length === 0}
+
+
No configurable arguments found for this group.
+
+ {:else}
+
+ {#each runfileArgs as arg}
+
+ {/each}
+
+
+
+
+ {isSaving ? 'Saving...' : 'Save All Changes'}
+
+
+ {/if}
+
+ {/if}
+
+ {#if statusMessage}
+
+ {isError ? '⚠️' : '✓'}
+ {statusMessage}
+ statusMessage = ''}>×
+
+ {/if}
+
+ {/if}
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/settings/SettingsView.svelte b/frontend/src/components/settings/SettingsView.svelte
index 86b93202..a327f474 100644
--- a/frontend/src/components/settings/SettingsView.svelte
+++ b/frontend/src/components/settings/SettingsView.svelte
@@ -1,7 +1,12 @@
+
diff --git a/frontend/src/components/Dashboard/cards/SystemInfoCard.svelte b/frontend/src/components/Dashboard/cards/SystemInfoCard.svelte
new file mode 100644
index 00000000..e5e8cd02
--- /dev/null
+++ b/frontend/src/components/Dashboard/cards/SystemInfoCard.svelte
@@ -0,0 +1,220 @@
+
+
+
+
+ {#if error}
+
{error}
+
Retry
+ {:else if systemInfo}
+
+
+
{typeof systemInfo.cpuUsage === 'number' ? systemInfo.cpuUsage.toFixed(2) : 'N/A'}%
+
System CPU Usage
+
+
+
+
{typeof systemInfo.memoryUsage === 'number' ? (systemInfo.memoryUsage/1024).toFixed(2) : 'N/A'} GB
+
Memory Consumption
+
+
+
+
{typeof systemInfo.diskUsage === 'number' ? (100-systemInfo.diskUsage).toFixed(2) : 'N/A'}%
+
Disk Space Unused
+
+
+
+
+
+
OS Version
+
{systemInfo.osName || 'Temple OS v3.14.15'} {systemInfo.osVersion || ''}
+
+
+
Kernel
+
{systemInfo.kernel || 'Cannot detect'}
+
+
+
Uptime
+
{systemInfo.uptime || 'Cannot detect'}
+
+
+
Backend IP Address
+
{systemInfo.backendIpAddress || 'Cannot detect'}
+
+
+
+
+
Last Refresh
+
{systemInfo.lastRefreshTime ? new Date(systemInfo.lastRefreshTime).toLocaleString() : 'Cannot detect'}
+
+
+ {:else}
+
Loading system information...
+ {/if}
+
+
+
\ No newline at end of file
diff --git a/src/steamserverui/systeminfo/systeminfo.go b/src/steamserverui/systeminfo/systeminfo.go
new file mode 100644
index 00000000..19395d75
--- /dev/null
+++ b/src/steamserverui/systeminfo/systeminfo.go
@@ -0,0 +1,403 @@
+package systeminfo
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+// OsStats represents the OS statistics that will be returned to the client
+type OsStats struct {
+ OSName string `json:"osName"`
+ OSVersion string `json:"osVersion"`
+ Kernel string `json:"kernel"`
+ Uptime string `json:"uptime"`
+ BackendIPAddr string `json:"backendIpAddress"`
+ CPUUsage float64 `json:"cpuUsage"`
+ MemoryUsage float64 `json:"memoryUsage"`
+ DiskUsage float64 `json:"diskUsage"`
+ LastRefreshTime string `json:"lastRefreshTime"`
+}
+
+var (
+ osStatsMutex sync.RWMutex
+ cachedOsStats *OsStats
+ lastRefreshTime time.Time
+ // Set cache duration to 1 minute
+ cacheDuration = 60 * time.Second
+)
+
+// CPUStats holds CPU time statistics
+type CPUStats struct {
+ idle uint64
+ total uint64
+}
+
+// refreshCachedStats updates the OS stats in the cache
+func RefreshCachedStats() (*OsStats, error) {
+ osStatsMutex.Lock()
+ defer osStatsMutex.Unlock()
+
+ // Check if cache is still valid
+ if cachedOsStats != nil && time.Since(lastRefreshTime) < cacheDuration {
+ return cachedOsStats, nil
+ }
+
+ // Gather all OS statistics
+ stats := &OsStats{}
+
+ // Get OS name and version
+ switch runtime.GOOS {
+ case "windows":
+ stats.OSName = "Windows"
+ cmd := exec.Command("powershell", "-Command", "(Get-WmiObject -class Win32_OperatingSystem).Caption")
+ output, err := cmd.Output()
+ if err == nil {
+ stats.OSVersion = strings.TrimSpace(string(output))
+ } else {
+ stats.OSVersion = "Unknown Windows Version"
+ }
+ case "linux":
+ // Try to get OS name from /etc/os-release
+ if data, err := os.ReadFile("/etc/os-release"); err == nil {
+ lines := strings.Split(string(data), "\n")
+ for _, line := range lines {
+ if strings.HasPrefix(line, "NAME=") {
+ stats.OSName = strings.Trim(strings.TrimPrefix(line, "NAME="), "\"")
+ } else if strings.HasPrefix(line, "VERSION=") {
+ stats.OSVersion = strings.Trim(strings.TrimPrefix(line, "VERSION="), "\"")
+ }
+ }
+ }
+
+ if stats.OSName == "" {
+ stats.OSName = "Linux"
+ }
+
+ if stats.OSVersion == "" {
+ // Try lsb_release if available
+ cmd := exec.Command("lsb_release", "-d")
+ output, err := cmd.Output()
+ if err == nil {
+ stats.OSVersion = strings.TrimSpace(strings.TrimPrefix(string(output), "Description:"))
+ } else {
+ stats.OSVersion = "Unknown Linux Version"
+ }
+ }
+ default:
+ stats.OSName = runtime.GOOS
+ stats.OSVersion = "Unknown Version"
+ }
+
+ // Get kernel version
+ switch runtime.GOOS {
+ case "windows":
+ cmd := exec.Command("powershell", "-Command", "(Get-WmiObject Win32_OperatingSystem).Version")
+ output, err := cmd.Output()
+ if err == nil {
+ stats.Kernel = strings.TrimSpace(string(output))
+ } else {
+ stats.Kernel = "Unknown"
+ }
+ case "linux":
+ cmd := exec.Command("uname", "-r")
+ output, err := cmd.Output()
+ if err == nil {
+ stats.Kernel = strings.TrimSpace(string(output))
+ } else {
+ stats.Kernel = "Unknown"
+ }
+ default:
+ stats.Kernel = "Unknown"
+ }
+
+ // Get uptime
+ switch runtime.GOOS {
+ case "windows":
+ cmd := exec.Command("powershell", "-Command", "(Get-WmiObject Win32_OperatingSystem).LastBootUpTime")
+ output, err := cmd.Output()
+ if err == nil {
+ bootTime := strings.TrimSpace(string(output))
+ bootTimeFormat := "20060102150405.000000-070"
+ t, err := time.Parse(bootTimeFormat, bootTime)
+ if err == nil {
+ uptime := time.Since(t)
+ days := int(uptime.Hours() / 24)
+ hours := int(uptime.Hours()) % 24
+ mins := int(uptime.Minutes()) % 60
+
+ if days > 0 {
+ stats.Uptime = fmt.Sprintf("%d days, %d hours, %d minutes", days, hours, mins)
+ } else if hours > 0 {
+ stats.Uptime = fmt.Sprintf("%d hours, %d minutes", hours, mins)
+ } else {
+ stats.Uptime = fmt.Sprintf("%d minutes", mins)
+ }
+ } else {
+ stats.Uptime = "Unknown"
+ }
+ } else {
+ stats.Uptime = "Unknown"
+ }
+ case "linux":
+ if data, err := os.ReadFile("/proc/uptime"); err == nil {
+ fields := strings.Fields(string(data))
+ if len(fields) > 0 {
+ uptimeSec, err := strconv.ParseFloat(fields[0], 64)
+ if err == nil {
+ uptime := time.Duration(uptimeSec) * time.Second
+ days := int(uptime.Hours() / 24)
+ hours := int(uptime.Hours()) % 24
+ mins := int(uptime.Minutes()) % 60
+
+ if days > 0 {
+ stats.Uptime = fmt.Sprintf("%d days, %d hours, %d minutes", days, hours, mins)
+ } else if hours > 0 {
+ stats.Uptime = fmt.Sprintf("%d hours, %d minutes", hours, mins)
+ } else {
+ stats.Uptime = fmt.Sprintf("%d minutes", mins)
+ }
+ } else {
+ stats.Uptime = "Unknown"
+ }
+ } else {
+ stats.Uptime = "Unknown"
+ }
+ } else {
+ stats.Uptime = "Unknown"
+ }
+ default:
+ stats.Uptime = "Unknown"
+ }
+
+ // Get backend IP address - try to get a non-loopback IP
+ stats.BackendIPAddr = getIPAddress()
+
+ // Get CPU usage
+ stats.CPUUsage = getCPUUsage()
+
+ // Get memory usage
+ stats.MemoryUsage = getMemoryUsage()
+
+ // Get disk usage
+ stats.DiskUsage = getDiskUsage()
+
+ // Update timestamp
+ now := time.Now()
+ stats.LastRefreshTime = now.Format(time.RFC3339)
+ lastRefreshTime = now
+ cachedOsStats = stats
+
+ return stats, nil
+}
+
+// getIPAddress returns a non-loopback IP address of the machine
+func getIPAddress() string {
+ switch runtime.GOOS {
+ case "windows":
+ cmd := exec.Command("powershell", "-Command", "Get-NetIPAddress | Where-Object {$_.AddressFamily -eq 'IPv4' -and $_.IPAddress -ne '127.0.0.1'} | Select-Object -ExpandProperty IPAddress -First 1")
+ output, err := cmd.Output()
+ if err == nil && len(output) > 0 {
+ return strings.TrimSpace(string(output))
+ }
+ case "linux":
+ cmd := exec.Command("hostname", "-I")
+ output, err := cmd.Output()
+ if err == nil && len(output) > 0 {
+ ips := strings.Fields(string(output))
+ if len(ips) > 0 {
+ return ips[0]
+ }
+ }
+ }
+ return "127.0.0.1" // Default to loopback if we can't find anything else
+}
+
+// getCPUUsage returns the CPU usage as a percentage
+func getCPUUsage() float64 {
+ switch runtime.GOOS {
+ case "windows":
+ cmd := exec.Command("powershell", "-Command", `(1..5 | ForEach-Object { (Get-CimInstance Win32_PerfFormattedData_PerfOS_Processor -Filter "Name='_Total'").PercentProcessorTime; Start-Sleep 1 } | Measure-Object -Average).Average`)
+ output, err := cmd.Output()
+ if err == nil {
+ if usage, err := strconv.ParseFloat(strings.TrimSpace(string(output)), 64); err == nil {
+ return usage
+ }
+ }
+ case "linux":
+ // First try using /proc/stat for accurate measurement across environments including containers
+ // We need to sample twice with a small delay to calculate CPU usage
+ prev, err := readCPUStats()
+ if err == nil {
+ // Small sleep to get a delta
+ time.Sleep(200 * time.Millisecond)
+ current, err := readCPUStats()
+ if err == nil {
+ // Calculate the delta between measurements
+ idleDelta := current.idle - prev.idle
+ totalDelta := current.total - prev.total
+
+ if totalDelta > 0 {
+ // CPU usage is the percentage of non-idle time
+ return 100.0 * (1.0 - float64(idleDelta)/float64(totalDelta))
+ }
+ }
+ }
+
+ // Fallback to other methods if /proc/stat didn't work
+ methods := []func() (float64, error){
+ // Method 1: Using top
+ func() (float64, error) {
+ cmd := exec.Command("sh", "-c", "top -bn1 | grep 'Cpu(s)' | awk '{print $2 + $4}'")
+ output, err := cmd.Output()
+ if err != nil {
+ return 0.0, err
+ }
+ return strconv.ParseFloat(strings.TrimSpace(string(output)), 64)
+ },
+ // Method 2: Using mpstat if available
+ func() (float64, error) {
+ cmd := exec.Command("sh", "-c", "command -v mpstat >/dev/null 2>&1 && mpstat 1 1 | awk '/Average:/ && $12 ~ /[0-9.]+/ {print 100 - $12}'")
+ output, err := cmd.Output()
+ if err != nil {
+ return 0.0, err
+ }
+ return strconv.ParseFloat(strings.TrimSpace(string(output)), 64)
+ },
+ // Method 3: Using /proc/loadavg as a rough indicator
+ func() (float64, error) {
+ data, err := os.ReadFile("/proc/loadavg")
+ if err != nil {
+ return 0.0, err
+ }
+ fields := strings.Fields(string(data))
+ if len(fields) < 1 {
+ return 0.0, fmt.Errorf("invalid format in /proc/loadavg")
+ }
+ loadavg, err := strconv.ParseFloat(fields[0], 64)
+ if err != nil {
+ return 0.0, err
+ }
+ // Get number of CPUs to normalize the load
+ var numCPU int
+ if cpuinfo, err := os.ReadFile("/proc/cpuinfo"); err == nil {
+ processors := strings.Count(string(cpuinfo), "processor")
+ if processors > 0 {
+ numCPU = processors
+ } else {
+ numCPU = runtime.NumCPU()
+ }
+ } else {
+ numCPU = runtime.NumCPU()
+ }
+ // Convert load average to a percentage (capped at 100%)
+ cpuUsage := (loadavg / float64(numCPU)) * 100
+ if cpuUsage > 100 {
+ cpuUsage = 100
+ }
+ return cpuUsage, nil
+ },
+ }
+
+ // Try each method until one works
+ for _, method := range methods {
+ if usage, err := method(); err == nil {
+ return usage
+ }
+ }
+ }
+ return 0.0 // Default to 0 if we can't determine the CPU usage
+}
+
+// readCPUStats reads the current CPU statistics from /proc/stat
+func readCPUStats() (CPUStats, error) {
+ data, err := os.ReadFile("/proc/stat")
+ if err != nil {
+ return CPUStats{}, err
+ }
+
+ lines := strings.Split(string(data), "\n")
+ for _, line := range lines {
+ fields := strings.Fields(line)
+ if len(fields) >= 5 && fields[0] == "cpu" {
+ // CPU line format: cpu user nice system idle iowait irq softirq steal guest guest_nice
+ // We need to sum all fields except idle (field 4) to get total CPU time
+ var total uint64
+ idle, _ := strconv.ParseUint(fields[4], 10, 64)
+
+ for i := 1; i < len(fields); i++ {
+ val, _ := strconv.ParseUint(fields[i], 10, 64)
+ total += val
+ }
+
+ return CPUStats{idle: idle, total: total}, nil
+ }
+ }
+
+ return CPUStats{}, fmt.Errorf("failed to parse CPU stats from /proc/stat")
+}
+
+// getMemoryUsage returns the systems memory usage in MB
+func getMemoryUsage() float64 {
+ switch runtime.GOOS {
+ case "windows":
+ cmd := exec.Command("powershell", "-Command", "[math]::Round((((Get-WmiObject Win32_OperatingSystem).TotalVisibleMemorySize - (Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory) /1024 ) )")
+ output, err := cmd.Output()
+ if err == nil {
+ if usage, err := strconv.ParseFloat(strings.TrimSpace(string(output)), 64); err == nil {
+ return usage
+ }
+ }
+ case "linux":
+ cmd := exec.Command("sh", "-c", "free | grep Mem | awk '{print ($3) / 1024}'")
+ output, err := cmd.Output()
+ if err == nil {
+ if usage, err := strconv.ParseFloat(strings.TrimSpace(string(output)), 64); err == nil {
+ return usage
+ }
+ }
+ }
+ return 0.0 // Default to 0 if we can't determine the memory usage
+}
+
+// getDiskUsage returns the disk usage as a percentage
+func getDiskUsage() float64 {
+ var path string
+ if runtime.GOOS == "windows" {
+ path = "C:\\"
+ } else {
+ path = "/"
+ }
+
+ switch runtime.GOOS {
+ case "windows":
+ cmd := exec.Command("powershell", "-Command", `[math]::Round(((Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='$((Get-Location).Drive.Name):'").Size - (Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='$((Get-Location).Drive.Name):'").FreeSpace) / (Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='$((Get-Location).Drive.Name):'").Size * 100, 2)`)
+ output, err := cmd.Output()
+ if err == nil {
+ if usage, err := strconv.ParseFloat(strings.TrimSpace(string(output)), 64); err == nil {
+ return usage
+ }
+ }
+ case "linux":
+ cmd := exec.Command("df", "-h", path)
+ output, err := cmd.Output()
+ if err == nil {
+ lines := strings.Split(string(output), "\n")
+ if len(lines) >= 2 {
+ fields := strings.Fields(lines[1])
+ if len(fields) >= 5 {
+ usage := strings.TrimSuffix(fields[4], "%")
+ if parsed, err := strconv.ParseFloat(usage, 64); err == nil {
+ return parsed
+ }
+ }
+ }
+ }
+ }
+ return 0.0 // Default to 0 if we can't determine the disk usage
+}
diff --git a/src/web/routes.go b/src/web/routes.go
index c24f40df..6b48b533 100644
--- a/src/web/routes.go
+++ b/src/web/routes.go
@@ -82,15 +82,20 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
// SteamServerUI
+ // --- RUNFILE ---
protectedMux.HandleFunc("/api/v2/runfile/groups", HandleRunfileGroups)
protectedMux.HandleFunc("/api/v2/runfile/args", HandleRunfileArgs)
protectedMux.HandleFunc("/api/v2/runfile/args/update", HandleRunfileArgUpdate)
protectedMux.HandleFunc("/api/v2/runfile", HandleRunfile)
protectedMux.HandleFunc("/api/v2/runfile/save", HandleRunfileSave)
protectedMux.HandleFunc("/api/v2/runfile/hardreset", HandleSetRunfileGame)
+ // --- LOADER ---
protectedMux.HandleFunc("/api/v2/loader/reloadrunfile", HandleReloadRunfile)
+ // --- SETTINGS ---
protectedMux.HandleFunc("/api/v2/settings/save", settings.SaveSetting)
protectedMux.HandleFunc("/api/v2/settings", settings.RetrieveSettings)
+ // --- OS STATS ---
+ protectedMux.HandleFunc("/api/v2/osstats", HandleGetOsStats)
return mux, protectedMux
}
diff --git a/src/web/systeminfo.go b/src/web/systeminfo.go
new file mode 100644
index 00000000..ccb52e99
--- /dev/null
+++ b/src/web/systeminfo.go
@@ -0,0 +1,35 @@
+package web
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/systeminfo"
+)
+
+func HandleGetOsStats(w http.ResponseWriter, r *http.Request) {
+ logger.Web.Debug("Received getOsStats request from API")
+ // accept only GET requests
+ if r.Method != http.MethodGet {
+ http.Error(w, "Only GET requests are allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ // Get cached stats or refresh if needed
+ stats, err := systeminfo.RefreshCachedStats()
+ if err != nil {
+ logger.Web.Error("Failed to get OS stats")
+ http.Error(w, "Failed to get OS stats", http.StatusInternalServerError)
+ return
+ }
+
+ // Set response headers and write JSON response
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ if err := json.NewEncoder(w).Encode(stats); err != nil {
+ logger.Web.Error("Failed to write response")
+ http.Error(w, "Failed to write response", http.StatusInternalServerError)
+ return
+ }
+}
From d24ff6170386f599a8a276ee03e38c07f446cd31 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Tue, 30 Sep 2025 23:12:03 +0200
Subject: [PATCH 05/93] added runfile gallery from SteamServerUI
---
frontend/src/App.svelte | 1 +
.../src/components/Gallery/ErrorPopup.svelte | 93 +++++
.../src/components/Gallery/RunfileCard.svelte | 311 ++++++++++++++++
.../components/Gallery/SteamCMDWait.svelte | 76 ++++
frontend/src/components/MainContent.svelte | 9 +
.../views/RunfileGalleryView.svelte | 349 ++++++++++++++++++
src/config/getters.go | 6 +
src/steamserverui/runfilegallery/gallery.go | 152 ++++++++
src/web/routes.go | 3 +
src/web/runfilegallery.go | 71 ++++
10 files changed, 1071 insertions(+)
create mode 100644 frontend/src/components/Gallery/ErrorPopup.svelte
create mode 100644 frontend/src/components/Gallery/RunfileCard.svelte
create mode 100644 frontend/src/components/Gallery/SteamCMDWait.svelte
create mode 100644 frontend/src/components/views/RunfileGalleryView.svelte
create mode 100644 src/steamserverui/runfilegallery/gallery.go
create mode 100644 src/web/runfilegallery.go
diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte
index 298bfb25..a9178b21 100644
--- a/frontend/src/App.svelte
+++ b/frontend/src/App.svelte
@@ -16,6 +16,7 @@
{ id: 'settings', name: 'Settings', icon: 'settings' },
{ id: 'console', name: 'Console', icon: 'terminal' },
{ id: 'logs', name: 'Logs', icon: 'file-text' },
+ { id: 'gallery', name: 'Gallery', icon: 'globe' },
];
// Set active view function
diff --git a/frontend/src/components/Gallery/ErrorPopup.svelte b/frontend/src/components/Gallery/ErrorPopup.svelte
new file mode 100644
index 00000000..69419a5a
--- /dev/null
+++ b/frontend/src/components/Gallery/ErrorPopup.svelte
@@ -0,0 +1,93 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/Gallery/RunfileCard.svelte b/frontend/src/components/Gallery/RunfileCard.svelte
new file mode 100644
index 00000000..7e3c0b74
--- /dev/null
+++ b/frontend/src/components/Gallery/RunfileCard.svelte
@@ -0,0 +1,311 @@
+
+
+
+
+
e.key === 'Enter'} tabindex="0" role="button" aria-label={`Runfile card for ${runfile.name}`}>
+
+
+
+
+
+
+
{runfile.name}
+
+
+
+
+
+
+ {#if isLoading}
+ {#if isSteamCMDRunning}
+
+ {:else}
+
+ {/if}
+ {:else}
+
+ Download & Apply
+
+ {/if}
+
+
+ {#if showErrorPopup}
+
+ {/if}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/Gallery/SteamCMDWait.svelte b/frontend/src/components/Gallery/SteamCMDWait.svelte
new file mode 100644
index 00000000..484a1a15
--- /dev/null
+++ b/frontend/src/components/Gallery/SteamCMDWait.svelte
@@ -0,0 +1,76 @@
+
+
+
+
+
{currentMessage}
+
Please don't poke the backend!
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/MainContent.svelte b/frontend/src/components/MainContent.svelte
index 482c074c..c1550d1d 100644
--- a/frontend/src/components/MainContent.svelte
+++ b/frontend/src/components/MainContent.svelte
@@ -4,6 +4,7 @@
import SettingsView from './settings/SettingsView.svelte';
import LogsView from './views/LogsView.svelte';
import ConsoleView from './views/ConsoleView.svelte';
+ import RunfileGalleryView from './views/RunfileGalleryView.svelte';
/**
* @typedef {Object} Props
@@ -35,6 +36,10 @@
title: 'Console',
description: 'View server console output'
},
+ gallery: {
+ title: 'Runfile Gallery',
+ description: 'Browse runfiles'
+ },
};
@@ -61,6 +66,10 @@
+ {:else if activeView === 'gallery'}
+
+
+
{/if}
diff --git a/frontend/src/components/views/RunfileGalleryView.svelte b/frontend/src/components/views/RunfileGalleryView.svelte
new file mode 100644
index 00000000..653ae3c9
--- /dev/null
+++ b/frontend/src/components/views/RunfileGalleryView.svelte
@@ -0,0 +1,349 @@
+
+
+
+
+
+ {#if error}
+
+ {/if}
+
+ {#if loading}
+
+
+
Loading runfiles...
+
+ {:else if !runfiles || runfiles.length === 0}
+
+
+
+
+
+
+
No runfiles available
+
Try refreshing the gallery to see available runfiles. Is this backend outdated?
+
+ {:else}
+
+ {runfiles.length} runfile{runfiles.length !== 1 ? 's' : ''} available
+
+
+ {#each runfiles as runfile (runfile.identifier)}
+
+ {/each}
+
+ {/if}
+
+
+
\ No newline at end of file
diff --git a/src/config/getters.go b/src/config/getters.go
index 27067b83..090fdff5 100644
--- a/src/config/getters.go
+++ b/src/config/getters.go
@@ -513,3 +513,9 @@ func GetIsDockerContainer() bool {
defer ConfigMu.RUnlock()
return IsDockerContainer
}
+
+func GetRunfilesFolder() string {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+ return RunFilesFolder
+}
diff --git a/src/steamserverui/runfilegallery/gallery.go b/src/steamserverui/runfilegallery/gallery.go
new file mode 100644
index 00000000..cad7fed6
--- /dev/null
+++ b/src/steamserverui/runfilegallery/gallery.go
@@ -0,0 +1,152 @@
+package runfilegallery
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+)
+
+// GalleryRunfile represents a runfile in the gallery
+type GalleryRunfile struct {
+ Name string `json:"name"`
+ Filename string `json:"filename"`
+ Version string `json:"version"`
+ BackgroundURL string `json:"background_url"`
+ LogoURL string `json:"logo_url"`
+ SupportedOS string `json:"supported_os"`
+ MinVersion string `json:"min_version"`
+}
+
+// galleryCache stores the parsed and filtered runfile list
+var (
+ galleryCache []GalleryRunfile
+ cacheMutex sync.Mutex
+)
+
+// GetRunfileGallery fetches the list of available runfiles from GitHub Pages
+func GetRunfileGallery(forceUpdate bool) ([]GalleryRunfile, error) {
+ cacheMutex.Lock()
+ defer cacheMutex.Unlock()
+
+ // Return cached results if not forcing an update and cache is populated
+ if !forceUpdate && len(galleryCache) > 0 {
+ logger.Runfile.Debug("Serving runfile gallery from cache")
+ return galleryCache, nil
+ }
+
+ // Fetch manifest from GitHub Pages
+ const manifestURL = "https://steamserverui.github.io/runfiles/manifest.ssui"
+ logger.Runfile.Info("Fetching runfile gallery from " + manifestURL)
+ resp, err := http.Get(manifestURL)
+ if err != nil {
+ logger.Runfile.Error(fmt.Sprintf("Failed to fetch manifest: %v", err))
+ return nil, fmt.Errorf("couldn't reach the gallery, network's playing hide and seek")
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ logger.Runfile.Error(fmt.Sprintf("Manifest fetch failed with status: %d", resp.StatusCode))
+ return nil, fmt.Errorf("gallery failed to fetch manifest from GitHub, status: %d", resp.StatusCode)
+ }
+
+ // Parse manifest
+ var runfiles []GalleryRunfile
+ if err := json.NewDecoder(resp.Body).Decode(&runfiles); err != nil {
+ logger.Runfile.Error(fmt.Sprintf("Failed to parse manifest: %v", err))
+ return nil, fmt.Errorf("manifest is gibberish, can't make sense of it")
+ }
+
+ // Filter by backend version
+ currentVersion := config.GetVersion()
+ var filtered []GalleryRunfile
+ for _, rf := range runfiles {
+ if compareVersions(rf.MinVersion, currentVersion) <= 0 {
+ filtered = append(filtered, rf)
+ } else {
+ logger.Runfile.Debug(fmt.Sprintf("Skipping runfile %s, requires version %s, current is %s", rf.Name, rf.MinVersion, currentVersion))
+ }
+ }
+
+ // Update cache
+ galleryCache = filtered
+ logger.Runfile.Info(fmt.Sprintf("Fetched and cached %d runfiles", len(filtered)))
+
+ if len(filtered) == 0 {
+ logger.Runfile.Warn("No runfiles compatible with backend version " + currentVersion)
+ }
+
+ return filtered, nil
+}
+
+// saveRunfileToDisk downloads a runfile by identifier and saves it to RunfilesDir
+func SaveRunfileToDisk(identifier string) error {
+ filename := fmt.Sprintf("run%s.ssui", identifier)
+ baseURL := "https://steamserverui.github.io/runfiles"
+ fileURL := fmt.Sprintf("%s/%s", baseURL, filename)
+
+ logger.Runfile.Info("Fetching runfile from " + fileURL)
+ resp, err := http.Get(fileURL)
+ if err != nil {
+ logger.Runfile.Error(fmt.Sprintf("Failed to fetch runfile %s: %v", filename, err))
+ return fmt.Errorf("couldn't grab %s, network's being a jerk", filename)
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ logger.Runfile.Error(fmt.Sprintf("Runfile %s fetch failed with status: %d", filename, resp.StatusCode))
+ return fmt.Errorf("%s is playing hard to get, status: %d", filename, resp.StatusCode)
+ }
+
+ saveFilePath := filepath.Join(config.GetRunfilesFolder(), filename)
+ logger.Runfile.Info("Saving runfile to " + saveFilePath)
+
+ // Create or overwrite the file
+ file, err := os.Create(saveFilePath)
+ if err != nil {
+ logger.Runfile.Error(fmt.Sprintf("Failed to create file %s: %v", saveFilePath, err))
+ return fmt.Errorf("disk's throwing a fit, can't save file")
+ }
+ defer file.Close()
+
+ // Copy response body to file
+ if _, err := io.Copy(file, resp.Body); err != nil {
+ logger.Runfile.Error(fmt.Sprintf("Failed to save runfile %s: %v", filename, err))
+ return fmt.Errorf("couldn't save %s, disk's being dramatic", filename)
+ }
+
+ logger.Runfile.Info("Successfully saved runfile " + filename)
+ return nil
+}
+
+// compareVersions compares two semantic version strings (x.y.z)
+// Returns -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
+func compareVersions(v1, v2 string) int {
+ v1Parts := strings.Split(v1, ".")
+ v2Parts := strings.Split(v2, ".")
+
+ // Ensure both versions have 3 parts
+ for i := 0; i < 3; i++ {
+ var n1, n2 int
+ if i < len(v1Parts) {
+ fmt.Sscanf(v1Parts[i], "%d", &n1)
+ }
+ if i < len(v2Parts) {
+ fmt.Sscanf(v2Parts[i], "%d", &n2)
+ }
+ if n1 < n2 {
+ return -1
+ }
+ if n1 > n2 {
+ return 1
+ }
+ }
+ return 0
+}
diff --git a/src/web/routes.go b/src/web/routes.go
index 6b48b533..7cc42aa1 100644
--- a/src/web/routes.go
+++ b/src/web/routes.go
@@ -96,6 +96,9 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
protectedMux.HandleFunc("/api/v2/settings", settings.RetrieveSettings)
// --- OS STATS ---
protectedMux.HandleFunc("/api/v2/osstats", HandleGetOsStats)
+ // --- RUNFILE GALLERY ---
+ protectedMux.HandleFunc("/api/v2/gallery", galleryHandler)
+ protectedMux.HandleFunc("/api/v2/gallery/select", selectHandler)
return mux, protectedMux
}
diff --git a/src/web/runfilegallery.go b/src/web/runfilegallery.go
new file mode 100644
index 00000000..68fffccc
--- /dev/null
+++ b/src/web/runfilegallery.go
@@ -0,0 +1,71 @@
+package web
+
+import (
+ "encoding/json"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfilegallery"
+)
+
+// response wraps API responses
+type response struct {
+ Data interface{} `json:"data,omitempty"`
+ Error string `json:"error,omitempty"`
+}
+
+// galleryHandler handles GET /api/v2/gallery
+func galleryHandler(w http.ResponseWriter, r *http.Request) {
+ logger.Runfile.Info("Handling GET /api/v2/gallery request")
+ forceUpdate := strings.ToLower(r.URL.Query().Get("forceUpdate")) == "true"
+
+ runfiles, err := runfilegallery.GetRunfileGallery(forceUpdate)
+ if err != nil {
+ logger.Runfile.Error("Gallery fetch failed: " + err.Error())
+ sendResponse(w, http.StatusInternalServerError, response{Error: err.Error()})
+ return
+ }
+
+ logger.Runfile.Info("Returning " + strconv.Itoa(len(runfiles)) + " runfiles from gallery")
+ sendResponse(w, http.StatusOK, response{Data: runfiles})
+}
+
+// selectHandler handles POST /api/v2/gallery/select
+func selectHandler(w http.ResponseWriter, r *http.Request) {
+ logger.Runfile.Info("Handling POST /api/v2/gallery/select request")
+
+ var req struct {
+ Identifier string `json:"identifier"`
+ }
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ logger.Runfile.Error("Invalid request body: " + err.Error())
+ sendResponse(w, http.StatusBadRequest, response{Error: "invalid JSON, check your request"})
+ return
+ }
+
+ if req.Identifier == "" {
+ logger.Runfile.Error("Missing identifier in request")
+ sendResponse(w, http.StatusBadRequest, response{Error: "identifier is required"})
+ return
+ }
+
+ if err := runfilegallery.SaveRunfileToDisk(req.Identifier); err != nil {
+ logger.Runfile.Error("Failed to save runfile " + req.Identifier + ": " + err.Error())
+ sendResponse(w, http.StatusInternalServerError, response{Error: err.Error()})
+ return
+ }
+
+ logger.Runfile.Info("Successfully saved runfile " + req.Identifier)
+ sendResponse(w, http.StatusOK, response{Data: "Runfile " + req.Identifier + " saved"})
+}
+
+// sendResponse writes a JSON response with the given status code
+func sendResponse(w http.ResponseWriter, status int, resp response) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(status)
+ if err := json.NewEncoder(w).Encode(resp); err != nil {
+ logger.Runfile.Error("Failed to encode response: " + err.Error())
+ }
+}
From 3e20c251e84cd2b2fcf9c23ed4d89272cded612e Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Wed, 1 Oct 2025 00:04:10 +0200
Subject: [PATCH 06/93] reorganized package names
---
src/core/loader/runfile.go | 4 +-
.../{runfilegallery => gallery}/gallery.go | 2 +-
.../{runfilemanager => runfile}/argexample.go | 2 +-
.../{runfilemanager => runfile}/args.go | 2 +-
.../{runfilemanager => runfile}/getters.go | 2 +-
src/web/runfile.go | 38 +++++++++----------
src/web/runfilegallery.go | 6 +--
7 files changed, 28 insertions(+), 28 deletions(-)
rename src/steamserverui/{runfilegallery => gallery}/gallery.go (99%)
rename src/steamserverui/{runfilemanager => runfile}/argexample.go (97%)
rename src/steamserverui/{runfilemanager => runfile}/args.go (99%)
rename src/steamserverui/{runfilemanager => runfile}/getters.go (99%)
diff --git a/src/core/loader/runfile.go b/src/core/loader/runfile.go
index 32a21c9d..1393c764 100644
--- a/src/core/loader/runfile.go
+++ b/src/core/loader/runfile.go
@@ -7,7 +7,7 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfilemanager"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
)
// used via Runfile Gallery
@@ -37,7 +37,7 @@ func InitRunfile(game string) error {
// used to only reload runfile into memory. Can be triggered from v1 UI -> Runfile Reset terminal
func ReloadRunfile() error {
- if err := runfilemanager.LoadRunfile(config.GetRunfileGame(), config.GetRunFilesFolder()); err != nil {
+ if err := runfile.LoadRunfile(config.GetRunfileGame(), config.GetRunFilesFolder()); err != nil {
logger.Runfile.Warn("Failed to reload runfile: " + err.Error())
return err
}
diff --git a/src/steamserverui/runfilegallery/gallery.go b/src/steamserverui/gallery/gallery.go
similarity index 99%
rename from src/steamserverui/runfilegallery/gallery.go
rename to src/steamserverui/gallery/gallery.go
index cad7fed6..00db95ec 100644
--- a/src/steamserverui/runfilegallery/gallery.go
+++ b/src/steamserverui/gallery/gallery.go
@@ -1,4 +1,4 @@
-package runfilegallery
+package gallery
import (
"encoding/json"
diff --git a/src/steamserverui/runfilemanager/argexample.go b/src/steamserverui/runfile/argexample.go
similarity index 97%
rename from src/steamserverui/runfilemanager/argexample.go
rename to src/steamserverui/runfile/argexample.go
index 63279371..23d43d57 100644
--- a/src/steamserverui/runfilemanager/argexample.go
+++ b/src/steamserverui/runfile/argexample.go
@@ -1,4 +1,4 @@
-package runfilemanager
+package runfile
import (
"fmt"
diff --git a/src/steamserverui/runfilemanager/args.go b/src/steamserverui/runfile/args.go
similarity index 99%
rename from src/steamserverui/runfilemanager/args.go
rename to src/steamserverui/runfile/args.go
index 275f8861..44d8604a 100644
--- a/src/steamserverui/runfilemanager/args.go
+++ b/src/steamserverui/runfile/args.go
@@ -1,4 +1,4 @@
-package runfilemanager
+package runfile
import (
"encoding/json"
diff --git a/src/steamserverui/runfilemanager/getters.go b/src/steamserverui/runfile/getters.go
similarity index 99%
rename from src/steamserverui/runfilemanager/getters.go
rename to src/steamserverui/runfile/getters.go
index 6fabab6d..c56075fc 100644
--- a/src/steamserverui/runfilemanager/getters.go
+++ b/src/steamserverui/runfile/getters.go
@@ -1,4 +1,4 @@
-package runfilemanager
+package runfile
import (
"fmt"
diff --git a/src/web/runfile.go b/src/web/runfile.go
index 3c7eea67..87a1f22a 100644
--- a/src/web/runfile.go
+++ b/src/web/runfile.go
@@ -8,7 +8,7 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/loader"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfilemanager"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
)
// APIGameArg is a DTO for GameArg, including RuntimeValue and all fields
@@ -29,7 +29,7 @@ type APIGameArg struct {
Disabled bool `json:"disabled"`
}
-// APIMeta mirrors runfilemanager.Meta for API responses
+// APIMeta mirrors runfile.Meta for API responses
type APIMeta struct {
Name string `json:"name"`
Version string `json:"version"`
@@ -51,8 +51,8 @@ type apiResponse struct {
Error string `json:"error,omitempty"`
}
-// toAPIGameArg converts runfilemanager.GameArg to APIGameArg
-func toAPIGameArg(arg runfilemanager.GameArg) APIGameArg {
+// toAPIGameArg converts runfile.GameArg to APIGameArg
+func toAPIGameArg(arg runfile.GameArg) APIGameArg {
return APIGameArg{
Flag: arg.Flag,
DefaultValue: arg.DefaultValue,
@@ -71,8 +71,8 @@ func toAPIGameArg(arg runfilemanager.GameArg) APIGameArg {
}
}
-// toAPIRunFile converts runfilemanager.RunFile to APIRunFile
-func toAPIRunFile(rf *runfilemanager.RunFile) APIRunFile {
+// toAPIRunFile converts runfile.RunFile to APIRunFile
+func toAPIRunFile(rf *runfile.RunFile) APIRunFile {
apiArgs := make(map[string][]APIGameArg)
for category, args := range rf.Args {
for _, arg := range args {
@@ -105,13 +105,13 @@ func writeJSONResponse(w http.ResponseWriter, status int, data interface{}, errM
// HandleRunfileGroups handles GET /api/v2/runfile/groups
func HandleRunfileGroups(w http.ResponseWriter, r *http.Request) {
logger.Runfile.Debug("GET /api/v2/runfile/groups")
- if runfilemanager.CurrentRunfile == nil {
+ if runfile.CurrentRunfile == nil {
logger.Runfile.Error("runfile not loaded")
writeJSONResponse(w, http.StatusInternalServerError, nil, "runfile not loaded")
return
}
- groups := runfilemanager.GetUIGroups()
+ groups := runfile.GetUIGroups()
logger.Runfile.Info("fetched UI groups")
writeJSONResponse(w, http.StatusOK, groups, "")
}
@@ -121,16 +121,16 @@ func HandleRunfileArgs(w http.ResponseWriter, r *http.Request) {
group := r.URL.Query().Get("group")
logger.Runfile.Debug(fmt.Sprintf("GET /api/v2/runfile/args group=%s", group))
- if runfilemanager.CurrentRunfile == nil {
+ if runfile.CurrentRunfile == nil {
logger.Runfile.Error("runfile not loaded")
writeJSONResponse(w, http.StatusInternalServerError, nil, "runfile not loaded")
return
}
- var args []runfilemanager.GameArg
+ var args []runfile.GameArg
if group != "" {
// Validate group
- validGroups := runfilemanager.GetUIGroups()
+ validGroups := runfile.GetUIGroups()
valid := false
for _, g := range validGroups {
if g == group {
@@ -143,9 +143,9 @@ func HandleRunfileArgs(w http.ResponseWriter, r *http.Request) {
writeJSONResponse(w, http.StatusBadRequest, nil, fmt.Sprintf("invalid group: %s", group))
return
}
- args = runfilemanager.GetArgsByGroup(group)
+ args = runfile.GetArgsByGroup(group)
} else {
- args = runfilemanager.GetAllArgs()
+ args = runfile.GetAllArgs()
}
// Convert to APIGameArg
@@ -162,7 +162,7 @@ func HandleRunfileArgs(w http.ResponseWriter, r *http.Request) {
func HandleRunfileArgUpdate(w http.ResponseWriter, r *http.Request) {
logger.Runfile.Debug("POST /api/v2/runfile/args")
- if runfilemanager.CurrentRunfile == nil {
+ if runfile.CurrentRunfile == nil {
logger.Runfile.Error("runfile not loaded")
writeJSONResponse(w, http.StatusInternalServerError, nil, "runfile not loaded")
return
@@ -184,7 +184,7 @@ func HandleRunfileArgUpdate(w http.ResponseWriter, r *http.Request) {
return
}
- if err := runfilemanager.SetArgValue(req.Flag, req.Value); err != nil {
+ if err := runfile.SetArgValue(req.Flag, req.Value); err != nil {
logger.Runfile.Error(fmt.Sprintf("failed to set arg %s: %v", req.Flag, err))
writeJSONResponse(w, http.StatusBadRequest, nil, fmt.Sprintf("failed to set arg: %v", err))
return
@@ -198,13 +198,13 @@ func HandleRunfileArgUpdate(w http.ResponseWriter, r *http.Request) {
func HandleRunfile(w http.ResponseWriter, r *http.Request) {
logger.Runfile.Debug("GET /api/v2/runfile")
- if runfilemanager.CurrentRunfile == nil {
+ if runfile.CurrentRunfile == nil {
logger.Runfile.Error("runfile not loaded")
writeJSONResponse(w, http.StatusInternalServerError, nil, "runfile not loaded")
return
}
- apiRunfile := toAPIRunFile(runfilemanager.CurrentRunfile)
+ apiRunfile := toAPIRunFile(runfile.CurrentRunfile)
logger.Runfile.Info("fetched runfile")
writeJSONResponse(w, http.StatusOK, apiRunfile, "")
}
@@ -213,13 +213,13 @@ func HandleRunfile(w http.ResponseWriter, r *http.Request) {
func HandleRunfileSave(w http.ResponseWriter, r *http.Request) {
logger.Runfile.Debug("POST /api/v2/runfile/save")
- if runfilemanager.CurrentRunfile == nil {
+ if runfile.CurrentRunfile == nil {
logger.Runfile.Error("runfile not loaded")
writeJSONResponse(w, http.StatusInternalServerError, nil, "runfile not loaded")
return
}
- if err := runfilemanager.SaveRunfile(); err != nil {
+ if err := runfile.SaveRunfile(); err != nil {
logger.Runfile.Error(fmt.Sprintf("failed to save runfile: %v", err))
writeJSONResponse(w, http.StatusInternalServerError, nil, fmt.Sprintf("failed to save runfile: %v", err))
return
diff --git a/src/web/runfilegallery.go b/src/web/runfilegallery.go
index 68fffccc..545349c3 100644
--- a/src/web/runfilegallery.go
+++ b/src/web/runfilegallery.go
@@ -7,7 +7,7 @@ import (
"strings"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfilegallery"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/gallery"
)
// response wraps API responses
@@ -21,7 +21,7 @@ func galleryHandler(w http.ResponseWriter, r *http.Request) {
logger.Runfile.Info("Handling GET /api/v2/gallery request")
forceUpdate := strings.ToLower(r.URL.Query().Get("forceUpdate")) == "true"
- runfiles, err := runfilegallery.GetRunfileGallery(forceUpdate)
+ runfiles, err := gallery.GetRunfileGallery(forceUpdate)
if err != nil {
logger.Runfile.Error("Gallery fetch failed: " + err.Error())
sendResponse(w, http.StatusInternalServerError, response{Error: err.Error()})
@@ -51,7 +51,7 @@ func selectHandler(w http.ResponseWriter, r *http.Request) {
return
}
- if err := runfilegallery.SaveRunfileToDisk(req.Identifier); err != nil {
+ if err := gallery.SaveRunfileToDisk(req.Identifier); err != nil {
logger.Runfile.Error("Failed to save runfile " + req.Identifier + ": " + err.Error())
sendResponse(w, http.StatusInternalServerError, response{Error: err.Error()})
return
From 085ca89cd088f02c3cbeb6685c85fbce2413644d Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Wed, 1 Oct 2025 00:05:09 +0200
Subject: [PATCH 07/93] added persistent IsSteamServerUI setting and switch in
steamcmd accordingly
---
src/config/config.go | 6 ++++++
src/config/getters.go | 6 ++++++
src/config/setters.go | 8 ++++++++
src/config/vars.go | 1 +
src/setup/install.go | 4 ++--
src/steamcmd/steamcmd.go | 32 +++++++++++++++++++++-----------
6 files changed, 44 insertions(+), 13 deletions(-)
diff --git a/src/config/config.go b/src/config/config.go
index 26fcf72f..db502bc5 100644
--- a/src/config/config.go
+++ b/src/config/config.go
@@ -73,6 +73,7 @@ type JsonConfig struct {
AutoStartServerOnStartup *bool `json:"AutoStartServerOnStartup"`
SSUIIdentifier string `json:"SSUIIdentifier"`
SSUIWebPort string `json:"SSUIWebPort"`
+ IsSteamServerUI *bool `json:"IsSteamServerUI"`
// Update Settings
IsUpdateEnabled *bool `json:"IsUpdateEnabled"`
@@ -265,6 +266,10 @@ func applyConfig(cfg *JsonConfig) {
AutoStartServerOnStartup = autoStartServerOnStartupVal
cfg.AutoStartServerOnStartup = &autoStartServerOnStartupVal
+ isSteamServerUIVal := getBool(cfg.IsSteamServerUI, "IS_STEAM_SERVER_UI", false)
+ IsSteamServerUI = isSteamServerUIVal
+ cfg.IsSteamServerUI = &isSteamServerUIVal
+
// Process SaveInfo to maintain backwards compatibility with pre-5.6.6 SaveInfo field (deprecated)
if SaveInfo != "" {
parts := strings.Split(SaveInfo, " ")
@@ -365,6 +370,7 @@ func safeSaveConfig() error {
AutoStartServerOnStartup: &AutoStartServerOnStartup,
SSUIIdentifier: SSUIIdentifier,
SSUIWebPort: SSUIWebPort,
+ IsSteamServerUI: &IsSteamServerUI,
}
file, err := os.Create(ConfigPath)
diff --git a/src/config/getters.go b/src/config/getters.go
index 090fdff5..1abf1c6b 100644
--- a/src/config/getters.go
+++ b/src/config/getters.go
@@ -519,3 +519,9 @@ func GetRunfilesFolder() string {
defer ConfigMu.Unlock()
return RunFilesFolder
}
+
+func GetIsSteamServerUI() bool {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+ return IsSteamServerUI
+}
diff --git a/src/config/setters.go b/src/config/setters.go
index 58627abb..b1b3654e 100644
--- a/src/config/setters.go
+++ b/src/config/setters.go
@@ -86,6 +86,14 @@ func SetWorldID(value string) error {
return nil
}
+func SetIsSteamServerUI(value bool) error {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+
+ IsSteamServerUI = value
+ return safeSaveConfig()
+}
+
// ALL SETTERS BELOW THIS LINE ARE UNUSED AT THE MOMENT
// ALL SETTERS BELOW THIS LINE ARE UNUSED AT THE MOMENT
// ALL SETTERS BELOW THIS LINE ARE UNUSED AT THE MOMENT
diff --git a/src/config/vars.go b/src/config/vars.go
index 6db79528..d40daa58 100644
--- a/src/config/vars.go
+++ b/src/config/vars.go
@@ -61,6 +61,7 @@ var (
LanguageSetting string
AutoStartServerOnStartup bool
SSUIIdentifier string
+ IsSteamServerUI bool
)
// Runtime only variables
diff --git a/src/setup/install.go b/src/setup/install.go
index 99e1d82d..c29ce7f4 100644
--- a/src/setup/install.go
+++ b/src/setup/install.go
@@ -42,8 +42,8 @@ func Install(wg *sync.WaitGroup) {
logger.Install.Info("✅Blacklist.txt verified or created.")
// Step 3: Install and run SteamCMD
logger.Install.Info("🔄Installing and running SteamCMD...")
- if config.GetSkipSteamCMD() {
- logger.Install.Info("✅Skipping SteamCMD installation, SkipSteamCMD is true")
+ if config.GetSkipSteamCMD() || config.GetIsSteamServerUI() {
+ logger.Install.Info("✅Skipping SteamCMD installation, SkipSteamCMD is true or IsSteamServerUI is true")
} else {
steamcmd.InstallAndRunSteamCMD()
}
diff --git a/src/steamcmd/steamcmd.go b/src/steamcmd/steamcmd.go
index 7312e714..cd7e2f7b 100644
--- a/src/steamcmd/steamcmd.go
+++ b/src/steamcmd/steamcmd.go
@@ -12,6 +12,7 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
)
@@ -61,7 +62,7 @@ func InstallAndRunSteamCMD() (int, error) {
return installSteamCMDLinux()
default:
err := fmt.Errorf("SteamCMD installation is not supported on this OS")
- logger.Install.Error("❌ " + err.Error() + "\n")
+ logger.Install.Error("❌ " + err.Error())
return -1, err
}
}
@@ -81,15 +82,15 @@ func runSteamCMD(steamCMDDir string) (int, error) {
defer logger.Core.Debug("🔄 Unlocking SteamMu after SteamCMD execution...")
currentDir, err := os.Getwd()
if err != nil {
- logger.Install.Error("❌ Error getting current working directory: " + err.Error() + "\n")
+ logger.Install.Error("❌ Error getting current working directory: " + err.Error())
return -1, err
}
- logger.Install.Debug("✅ Current working directory: " + currentDir + "\n")
+ logger.Install.Debug("✅ Current working directory: " + currentDir)
// Ensure permissions every time if we run on linux
if runtime.GOOS != "windows" {
if err := setExecutablePermissions(steamCMDDir); err != nil {
- logger.Install.Error("❌ Error setting executable permissions, your Steamcmd install might be broken: " + err.Error() + "\n")
+ logger.Install.Error("❌ Error setting executable permissions, your Steamcmd install might be broken: " + err.Error())
return -1, err
}
}
@@ -130,24 +131,33 @@ func runSteamCMD(steamCMDDir string) (int, error) {
err = cmd.Run()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
- logger.Install.Error("❌ SteamCMD exited unsuccessfully: " + err.Error() + "\n")
+ logger.Install.Error("❌ SteamCMD exited unsuccessfully: " + err.Error())
return exitErr.ExitCode(), err
}
- logger.Install.Error("❌ Error running SteamCMD: " + err.Error() + "\n")
+ logger.Install.Error("❌ Error running SteamCMD: " + err.Error())
return -1, err
}
- logger.Install.Info("✅ SteamCMD executed successfully.\n")
+ logger.Install.Info("✅ SteamCMD executed successfully.")
return 0, nil
}
// buildSteamCMDCommand constructs the SteamCMD command based on the OS.
func buildSteamCMDCommand(steamCMDDir, currentDir string) *exec.Cmd {
//print the config.GameBranch and config.GameServerAppID
- logger.Install.Info("🔍 Game Branch: " + config.GetGameBranch())
- logger.Install.Debug("🔍 Game Server App ID: " + config.GetGameServerAppID())
+ steamAppID := config.GetGameServerAppID()
+ if config.GetIsSteamServerUI() {
+ logger.Install.Info("🔍 SSUI Runfile Identifier: " + runfile.CurrentRunfile.Meta.Name)
+ logger.Install.Info("🔍 Game Branch: " + config.GetGameBranch())
+ logger.Install.Info("🔍 Game Server App ID: " + runfile.CurrentRunfile.SteamAppID)
+ steamAppID = runfile.CurrentRunfile.SteamAppID
+ } else {
+ logger.Install.Info("🔍 Game Branch: " + config.GetGameBranch())
+ logger.Install.Info("🔍 Game Server App ID: " + config.GetGameServerAppID())
+
+ }
if runtime.GOOS == "windows" {
- return exec.Command(filepath.Join(steamCMDDir, "steamcmd.exe"), "+force_install_dir", currentDir, "+login", "anonymous", "+app_update", config.GetGameServerAppID(), "-beta", config.GetGameBranch(), "validate", "+quit")
+ return exec.Command(filepath.Join(steamCMDDir, "steamcmd.exe"), "+force_install_dir", currentDir, "+login", "anonymous", "+app_update", steamAppID, "-beta", config.GetGameBranch(), "validate", "+quit")
}
- return exec.Command(filepath.Join(steamCMDDir, "steamcmd.sh"), "+force_install_dir", currentDir, "+login", "anonymous", "+app_update", config.GetGameServerAppID(), "-beta", config.GetGameBranch(), "validate", "+quit")
+ return exec.Command(filepath.Join(steamCMDDir, "steamcmd.sh"), "+force_install_dir", currentDir, "+login", "anonymous", "+app_update", steamAppID, "-beta", config.GetGameBranch(), "validate", "+quit")
}
From 9564c4c25c98c8f0498bbc01f1a6cb150ac806f9 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Wed, 1 Oct 2025 00:17:32 +0200
Subject: [PATCH 08/93] refactored runfile handling to use persistent
"RunfileIdentifier"; updated getters/setters and removed deprecated variable
---
src/config/config.go | 49 +++++++++++++++--------------
src/config/getters.go | 6 ----
src/config/steamserverui-getters.go | 11 +++++--
src/config/steamserverui-setter.go | 4 +--
src/config/steamserverui-vars.go | 5 ---
src/config/vars.go | 7 ++++-
src/core/loader/runfile.go | 4 +--
src/steamserverui/runfile/args.go | 2 +-
8 files changed, 46 insertions(+), 42 deletions(-)
delete mode 100644 src/config/steamserverui-vars.go
diff --git a/src/config/config.go b/src/config/config.go
index db502bc5..451c14d1 100644
--- a/src/config/config.go
+++ b/src/config/config.go
@@ -26,29 +26,30 @@ type JsonConfig struct {
// reordered in 5.6.4 to simplify the order of the config file.
// Gameserver Settings
- GameBranch string `json:"gameBranch"`
- GamePort string `json:"GamePort"`
- ServerName string `json:"ServerName"`
- SaveInfo string `json:"SaveInfo,omitempty"` // deprecated, kept for backwards compatibility
- SaveName string `json:"SaveName"` // replaces SaveInfo
- WorldID string `json:"WorldID"` // replaces SaveInfo
- ServerMaxPlayers string `json:"ServerMaxPlayers"`
- ServerPassword string `json:"ServerPassword"`
- ServerAuthSecret string `json:"ServerAuthSecret"`
- AdminPassword string `json:"AdminPassword"`
- UpdatePort string `json:"UpdatePort"`
- UPNPEnabled *bool `json:"UPNPEnabled"`
- AutoSave *bool `json:"AutoSave"`
- SaveInterval string `json:"SaveInterval"`
- AutoPauseServer *bool `json:"AutoPauseServer"`
- LocalIpAddress string `json:"LocalIpAddress"`
- StartLocalHost *bool `json:"StartLocalHost"`
- ServerVisible *bool `json:"ServerVisible"`
- UseSteamP2P *bool `json:"UseSteamP2P"`
- AdditionalParams string `json:"AdditionalParams"`
- Difficulty string `json:"Difficulty"`
- StartCondition string `json:"StartCondition"`
- StartLocation string `json:"StartLocation"`
+ RunfileIdentifier string `json:"RunfileIdentifier"`
+ GameBranch string `json:"gameBranch"`
+ GamePort string `json:"GamePort"`
+ ServerName string `json:"ServerName"`
+ SaveInfo string `json:"SaveInfo,omitempty"` // deprecated, kept for backwards compatibility
+ SaveName string `json:"SaveName"` // replaces SaveInfo
+ WorldID string `json:"WorldID"` // replaces SaveInfo
+ ServerMaxPlayers string `json:"ServerMaxPlayers"`
+ ServerPassword string `json:"ServerPassword"`
+ ServerAuthSecret string `json:"ServerAuthSecret"`
+ AdminPassword string `json:"AdminPassword"`
+ UpdatePort string `json:"UpdatePort"`
+ UPNPEnabled *bool `json:"UPNPEnabled"`
+ AutoSave *bool `json:"AutoSave"`
+ SaveInterval string `json:"SaveInterval"`
+ AutoPauseServer *bool `json:"AutoPauseServer"`
+ LocalIpAddress string `json:"LocalIpAddress"`
+ StartLocalHost *bool `json:"StartLocalHost"`
+ ServerVisible *bool `json:"ServerVisible"`
+ UseSteamP2P *bool `json:"UseSteamP2P"`
+ AdditionalParams string `json:"AdditionalParams"`
+ Difficulty string `json:"Difficulty"`
+ StartCondition string `json:"StartCondition"`
+ StartLocation string `json:"StartLocation"`
// Logging and debug settings
Debug *bool `json:"Debug"`
@@ -143,6 +144,7 @@ func applyConfig(cfg *JsonConfig) {
ControlPanelChannelID = getString(cfg.ControlPanelChannelID, "CONTROL_PANEL_CHANNEL_ID", "")
DiscordCharBufferSize = getInt(cfg.DiscordCharBufferSize, "DISCORD_CHAR_BUFFER_SIZE", 1000)
BlackListFilePath = getString(cfg.BlackListFilePath, "BLACKLIST_FILE_PATH", "./Blacklist.txt")
+ RunfileIdentifier = getString(cfg.RunfileIdentifier, "RUNFILE_IDENTIFIER", "")
isDiscordEnabledVal := getBool(cfg.IsDiscordEnabled, "IS_DISCORD_ENABLED", false)
IsDiscordEnabled = isDiscordEnabledVal
@@ -371,6 +373,7 @@ func safeSaveConfig() error {
SSUIIdentifier: SSUIIdentifier,
SSUIWebPort: SSUIWebPort,
IsSteamServerUI: &IsSteamServerUI,
+ RunfileIdentifier: RunfileIdentifier,
}
file, err := os.Create(ConfigPath)
diff --git a/src/config/getters.go b/src/config/getters.go
index 1abf1c6b..090fdff5 100644
--- a/src/config/getters.go
+++ b/src/config/getters.go
@@ -519,9 +519,3 @@ func GetRunfilesFolder() string {
defer ConfigMu.Unlock()
return RunFilesFolder
}
-
-func GetIsSteamServerUI() bool {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
- return IsSteamServerUI
-}
diff --git a/src/config/steamserverui-getters.go b/src/config/steamserverui-getters.go
index 7b4138c3..a9806fc8 100644
--- a/src/config/steamserverui-getters.go
+++ b/src/config/steamserverui-getters.go
@@ -1,5 +1,12 @@
package config
+// GetIsSteamServerUI returns if the system is in SteamServerUI mode
+func GetIsSteamServerUI() bool {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+ return IsSteamServerUI
+}
+
// GetRunFilesFolder returns the RunFilesFolder
func GetRunFilesFolder() string {
ConfigMu.Lock()
@@ -8,8 +15,8 @@ func GetRunFilesFolder() string {
}
// GetRunfileGame returns the RunfileGame
-func GetRunfileGame() string {
+func GetRunfileIdentifier() string {
ConfigMu.Lock()
defer ConfigMu.Unlock()
- return RunfileGame
+ return RunfileIdentifier
}
diff --git a/src/config/steamserverui-setter.go b/src/config/steamserverui-setter.go
index d79fde4e..a2958081 100644
--- a/src/config/steamserverui-setter.go
+++ b/src/config/steamserverui-setter.go
@@ -6,7 +6,7 @@ import (
)
// SetRunfileGame sets the RunfileGame with validation
-func SetRunfileGame(value string) error {
+func SetRunfileIdentifier(value string) error {
ConfigMu.Lock()
defer ConfigMu.Unlock()
@@ -14,7 +14,7 @@ func SetRunfileGame(value string) error {
return fmt.Errorf("runfile game cannot be empty")
}
- RunfileGame = value
+ RunfileIdentifier = value
return nil
//return saveConfig()
}
diff --git a/src/config/steamserverui-vars.go b/src/config/steamserverui-vars.go
deleted file mode 100644
index e817e5a2..00000000
--- a/src/config/steamserverui-vars.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package config
-
-var (
- RunfileGame string
-)
diff --git a/src/config/vars.go b/src/config/vars.go
index d40daa58..6e7f8359 100644
--- a/src/config/vars.go
+++ b/src/config/vars.go
@@ -61,7 +61,12 @@ var (
LanguageSetting string
AutoStartServerOnStartup bool
SSUIIdentifier string
- IsSteamServerUI bool
+)
+
+// SteamServerUI Settings
+var (
+ IsSteamServerUI bool
+ RunfileIdentifier string
)
// Runtime only variables
diff --git a/src/core/loader/runfile.go b/src/core/loader/runfile.go
index 1393c764..95a96baf 100644
--- a/src/core/loader/runfile.go
+++ b/src/core/loader/runfile.go
@@ -21,7 +21,7 @@ func InitRunfile(game string) error {
logger.Runfile.Info("Updating runfile game to " + game)
logger.Runfile.Info("Stopping server if running")
gamemgr.InternalStopServer()
- config.SetRunfileGame(game)
+ config.SetRunfileIdentifier(game)
if err := ReloadRunfile(); err != nil {
return err
@@ -37,7 +37,7 @@ func InitRunfile(game string) error {
// used to only reload runfile into memory. Can be triggered from v1 UI -> Runfile Reset terminal
func ReloadRunfile() error {
- if err := runfile.LoadRunfile(config.GetRunfileGame(), config.GetRunFilesFolder()); err != nil {
+ if err := runfile.LoadRunfile(config.GetRunfileIdentifier(), config.GetRunFilesFolder()); err != nil {
logger.Runfile.Warn("Failed to reload runfile: " + err.Error())
return err
}
diff --git a/src/steamserverui/runfile/args.go b/src/steamserverui/runfile/args.go
index 44d8604a..3faf86a1 100644
--- a/src/steamserverui/runfile/args.go
+++ b/src/steamserverui/runfile/args.go
@@ -228,7 +228,7 @@ func SaveRunfile() error {
}
// Build filepath
- filePath := filepath.Join(config.GetRunFilesFolder(), fmt.Sprintf("run%s.ssui", config.GetRunfileGame()))
+ filePath := filepath.Join(config.GetRunFilesFolder(), fmt.Sprintf("run%s.ssui", config.GetRunfileIdentifier()))
logger.Runfile.Debug(fmt.Sprintf("saving runfile: path=%s", filePath))
// Update DefaultValue from RuntimeValue
From de6d5737506d7217110141519bebdfdd509dd116 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Wed, 1 Oct 2025 00:55:47 +0200
Subject: [PATCH 09/93] added runfile loading to loader
---
src/core/loader/loader.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/core/loader/loader.go b/src/core/loader/loader.go
index 6c224cc4..c0e7631a 100644
--- a/src/core/loader/loader.go
+++ b/src/core/loader/loader.go
@@ -23,6 +23,7 @@ func InitBackend(wg *sync.WaitGroup) {
wg.Add(1)
defer wg.Done()
ReloadConfig()
+ ReloadRunfile()
ReloadSSCM()
ReloadBackupManager()
ReloadLocalizer()
From 09d45af9c311301fb425f95a8088bfe05e2d018a Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Wed, 1 Oct 2025 00:56:15 +0200
Subject: [PATCH 10/93] added steamcmd call to loader when init runfile is
called
---
src/core/loader/runfile.go | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/core/loader/runfile.go b/src/core/loader/runfile.go
index 95a96baf..927a976b 100644
--- a/src/core/loader/runfile.go
+++ b/src/core/loader/runfile.go
@@ -7,6 +7,7 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
)
@@ -27,16 +28,18 @@ func InitRunfile(game string) error {
return err
}
- logger.Runfile.Info("Running SteamCMD, this may take a while...")
- //steammgr.RunSteamCMD()
- logger.Runfile.Warn("Steamcmd for runfile not implemented yet")
logger.Runfile.Info("Runfile game updated to " + game)
+ logger.Runfile.Info("Running SteamCMD, this may take a while...")
+ steamcmd.InstallAndRunSteamCMD()
return nil
}
-// used to only reload runfile into memory. Can be triggered from v1 UI -> Runfile Reset terminal
func ReloadRunfile() error {
+ if !config.GetIsSteamServerUI() {
+ return nil
+ }
+
if err := runfile.LoadRunfile(config.GetRunfileIdentifier(), config.GetRunFilesFolder()); err != nil {
logger.Runfile.Warn("Failed to reload runfile: " + err.Error())
return err
From ffc9d85d46a2c9fe88e9c44f772418374781f95d Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Fri, 3 Oct 2025 17:08:12 +0200
Subject: [PATCH 11/93] small logging and testing improvements
---
src/core/loader/runfile.go | 1 +
src/steamcmd/steamcmd.go | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/src/core/loader/runfile.go b/src/core/loader/runfile.go
index 927a976b..4abe145d 100644
--- a/src/core/loader/runfile.go
+++ b/src/core/loader/runfile.go
@@ -37,6 +37,7 @@ func InitRunfile(game string) error {
func ReloadRunfile() error {
if !config.GetIsSteamServerUI() {
+ logger.Runfile.Warn("Runfile reloading is only supported in SteamServerUI mode")
return nil
}
diff --git a/src/steamcmd/steamcmd.go b/src/steamcmd/steamcmd.go
index cd7e2f7b..7c139ac1 100644
--- a/src/steamcmd/steamcmd.go
+++ b/src/steamcmd/steamcmd.go
@@ -35,6 +35,10 @@ const (
// InstallAndRunSteamCMD installs and runs SteamCMD based on the platform (Windows/Linux).
// It returns the exit status of the SteamCMD execution and any error encountered.
func InstallAndRunSteamCMD() (int, error) {
+ if config.GetSkipSteamCMD() {
+ logger.Install.Info("✅Skipping SteamCMD installation")
+ }
+
if isUpdatingMu.TryLock() {
// Successfully acquired the lock; we are not updating currently
logger.Core.Debug("🔄 Locking isUpdatingMu for SteamCMD Update run...")
From 0b48572694f95c51f4c795ece7232747d6f1f01c Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Fri, 3 Oct 2025 17:13:01 +0200
Subject: [PATCH 12/93] renamed "default" value in runfiles to "value" as that
field stores the set value, not only the default
---
src/steamserverui/runfile/args.go | 10 +++++-----
src/web/runfile.go | 4 ++--
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/steamserverui/runfile/args.go b/src/steamserverui/runfile/args.go
index 3faf86a1..6f86b5bb 100644
--- a/src/steamserverui/runfile/args.go
+++ b/src/steamserverui/runfile/args.go
@@ -52,7 +52,7 @@ func (e ErrValidation) Error() string {
type GameArg struct {
Flag string `json:"flag"`
- DefaultValue string `json:"default"`
+ Value string `json:"value"`
RuntimeValue string `json:"-"`
Required bool `json:"required"`
RequiresValue bool `json:"requires_value"`
@@ -196,10 +196,10 @@ func LoadRunfile(gameName, runFilesFolder string) error {
// Initialize runtime values *before* validation
for category := range runfile.Args {
for i := range runfile.Args[category] {
- runfile.Args[category][i].RuntimeValue = runfile.Args[category][i].DefaultValue
+ runfile.Args[category][i].RuntimeValue = runfile.Args[category][i].Value
logger.Runfile.Debug(fmt.Sprintf("initialized arg: flag=%s, default=%s, runtime=%s",
runfile.Args[category][i].Flag,
- runfile.Args[category][i].DefaultValue,
+ runfile.Args[category][i].Value,
runfile.Args[category][i].RuntimeValue))
}
}
@@ -231,10 +231,10 @@ func SaveRunfile() error {
filePath := filepath.Join(config.GetRunFilesFolder(), fmt.Sprintf("run%s.ssui", config.GetRunfileIdentifier()))
logger.Runfile.Debug(fmt.Sprintf("saving runfile: path=%s", filePath))
- // Update DefaultValue from RuntimeValue
+ // Update Value from RuntimeValue
for category := range CurrentRunfile.Args {
for i := range CurrentRunfile.Args[category] {
- CurrentRunfile.Args[category][i].DefaultValue = CurrentRunfile.Args[category][i].RuntimeValue
+ CurrentRunfile.Args[category][i].Value = CurrentRunfile.Args[category][i].RuntimeValue
}
}
diff --git a/src/web/runfile.go b/src/web/runfile.go
index 87a1f22a..27ff62cf 100644
--- a/src/web/runfile.go
+++ b/src/web/runfile.go
@@ -14,7 +14,7 @@ import (
// APIGameArg is a DTO for GameArg, including RuntimeValue and all fields
type APIGameArg struct {
Flag string `json:"flag"`
- DefaultValue string `json:"default"`
+ Value string `json:"value"`
RuntimeValue string `json:"runtime_value"`
Required bool `json:"required"`
RequiresValue bool `json:"requires_value"`
@@ -55,7 +55,7 @@ type apiResponse struct {
func toAPIGameArg(arg runfile.GameArg) APIGameArg {
return APIGameArg{
Flag: arg.Flag,
- DefaultValue: arg.DefaultValue,
+ Value: arg.Value,
RuntimeValue: arg.RuntimeValue,
Required: arg.Required,
RequiresValue: arg.RequiresValue,
From 8e224ea31c0c56f8d9ff18f4e67a06443d907149 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Fri, 3 Oct 2025 17:17:01 +0200
Subject: [PATCH 13/93] renamed setting "IsSteamServerUI" to "UseRunfiles"
---
src/config/config.go | 10 +++++-----
src/config/setters.go | 4 ++--
src/config/steamserverui-getters.go | 4 ++--
src/config/vars.go | 2 +-
src/core/loader/runfile.go | 2 +-
src/setup/install.go | 2 +-
src/steamcmd/steamcmd.go | 2 +-
7 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/src/config/config.go b/src/config/config.go
index 451c14d1..3dae6c5b 100644
--- a/src/config/config.go
+++ b/src/config/config.go
@@ -74,7 +74,7 @@ type JsonConfig struct {
AutoStartServerOnStartup *bool `json:"AutoStartServerOnStartup"`
SSUIIdentifier string `json:"SSUIIdentifier"`
SSUIWebPort string `json:"SSUIWebPort"`
- IsSteamServerUI *bool `json:"IsSteamServerUI"`
+ UseRunfiles *bool `json:"UseRunfiles"`
// Update Settings
IsUpdateEnabled *bool `json:"IsUpdateEnabled"`
@@ -268,9 +268,9 @@ func applyConfig(cfg *JsonConfig) {
AutoStartServerOnStartup = autoStartServerOnStartupVal
cfg.AutoStartServerOnStartup = &autoStartServerOnStartupVal
- isSteamServerUIVal := getBool(cfg.IsSteamServerUI, "IS_STEAM_SERVER_UI", false)
- IsSteamServerUI = isSteamServerUIVal
- cfg.IsSteamServerUI = &isSteamServerUIVal
+ isUseRunfilesVal := getBool(cfg.UseRunfiles, "USE_RUNFILES", true)
+ UseRunfiles = isUseRunfilesVal
+ cfg.UseRunfiles = &isUseRunfilesVal
// Process SaveInfo to maintain backwards compatibility with pre-5.6.6 SaveInfo field (deprecated)
if SaveInfo != "" {
@@ -372,7 +372,7 @@ func safeSaveConfig() error {
AutoStartServerOnStartup: &AutoStartServerOnStartup,
SSUIIdentifier: SSUIIdentifier,
SSUIWebPort: SSUIWebPort,
- IsSteamServerUI: &IsSteamServerUI,
+ UseRunfiles: &UseRunfiles,
RunfileIdentifier: RunfileIdentifier,
}
diff --git a/src/config/setters.go b/src/config/setters.go
index b1b3654e..1b1acbf3 100644
--- a/src/config/setters.go
+++ b/src/config/setters.go
@@ -86,11 +86,11 @@ func SetWorldID(value string) error {
return nil
}
-func SetIsSteamServerUI(value bool) error {
+func SetUseRunfiles(value bool) error {
ConfigMu.Lock()
defer ConfigMu.Unlock()
- IsSteamServerUI = value
+ UseRunfiles = value
return safeSaveConfig()
}
diff --git a/src/config/steamserverui-getters.go b/src/config/steamserverui-getters.go
index a9806fc8..b04d9d9f 100644
--- a/src/config/steamserverui-getters.go
+++ b/src/config/steamserverui-getters.go
@@ -1,10 +1,10 @@
package config
// GetIsSteamServerUI returns if the system is in SteamServerUI mode
-func GetIsSteamServerUI() bool {
+func GetUseRunfiles() bool {
ConfigMu.Lock()
defer ConfigMu.Unlock()
- return IsSteamServerUI
+ return UseRunfiles
}
// GetRunFilesFolder returns the RunFilesFolder
diff --git a/src/config/vars.go b/src/config/vars.go
index 6e7f8359..3873999d 100644
--- a/src/config/vars.go
+++ b/src/config/vars.go
@@ -65,7 +65,7 @@ var (
// SteamServerUI Settings
var (
- IsSteamServerUI bool
+ UseRunfiles bool
RunfileIdentifier string
)
diff --git a/src/core/loader/runfile.go b/src/core/loader/runfile.go
index 4abe145d..7357bf05 100644
--- a/src/core/loader/runfile.go
+++ b/src/core/loader/runfile.go
@@ -36,7 +36,7 @@ func InitRunfile(game string) error {
}
func ReloadRunfile() error {
- if !config.GetIsSteamServerUI() {
+ if !config.GetUseRunfiles() {
logger.Runfile.Warn("Runfile reloading is only supported in SteamServerUI mode")
return nil
}
diff --git a/src/setup/install.go b/src/setup/install.go
index c29ce7f4..2dcb07ee 100644
--- a/src/setup/install.go
+++ b/src/setup/install.go
@@ -42,7 +42,7 @@ func Install(wg *sync.WaitGroup) {
logger.Install.Info("✅Blacklist.txt verified or created.")
// Step 3: Install and run SteamCMD
logger.Install.Info("🔄Installing and running SteamCMD...")
- if config.GetSkipSteamCMD() || config.GetIsSteamServerUI() {
+ if config.GetSkipSteamCMD() || config.GetUseRunfiles() {
logger.Install.Info("✅Skipping SteamCMD installation, SkipSteamCMD is true or IsSteamServerUI is true")
} else {
steamcmd.InstallAndRunSteamCMD()
diff --git a/src/steamcmd/steamcmd.go b/src/steamcmd/steamcmd.go
index 7c139ac1..880a046e 100644
--- a/src/steamcmd/steamcmd.go
+++ b/src/steamcmd/steamcmd.go
@@ -149,7 +149,7 @@ func runSteamCMD(steamCMDDir string) (int, error) {
func buildSteamCMDCommand(steamCMDDir, currentDir string) *exec.Cmd {
//print the config.GameBranch and config.GameServerAppID
steamAppID := config.GetGameServerAppID()
- if config.GetIsSteamServerUI() {
+ if config.GetUseRunfiles() {
logger.Install.Info("🔍 SSUI Runfile Identifier: " + runfile.CurrentRunfile.Meta.Name)
logger.Install.Info("🔍 Game Branch: " + config.GetGameBranch())
logger.Install.Info("🔍 Game Server App ID: " + runfile.CurrentRunfile.SteamAppID)
From db8d7290cd8bc4b21924ac2557b54d7270cd4f60 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Fri, 3 Oct 2025 18:07:28 +0200
Subject: [PATCH 14/93] skip steamcmd when runfiles are true for better testing
---
src/steamcmd/steamcmd.go | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/steamcmd/steamcmd.go b/src/steamcmd/steamcmd.go
index 880a046e..124212c7 100644
--- a/src/steamcmd/steamcmd.go
+++ b/src/steamcmd/steamcmd.go
@@ -35,8 +35,9 @@ const (
// InstallAndRunSteamCMD installs and runs SteamCMD based on the platform (Windows/Linux).
// It returns the exit status of the SteamCMD execution and any error encountered.
func InstallAndRunSteamCMD() (int, error) {
- if config.GetSkipSteamCMD() {
+ if config.GetSkipSteamCMD() || config.GetUseRunfiles() {
logger.Install.Info("✅Skipping SteamCMD installation")
+ return -1, nil
}
if isUpdatingMu.TryLock() {
From c9dc0f0801e31e3398da10087334406d520ede50 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Fri, 3 Oct 2025 18:07:48 +0200
Subject: [PATCH 15/93] fix loggin in runfile init
---
src/steamserverui/runfile/args.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/steamserverui/runfile/args.go b/src/steamserverui/runfile/args.go
index 6f86b5bb..204aa4a4 100644
--- a/src/steamserverui/runfile/args.go
+++ b/src/steamserverui/runfile/args.go
@@ -197,7 +197,7 @@ func LoadRunfile(gameName, runFilesFolder string) error {
for category := range runfile.Args {
for i := range runfile.Args[category] {
runfile.Args[category][i].RuntimeValue = runfile.Args[category][i].Value
- logger.Runfile.Debug(fmt.Sprintf("initialized arg: flag=%s, default=%s, runtime=%s",
+ logger.Runfile.Debug(fmt.Sprintf("initialized arg: flag=%s, value=%s, runtime=%s",
runfile.Args[category][i].Flag,
runfile.Args[category][i].Value,
runfile.Args[category][i].RuntimeValue))
From 98fedc225f862f64096c849ea12faf78e46d2ac7 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Fri, 3 Oct 2025 19:03:21 +0200
Subject: [PATCH 16/93] add TestArgBuilder and register new CLI command for
argument building functionality
---
src/cli/runtimecommands.go | 6 ++++++
src/steamserverui/runfile/argexample.go | 8 ++++++++
2 files changed, 14 insertions(+)
diff --git a/src/cli/runtimecommands.go b/src/cli/runtimecommands.go
index 1e282b82..673a76e0 100644
--- a/src/cli/runtimecommands.go
+++ b/src/cli/runtimecommands.go
@@ -24,6 +24,7 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
)
// ANSI escape codes for green text and reset
@@ -163,6 +164,7 @@ func init() {
RegisterCommand("getbuildid", WrapNoReturn(getBuildID), "gbid")
RegisterCommand("setdummybuildid", WrapNoReturn(setDummyBuildID), "sdbid")
RegisterCommand("printconfig", WrapNoReturn(printConfig), "pc")
+ RegisterCommand("testargbuilder", WrapNoReturn(TestArgBuilder), "targb")
}
func startServer() {
@@ -311,3 +313,7 @@ func supportPackage() {
w, _ = zw.Create("system_info.txt")
w.Write([]byte(info))
}
+
+func TestArgBuilder() {
+ runfile.TestArgBuilder()
+}
diff --git a/src/steamserverui/runfile/argexample.go b/src/steamserverui/runfile/argexample.go
index 23d43d57..ad248216 100644
--- a/src/steamserverui/runfile/argexample.go
+++ b/src/steamserverui/runfile/argexample.go
@@ -49,3 +49,11 @@ func Examples() {
}
fmt.Println(arg.RuntimeValue) // Access the argument's properties
}
+
+func TestArgBuilder() {
+ args, err := BuildCommandArgs()
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println(args)
+}
From a471203a534011e4f4a16347b183752e7e02ca8d Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Fri, 3 Oct 2025 19:04:16 +0200
Subject: [PATCH 17/93] changed BuildCommandArgs to allow appending the value
but not the flag (for use with -file start arguments)
---
src/steamserverui/runfile/args.go | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/src/steamserverui/runfile/args.go b/src/steamserverui/runfile/args.go
index 204aa4a4..1b77445c 100644
--- a/src/steamserverui/runfile/args.go
+++ b/src/steamserverui/runfile/args.go
@@ -347,15 +347,11 @@ func BuildCommandArgs() ([]string, error) {
})
for _, arg := range allArgs {
- if arg.Disabled {
- continue
- }
- if !arg.Required && arg.RequiresValue && arg.RuntimeValue == "" {
+ if arg.Disabled || (!arg.Required && arg.RequiresValue && arg.RuntimeValue == "") { //Clear text for clarity: skip if disabled OR if it's an optional argument that needs a value but doesn't have one set
continue
}
- args = append(args, arg.Flag)
- // Special handling
+ // Handle space_delimited: split and append non-empty parts
if arg.Special == "space_delimited" {
parts := strings.Split(arg.RuntimeValue, " ")
for _, part := range parts {
@@ -366,7 +362,12 @@ func BuildCommandArgs() ([]string, error) {
continue
}
- // Only add value if the argument requires one
+ // Append non-empty flags
+ if arg.Flag != "" && arg.Special != "dont_append_flag_just_value" {
+ args = append(args, arg.Flag)
+ }
+
+ // Append value if required and non-empty
if arg.RequiresValue && arg.RuntimeValue != "" {
args = append(args, arg.RuntimeValue)
}
From 229fbd16743283c33c7aace28f1f075f03f92500 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 4 Oct 2025 15:07:46 +0200
Subject: [PATCH 18/93] save config when RunfileIdentifier is updated
---
src/config/steamserverui-setter.go | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/config/steamserverui-setter.go b/src/config/steamserverui-setter.go
index a2958081..7a0cf22d 100644
--- a/src/config/steamserverui-setter.go
+++ b/src/config/steamserverui-setter.go
@@ -15,6 +15,5 @@ func SetRunfileIdentifier(value string) error {
}
RunfileIdentifier = value
- return nil
- //return saveConfig()
+ return safeSaveConfig()
}
From a4f227c25e0503f7db6d6a0d6f03862f65882b2c Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 4 Oct 2025 21:27:47 +0200
Subject: [PATCH 19/93] added a arg.Special to hide args from the rf on the UI
---
src/steamserverui/runfile/getters.go | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/steamserverui/runfile/getters.go b/src/steamserverui/runfile/getters.go
index c56075fc..b6770787 100644
--- a/src/steamserverui/runfile/getters.go
+++ b/src/steamserverui/runfile/getters.go
@@ -35,6 +35,7 @@ func GetUIGroups() []string {
return result
}
+// GetArgsByGroup returns GameArgs from the rf for use in UI context - and skips args that have arg.Special is not "hide_in_ui"
func GetArgsByGroup(group string) []GameArg {
if CurrentRunfile == nil {
logger.Runfile.Error("runfile not loaded")
@@ -44,7 +45,9 @@ func GetArgsByGroup(group string) []GameArg {
var result []GameArg
for _, arg := range CurrentRunfile.getAllArgs() {
if arg.UIGroup == group {
- result = append(result, arg)
+ if arg.Special != "hide_in_ui" {
+ result = append(result, arg)
+ }
}
}
return result
From a36f8030d8f5b8ee229ef63d0ecf833f95fb0504 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sun, 5 Oct 2025 13:55:42 +0200
Subject: [PATCH 20/93] use runfile for steamcmd auto update poller if
configured, otherwise fallback to Stationeers game server app ID
---
src/steamcmd/getappinfo.go | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/steamcmd/getappinfo.go b/src/steamcmd/getappinfo.go
index 53e68541..57be19fe 100644
--- a/src/steamcmd/getappinfo.go
+++ b/src/steamcmd/getappinfo.go
@@ -17,6 +17,7 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/commandmgr"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
)
var (
@@ -84,8 +85,13 @@ func getAppInfo() error {
}
steamcmddir := SteamCMDLinuxDir
executable := "steamcmd.sh"
+
appid := config.GetGameServerAppID()
+ if config.GetUseRunfiles() {
+ appid = runfile.CurrentRunfile.SteamAppID
+ }
+
if runtime.GOOS == "windows" {
executable = "steamcmd.exe"
steamcmddir = SteamCMDWindowsDir
From e118d278c9a86609f0599b57c7709bcae40afeda Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sun, 5 Oct 2025 13:56:05 +0200
Subject: [PATCH 21/93] added IsStationeersMode setting
---
src/config/config.go | 5 +++++
src/config/getters.go | 6 ++++++
src/config/vars.go | 1 +
src/core/loader/loader.go | 26 +++++++++++++++-----------
4 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/src/config/config.go b/src/config/config.go
index 3dae6c5b..5c4336a2 100644
--- a/src/config/config.go
+++ b/src/config/config.go
@@ -75,6 +75,7 @@ type JsonConfig struct {
SSUIIdentifier string `json:"SSUIIdentifier"`
SSUIWebPort string `json:"SSUIWebPort"`
UseRunfiles *bool `json:"UseRunfiles"`
+ IsStationeersMode *bool `json:"StationeersMode"`
// Update Settings
IsUpdateEnabled *bool `json:"IsUpdateEnabled"`
@@ -272,6 +273,10 @@ func applyConfig(cfg *JsonConfig) {
UseRunfiles = isUseRunfilesVal
cfg.UseRunfiles = &isUseRunfilesVal
+ isStationeersModeVal := getBool(cfg.IsStationeersMode, "STATIONEERS_MODE", false)
+ IsStationeersMode = isStationeersModeVal
+ cfg.IsStationeersMode = &isStationeersModeVal
+
// Process SaveInfo to maintain backwards compatibility with pre-5.6.6 SaveInfo field (deprecated)
if SaveInfo != "" {
parts := strings.Split(SaveInfo, " ")
diff --git a/src/config/getters.go b/src/config/getters.go
index 090fdff5..8f2cce2e 100644
--- a/src/config/getters.go
+++ b/src/config/getters.go
@@ -519,3 +519,9 @@ func GetRunfilesFolder() string {
defer ConfigMu.Unlock()
return RunFilesFolder
}
+
+func GetIsStationeersMode() bool {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+ return IsStationeersMode
+}
diff --git a/src/config/vars.go b/src/config/vars.go
index 3873999d..6b9d8f0d 100644
--- a/src/config/vars.go
+++ b/src/config/vars.go
@@ -67,6 +67,7 @@ var (
var (
UseRunfiles bool
RunfileIdentifier string
+ IsStationeersMode bool
)
// Runtime only variables
diff --git a/src/core/loader/loader.go b/src/core/loader/loader.go
index c0e7631a..bf6a2b77 100644
--- a/src/core/loader/loader.go
+++ b/src/core/loader/loader.go
@@ -24,8 +24,8 @@ func InitBackend(wg *sync.WaitGroup) {
defer wg.Done()
ReloadConfig()
ReloadRunfile()
- ReloadSSCM()
- ReloadBackupManager()
+ ReloadStationeersSSCM()
+ ReloadStationeersBackupManager()
ReloadLocalizer()
ReloadAppInfoPoller()
ReloadDiscordBot()
@@ -37,8 +37,8 @@ func ReloadBackend() {
logger.Core.Info("Reloading backend...")
ReloadConfig()
- ReloadSSCM()
- ReloadBackupManager()
+ ReloadStationeersSSCM()
+ ReloadStationeersBackupManager()
ReloadLocalizer()
ReloadAppInfoPoller()
PrintConfigDetails()
@@ -55,16 +55,20 @@ func ReloadConfig() {
}
-func ReloadSSCM() {
- if config.GetIsSSCMEnabled() {
- setup.InstallSSCM()
+func ReloadStationeersSSCM() {
+ if config.GetIsStationeersMode() {
+ if config.GetIsSSCMEnabled() {
+ setup.InstallSSCM()
+ }
}
}
-func ReloadBackupManager() {
- if err := backupmgr.ReloadBackupManagerFromConfig(); err != nil {
- logger.Backup.Error("Failed to reload backup manager: " + err.Error())
- return
+func ReloadStationeersBackupManager() {
+ if config.GetIsStationeersMode() {
+ if err := backupmgr.ReloadBackupManagerFromConfig(); err != nil {
+ logger.Backup.Error("Failed to reload backup manager: " + err.Error())
+ return
+ }
}
}
From a1f1100f876aa882a7550e36ffa1b41cc84ea003 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sun, 5 Oct 2025 13:58:43 +0200
Subject: [PATCH 22/93] update Discord RPC activity wording
---
src/discordrpc/discordrpc.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/discordrpc/discordrpc.go b/src/discordrpc/discordrpc.go
index 1f4d0d49..e8bb22ec 100644
--- a/src/discordrpc/discordrpc.go
+++ b/src/discordrpc/discordrpc.go
@@ -12,10 +12,10 @@ import (
func StartDiscordRPC() (*discordrichpresence.Client, error) {
client := discordrichpresence.NewClient("1408848834875887669")
activity := discordrichpresence.NewActivity().
- State("Managing a Stationeers Server").
- Details("Your one-stop-shop for running a Stationeers server").
+ State("Managing a Gameserver").
+ Details("Your one-stop-shop for running a Gameserver").
StartTime(time.Now()).
- LargeImage("logo", "The easy to use Stationeers Dedicated Server Manager").
+ LargeImage("logo", "The easy to use Dedicated Server Manager").
SmallImage("rocket", "Online and Active").
Type(0).
Build()
From eb5bd1b6e2e605befbfed8366d2b124bc68fe3f4 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sun, 5 Oct 2025 14:04:25 +0200
Subject: [PATCH 23/93] unified setter & getter files
---
src/config/getters.go | 21 +++++++++++++++++++++
src/config/setters.go | 17 ++++++++++++-----
src/config/steamserverui-getters.go | 22 ----------------------
src/config/steamserverui-setter.go | 19 -------------------
4 files changed, 33 insertions(+), 46 deletions(-)
delete mode 100644 src/config/steamserverui-getters.go
delete mode 100644 src/config/steamserverui-setter.go
diff --git a/src/config/getters.go b/src/config/getters.go
index 8f2cce2e..31ea5ca8 100644
--- a/src/config/getters.go
+++ b/src/config/getters.go
@@ -525,3 +525,24 @@ func GetIsStationeersMode() bool {
defer ConfigMu.Unlock()
return IsStationeersMode
}
+
+// GetIsSteamServerUI returns if the system is in SteamServerUI mode
+func GetUseRunfiles() bool {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+ return UseRunfiles
+}
+
+// GetRunFilesFolder returns the RunFilesFolder
+func GetRunFilesFolder() string {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+ return RunFilesFolder
+}
+
+// GetRunfileGame returns the RunfileGame
+func GetRunfileIdentifier() string {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+ return RunfileIdentifier
+}
diff --git a/src/config/setters.go b/src/config/setters.go
index 1b1acbf3..0f886bd2 100644
--- a/src/config/setters.go
+++ b/src/config/setters.go
@@ -94,11 +94,18 @@ func SetUseRunfiles(value bool) error {
return safeSaveConfig()
}
-// ALL SETTERS BELOW THIS LINE ARE UNUSED AT THE MOMENT
-// ALL SETTERS BELOW THIS LINE ARE UNUSED AT THE MOMENT
-// ALL SETTERS BELOW THIS LINE ARE UNUSED AT THE MOMENT
-// ALL SETTERS BELOW THIS LINE ARE UNUSED AT THE MOMENT
-// ALL SETTERS BELOW THIS LINE ARE UNUSED AT THE MOMENT
+// SetRunfileGame sets the RunfileGame with validation
+func SetRunfileIdentifier(value string) error {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+
+ if strings.TrimSpace(value) == "" {
+ return fmt.Errorf("runfile game cannot be empty")
+ }
+
+ RunfileIdentifier = value
+ return safeSaveConfig()
+}
// Debug and Logging Settings
func SetIsDebugMode(value bool) error {
diff --git a/src/config/steamserverui-getters.go b/src/config/steamserverui-getters.go
deleted file mode 100644
index b04d9d9f..00000000
--- a/src/config/steamserverui-getters.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package config
-
-// GetIsSteamServerUI returns if the system is in SteamServerUI mode
-func GetUseRunfiles() bool {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
- return UseRunfiles
-}
-
-// GetRunFilesFolder returns the RunFilesFolder
-func GetRunFilesFolder() string {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
- return RunFilesFolder
-}
-
-// GetRunfileGame returns the RunfileGame
-func GetRunfileIdentifier() string {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
- return RunfileIdentifier
-}
diff --git a/src/config/steamserverui-setter.go b/src/config/steamserverui-setter.go
deleted file mode 100644
index 7a0cf22d..00000000
--- a/src/config/steamserverui-setter.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package config
-
-import (
- "fmt"
- "strings"
-)
-
-// SetRunfileGame sets the RunfileGame with validation
-func SetRunfileIdentifier(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- if strings.TrimSpace(value) == "" {
- return fmt.Errorf("runfile game cannot be empty")
- }
-
- RunfileIdentifier = value
- return safeSaveConfig()
-}
From b270df5230788e2858a2b16d05e2f9a3962b2bbe Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sun, 5 Oct 2025 18:32:24 +0200
Subject: [PATCH 24/93] re-enable steamcmd for testing
---
src/steamcmd/steamcmd.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/steamcmd/steamcmd.go b/src/steamcmd/steamcmd.go
index 124212c7..d72fcc6e 100644
--- a/src/steamcmd/steamcmd.go
+++ b/src/steamcmd/steamcmd.go
@@ -35,10 +35,6 @@ const (
// InstallAndRunSteamCMD installs and runs SteamCMD based on the platform (Windows/Linux).
// It returns the exit status of the SteamCMD execution and any error encountered.
func InstallAndRunSteamCMD() (int, error) {
- if config.GetSkipSteamCMD() || config.GetUseRunfiles() {
- logger.Install.Info("✅Skipping SteamCMD installation")
- return -1, nil
- }
if isUpdatingMu.TryLock() {
// Successfully acquired the lock; we are not updating currently
From 75390463070bb03e9849f1965dfbad8df0127f9a Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sun, 5 Oct 2025 18:32:52 +0200
Subject: [PATCH 25/93] added v6 arg builder to startup
---
src/managers/gamemgr/processmanagement.go | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/managers/gamemgr/processmanagement.go b/src/managers/gamemgr/processmanagement.go
index 06e1a296..1f04fe71 100644
--- a/src/managers/gamemgr/processmanagement.go
+++ b/src/managers/gamemgr/processmanagement.go
@@ -14,6 +14,7 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
)
var (
@@ -35,6 +36,10 @@ func InternalStartServer() error {
args := buildCommandArgs()
+ if config.UseRunfiles {
+ args, err = runfile.BuildCommandArgs()
+ }
+
logger.Core.Info("=== GAMESERVER STARTING ===")
if config.GetIsSSCMEnabled() && runtime.GOOS == "linux" {
From 5398c33e2b6f2bfabf2a88f9a6143a7d43628ab2 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sun, 5 Oct 2025 19:32:06 +0200
Subject: [PATCH 26/93] [Megacommit] Rework of many systems, see following
lines: Add support for generic BepInEx without SSCM change BepInEx setup
logic refactor SSCM integration across relevant files add better arg printing
in startup for debugging switched to only supporting runfiles in v6 removed
running steamcmd during startup entirely hooked up runfile arg builder fully
this should have been multiple commits, whoops
---
src/config/config.go | 9 +-
src/config/getters.go | 13 ++-
src/config/setters.go | 8 ++
src/config/vars.go | 5 +-
src/core/loader/loader.go | 11 +-
src/core/loader/runfile.go | 4 -
src/managers/gamemgr/args.go | 118 ----------------------
src/managers/gamemgr/bepinex.go | 9 +-
src/managers/gamemgr/processmanagement.go | 50 ++++++---
src/setup/{sscm.go => bepinex.go} | 12 ---
src/setup/install.go | 8 --
src/steamcmd/getappinfo.go | 6 +-
src/steamcmd/steamcmd.go | 15 +--
13 files changed, 79 insertions(+), 189 deletions(-)
delete mode 100644 src/managers/gamemgr/args.go
rename src/setup/{sscm.go => bepinex.go} (95%)
diff --git a/src/config/config.go b/src/config/config.go
index 5c4336a2..aaba18e9 100644
--- a/src/config/config.go
+++ b/src/config/config.go
@@ -68,6 +68,7 @@ type JsonConfig struct {
ExePath string `json:"ExePath"`
LogClutterToConsole *bool `json:"LogClutterToConsole"`
IsSSCMEnabled *bool `json:"IsSSCMEnabled"`
+ IsBepInExEnabled *bool `json:"IsBepInExEnabled"`
AutoRestartServerTimer string `json:"AutoRestartServerTimer"`
IsConsoleEnabled *bool `json:"IsConsoleEnabled"`
LanguageSetting string `json:"LanguageSetting"`
@@ -253,7 +254,12 @@ func applyConfig(cfg *JsonConfig) {
SubsystemFilters = getStringSlice(cfg.SubsystemFilters, "SUBSYSTEM_FILTERS", []string{})
AutoRestartServerTimer = getString(cfg.AutoRestartServerTimer, "AUTO_RESTART_SERVER_TIMER", "0")
- isSSCMEnabledVal := getBool(cfg.IsSSCMEnabled, "IS_SSCM_ENABLED", true)
+
+ isBepInExEnabledVal := getBool(cfg.IsBepInExEnabled, "IS_BEPINEX_ENABLED", false)
+ IsBepInExEnabled = isBepInExEnabledVal
+ cfg.IsBepInExEnabled = &isBepInExEnabledVal
+
+ isSSCMEnabledVal := getBool(cfg.IsSSCMEnabled, "IS_SSCM_ENABLED", false)
IsSSCMEnabled = isSSCMEnabledVal
cfg.IsSSCMEnabled = &isSSCMEnabledVal
@@ -368,6 +374,7 @@ func safeSaveConfig() error {
SubsystemFilters: SubsystemFilters,
IsUpdateEnabled: &IsUpdateEnabled,
IsSSCMEnabled: &IsSSCMEnabled,
+ IsBepInExEnabled: &IsBepInExEnabled,
AutoRestartServerTimer: AutoRestartServerTimer,
AllowPrereleaseUpdates: &AllowPrereleaseUpdates,
AllowMajorUpdates: &AllowMajorUpdates,
diff --git a/src/config/getters.go b/src/config/getters.go
index 31ea5ca8..80b57d0b 100644
--- a/src/config/getters.go
+++ b/src/config/getters.go
@@ -327,6 +327,12 @@ func GetIsSSCMEnabled() bool {
return IsSSCMEnabled
}
+func GetIsBepInExEnabled() bool {
+ ConfigMu.RLock()
+ defer ConfigMu.RUnlock()
+ return IsBepInExEnabled
+}
+
func GetSSCMFilePath() string {
ConfigMu.RLock()
defer ConfigMu.RUnlock()
@@ -526,13 +532,6 @@ func GetIsStationeersMode() bool {
return IsStationeersMode
}
-// GetIsSteamServerUI returns if the system is in SteamServerUI mode
-func GetUseRunfiles() bool {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
- return UseRunfiles
-}
-
// GetRunFilesFolder returns the RunFilesFolder
func GetRunFilesFolder() string {
ConfigMu.Lock()
diff --git a/src/config/setters.go b/src/config/setters.go
index 0f886bd2..50a42b9b 100644
--- a/src/config/setters.go
+++ b/src/config/setters.go
@@ -30,6 +30,14 @@ func SetIsSSCMEnabled(value bool) error {
return safeSaveConfig()
}
+func SetIsBepInExEnabled(value bool) error {
+ ConfigMu.Lock()
+ defer ConfigMu.Unlock()
+
+ IsBepInExEnabled = value
+ return safeSaveConfig()
+}
+
func SetCurrentBranchBuildID(value string) error {
ConfigMu.Lock()
defer ConfigMu.Unlock()
diff --git a/src/config/vars.go b/src/config/vars.go
index 6b9d8f0d..3b75400a 100644
--- a/src/config/vars.go
+++ b/src/config/vars.go
@@ -128,10 +128,11 @@ var (
AllowAutoGameServerUpdates bool
)
-// SSCM (Stationeers Server Command Manager) settings
+// BepInEx settings
var (
- IsSSCMEnabled bool
+ IsSSCMEnabled bool
+ IsBepInExEnabled bool
)
// File paths
diff --git a/src/core/loader/loader.go b/src/core/loader/loader.go
index bf6a2b77..b042b6a4 100644
--- a/src/core/loader/loader.go
+++ b/src/core/loader/loader.go
@@ -24,7 +24,7 @@ func InitBackend(wg *sync.WaitGroup) {
defer wg.Done()
ReloadConfig()
ReloadRunfile()
- ReloadStationeersSSCM()
+ ReloadBepInEx()
ReloadStationeersBackupManager()
ReloadLocalizer()
ReloadAppInfoPoller()
@@ -37,7 +37,7 @@ func ReloadBackend() {
logger.Core.Info("Reloading backend...")
ReloadConfig()
- ReloadStationeersSSCM()
+ ReloadBepInEx()
ReloadStationeersBackupManager()
ReloadLocalizer()
ReloadAppInfoPoller()
@@ -55,10 +55,11 @@ func ReloadConfig() {
}
-func ReloadStationeersSSCM() {
- if config.GetIsStationeersMode() {
+func ReloadBepInEx() {
+ if config.GetIsBepInExEnabled() {
+ setup.CheckAndInstallBepInEx()
if config.GetIsSSCMEnabled() {
- setup.InstallSSCM()
+ setup.CheckAndDownloadSSCM()
}
}
}
diff --git a/src/core/loader/runfile.go b/src/core/loader/runfile.go
index 7357bf05..5af0c2d2 100644
--- a/src/core/loader/runfile.go
+++ b/src/core/loader/runfile.go
@@ -36,10 +36,6 @@ func InitRunfile(game string) error {
}
func ReloadRunfile() error {
- if !config.GetUseRunfiles() {
- logger.Runfile.Warn("Runfile reloading is only supported in SteamServerUI mode")
- return nil
- }
if err := runfile.LoadRunfile(config.GetRunfileIdentifier(), config.GetRunFilesFolder()); err != nil {
logger.Runfile.Warn("Failed to reload runfile: " + err.Error())
diff --git a/src/managers/gamemgr/args.go b/src/managers/gamemgr/args.go
deleted file mode 100644
index 3ccba137..00000000
--- a/src/managers/gamemgr/args.go
+++ /dev/null
@@ -1,118 +0,0 @@
-package gamemgr
-
-import (
- "runtime"
- "strconv"
- "strings"
-
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
-)
-
-type Arg struct {
- Flag string
- Value string
- RequiresValue bool
- Condition func() bool
- NoQuote bool
-}
-
-func buildCommandArgs() []string {
- var argOrder []Arg
-
- if config.IsNewTerrainAndSaveSystem {
- argOrder = []Arg{
- {Flag: "-nographics", RequiresValue: false},
- {Flag: "-batchmode", RequiresValue: false},
- /* file start: (expects up to four optional args for:
- -worldid (Optional to LOAD, required to CREATE save, if start value is not found, tries to create map with worldid) -> config.BackupWorldName for legacy reasons
- -difficulty (Optional, defaults to "Normal" if not provided)
- -startcondition (Optional, defaults to the default start condition for the world setting if not provided.)
- -startlocation (Optional, defaults to "DefaultStartLocation" if not provided.)
- */
- {Flag: "-file", RequiresValue: false},
- {Flag: "start", Value: config.GetSaveName(), RequiresValue: true},
- {Flag: config.GetWorldID(), RequiresValue: false},
- {Flag: config.GetDifficulty(), RequiresValue: false, Condition: func() bool { return config.GetDifficulty() != "" }},
- {Flag: config.GetStartCondition(), RequiresValue: false, Condition: func() bool { return config.GetStartCondition() != "" }},
- {Flag: config.GetStartLocation(), RequiresValue: false, Condition: func() bool { return config.GetStartLocation() != "" }},
- // file start end
- {Flag: "-logFile", Value: "./debug.log", Condition: func() bool { return runtime.GOOS == "linux" }, RequiresValue: true},
- {Flag: "-settings", RequiresValue: false},
- {Flag: "StartLocalHost", Value: strconv.FormatBool(config.GetStartLocalHost()), RequiresValue: true},
- {Flag: "ServerVisible", Value: strconv.FormatBool(config.GetServerVisible()), RequiresValue: true},
- {Flag: "GamePort", Value: config.GetGamePort(), RequiresValue: true},
- {Flag: "UPNPEnabled", Value: strconv.FormatBool(config.GetUPNPEnabled()), RequiresValue: true},
- {Flag: "ServerName", Value: config.GetServerName(), RequiresValue: true},
- {Flag: "ServerPassword", Value: config.GetServerPassword(), Condition: func() bool { return config.GetServerPassword() != "" }, RequiresValue: true},
- {Flag: "ServerMaxPlayers", Value: config.GetServerMaxPlayers(), RequiresValue: true},
- {Flag: "AutoSave", Value: strconv.FormatBool(config.GetAutoSave()), RequiresValue: true},
- {Flag: "SaveInterval", Value: config.GetSaveInterval(), RequiresValue: true},
- {Flag: "ServerAuthSecret", Value: config.GetServerAuthSecret(), Condition: func() bool { return config.GetServerAuthSecret() != "" }, RequiresValue: true},
- {Flag: "UpdatePort", Value: config.GetUpdatePort(), RequiresValue: true},
- {Flag: "AutoPauseServer", Value: strconv.FormatBool(config.GetAutoPauseServer()), RequiresValue: true},
- {Flag: "UseSteamP2P", Value: strconv.FormatBool(config.GetUseSteamP2P()), RequiresValue: true},
- {Flag: "AdminPassword", Value: config.GetAdminPassword(), Condition: func() bool { return config.GetAdminPassword() != "" }, RequiresValue: true},
- }
- }
- if !config.GetIsNewTerrainAndSaveSystem() {
- argOrder = []Arg{
- {Flag: "-nographics", RequiresValue: false},
- {Flag: "-batchmode", RequiresValue: false},
- {Flag: "-LOAD", Value: config.GetLegacySaveInfo(), RequiresValue: true, NoQuote: true}, // LOAD has special handling because the gameserver expects 2 parameters
- {Flag: "-logFile", Value: "./debug.log", Condition: func() bool { return runtime.GOOS == "linux" }, RequiresValue: true},
- {Flag: "-settings", RequiresValue: false},
- {Flag: "StartLocalHost", Value: strconv.FormatBool(config.GetStartLocalHost()), RequiresValue: true},
- {Flag: "ServerVisible", Value: strconv.FormatBool(config.GetServerVisible()), RequiresValue: true},
- {Flag: "GamePort", Value: config.GetGamePort(), RequiresValue: true},
- {Flag: "UPNPEnabled", Value: strconv.FormatBool(config.GetUPNPEnabled()), RequiresValue: true},
- {Flag: "ServerName", Value: config.GetServerName(), RequiresValue: true},
- {Flag: "ServerPassword", Value: config.GetServerPassword(), Condition: func() bool { return config.GetServerPassword() != "" }, RequiresValue: true},
- {Flag: "ServerMaxPlayers", Value: config.GetServerMaxPlayers(), RequiresValue: true},
- {Flag: "AutoSave", Value: strconv.FormatBool(config.GetAutoSave()), RequiresValue: true},
- {Flag: "SaveInterval", Value: config.GetSaveInterval(), RequiresValue: true},
- {Flag: "ServerAuthSecret", Value: config.GetServerAuthSecret(), Condition: func() bool { return config.GetServerAuthSecret() != "" }, RequiresValue: true},
- {Flag: "UpdatePort", Value: config.GetUpdatePort(), RequiresValue: true},
- {Flag: "AutoPauseServer", Value: strconv.FormatBool(config.GetAutoPauseServer()), RequiresValue: true},
- {Flag: "UseSteamP2P", Value: strconv.FormatBool(config.GetUseSteamP2P()), RequiresValue: true},
- {Flag: "AdminPassword", Value: config.GetAdminPassword(), Condition: func() bool { return config.GetAdminPassword() != "" }, RequiresValue: true},
- }
- }
-
- var args []string
- for _, arg := range argOrder {
- if arg.Condition != nil && !arg.Condition() {
- continue
- }
- if arg.RequiresValue && arg.Value == "" {
- continue
- }
-
- args = append(args, arg.Flag)
-
- // handling of Legacy SaveInfo: Split on semicolon and add each part as a separate arg. This is a hack to continue to support the old saveinfo format for preterrain servers.
- if arg.Flag == "-LOAD" && arg.Value != "" {
- parts := strings.SplitN(arg.Value, ";", 2)
- for _, part := range parts {
- if part != "" {
- args = append(args, part)
- }
- }
- continue
- }
-
- if arg.Value != "" {
- args = append(args, arg.Value)
- }
- }
-
- if config.GetAdditionalParams() != "" {
- args = append(args, strings.Fields(config.GetAdditionalParams())...)
- }
-
- if config.GetLocalIpAddress() != "" {
- args = append(args, "LocalIpAddress")
- args = append(args, config.GetLocalIpAddress())
- }
-
- return args
-}
diff --git a/src/managers/gamemgr/bepinex.go b/src/managers/gamemgr/bepinex.go
index 081e8bcf..7a66aa4b 100644
--- a/src/managers/gamemgr/bepinex.go
+++ b/src/managers/gamemgr/bepinex.go
@@ -7,6 +7,7 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
)
// BepInEx version: 5.4.23.2 or v5-lts
@@ -15,9 +16,13 @@ import (
// Returns a map of environment variables and an error if setup fails.
func SetupBepInExEnvironment() ([]string, error) {
- executablePath := config.GetExePath()
+ executablePath, err := runfile.CurrentRunfile.GetExecutable()
+ if err != nil {
+ logger.Core.Error("Failed to get executable path from runfile for Bepinex setup: " + err.Error())
+ return nil, err
+ }
- if !config.GetIsSSCMEnabled() {
+ if !config.GetIsBepInExEnabled() {
logger.Core.Debug("SSCM is disabled, skipping environment setup")
return nil, nil
}
diff --git a/src/managers/gamemgr/processmanagement.go b/src/managers/gamemgr/processmanagement.go
index 1f04fe71..6217fd80 100644
--- a/src/managers/gamemgr/processmanagement.go
+++ b/src/managers/gamemgr/processmanagement.go
@@ -21,7 +21,6 @@ var (
cmd *exec.Cmd
mu sync.Mutex
logDone chan struct{}
- err error
processExited chan struct{}
// autoRestartDone is defined in autorestart.go
)
@@ -34,15 +33,22 @@ func InternalStartServer() error {
return fmt.Errorf("server is already running")
}
- args := buildCommandArgs()
+ args, err := runfile.BuildCommandArgs()
+ if err != nil {
+ logger.Core.Error("Failed to build command args: " + err.Error())
+ return err
+ }
- if config.UseRunfiles {
- args, err = runfile.BuildCommandArgs()
+ executable, err := runfile.CurrentRunfile.GetExecutable()
+ if err != nil {
+ logger.Core.Error("Failed to get executable path from runfile: " + err.Error())
+ return err
}
logger.Core.Info("=== GAMESERVER STARTING ===")
+ logger.Core.Info("BepInEx/Doorstop enabled: " + strconv.FormatBool(config.GetIsBepInExEnabled()))
- if config.GetIsSSCMEnabled() && runtime.GOOS == "linux" {
+ if config.GetIsBepInExEnabled() && runtime.GOOS == "linux" {
var envVars []string
// Set up SSCM (BepInEx/Doorstop) environment
@@ -51,28 +57,44 @@ func InternalStartServer() error {
return fmt.Errorf("failed to set up SSCM environment: %v", err)
}
// Create command after environment is set
- cmd = exec.Command(config.GetExePath(), args...)
+ cmd = exec.Command(executable, args...)
// Set the environment for the command
if envVars != nil {
cmd.Env = envVars
logger.Core.Info("BepInEx/Doorstop environment configured for server process")
}
- logger.Core.Info("• Executable: " + config.GetExePath() + " (with SSCM)")
- logger.Core.Info("• Arguments: " + strings.Join(args, " "))
+ logger.Core.Info("• Executable: " + executable)
+ var formattedArgs []string
+ for _, arg := range args {
+ if strings.ContainsAny(arg, " \t\n\"'") {
+ formattedArgs = append(formattedArgs, `"`+strings.ReplaceAll(arg, `"`, `\"`)+`"`)
+ } else {
+ formattedArgs = append(formattedArgs, arg)
+ }
+ }
+ logger.Core.Info("• Arguments: " + strings.Join(formattedArgs, " "))
}
- if !config.GetIsSSCMEnabled() && runtime.GOOS == "linux" {
+ if !config.GetIsBepInExEnabled() && runtime.GOOS == "linux" {
// Use ExePath directly as the command
- cmd = exec.Command(config.GetExePath(), args...)
- logger.Core.Info("• Executable: " + config.GetExePath())
- logger.Core.Info("• Arguments: " + strings.Join(args, " "))
+ cmd = exec.Command(executable, args...)
+ logger.Core.Info("• Executable: " + executable)
+ var formattedArgs []string
+ for _, arg := range args {
+ if strings.ContainsAny(arg, " \t\n\"'") {
+ formattedArgs = append(formattedArgs, `"`+strings.ReplaceAll(arg, `"`, `\"`)+`"`)
+ } else {
+ formattedArgs = append(formattedArgs, arg)
+ }
+ }
+ logger.Core.Info("• Arguments: " + strings.Join(formattedArgs, " "))
}
if runtime.GOOS == "windows" {
// On Windows, set the command to use the executable path and arguments
- cmd = exec.Command(config.GetExePath(), args...)
- logger.Core.Info("• Executable: " + config.GetExePath())
+ cmd = exec.Command(executable, args...)
+ logger.Core.Info("• Executable: " + executable)
logger.Core.Debug("Switching to pipes for logs as we are on Windows!")
stdout, err := cmd.StdoutPipe()
diff --git a/src/setup/sscm.go b/src/setup/bepinex.go
similarity index 95%
rename from src/setup/sscm.go
rename to src/setup/bepinex.go
index d278fa97..ce2d185e 100644
--- a/src/setup/sscm.go
+++ b/src/setup/bepinex.go
@@ -161,15 +161,3 @@ func downloadAndInstallBepInEx(url string) error {
return nil
}
-
-func InstallSSCM() {
- logger.Install.Info("🕑Installing SSCM...")
-
- CheckAndInstallBepInEx()
- CheckAndDownloadSSCM()
-
- // Enable SSCM
- config.SetIsSSCMEnabled(true)
-
- logger.Install.Info("✅SSCM enabled")
-}
diff --git a/src/setup/install.go b/src/setup/install.go
index 2dcb07ee..5c25cd95 100644
--- a/src/setup/install.go
+++ b/src/setup/install.go
@@ -17,7 +17,6 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/setup/update"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd"
)
var downloadBranch string // Holds the branch to download from
@@ -40,13 +39,6 @@ func Install(wg *sync.WaitGroup) {
logger.Install.Info("🔄Checking for Blacklist.txt...")
checkAndCreateBlacklist()
logger.Install.Info("✅Blacklist.txt verified or created.")
- // Step 3: Install and run SteamCMD
- logger.Install.Info("🔄Installing and running SteamCMD...")
- if config.GetSkipSteamCMD() || config.GetUseRunfiles() {
- logger.Install.Info("✅Skipping SteamCMD installation, SkipSteamCMD is true or IsSteamServerUI is true")
- } else {
- steamcmd.InstallAndRunSteamCMD()
- }
logger.Install.Info("✅Setup complete!")
}
diff --git a/src/steamcmd/getappinfo.go b/src/steamcmd/getappinfo.go
index 57be19fe..ab47bda6 100644
--- a/src/steamcmd/getappinfo.go
+++ b/src/steamcmd/getappinfo.go
@@ -86,11 +86,7 @@ func getAppInfo() error {
steamcmddir := SteamCMDLinuxDir
executable := "steamcmd.sh"
- appid := config.GetGameServerAppID()
-
- if config.GetUseRunfiles() {
- appid = runfile.CurrentRunfile.SteamAppID
- }
+ appid := runfile.CurrentRunfile.SteamAppID
if runtime.GOOS == "windows" {
executable = "steamcmd.exe"
diff --git a/src/steamcmd/steamcmd.go b/src/steamcmd/steamcmd.go
index d72fcc6e..ce6c4aa1 100644
--- a/src/steamcmd/steamcmd.go
+++ b/src/steamcmd/steamcmd.go
@@ -145,17 +145,10 @@ func runSteamCMD(steamCMDDir string) (int, error) {
// buildSteamCMDCommand constructs the SteamCMD command based on the OS.
func buildSteamCMDCommand(steamCMDDir, currentDir string) *exec.Cmd {
//print the config.GameBranch and config.GameServerAppID
- steamAppID := config.GetGameServerAppID()
- if config.GetUseRunfiles() {
- logger.Install.Info("🔍 SSUI Runfile Identifier: " + runfile.CurrentRunfile.Meta.Name)
- logger.Install.Info("🔍 Game Branch: " + config.GetGameBranch())
- logger.Install.Info("🔍 Game Server App ID: " + runfile.CurrentRunfile.SteamAppID)
- steamAppID = runfile.CurrentRunfile.SteamAppID
- } else {
- logger.Install.Info("🔍 Game Branch: " + config.GetGameBranch())
- logger.Install.Info("🔍 Game Server App ID: " + config.GetGameServerAppID())
-
- }
+ logger.Install.Info("🔍 SSUI Runfile Identifier: " + runfile.CurrentRunfile.Meta.Name)
+ logger.Install.Info("🔍 Game Branch: " + config.GetGameBranch())
+ logger.Install.Info("🔍 Game Server App ID: " + runfile.CurrentRunfile.SteamAppID)
+ steamAppID := runfile.CurrentRunfile.SteamAppID
if runtime.GOOS == "windows" {
return exec.Command(filepath.Join(steamCMDDir, "steamcmd.exe"), "+force_install_dir", currentDir, "+login", "anonymous", "+app_update", steamAppID, "-beta", config.GetGameBranch(), "validate", "+quit")
From e38fddb52f25a12c1aa64d86db89ab1accd679c7 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sun, 5 Oct 2025 20:01:03 +0200
Subject: [PATCH 27/93] allow any category name from the rf on the UI
---
src/steamserverui/runfile/args.go | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/steamserverui/runfile/args.go b/src/steamserverui/runfile/args.go
index 1b77445c..9bda8eec 100644
--- a/src/steamserverui/runfile/args.go
+++ b/src/steamserverui/runfile/args.go
@@ -139,10 +139,8 @@ func (rf *RunFile) Validate() error {
// getAllArgs returns all GameArgs (internal method for validation)
func (rf *RunFile) getAllArgs() []GameArg {
var allArgs []GameArg
- for _, category := range []string{"basic", "network", "advanced"} {
- if args, exists := rf.Args[category]; exists {
- allArgs = append(allArgs, args...)
- }
+ for _, args := range rf.Args {
+ allArgs = append(allArgs, args...)
}
return allArgs
}
@@ -377,13 +375,14 @@ func BuildCommandArgs() ([]string, error) {
}
// switchCategoryWeight maps UIGroup to a weight for sorting
+// not functioning as intended, order is not consistent
func switchCategoryWeight(group string) int {
switch group {
case "Basic":
return 0
case "Network":
return 1
- case "Advanced":
+ case "Special":
return 2
default:
return 3
From c14339562ae50066203a6e9498f66d68f790aebb Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Fri, 10 Oct 2025 20:02:09 +0200
Subject: [PATCH 28/93] Added socket server: Unix socket / named pipes and add
dedicated logging for socket operations
---
go.mod | 1 +
go.sum | 2 +
server.go | 3 +
src/logger/logger.go | 3 +
src/web/socket/examples.md | 126 +++++++++++++++++++++++++++++++++++
src/web/socket/socket-lin.go | 69 +++++++++++++++++++
src/web/socket/socket-win.go | 58 ++++++++++++++++
src/web/start.go | 6 +-
8 files changed, 265 insertions(+), 3 deletions(-)
create mode 100644 src/web/socket/examples.md
create mode 100644 src/web/socket/socket-lin.go
create mode 100644 src/web/socket/socket-win.go
diff --git a/go.mod b/go.mod
index 679eae5c..66419a87 100644
--- a/go.mod
+++ b/go.mod
@@ -8,6 +8,7 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/google/uuid v1.6.0
github.com/jacksonthemaster/discordrichpresence v1.1.0
+ github.com/microsoft/go-winio v0.4.12
golang.org/x/crypto v0.37.0
golang.org/x/sys v0.35.0
)
diff --git a/go.sum b/go.sum
index 870eb11e..1be18036 100644
--- a/go.sum
+++ b/go.sum
@@ -11,6 +11,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jacksonthemaster/discordrichpresence v1.1.0 h1:4UmompqAKyEpspR/Z0LFj6vdSWJf6FZQ/J787KvdxMM=
github.com/jacksonthemaster/discordrichpresence v1.1.0/go.mod h1:XA0SB8bsEc5oJCQcXjC78BfNBj9FoLFA4ysfevkUjHE=
+github.com/microsoft/go-winio v0.4.12 h1:3vDRRsUnj2dKE7QKoedntu9hbuD8gzaVd2E2UZioqx4=
+github.com/microsoft/go-winio v0.4.12/go.mod h1:kcIxxtKZE55DEncT/EOvFiygPobhUWpSDqDb47poQOU=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
diff --git a/server.go b/server.go
index 94cb1576..43d6838d 100644
--- a/server.go
+++ b/server.go
@@ -29,6 +29,7 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/setup"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/web"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/web/socket"
)
//go:embed UIMod/onboard_bundled
@@ -56,6 +57,8 @@ func main() {
wg.Wait()
logger.Main.Debug("Starting webserver...")
web.StartWebServer(&wg)
+ logger.Main.Debug("Starting socket server...")
+ socket.StartSocketServer(&wg)
logger.Main.Debug("Initializing SSUICLI...")
cli.StartConsole(&wg)
wg.Wait()
diff --git a/src/logger/logger.go b/src/logger/logger.go
index 96ed57eb..0c888f27 100644
--- a/src/logger/logger.go
+++ b/src/logger/logger.go
@@ -25,6 +25,7 @@ var (
Security = &Logger{suffix: SYS_SECURITY}
Localization = &Logger{suffix: SYS_LOCALIZATION}
Runfile = &Logger{suffix: SYS_RUNFILE}
+ Socket = &Logger{suffix: SYS_SOCKET}
)
// Severity Levels
@@ -50,6 +51,7 @@ const (
SYS_SECURITY = "SECURITY"
SYS_LOCALIZATION = "LOCALIZATION"
SYS_RUNFILE = "RUNFILE"
+ SYS_SOCKET = "SOCKET"
)
const (
@@ -75,6 +77,7 @@ var subsystemColors = map[string]string{
SYS_SSE: colorCyan, // Matches WEB, streaming vibe
SYS_SECURITY: colorRed, // Screams "pay attention"
SYS_LOCALIZATION: colorCyan, // Matches WEB, localization-related
+ SYS_SOCKET: colorCyan, // Matches WEB, socket-related
}
// Global channels and mutex for all loggers
diff --git a/src/web/socket/examples.md b/src/web/socket/examples.md
new file mode 100644
index 00000000..455b8b37
--- /dev/null
+++ b/src/web/socket/examples.md
@@ -0,0 +1,126 @@
+# Socket Server Usage
+
+This guide explains how to interact with the socket-based `SSUI-API`, which exposes the same HTTP endpoints as the http server over a _Unix socket on Linux_ or a _named pipe on Windows_. This allows local, lightweight access to the API without network overhead or authentication, mainly for the planned plugin system but also for advancedscripting or external local tools. The server is built with Go’s standard library on Linux and `microsoft/go-winio` on Windows for cross-platform compatibility.
+
+## Overview
+
+The socket server reuses the HTTP routes defined in `routes.go` but serves them over:
+- **Linux**: A Unix socket at `/tmp/ssui-api.sock`.
+- **Windows**: A named pipe at `\\.\pipe\ssui-api`.
+
+This is similar to how Docker uses `/var/run/docker.sock` for local API access.
+
+## Prerequisites
+
+- **Linux**:
+ - `curl` installed for testing.
+- **Windows**:
+ - PowerShell 7+ for testing.
+
+## Testing the Socket Server
+
+### Linux: Using a Unix Socket
+
+Use `curl` with the `--unix-socket` flag to send HTTP requests to the socket. Example for the `/api/v2/settings` endpoint:
+
+```bash
+curl --unix-socket /tmp/ssui-api.sock http://localhost/api/v2/settings
+```
+
+**Expected Output**: JSON response from the `settings.RetrieveSettings` handler, e.g.:
+```json
+{
+ "settings": {
+ "some_key": "some_value"
+ }
+}
+```
+
+### Windows: Using a Named Pipe
+
+Use the following PowerShell 7 script to send an HTTP request to the named pipe. Save as `test_namedpipe.ps1` and run it.
+
+```powershell
+# test_namedpipe.ps1
+
+$pipeName = "\\.\pipe\ssui-api"
+$endpoint = "/api/v2/server/status"
+$hostHeader = "localhost" # Dummy host for HTTP request
+
+# Create the HTTP request
+$request = "GET $endpoint HTTP/1.1`r`nHost: $hostHeader`r`nConnection: close`r`n`r`n"
+
+try {
+ # Connect to the named pipe
+ $pipe = New-Object System.IO.Pipes.NamedPipeClientStream(".", "ssui-api", [System.IO.Pipes.PipeDirection]::InOut)
+ $pipe.Connect(5000) # 5-second timeout
+
+ # Convert request to bytes and send
+ $requestBytes = [System.Text.Encoding]::UTF8.GetBytes($request)
+ $pipe.Write($requestBytes, 0, $requestBytes.Length)
+ $pipe.Flush()
+
+ # Read response
+ $reader = New-Object System.IO.StreamReader($pipe)
+ $response = ""
+ while ($null -ne ($line = $reader.ReadLine())) {
+ $response += "$line`n"
+ }
+
+ # Output the response
+ Write-Output $response
+}
+catch {
+ Write-Error "Error connecting to named pipe or reading response: $_"
+}
+finally {
+ # Clean up
+ if ($null -ne $pipe) {
+ $pipe.Close()
+ $pipe.Dispose()
+ }
+ if ($null -ne $reader) {
+ $reader.Close()
+ $reader.Dispose()
+ }
+}
+```
+
+Run it:
+```powershell
+.\test_namedpipe.ps1
+```
+
+**Expected Output**: HTTP response with headers and JSON body, e.g.:
+```
+Response from \\.\pipe\ssui-api/api/v2/server/status:
+HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 123
+
+{"status":"running","players":0}
+```
+
+## Testing Other Endpoints
+
+All routes from `routes.go` (e.g., `/api/v2/server/start`, `/api/v2/backups`) are available. Modify the endpoint in the commands:
+
+- **Linux**:
+ ```bash
+ curl --unix-socket /tmp/ssui-api.sock http://localhost/api/v2/backups
+ ```
+- **Windows**: Edit `$endpoint` in `test_namedpipe.ps1`, e.g., `$endpoint = "/api/v2/backups"`.
+
+For POST requests (e.g., `/api/v2/server/start`), add a JSON payload:
+- **Linux**:
+ ```bash
+ curl --unix-socket /tmp/ssui-api.sock -X POST -H "Content-Type: application/json" -d '{"action":"start"}' http://localhost/api/v2/server/start
+ ```
+- **Windows**: Update the PowerShell script’s `$request`:
+ ```powershell
+ $body = '{"action":"start"}'
+ $request = "POST $endpoint HTTP/1.1`r`nHost: $hostHeader`r`nContent-Type: application/json`r`nContent-Length: $($body.Length)`r`n`r`n$body"
+ ```
+
+## Notes
+- The socket server skips authentication, assuming local access is secure (like Docker’s socket).
\ No newline at end of file
diff --git a/src/web/socket/socket-lin.go b/src/web/socket/socket-lin.go
new file mode 100644
index 00000000..c702c98e
--- /dev/null
+++ b/src/web/socket/socket-lin.go
@@ -0,0 +1,69 @@
+//go:build linux
+// +build linux
+
+package socket
+
+import (
+ "context"
+ "log"
+ "net"
+ "net/http"
+ "os"
+ "sync"
+
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/web"
+)
+
+const socketPath = "/tmp/ssui-api.sock"
+
+func StartSocketServer(wg *sync.WaitGroup) {
+ logger.Socket.Info("Starting Unix socket server...")
+
+ // Remove existing socket file if it exists
+ if err := os.RemoveAll(socketPath); err != nil {
+ logger.Socket.Error("Error removing existing socket: " + err.Error())
+ }
+
+ // Set up routes
+ mux, protectedMux := web.SetupRoutes()
+ mux.Handle("/", protectedMux)
+
+ // Create Unix socket listener
+ listener, err := net.Listen("unix", socketPath)
+ if err != nil {
+ logger.Socket.Error("Error starting Unix socket server: " + err.Error())
+ return
+ }
+
+ // Set socket permissions
+ if err := os.Chmod(socketPath, 0666); err != nil {
+ logger.Socket.Error("Error setting socket permissions: " + err.Error())
+ }
+
+ // Create HTTP server
+ server := &http.Server{
+ Handler: mux,
+ ErrorLog: log.New(&web.WebServerLogger{}, "", 0),
+ }
+
+ // Start server in a goroutine
+ wg.Go(func() {
+ logger.Socket.Info("Unix socket server running at " + socketPath)
+ if err := server.Serve(listener); err != nil && err != http.ErrServerClosed {
+ logger.Socket.Error("Unix socket server error: " + err.Error())
+ }
+ })
+
+ // Handle graceful shutdown
+ go func() {
+ <-context.Background().Done()
+ logger.Socket.Info("Shutting down Unix socket server...")
+ if err := server.Shutdown(context.Background()); err != nil {
+ logger.Socket.Error("Error shutting down Unix socket server: " + err.Error())
+ }
+ if err := os.RemoveAll(socketPath); err != nil {
+ logger.Socket.Error("Error removing socket file: " + err.Error())
+ }
+ }()
+}
diff --git a/src/web/socket/socket-win.go b/src/web/socket/socket-win.go
new file mode 100644
index 00000000..ae037f9e
--- /dev/null
+++ b/src/web/socket/socket-win.go
@@ -0,0 +1,58 @@
+//go:build windows
+// +build windows
+
+package socket
+
+import (
+ "context"
+ "log"
+ "net/http"
+ "sync"
+
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/web"
+ "github.com/microsoft/go-winio"
+)
+
+const pipePath = `\\.\pipe\ssui-api`
+
+func StartSocketServer(wg *sync.WaitGroup) {
+ logger.Socket.Info("Starting named pipe server...")
+
+ // Set up routes
+ mux, protectedMux := web.SetupRoutes()
+ mux.Handle("/", protectedMux)
+
+ // Create named pipe listener
+ listener, err := winio.ListenPipe(pipePath, nil)
+ if err != nil {
+ logger.Socket.Error("Error starting named pipe server: " + err.Error())
+ return
+ }
+
+ // Create HTTP server
+ server := &http.Server{
+ Handler: mux,
+ ErrorLog: log.New(&web.WebServerLogger{}, "", 0),
+ }
+
+ // Start server in a goroutine
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ logger.Socket.Info("Named pipe server running at " + pipePath)
+ if err := server.Serve(listener); err != nil && err != http.ErrServerClosed {
+ logger.Socket.Error("Named pipe server error: " + err.Error())
+ }
+ }()
+
+ // Handle graceful shutdown
+ go func() {
+ <-context.Background().Done()
+ logger.Socket.Info("Shutting down named pipe server...")
+ if err := server.Shutdown(context.Background()); err != nil {
+ logger.Socket.Error("Error shutting down named pipe server: " + err.Error())
+ }
+ listener.Close() // Ensure the pipe is closed
+ }()
+}
diff --git a/src/web/start.go b/src/web/start.go
index 3b52b85b..4360571e 100644
--- a/src/web/start.go
+++ b/src/web/start.go
@@ -12,9 +12,9 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
)
-type webServerLogger struct{}
+type WebServerLogger struct{}
-func (cl *webServerLogger) Write(p []byte) (n int, err error) {
+func (cl *WebServerLogger) Write(p []byte) (n int, err error) {
// Redirect HTTP server logs (like TLS handshake errors) to logger.Web.Debug
logger.Web.Debug(string(p))
return len(p), nil
@@ -28,7 +28,7 @@ func StartWebServer(wg *sync.WaitGroup) {
// Apply middleware only to protected routes
mux.Handle("/", AuthMiddleware(protectedMux)) // Wrap protected routes under root
- httpLogger := log.New(&webServerLogger{}, "", 0)
+ httpLogger := log.New(&WebServerLogger{}, "", 0)
// Start HTTP server
wg.Add(1)
go func() {
From 4238c9089c077fed9d611a0f40903e733e7af0d9 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Fri, 10 Oct 2025 20:02:49 +0200
Subject: [PATCH 29/93] add runfiles folder to gitignore
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index ecfce688..afe048f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,3 +44,4 @@ frontend/node_modules
frontend/dist
frontend/build
UIMod/onboard_bundled/v2
+UIMod/runfiles/**
From 58850f0200d0077c6abd9bdf6ac88053a6afc8fc Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 01:05:25 +0200
Subject: [PATCH 30/93] updated logger references in future api package to use
logger.API
---
src/logger/logger.go | 3 +++
src/managers/backupmgr/backuphttp.go | 2 +-
src/web/http.go | 14 +++++++-------
src/web/runfile.go | 2 +-
src/web/start.go | 10 ++++------
src/web/svelteui.go | 2 +-
src/web/systeminfo.go | 6 +++---
7 files changed, 20 insertions(+), 19 deletions(-)
diff --git a/src/logger/logger.go b/src/logger/logger.go
index 0c888f27..e6a155d8 100644
--- a/src/logger/logger.go
+++ b/src/logger/logger.go
@@ -15,6 +15,7 @@ import (
var (
Main = &Logger{suffix: SYS_MAIN}
Web = &Logger{suffix: SYS_WEB}
+ API = &Logger{suffix: SYS_API}
Discord = &Logger{suffix: SYS_DISCORD}
Backup = &Logger{suffix: SYS_BACKUP}
Detection = &Logger{suffix: SYS_DETECT}
@@ -41,6 +42,7 @@ const (
const (
SYS_MAIN = "MAIN"
SYS_WEB = "WEB"
+ SYS_API = "API"
SYS_DISCORD = "DISCORD"
SYS_BACKUP = "BACKUP"
SYS_DETECT = "DETECT"
@@ -68,6 +70,7 @@ const (
var subsystemColors = map[string]string{
SYS_MAIN: colorBlue, // Calm, default system
SYS_WEB: colorCyan, // Clean, UI-related
+ SYS_API: colorCyan, // Matches WEB
SYS_DISCORD: colorMagenta, // Flashy, chatty subsystem
SYS_BACKUP: colorGreen, // Safe, reliable vibe
SYS_DETECT: colorYellow, // Attention-grabbing for detection
diff --git a/src/managers/backupmgr/backuphttp.go b/src/managers/backupmgr/backuphttp.go
index 8ec322d4..729cc1df 100644
--- a/src/managers/backupmgr/backuphttp.go
+++ b/src/managers/backupmgr/backuphttp.go
@@ -68,7 +68,7 @@ func (h *HTTPHandler) ListBackupsHandler(w http.ResponseWriter, r *http.Request)
// RestoreBackupHandler handles requests to restore a backup
func (h *HTTPHandler) RestoreBackupHandler(w http.ResponseWriter, r *http.Request) {
- logger.Web.Debug("Received restore request")
+ logger.API.Debug("Received restore request")
indexStr := r.URL.Query().Get("index")
if indexStr == "" {
http.Error(w, "index parameter is required", http.StatusBadRequest)
diff --git a/src/web/http.go b/src/web/http.go
index fd010f65..c290912e 100644
--- a/src/web/http.go
+++ b/src/web/http.go
@@ -19,32 +19,32 @@ import (
// StartServer HTTP handler
func StartServer(w http.ResponseWriter, r *http.Request) {
- logger.Web.Debug("Received start request from API")
+ logger.API.Debug("Received start request from API")
if err := gamemgr.InternalStartServer(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
- logger.Web.Error("Error starting server: " + err.Error())
+ logger.API.Error("Error starting server: " + err.Error())
return
}
fmt.Fprint(w, localization.GetString("BackendText_ServerStarted"))
- logger.Web.Info("Server started.")
+ logger.API.Info("Server started.")
}
// StopServer HTTP handler
func StopServer(w http.ResponseWriter, r *http.Request) {
- logger.Web.Debug("Received stop request from API")
+ logger.API.Debug("Received stop request from API")
if err := gamemgr.InternalStopServer(); err != nil {
if err.Error() == "server not running" {
fmt.Fprint(w, localization.GetString("BackendText_ServerNotRunningOrAlreadyStopped"))
- logger.Web.Warn("Server not running or was already stopped")
+ logger.API.Warn("Server not running or was already stopped")
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
- logger.Web.Error("Error stopping server: " + err.Error())
+ logger.API.Error("Error stopping server: " + err.Error())
return
}
detectionmgr.ClearPlayers(detectionmgr.GetDetector())
fmt.Fprint(w, localization.GetString("BackendText_ServerStopped"))
- logger.Web.Info("Server stopped.")
+ logger.API.Info("Server stopped.")
}
func GetGameServerRunState(w http.ResponseWriter, r *http.Request) {
diff --git a/src/web/runfile.go b/src/web/runfile.go
index 27ff62cf..bf9a7e18 100644
--- a/src/web/runfile.go
+++ b/src/web/runfile.go
@@ -286,7 +286,7 @@ func HandleSetRunfileGame(w http.ResponseWriter, r *http.Request) {
}
func HandleReloadRunfile(w http.ResponseWriter, r *http.Request) {
- logger.Web.Debug("Received reloadrunfile request from API")
+ logger.API.Debug("Received reloadrunfile request from API")
reloadMu.Lock()
defer reloadMu.Unlock()
// accept only GET requests
diff --git a/src/web/start.go b/src/web/start.go
index 4360571e..721bc09f 100644
--- a/src/web/start.go
+++ b/src/web/start.go
@@ -15,8 +15,8 @@ import (
type WebServerLogger struct{}
func (cl *WebServerLogger) Write(p []byte) (n int, err error) {
- // Redirect HTTP server logs (like TLS handshake errors) to logger.Web.Debug
- logger.Web.Debug(string(p))
+ // Redirect HTTP server logs (like TLS handshake errors) to logger.API.Debug
+ logger.API.Debug(string(p))
return len(p), nil
}
@@ -54,9 +54,7 @@ func StartWebServer(wg *sync.WaitGroup) {
// Start the pprof server if debug mode is enabled (HTTP/1.1)
if config.GetIsDebugMode() { // if debug mode is enabled, start pprof server
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
pprofMux := http.NewServeMux()
// Register pprof handler
pprofMux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
@@ -65,6 +63,6 @@ func StartWebServer(wg *sync.WaitGroup) {
if err != nil {
logger.Web.Error("Error starting pprof server: " + err.Error())
}
- }()
+ })
}
}
diff --git a/src/web/svelteui.go b/src/web/svelteui.go
index c563df63..db0201ce 100644
--- a/src/web/svelteui.go
+++ b/src/web/svelteui.go
@@ -38,7 +38,7 @@ func ServeSvelteUI(w http.ResponseWriter, r *http.Request) {
}
func HandleReloadAll(w http.ResponseWriter, r *http.Request) {
- logger.Web.Debug("Received reloadbackend request from API")
+ logger.API.Debug("Received reloadbackend request from API")
reloadMu.Lock()
defer reloadMu.Unlock()
// accept only GET requests
diff --git a/src/web/systeminfo.go b/src/web/systeminfo.go
index ccb52e99..c02e4b41 100644
--- a/src/web/systeminfo.go
+++ b/src/web/systeminfo.go
@@ -9,7 +9,7 @@ import (
)
func HandleGetOsStats(w http.ResponseWriter, r *http.Request) {
- logger.Web.Debug("Received getOsStats request from API")
+ logger.API.Debug("Received getOsStats request from API")
// accept only GET requests
if r.Method != http.MethodGet {
http.Error(w, "Only GET requests are allowed", http.StatusMethodNotAllowed)
@@ -19,7 +19,7 @@ func HandleGetOsStats(w http.ResponseWriter, r *http.Request) {
// Get cached stats or refresh if needed
stats, err := systeminfo.RefreshCachedStats()
if err != nil {
- logger.Web.Error("Failed to get OS stats")
+ logger.API.Error("Failed to get OS stats")
http.Error(w, "Failed to get OS stats", http.StatusInternalServerError)
return
}
@@ -28,7 +28,7 @@ func HandleGetOsStats(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(stats); err != nil {
- logger.Web.Error("Failed to write response")
+ logger.API.Error("Failed to write response")
http.Error(w, "Failed to write response", http.StatusInternalServerError)
return
}
From 11bb32b0717d71f475b45b17a20253c993963229 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 01:09:46 +0200
Subject: [PATCH 31/93] renamed web package to api, changed references
accordingly
---
server.go | 8 ++++----
src/{web => api}/TwoBoxForm.go | 2 +-
src/{web => api}/commands.go | 2 +-
src/{web => api}/configpage.go | 2 +-
src/{web => api}/connectedplayers.go | 2 +-
src/{web => api}/detectionmanagerpage.go | 2 +-
src/{web => api}/http-sse.go | 2 +-
src/{web => api}/http.go | 2 +-
src/{web => api}/indexpage.go | 2 +-
src/{web => api}/login.go | 2 +-
src/{web => api}/routes.go | 2 +-
src/{web => api}/runfile.go | 2 +-
src/{web => api}/runfilegallery.go | 2 +-
src/{web => api}/socket/examples.md | 0
src/{web => api}/socket/socket-lin.go | 6 +++---
src/{web => api}/socket/socket-win.go | 6 +++---
src/{web => api}/start.go | 8 ++++----
src/{web => api}/svelteui.go | 2 +-
src/{web => api}/systeminfo.go | 2 +-
src/{web => api}/templatevars.go | 2 +-
src/{web => api}/whoami.go | 2 +-
21 files changed, 30 insertions(+), 30 deletions(-)
rename src/{web => api}/TwoBoxForm.go (99%)
rename src/{web => api}/commands.go (99%)
rename src/{web => api}/configpage.go (99%)
rename src/{web => api}/connectedplayers.go (99%)
rename src/{web => api}/detectionmanagerpage.go (98%)
rename src/{web => api}/http-sse.go (99%)
rename src/{web => api}/http.go (99%)
rename src/{web => api}/indexpage.go (99%)
rename src/{web => api}/login.go (99%)
rename src/{web => api}/routes.go (99%)
rename src/{web => api}/runfile.go (99%)
rename src/{web => api}/runfilegallery.go (99%)
rename src/{web => api}/socket/examples.md (100%)
rename src/{web => api}/socket/socket-lin.go (91%)
rename src/{web => api}/socket/socket-win.go (89%)
rename src/{web => api}/start.go (91%)
rename src/{web => api}/svelteui.go (99%)
rename src/{web => api}/systeminfo.go (98%)
rename src/{web => api}/templatevars.go (99%)
rename src/{web => api}/whoami.go (97%)
diff --git a/server.go b/server.go
index 43d6838d..4e04bd55 100644
--- a/server.go
+++ b/server.go
@@ -24,12 +24,12 @@ import (
"embed"
"sync"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/api"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/api/socket"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/cli"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/loader"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/setup"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/web"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/web/socket"
)
//go:embed UIMod/onboard_bundled
@@ -55,10 +55,10 @@ func main() {
logger.Main.Debug("Initializing after start tasks...")
loader.AfterStartComplete(&wg)
wg.Wait()
- logger.Main.Debug("Starting webserver...")
- web.StartWebServer(&wg)
logger.Main.Debug("Starting socket server...")
socket.StartSocketServer(&wg)
+ logger.Main.Debug("Starting webserver...")
+ api.StartWebServer(&wg)
logger.Main.Debug("Initializing SSUICLI...")
cli.StartConsole(&wg)
wg.Wait()
diff --git a/src/web/TwoBoxForm.go b/src/api/TwoBoxForm.go
similarity index 99%
rename from src/web/TwoBoxForm.go
rename to src/api/TwoBoxForm.go
index c5b88773..c9bf9318 100644
--- a/src/web/TwoBoxForm.go
+++ b/src/api/TwoBoxForm.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"io/fs"
diff --git a/src/web/commands.go b/src/api/commands.go
similarity index 99%
rename from src/web/commands.go
rename to src/api/commands.go
index 64a5c1c7..9b770265 100644
--- a/src/web/commands.go
+++ b/src/api/commands.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"encoding/json"
diff --git a/src/web/configpage.go b/src/api/configpage.go
similarity index 99%
rename from src/web/configpage.go
rename to src/api/configpage.go
index a7e77269..d52bc8f5 100644
--- a/src/web/configpage.go
+++ b/src/api/configpage.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"fmt"
diff --git a/src/web/connectedplayers.go b/src/api/connectedplayers.go
similarity index 99%
rename from src/web/connectedplayers.go
rename to src/api/connectedplayers.go
index 295a8585..ccb61165 100644
--- a/src/web/connectedplayers.go
+++ b/src/api/connectedplayers.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"encoding/json"
diff --git a/src/web/detectionmanagerpage.go b/src/api/detectionmanagerpage.go
similarity index 98%
rename from src/web/detectionmanagerpage.go
rename to src/api/detectionmanagerpage.go
index 74f8a642..96d5f360 100644
--- a/src/web/detectionmanagerpage.go
+++ b/src/api/detectionmanagerpage.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"fmt"
diff --git a/src/web/http-sse.go b/src/api/http-sse.go
similarity index 99%
rename from src/web/http-sse.go
rename to src/api/http-sse.go
index 56fc3f23..447704db 100644
--- a/src/web/http-sse.go
+++ b/src/api/http-sse.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"net/http"
diff --git a/src/web/http.go b/src/api/http.go
similarity index 99%
rename from src/web/http.go
rename to src/api/http.go
index c290912e..479ef546 100644
--- a/src/web/http.go
+++ b/src/api/http.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"encoding/json"
diff --git a/src/web/indexpage.go b/src/api/indexpage.go
similarity index 99%
rename from src/web/indexpage.go
rename to src/api/indexpage.go
index 81d72180..f42c732a 100644
--- a/src/web/indexpage.go
+++ b/src/api/indexpage.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"io/fs"
diff --git a/src/web/login.go b/src/api/login.go
similarity index 99%
rename from src/web/login.go
rename to src/api/login.go
index e2c8f0db..90b853f0 100644
--- a/src/web/login.go
+++ b/src/api/login.go
@@ -1,5 +1,5 @@
// handlers.go
-package web
+package api
import (
"encoding/json"
diff --git a/src/web/routes.go b/src/api/routes.go
similarity index 99%
rename from src/web/routes.go
rename to src/api/routes.go
index 7cc42aa1..f1f50c91 100644
--- a/src/web/routes.go
+++ b/src/api/routes.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"io/fs"
diff --git a/src/web/runfile.go b/src/api/runfile.go
similarity index 99%
rename from src/web/runfile.go
rename to src/api/runfile.go
index bf9a7e18..2b07a538 100644
--- a/src/web/runfile.go
+++ b/src/api/runfile.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"encoding/json"
diff --git a/src/web/runfilegallery.go b/src/api/runfilegallery.go
similarity index 99%
rename from src/web/runfilegallery.go
rename to src/api/runfilegallery.go
index 545349c3..eff60b5d 100644
--- a/src/web/runfilegallery.go
+++ b/src/api/runfilegallery.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"encoding/json"
diff --git a/src/web/socket/examples.md b/src/api/socket/examples.md
similarity index 100%
rename from src/web/socket/examples.md
rename to src/api/socket/examples.md
diff --git a/src/web/socket/socket-lin.go b/src/api/socket/socket-lin.go
similarity index 91%
rename from src/web/socket/socket-lin.go
rename to src/api/socket/socket-lin.go
index c702c98e..2ca674f6 100644
--- a/src/web/socket/socket-lin.go
+++ b/src/api/socket/socket-lin.go
@@ -11,8 +11,8 @@ import (
"os"
"sync"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/api"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/web"
)
const socketPath = "/tmp/ssui-api.sock"
@@ -26,7 +26,7 @@ func StartSocketServer(wg *sync.WaitGroup) {
}
// Set up routes
- mux, protectedMux := web.SetupRoutes()
+ mux, protectedMux := api.SetupRoutes()
mux.Handle("/", protectedMux)
// Create Unix socket listener
@@ -44,7 +44,7 @@ func StartSocketServer(wg *sync.WaitGroup) {
// Create HTTP server
server := &http.Server{
Handler: mux,
- ErrorLog: log.New(&web.WebServerLogger{}, "", 0),
+ ErrorLog: log.New(&api.APIServerLogger{}, "", 0),
}
// Start server in a goroutine
diff --git a/src/web/socket/socket-win.go b/src/api/socket/socket-win.go
similarity index 89%
rename from src/web/socket/socket-win.go
rename to src/api/socket/socket-win.go
index ae037f9e..3598b772 100644
--- a/src/web/socket/socket-win.go
+++ b/src/api/socket/socket-win.go
@@ -9,8 +9,8 @@ import (
"net/http"
"sync"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/api"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/web"
"github.com/microsoft/go-winio"
)
@@ -20,7 +20,7 @@ func StartSocketServer(wg *sync.WaitGroup) {
logger.Socket.Info("Starting named pipe server...")
// Set up routes
- mux, protectedMux := web.SetupRoutes()
+ mux, protectedMux := api.SetupRoutes()
mux.Handle("/", protectedMux)
// Create named pipe listener
@@ -33,7 +33,7 @@ func StartSocketServer(wg *sync.WaitGroup) {
// Create HTTP server
server := &http.Server{
Handler: mux,
- ErrorLog: log.New(&web.WebServerLogger{}, "", 0),
+ ErrorLog: log.New(&api.APIServerLogger{}, "", 0),
}
// Start server in a goroutine
diff --git a/src/web/start.go b/src/api/start.go
similarity index 91%
rename from src/web/start.go
rename to src/api/start.go
index 721bc09f..542cff82 100644
--- a/src/web/start.go
+++ b/src/api/start.go
@@ -1,5 +1,5 @@
// start.go
-package web
+package api
import (
"log"
@@ -12,9 +12,9 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
)
-type WebServerLogger struct{}
+type APIServerLogger struct{}
-func (cl *WebServerLogger) Write(p []byte) (n int, err error) {
+func (cl *APIServerLogger) Write(p []byte) (n int, err error) {
// Redirect HTTP server logs (like TLS handshake errors) to logger.API.Debug
logger.API.Debug(string(p))
return len(p), nil
@@ -28,7 +28,7 @@ func StartWebServer(wg *sync.WaitGroup) {
// Apply middleware only to protected routes
mux.Handle("/", AuthMiddleware(protectedMux)) // Wrap protected routes under root
- httpLogger := log.New(&WebServerLogger{}, "", 0)
+ httpLogger := log.New(&APIServerLogger{}, "", 0)
// Start HTTP server
wg.Add(1)
go func() {
diff --git a/src/web/svelteui.go b/src/api/svelteui.go
similarity index 99%
rename from src/web/svelteui.go
rename to src/api/svelteui.go
index db0201ce..548f16dc 100644
--- a/src/web/svelteui.go
+++ b/src/api/svelteui.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"encoding/json"
diff --git a/src/web/systeminfo.go b/src/api/systeminfo.go
similarity index 98%
rename from src/web/systeminfo.go
rename to src/api/systeminfo.go
index c02e4b41..d378c195 100644
--- a/src/web/systeminfo.go
+++ b/src/api/systeminfo.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"encoding/json"
diff --git a/src/web/templatevars.go b/src/api/templatevars.go
similarity index 99%
rename from src/web/templatevars.go
rename to src/api/templatevars.go
index 8c0cc2ab..4e0e7c62 100644
--- a/src/web/templatevars.go
+++ b/src/api/templatevars.go
@@ -1,4 +1,4 @@
-package web
+package api
// TemplateData holds data to be passed to templates
type IndexTemplateData struct {
diff --git a/src/web/whoami.go b/src/api/whoami.go
similarity index 97%
rename from src/web/whoami.go
rename to src/api/whoami.go
index 0baa5cc3..8b83c285 100644
--- a/src/web/whoami.go
+++ b/src/api/whoami.go
@@ -1,4 +1,4 @@
-package web
+package api
import (
"encoding/json"
From 7920074b903252d70731e2a4f3aabad11ad62995 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 01:14:43 +0200
Subject: [PATCH 32/93] improve socket paths
---
src/api/socket/examples.md | 20 ++++++++++----------
src/api/socket/socket-lin.go | 2 +-
src/api/socket/socket-win.go | 2 +-
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/api/socket/examples.md b/src/api/socket/examples.md
index 455b8b37..f3ee2541 100644
--- a/src/api/socket/examples.md
+++ b/src/api/socket/examples.md
@@ -5,11 +5,14 @@ This guide explains how to interact with the socket-based `SSUI-API`, which expo
## Overview
The socket server reuses the HTTP routes defined in `routes.go` but serves them over:
-- **Linux**: A Unix socket at `/tmp/ssui-api.sock`.
-- **Windows**: A named pipe at `\\.\pipe\ssui-api`.
+- **Linux**: A Unix socket at `/var/run/ssui.sock`.
+- **Windows**: A named pipe at `\\.\pipe\ssui`.
This is similar to how Docker uses `/var/run/docker.sock` for local API access.
+## Notes
+- The socket server skips authentication, assuming local access is secure (like Docker’s socket).
+
## Prerequisites
- **Linux**:
@@ -24,7 +27,7 @@ This is similar to how Docker uses `/var/run/docker.sock` for local API access.
Use `curl` with the `--unix-socket` flag to send HTTP requests to the socket. Example for the `/api/v2/settings` endpoint:
```bash
-curl --unix-socket /tmp/ssui-api.sock http://localhost/api/v2/settings
+curl --unix-socket /var/run/ssui.sock http://localhost/api/v2/settings
```
**Expected Output**: JSON response from the `settings.RetrieveSettings` handler, e.g.:
@@ -43,7 +46,7 @@ Use the following PowerShell 7 script to send an HTTP request to the named pipe.
```powershell
# test_namedpipe.ps1
-$pipeName = "\\.\pipe\ssui-api"
+$pipeName = "\\.\pipe\ssui"
$endpoint = "/api/v2/server/status"
$hostHeader = "localhost" # Dummy host for HTTP request
@@ -93,7 +96,7 @@ Run it:
**Expected Output**: HTTP response with headers and JSON body, e.g.:
```
-Response from \\.\pipe\ssui-api/api/v2/server/status:
+Response from \\.\pipe\ssui/api/v2/server/status:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 123
@@ -107,20 +110,17 @@ All routes from `routes.go` (e.g., `/api/v2/server/start`, `/api/v2/backups`) ar
- **Linux**:
```bash
- curl --unix-socket /tmp/ssui-api.sock http://localhost/api/v2/backups
+ curl --unix-socket /var/run/ssui.sock http://localhost/api/v2/backups
```
- **Windows**: Edit `$endpoint` in `test_namedpipe.ps1`, e.g., `$endpoint = "/api/v2/backups"`.
For POST requests (e.g., `/api/v2/server/start`), add a JSON payload:
- **Linux**:
```bash
- curl --unix-socket /tmp/ssui-api.sock -X POST -H "Content-Type: application/json" -d '{"action":"start"}' http://localhost/api/v2/server/start
+ curl --unix-socket /var/run/ssui.sock -X POST -H "Content-Type: application/json" -d '{"action":"start"}' http://localhost/api/v2/server/start
```
- **Windows**: Update the PowerShell script’s `$request`:
```powershell
$body = '{"action":"start"}'
$request = "POST $endpoint HTTP/1.1`r`nHost: $hostHeader`r`nContent-Type: application/json`r`nContent-Length: $($body.Length)`r`n`r`n$body"
```
-
-## Notes
-- The socket server skips authentication, assuming local access is secure (like Docker’s socket).
\ No newline at end of file
diff --git a/src/api/socket/socket-lin.go b/src/api/socket/socket-lin.go
index 2ca674f6..93a0dd80 100644
--- a/src/api/socket/socket-lin.go
+++ b/src/api/socket/socket-lin.go
@@ -15,7 +15,7 @@ import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
)
-const socketPath = "/tmp/ssui-api.sock"
+const socketPath = "/var/run/ssui.sock"
func StartSocketServer(wg *sync.WaitGroup) {
logger.Socket.Info("Starting Unix socket server...")
diff --git a/src/api/socket/socket-win.go b/src/api/socket/socket-win.go
index 3598b772..dd840eca 100644
--- a/src/api/socket/socket-win.go
+++ b/src/api/socket/socket-win.go
@@ -14,7 +14,7 @@ import (
"github.com/microsoft/go-winio"
)
-const pipePath = `\\.\pipe\ssui-api`
+const pipePath = `\\.\pipe\ssui`
func StartSocketServer(wg *sync.WaitGroup) {
logger.Socket.Info("Starting named pipe server...")
From fc3117e5e13876f6b47ba0e028a97e8650bf379b Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 01:34:40 +0200
Subject: [PATCH 33/93] updated devcontainer config
---
.devcontainer/devcontainer.json | 40 ++++++++++++++++++++++++++-------
1 file changed, 32 insertions(+), 8 deletions(-)
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 0ed4e03b..0346bdee 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,8 +1,10 @@
{
- "name": "StationeersServerUI",
+ "name": "SteamSererUI",
"build": {
"dockerfile": "Dockerfile",
- "args": { "TZ": "${localEnv:TZ:Europe/Stockholm}" }
+ "args": {
+ "TZ": "${localEnv:TZ:Europe/Stockholm}"
+ }
},
"workspaceFolder": "/workspaces/project",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/project,type=bind,consistency=cached",
@@ -11,11 +13,17 @@
"version": "1.25.0"
}
},
- "forwardPorts": [8443],
- "runArgs": ["-p=27015:27015/udp", "-p=27016:27016/udp"],
+ "forwardPorts": [
+ 8443
+ ],
+ "runArgs": [
+ "-p=27015:27015/udp",
+ "-p=27016:27016/udp"
+ ],
"portsAttributes": {
- "8443": { "label": "Go Backend Server" }
-
+ "8443": {
+ "label": "Go Backend Server"
+ }
},
"customizations": {
"vscode": {
@@ -23,11 +31,27 @@
"golang.go",
"svelte.svelte-vscode",
"supermaven.supermaven",
- "eamodio.gitlens"
+ "eamodio.gitlens",
+ "pkief.material-icon-theme"
],
"settings": {
"go.toolsManagement.autoUpdate": true,
- "svelte.enable-ts-plugin": true
+ "svelte.enable-ts-plugin": true,
+ "workbench.iconTheme": "material-icon-theme",
+ "workbench.colorTheme": "Solarized Dark",
+ "material-icon-theme.files.customClones": [
+ {
+ "name": "ssui",
+ "base": "rocket",
+ "color": "blue-500",
+ "fileExtensions": [
+ "ssui"
+ ]
+ }
+ ],
+ "files.associations": {
+ "*.ssui": "json"
+ }
}
}
},
From fed75087d3c9f72cacb691d8db1e3d415a34af6e Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 01:40:18 +0200
Subject: [PATCH 34/93] moved discordbot and discordrpc to dedicated discord
folder
---
src/core/loader/afterstart.go | 2 +-
src/core/loader/loader.go | 2 +-
src/{ => discord}/discordbot/connectedplayers.go | 0
src/{ => discord}/discordbot/controlpanel.go | 0
src/{ => discord}/discordbot/discordgo-license.md | 0
src/{ => discord}/discordbot/embeds.go | 0
src/{ => discord}/discordbot/handleBlacklist.go | 0
src/{ => discord}/discordbot/handleReactions.go | 0
src/{ => discord}/discordbot/handleSlashcommands.go | 0
src/{ => discord}/discordbot/interface.go | 0
src/{ => discord}/discordbot/logstream.go | 0
src/{ => discord}/discordbot/registerSlashcommands.go | 0
src/{ => discord}/discordbot/sendMessage.go | 0
src/{ => discord}/discordbot/types.go | 0
src/{ => discord}/discordrpc/discordrpc.go | 0
src/managers/detectionmgr/detector.go | 2 +-
src/managers/detectionmgr/handlers.go | 2 +-
src/managers/detectionmgr/logstream.go | 2 +-
18 files changed, 5 insertions(+), 5 deletions(-)
rename src/{ => discord}/discordbot/connectedplayers.go (100%)
rename src/{ => discord}/discordbot/controlpanel.go (100%)
rename src/{ => discord}/discordbot/discordgo-license.md (100%)
rename src/{ => discord}/discordbot/embeds.go (100%)
rename src/{ => discord}/discordbot/handleBlacklist.go (100%)
rename src/{ => discord}/discordbot/handleReactions.go (100%)
rename src/{ => discord}/discordbot/handleSlashcommands.go (100%)
rename src/{ => discord}/discordbot/interface.go (100%)
rename src/{ => discord}/discordbot/logstream.go (100%)
rename src/{ => discord}/discordbot/registerSlashcommands.go (100%)
rename src/{ => discord}/discordbot/sendMessage.go (100%)
rename src/{ => discord}/discordbot/types.go (100%)
rename src/{ => discord}/discordrpc/discordrpc.go (100%)
diff --git a/src/core/loader/afterstart.go b/src/core/loader/afterstart.go
index 91c3824b..4783164e 100644
--- a/src/core/loader/afterstart.go
+++ b/src/core/loader/afterstart.go
@@ -4,7 +4,7 @@ import (
"sync"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discordrpc"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discord/discordrpc"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/setup"
diff --git a/src/core/loader/loader.go b/src/core/loader/loader.go
index b042b6a4..d02f1a27 100644
--- a/src/core/loader/loader.go
+++ b/src/core/loader/loader.go
@@ -8,7 +8,7 @@ import (
"time"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discordbot"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discord/discordbot"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/localization"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/backupmgr"
diff --git a/src/discordbot/connectedplayers.go b/src/discord/discordbot/connectedplayers.go
similarity index 100%
rename from src/discordbot/connectedplayers.go
rename to src/discord/discordbot/connectedplayers.go
diff --git a/src/discordbot/controlpanel.go b/src/discord/discordbot/controlpanel.go
similarity index 100%
rename from src/discordbot/controlpanel.go
rename to src/discord/discordbot/controlpanel.go
diff --git a/src/discordbot/discordgo-license.md b/src/discord/discordbot/discordgo-license.md
similarity index 100%
rename from src/discordbot/discordgo-license.md
rename to src/discord/discordbot/discordgo-license.md
diff --git a/src/discordbot/embeds.go b/src/discord/discordbot/embeds.go
similarity index 100%
rename from src/discordbot/embeds.go
rename to src/discord/discordbot/embeds.go
diff --git a/src/discordbot/handleBlacklist.go b/src/discord/discordbot/handleBlacklist.go
similarity index 100%
rename from src/discordbot/handleBlacklist.go
rename to src/discord/discordbot/handleBlacklist.go
diff --git a/src/discordbot/handleReactions.go b/src/discord/discordbot/handleReactions.go
similarity index 100%
rename from src/discordbot/handleReactions.go
rename to src/discord/discordbot/handleReactions.go
diff --git a/src/discordbot/handleSlashcommands.go b/src/discord/discordbot/handleSlashcommands.go
similarity index 100%
rename from src/discordbot/handleSlashcommands.go
rename to src/discord/discordbot/handleSlashcommands.go
diff --git a/src/discordbot/interface.go b/src/discord/discordbot/interface.go
similarity index 100%
rename from src/discordbot/interface.go
rename to src/discord/discordbot/interface.go
diff --git a/src/discordbot/logstream.go b/src/discord/discordbot/logstream.go
similarity index 100%
rename from src/discordbot/logstream.go
rename to src/discord/discordbot/logstream.go
diff --git a/src/discordbot/registerSlashcommands.go b/src/discord/discordbot/registerSlashcommands.go
similarity index 100%
rename from src/discordbot/registerSlashcommands.go
rename to src/discord/discordbot/registerSlashcommands.go
diff --git a/src/discordbot/sendMessage.go b/src/discord/discordbot/sendMessage.go
similarity index 100%
rename from src/discordbot/sendMessage.go
rename to src/discord/discordbot/sendMessage.go
diff --git a/src/discordbot/types.go b/src/discord/discordbot/types.go
similarity index 100%
rename from src/discordbot/types.go
rename to src/discord/discordbot/types.go
diff --git a/src/discordrpc/discordrpc.go b/src/discord/discordrpc/discordrpc.go
similarity index 100%
rename from src/discordrpc/discordrpc.go
rename to src/discord/discordrpc/discordrpc.go
diff --git a/src/managers/detectionmgr/detector.go b/src/managers/detectionmgr/detector.go
index 24e8c6df..743f915a 100644
--- a/src/managers/detectionmgr/detector.go
+++ b/src/managers/detectionmgr/detector.go
@@ -8,7 +8,7 @@ import (
"time"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discordbot"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discord/discordbot"
)
/*
diff --git a/src/managers/detectionmgr/handlers.go b/src/managers/detectionmgr/handlers.go
index d8ff75f3..3b09eb6b 100644
--- a/src/managers/detectionmgr/handlers.go
+++ b/src/managers/detectionmgr/handlers.go
@@ -7,7 +7,7 @@ import (
"time"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/ssestream"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discordbot"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discord/discordbot"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
)
diff --git a/src/managers/detectionmgr/logstream.go b/src/managers/detectionmgr/logstream.go
index 264ec04e..7014f8c2 100644
--- a/src/managers/detectionmgr/logstream.go
+++ b/src/managers/detectionmgr/logstream.go
@@ -4,7 +4,7 @@ package detectionmgr
import (
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/ssestream"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discordbot"
+ "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discord/discordbot"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
)
From 010ff171f4e8e957b1e6edd62299d828ab704b9b Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 02:29:55 +0200
Subject: [PATCH 35/93] apply fix for SSE Stream race condition in v7 SSE
handling Fixes #120 originally fixed in #117
---
src/core/ssestream/ssemanager.go | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/core/ssestream/ssemanager.go b/src/core/ssestream/ssemanager.go
index 16e86919..48c56e86 100644
--- a/src/core/ssestream/ssemanager.go
+++ b/src/core/ssestream/ssemanager.go
@@ -106,10 +106,7 @@ func (m *SSEManager) CreateStreamHandler(streamType string) http.HandlerFunc {
notify := r.Context().Done()
// Start streaming messages
- go m.streamMessages(w, flusher, client, streamType, notify)
-
- // Wait for client disconnection
- <-notify
+ m.streamMessages(w, flusher, client, notify)
}
}
@@ -118,7 +115,6 @@ func (m *SSEManager) streamMessages(
w http.ResponseWriter,
flusher http.Flusher,
client *Client,
- streamType string,
notify <-chan struct{},
) {
defer m.removeClient(client)
From cd00e8b030674a12a0d048c586792a293e3827f0 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 03:09:48 +0200
Subject: [PATCH 36/93] change module name from
JacksonTheMaster/StationeersServerUI to SteamServerUI/SteamServerUI/v7
---
build/build.go | 2 +-
build/version.go | 2 +-
go.mod | 2 +-
server.go | 12 ++++++------
src/api/TwoBoxForm.go | 6 +++---
src/api/commands.go | 4 ++--
src/api/configpage.go | 6 +++---
src/api/connectedplayers.go | 2 +-
src/api/detectionmanagerpage.go | 2 +-
src/api/http-sse.go | 2 +-
src/api/http.go | 14 +++++++-------
src/api/indexpage.go | 6 +++---
src/api/login.go | 10 +++++-----
src/api/routes.go | 10 +++++-----
src/api/runfile.go | 6 +++---
src/api/runfilegallery.go | 4 ++--
src/api/socket/socket-lin.go | 4 ++--
src/api/socket/socket-win.go | 4 ++--
src/api/start.go | 6 +++---
src/api/svelteui.go | 6 +++---
src/api/systeminfo.go | 4 ++--
src/cli/runtimecommands.go | 14 +++++++-------
src/config/configchanger/changeconfig.go | 2 +-
src/config/configchanger/saveconfig.go | 6 +++---
src/core/loader/afterstart.go | 10 +++++-----
src/core/loader/cmdargs.go | 6 +++---
src/core/loader/helpers.go | 4 ++--
src/core/loader/loader.go | 18 +++++++++---------
src/core/loader/runfile.go | 10 +++++-----
src/core/loader/sanitycheck.go | 2 +-
src/core/loader/terminalmsg.go | 4 ++--
src/core/security/auth.go | 2 +-
src/core/security/tls.go | 4 ++--
src/core/ssestream/ssemanager.go | 2 +-
src/core/ssestream/sseutils.go | 2 +-
src/discord/discordbot/connectedplayers.go | 4 ++--
src/discord/discordbot/controlpanel.go | 8 ++++----
src/discord/discordbot/handleBlacklist.go | 2 +-
src/discord/discordbot/handleReactions.go | 6 +++---
src/discord/discordbot/handleSlashcommands.go | 12 ++++++------
src/discord/discordbot/interface.go | 4 ++--
src/discord/discordbot/logstream.go | 4 ++--
.../discordbot/registerSlashcommands.go | 2 +-
src/discord/discordbot/sendMessage.go | 4 ++--
src/discord/discordrpc/discordrpc.go | 2 +-
src/localization/localization.go | 4 ++--
src/logger/log-helpers.go | 2 +-
src/logger/logger.go | 4 ++--
src/managers/backupmgr/backuphttp.go | 4 ++--
src/managers/backupmgr/backupinterface.go | 4 ++--
src/managers/backupmgr/cleanup.go | 2 +-
src/managers/backupmgr/manager.go | 6 +++---
src/managers/backupmgr/restore.go | 2 +-
src/managers/backupmgr/watcher.go | 2 +-
src/managers/commandmgr/commandmgr.go | 2 +-
src/managers/detectionmgr/customdetections.go | 2 +-
src/managers/detectionmgr/detector.go | 4 ++--
src/managers/detectionmgr/handlers.go | 6 +++---
src/managers/detectionmgr/logstream.go | 8 ++++----
src/managers/gamemgr/autorestart.go | 6 +++---
src/managers/gamemgr/bepinex.go | 6 +++---
src/managers/gamemgr/processmanagement.go | 6 +++---
src/managers/gamemgr/runcheck.go | 2 +-
src/managers/gamemgr/serverlog.go | 4 ++--
src/managers/gamemgr/uuid.go | 2 +-
src/setup/autostartscripts.go | 2 +-
src/setup/bepinex.go | 6 +++---
src/setup/cleanup.go | 4 ++--
src/setup/install.go | 6 +++---
src/setup/update/updater.go | 4 ++--
src/steamcmd/getappinfo.go | 10 +++++-----
src/steamcmd/install.go | 2 +-
src/steamcmd/steamcmd-helper.go | 2 +-
src/steamcmd/steamcmd.go | 8 ++++----
src/steamserverui/gallery/gallery.go | 4 ++--
src/steamserverui/runfile/argexample.go | 2 +-
src/steamserverui/runfile/args.go | 4 ++--
src/steamserverui/runfile/getters.go | 2 +-
src/steamserverui/settings/retrieve.go | 2 +-
src/steamserverui/settings/save.go | 2 +-
80 files changed, 193 insertions(+), 193 deletions(-)
diff --git a/build/build.go b/build/build.go
index 5ac9233d..594fd57a 100644
--- a/build/build.go
+++ b/build/build.go
@@ -15,7 +15,7 @@ import (
"strconv"
"strings"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
const (
diff --git a/build/version.go b/build/version.go
index dc240999..ac30672e 100644
--- a/build/version.go
+++ b/build/version.go
@@ -13,7 +13,7 @@ import (
"path/filepath"
"regexp"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
func main() {
diff --git a/go.mod b/go.mod
index 66419a87..b1d91ee0 100644
--- a/go.mod
+++ b/go.mod
@@ -1,4 +1,4 @@
-module github.com/JacksonTheMaster/StationeersServerUI/v5
+module github.com/SteamServerUI/SteamServerUI/v7
go 1.25.0
diff --git a/server.go b/server.go
index 4e04bd55..c6f72e8b 100644
--- a/server.go
+++ b/server.go
@@ -24,12 +24,12 @@ import (
"embed"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/api"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/api/socket"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/cli"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/loader"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/setup"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api/socket"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/cli"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/setup"
)
//go:embed UIMod/onboard_bundled
diff --git a/src/api/TwoBoxForm.go b/src/api/TwoBoxForm.go
index c9bf9318..7ef16e2b 100644
--- a/src/api/TwoBoxForm.go
+++ b/src/api/TwoBoxForm.go
@@ -5,9 +5,9 @@ import (
"net/http"
"text/template"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/localization"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/localization"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) {
diff --git a/src/api/commands.go b/src/api/commands.go
index 9b770265..99249861 100644
--- a/src/api/commands.go
+++ b/src/api/commands.go
@@ -4,8 +4,8 @@ import (
"encoding/json"
"net/http"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/commandmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/commandmgr"
)
type CommandRequest struct {
diff --git a/src/api/configpage.go b/src/api/configpage.go
index d52bc8f5..870c6cbe 100644
--- a/src/api/configpage.go
+++ b/src/api/configpage.go
@@ -6,9 +6,9 @@ import (
"net/http"
"text/template"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/localization"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/localization"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
func ServeConfigPage(w http.ResponseWriter, r *http.Request) {
diff --git a/src/api/connectedplayers.go b/src/api/connectedplayers.go
index ccb61165..bcb69228 100644
--- a/src/api/connectedplayers.go
+++ b/src/api/connectedplayers.go
@@ -4,7 +4,7 @@ import (
"encoding/json"
"net/http"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/detectionmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/detectionmgr"
)
// PrintConnectedPlayersHandler handles HTTP requests to list connected players.
diff --git a/src/api/detectionmanagerpage.go b/src/api/detectionmanagerpage.go
index 96d5f360..19ff5a5b 100644
--- a/src/api/detectionmanagerpage.go
+++ b/src/api/detectionmanagerpage.go
@@ -6,7 +6,7 @@ import (
"io/fs"
"net/http"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
func ServeDetectionManager(w http.ResponseWriter, r *http.Request) {
diff --git a/src/api/http-sse.go b/src/api/http-sse.go
index 447704db..4d0010b3 100644
--- a/src/api/http-sse.go
+++ b/src/api/http-sse.go
@@ -3,7 +3,7 @@ package api
import (
"net/http"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/ssestream"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/ssestream"
)
// handler for the /console endpoint
diff --git a/src/api/http.go b/src/api/http.go
index 479ef546..cae38d09 100644
--- a/src/api/http.go
+++ b/src/api/http.go
@@ -8,13 +8,13 @@ import (
"os"
"strings"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/localization"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/commandmgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/detectionmgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/localization"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/commandmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/detectionmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamcmd"
)
// StartServer HTTP handler
diff --git a/src/api/indexpage.go b/src/api/indexpage.go
index f42c732a..db6adcdf 100644
--- a/src/api/indexpage.go
+++ b/src/api/indexpage.go
@@ -5,9 +5,9 @@ import (
"net/http"
"text/template"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/localization"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/localization"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
func ServeIndex(w http.ResponseWriter, r *http.Request) {
diff --git a/src/api/login.go b/src/api/login.go
index 90b853f0..1423fb6a 100644
--- a/src/api/login.go
+++ b/src/api/login.go
@@ -9,11 +9,11 @@ import (
"strings"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config/configchanger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/loader"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/security"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config/configchanger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/security"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
var setupReminderCount = 0 // to limit the number of setup reminders shown to the user
diff --git a/src/api/routes.go b/src/api/routes.go
index f1f50c91..d6ae6d11 100644
--- a/src/api/routes.go
+++ b/src/api/routes.go
@@ -4,11 +4,11 @@ import (
"io/fs"
"net/http"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config/configchanger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/backupmgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/detectionmgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/settings"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config/configchanger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/backupmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/detectionmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/settings"
)
func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
diff --git a/src/api/runfile.go b/src/api/runfile.go
index 2b07a538..5dfdc488 100644
--- a/src/api/runfile.go
+++ b/src/api/runfile.go
@@ -6,9 +6,9 @@ import (
"net/http"
"strings"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/loader"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/runfile"
)
// APIGameArg is a DTO for GameArg, including RuntimeValue and all fields
diff --git a/src/api/runfilegallery.go b/src/api/runfilegallery.go
index eff60b5d..6145185d 100644
--- a/src/api/runfilegallery.go
+++ b/src/api/runfilegallery.go
@@ -6,8 +6,8 @@ import (
"strconv"
"strings"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/gallery"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/gallery"
)
// response wraps API responses
diff --git a/src/api/socket/socket-lin.go b/src/api/socket/socket-lin.go
index 93a0dd80..ea1cdb61 100644
--- a/src/api/socket/socket-lin.go
+++ b/src/api/socket/socket-lin.go
@@ -11,8 +11,8 @@ import (
"os"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/api"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
const socketPath = "/var/run/ssui.sock"
diff --git a/src/api/socket/socket-win.go b/src/api/socket/socket-win.go
index dd840eca..ceed2717 100644
--- a/src/api/socket/socket-win.go
+++ b/src/api/socket/socket-win.go
@@ -9,8 +9,8 @@ import (
"net/http"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/api"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
"github.com/microsoft/go-winio"
)
diff --git a/src/api/start.go b/src/api/start.go
index 542cff82..5c4b55e2 100644
--- a/src/api/start.go
+++ b/src/api/start.go
@@ -7,9 +7,9 @@ import (
"net/http/pprof"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/security"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/security"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
type APIServerLogger struct{}
diff --git a/src/api/svelteui.go b/src/api/svelteui.go
index 548f16dc..2475dc95 100644
--- a/src/api/svelteui.go
+++ b/src/api/svelteui.go
@@ -7,9 +7,9 @@ import (
"net/http"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/loader"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
var reloadMu sync.Mutex
diff --git a/src/api/systeminfo.go b/src/api/systeminfo.go
index d378c195..a3386555 100644
--- a/src/api/systeminfo.go
+++ b/src/api/systeminfo.go
@@ -4,8 +4,8 @@ import (
"encoding/json"
"net/http"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/systeminfo"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/systeminfo"
)
func HandleGetOsStats(w http.ResponseWriter, r *http.Request) {
diff --git a/src/cli/runtimecommands.go b/src/cli/runtimecommands.go
index 673a76e0..8c617127 100644
--- a/src/cli/runtimecommands.go
+++ b/src/cli/runtimecommands.go
@@ -18,13 +18,13 @@ import (
"sync"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/loader"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/localization"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/localization"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamcmd"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/runfile"
)
// ANSI escape codes for green text and reset
diff --git a/src/config/configchanger/changeconfig.go b/src/config/configchanger/changeconfig.go
index 25a89aeb..073d7b40 100644
--- a/src/config/configchanger/changeconfig.go
+++ b/src/config/configchanger/changeconfig.go
@@ -8,7 +8,7 @@ import (
"reflect"
"strconv"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
func SaveConfigForm(w http.ResponseWriter, r *http.Request) {
diff --git a/src/config/configchanger/saveconfig.go b/src/config/configchanger/saveconfig.go
index 6163b3fb..2378a3b8 100644
--- a/src/config/configchanger/saveconfig.go
+++ b/src/config/configchanger/saveconfig.go
@@ -1,9 +1,9 @@
package configchanger
import (
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/loader"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
func SaveConfig(cfg *config.JsonConfig, reloadBackend ...bool) error {
diff --git a/src/core/loader/afterstart.go b/src/core/loader/afterstart.go
index 4783164e..b5dc2d51 100644
--- a/src/core/loader/afterstart.go
+++ b/src/core/loader/afterstart.go
@@ -3,11 +3,11 @@ package loader
import (
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discord/discordrpc"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/setup"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/discord/discordrpc"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/setup"
)
func AfterStartComplete(wg *sync.WaitGroup) {
diff --git a/src/core/loader/cmdargs.go b/src/core/loader/cmdargs.go
index 1b04418c..76d2c2ea 100644
--- a/src/core/loader/cmdargs.go
+++ b/src/core/loader/cmdargs.go
@@ -6,9 +6,9 @@ import (
"strings"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/security"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/security"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// Define flags matching the config variable names
diff --git a/src/core/loader/helpers.go b/src/core/loader/helpers.go
index fccb5e1b..540489c7 100644
--- a/src/core/loader/helpers.go
+++ b/src/core/loader/helpers.go
@@ -7,8 +7,8 @@ import (
"strings"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
func PrintConfigDetails(logLevel ...string) {
diff --git a/src/core/loader/loader.go b/src/core/loader/loader.go
index d02f1a27..4d1c15fe 100644
--- a/src/core/loader/loader.go
+++ b/src/core/loader/loader.go
@@ -7,15 +7,15 @@ import (
"sync"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discord/discordbot"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/localization"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/backupmgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/detectionmgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/setup"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/setup/update"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/discord/discordbot"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/localization"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/backupmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/detectionmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/setup"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/setup/update"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamcmd"
)
// only call this once at startup
diff --git a/src/core/loader/runfile.go b/src/core/loader/runfile.go
index 5af0c2d2..bf16600e 100644
--- a/src/core/loader/runfile.go
+++ b/src/core/loader/runfile.go
@@ -4,11 +4,11 @@ import (
"fmt"
"strings"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamcmd"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/runfile"
)
// used via Runfile Gallery
diff --git a/src/core/loader/sanitycheck.go b/src/core/loader/sanitycheck.go
index e7201e8f..fd5ff78a 100644
--- a/src/core/loader/sanitycheck.go
+++ b/src/core/loader/sanitycheck.go
@@ -8,7 +8,7 @@ import (
"strings"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
var containerCheckWG = sync.WaitGroup{}
diff --git a/src/core/loader/terminalmsg.go b/src/core/loader/terminalmsg.go
index 588eed6f..fe1eb952 100644
--- a/src/core/loader/terminalmsg.go
+++ b/src/core/loader/terminalmsg.go
@@ -4,8 +4,8 @@ import (
"runtime"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// PrintStartupMessage prints a stylish startup message to the terminal
diff --git a/src/core/security/auth.go b/src/core/security/auth.go
index a052eb24..0194fbef 100644
--- a/src/core/security/auth.go
+++ b/src/core/security/auth.go
@@ -6,7 +6,7 @@ package security
import (
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
"github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
diff --git a/src/core/security/tls.go b/src/core/security/tls.go
index 2d4a9756..e478ef1e 100644
--- a/src/core/security/tls.go
+++ b/src/core/security/tls.go
@@ -13,8 +13,8 @@ import (
"path/filepath"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// EnsureTLSCerts ensures TLS certificates exist and are valid at config.GetTLSCertPath() and config.GetTLSKeyPath(), generating self-signed ones if needed.
diff --git a/src/core/ssestream/ssemanager.go b/src/core/ssestream/ssemanager.go
index 48c56e86..10b0eb51 100644
--- a/src/core/ssestream/ssemanager.go
+++ b/src/core/ssestream/ssemanager.go
@@ -8,7 +8,7 @@ import (
"sync"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
// The SSE blocking issue is NOT related to the backend; the API handles 200 clients per channel fine.
diff --git a/src/core/ssestream/sseutils.go b/src/core/ssestream/sseutils.go
index eafa4a51..d7c98a07 100644
--- a/src/core/ssestream/sseutils.go
+++ b/src/core/ssestream/sseutils.go
@@ -2,7 +2,7 @@
package ssestream
import (
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
// Global managers for SSE streams
diff --git a/src/discord/discordbot/connectedplayers.go b/src/discord/discordbot/connectedplayers.go
index 6817a560..dd6d7dfe 100644
--- a/src/discord/discordbot/connectedplayers.go
+++ b/src/discord/discordbot/connectedplayers.go
@@ -6,8 +6,8 @@ import (
"sync"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
var (
diff --git a/src/discord/discordbot/controlpanel.go b/src/discord/discordbot/controlpanel.go
index 323e472d..1cb31f5c 100644
--- a/src/discord/discordbot/controlpanel.go
+++ b/src/discord/discordbot/controlpanel.go
@@ -4,10 +4,10 @@ import (
"fmt"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamcmd"
"github.com/bwmarrin/discordgo"
)
diff --git a/src/discord/discordbot/handleBlacklist.go b/src/discord/discordbot/handleBlacklist.go
index 9af21503..164b23e1 100644
--- a/src/discord/discordbot/handleBlacklist.go
+++ b/src/discord/discordbot/handleBlacklist.go
@@ -7,7 +7,7 @@ import (
"strings"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
var blacklistMutex sync.Mutex
diff --git a/src/discord/discordbot/handleReactions.go b/src/discord/discordbot/handleReactions.go
index 351c9d2b..fb7c523b 100644
--- a/src/discord/discordbot/handleReactions.go
+++ b/src/discord/discordbot/handleReactions.go
@@ -4,9 +4,9 @@ import (
"fmt"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
"github.com/bwmarrin/discordgo"
)
diff --git a/src/discord/discordbot/handleSlashcommands.go b/src/discord/discordbot/handleSlashcommands.go
index 20d315aa..4c7e1e4c 100644
--- a/src/discord/discordbot/handleSlashcommands.go
+++ b/src/discord/discordbot/handleSlashcommands.go
@@ -7,12 +7,12 @@ import (
"strings"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/backupmgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/commandmgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/backupmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/commandmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamcmd"
"github.com/bwmarrin/discordgo"
)
diff --git a/src/discord/discordbot/interface.go b/src/discord/discordbot/interface.go
index 773f8f51..057f499e 100644
--- a/src/discord/discordbot/interface.go
+++ b/src/discord/discordbot/interface.go
@@ -3,8 +3,8 @@ package discordbot
import (
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
"github.com/bwmarrin/discordgo"
)
diff --git a/src/discord/discordbot/logstream.go b/src/discord/discordbot/logstream.go
index 9212d9a5..672a0446 100644
--- a/src/discord/discordbot/logstream.go
+++ b/src/discord/discordbot/logstream.go
@@ -1,8 +1,8 @@
package discordbot
import (
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// PassLogMessageToDiscordLogBuffer is called from the detection module to add a log message to the buffer.
diff --git a/src/discord/discordbot/registerSlashcommands.go b/src/discord/discordbot/registerSlashcommands.go
index 8756d3ce..00afe0a2 100644
--- a/src/discord/discordbot/registerSlashcommands.go
+++ b/src/discord/discordbot/registerSlashcommands.go
@@ -4,7 +4,7 @@ import (
"sync"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
"github.com/bwmarrin/discordgo"
)
diff --git a/src/discord/discordbot/sendMessage.go b/src/discord/discordbot/sendMessage.go
index 1654183f..6f06c52e 100644
--- a/src/discord/discordbot/sendMessage.go
+++ b/src/discord/discordbot/sendMessage.go
@@ -4,9 +4,9 @@ import (
"strings"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
"github.com/bwmarrin/discordgo"
)
diff --git a/src/discord/discordrpc/discordrpc.go b/src/discord/discordrpc/discordrpc.go
index e8bb22ec..69131ef7 100644
--- a/src/discord/discordrpc/discordrpc.go
+++ b/src/discord/discordrpc/discordrpc.go
@@ -3,7 +3,7 @@ package discordrpc
import (
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
"github.com/jacksonthemaster/discordrichpresence"
)
diff --git a/src/localization/localization.go b/src/localization/localization.go
index 55e77c0e..7a0e417d 100644
--- a/src/localization/localization.go
+++ b/src/localization/localization.go
@@ -6,8 +6,8 @@ import (
"strings"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// translations stores language code to key-value pairs
diff --git a/src/logger/log-helpers.go b/src/logger/log-helpers.go
index 76ad7b69..6c99e8dd 100644
--- a/src/logger/log-helpers.go
+++ b/src/logger/log-helpers.go
@@ -7,7 +7,7 @@ import (
"strings"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
func (l *Logger) writeToFile(logLine, subsystem string) {
diff --git a/src/logger/logger.go b/src/logger/logger.go
index e6a155d8..1732d345 100644
--- a/src/logger/logger.go
+++ b/src/logger/logger.go
@@ -7,8 +7,8 @@ import (
"sync"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/ssestream"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/ssestream"
)
// Logger instances
diff --git a/src/managers/backupmgr/backuphttp.go b/src/managers/backupmgr/backuphttp.go
index 729cc1df..c93419b4 100644
--- a/src/managers/backupmgr/backuphttp.go
+++ b/src/managers/backupmgr/backuphttp.go
@@ -7,8 +7,8 @@ import (
"strconv"
"strings"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
)
// HTTPHandler provides HTTP endpoints for backup operations
diff --git a/src/managers/backupmgr/backupinterface.go b/src/managers/backupmgr/backupinterface.go
index 6f0964be..1c3d55f0 100644
--- a/src/managers/backupmgr/backupinterface.go
+++ b/src/managers/backupmgr/backupinterface.go
@@ -4,8 +4,8 @@ import (
"sync"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
"github.com/google/uuid"
)
diff --git a/src/managers/backupmgr/cleanup.go b/src/managers/backupmgr/cleanup.go
index 946b1b84..4621691b 100644
--- a/src/managers/backupmgr/cleanup.go
+++ b/src/managers/backupmgr/cleanup.go
@@ -8,7 +8,7 @@ import (
"strings"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// Cleanup performs backup cleanup according to retention policy
diff --git a/src/managers/backupmgr/manager.go b/src/managers/backupmgr/manager.go
index 69338718..5d61ce12 100644
--- a/src/managers/backupmgr/manager.go
+++ b/src/managers/backupmgr/manager.go
@@ -8,9 +8,9 @@ import (
"sort"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/commandmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/commandmgr"
"github.com/fsnotify/fsnotify"
)
diff --git a/src/managers/backupmgr/restore.go b/src/managers/backupmgr/restore.go
index 8ce94533..afcfc283 100644
--- a/src/managers/backupmgr/restore.go
+++ b/src/managers/backupmgr/restore.go
@@ -10,7 +10,7 @@ import (
"strings"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// RestoreBackup restores a backup with the given index
diff --git a/src/managers/backupmgr/watcher.go b/src/managers/backupmgr/watcher.go
index 179126d4..05a2a966 100644
--- a/src/managers/backupmgr/watcher.go
+++ b/src/managers/backupmgr/watcher.go
@@ -5,7 +5,7 @@ import (
"os"
"path/filepath"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
"github.com/fsnotify/fsnotify"
)
diff --git a/src/managers/commandmgr/commandmgr.go b/src/managers/commandmgr/commandmgr.go
index 30b55d5e..0b0c69b7 100644
--- a/src/managers/commandmgr/commandmgr.go
+++ b/src/managers/commandmgr/commandmgr.go
@@ -7,7 +7,7 @@ import (
"path/filepath"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
var mutex = &sync.Mutex{}
diff --git a/src/managers/detectionmgr/customdetections.go b/src/managers/detectionmgr/customdetections.go
index 459bff75..cf656485 100644
--- a/src/managers/detectionmgr/customdetections.go
+++ b/src/managers/detectionmgr/customdetections.go
@@ -10,7 +10,7 @@ import (
"regexp"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
/*
diff --git a/src/managers/detectionmgr/detector.go b/src/managers/detectionmgr/detector.go
index 743f915a..65be1fb2 100644
--- a/src/managers/detectionmgr/detector.go
+++ b/src/managers/detectionmgr/detector.go
@@ -7,8 +7,8 @@ import (
"strings"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discord/discordbot"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/discord/discordbot"
)
/*
diff --git a/src/managers/detectionmgr/handlers.go b/src/managers/detectionmgr/handlers.go
index 3b09eb6b..9dc421cf 100644
--- a/src/managers/detectionmgr/handlers.go
+++ b/src/managers/detectionmgr/handlers.go
@@ -6,9 +6,9 @@ import (
"strings"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/ssestream"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discord/discordbot"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/ssestream"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/discord/discordbot"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
/*
diff --git a/src/managers/detectionmgr/logstream.go b/src/managers/detectionmgr/logstream.go
index 7014f8c2..d54fe498 100644
--- a/src/managers/detectionmgr/logstream.go
+++ b/src/managers/detectionmgr/logstream.go
@@ -2,10 +2,10 @@
package detectionmgr
import (
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/ssestream"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/discord/discordbot"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/ssestream"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/discord/discordbot"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
/*
diff --git a/src/managers/gamemgr/autorestart.go b/src/managers/gamemgr/autorestart.go
index 3bec7653..e4a8aede 100644
--- a/src/managers/gamemgr/autorestart.go
+++ b/src/managers/gamemgr/autorestart.go
@@ -4,9 +4,9 @@ import (
"strconv"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/commandmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/commandmgr"
)
var (
diff --git a/src/managers/gamemgr/bepinex.go b/src/managers/gamemgr/bepinex.go
index 7a66aa4b..90383b6d 100644
--- a/src/managers/gamemgr/bepinex.go
+++ b/src/managers/gamemgr/bepinex.go
@@ -5,9 +5,9 @@ import (
"os"
"path/filepath"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/runfile"
)
// BepInEx version: 5.4.23.2 or v5-lts
diff --git a/src/managers/gamemgr/processmanagement.go b/src/managers/gamemgr/processmanagement.go
index 6217fd80..2b6c4255 100644
--- a/src/managers/gamemgr/processmanagement.go
+++ b/src/managers/gamemgr/processmanagement.go
@@ -12,9 +12,9 @@ import (
"syscall"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/runfile"
)
var (
diff --git a/src/managers/gamemgr/runcheck.go b/src/managers/gamemgr/runcheck.go
index a57734f4..605eeeea 100644
--- a/src/managers/gamemgr/runcheck.go
+++ b/src/managers/gamemgr/runcheck.go
@@ -4,7 +4,7 @@ import (
"runtime"
"syscall"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// InternalIsServerRunning checks if the server process is running.
diff --git a/src/managers/gamemgr/serverlog.go b/src/managers/gamemgr/serverlog.go
index fc950cff..504ad287 100644
--- a/src/managers/gamemgr/serverlog.go
+++ b/src/managers/gamemgr/serverlog.go
@@ -10,8 +10,8 @@ import (
"strconv"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/ssestream"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/ssestream"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// readPipe for Windows
diff --git a/src/managers/gamemgr/uuid.go b/src/managers/gamemgr/uuid.go
index e968648b..f3ffde77 100644
--- a/src/managers/gamemgr/uuid.go
+++ b/src/managers/gamemgr/uuid.go
@@ -1,7 +1,7 @@
package gamemgr
import (
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
"github.com/google/uuid"
)
diff --git a/src/setup/autostartscripts.go b/src/setup/autostartscripts.go
index 4fe653d4..ae29edcd 100644
--- a/src/setup/autostartscripts.go
+++ b/src/setup/autostartscripts.go
@@ -6,7 +6,7 @@ import (
"os"
"runtime"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
func SetupAutostartScripts() {
diff --git a/src/setup/bepinex.go b/src/setup/bepinex.go
index ce2d185e..89937ccc 100644
--- a/src/setup/bepinex.go
+++ b/src/setup/bepinex.go
@@ -6,9 +6,9 @@ import (
"runtime"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamcmd"
)
// BepInEx version: 5.4.23.2 or v5-lts
diff --git a/src/setup/cleanup.go b/src/setup/cleanup.go
index 323be66d..6ef81c57 100644
--- a/src/setup/cleanup.go
+++ b/src/setup/cleanup.go
@@ -7,8 +7,8 @@ import (
"regexp"
"strings"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
func CleanUpOldUIModFolderFiles() error {
diff --git a/src/setup/install.go b/src/setup/install.go
index 5c25cd95..6cd8fb94 100644
--- a/src/setup/install.go
+++ b/src/setup/install.go
@@ -14,9 +14,9 @@ import (
"sync"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/setup/update"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/setup/update"
)
var downloadBranch string // Holds the branch to download from
diff --git a/src/setup/update/updater.go b/src/setup/update/updater.go
index f5ef8b7c..3e37ed8d 100644
--- a/src/setup/update/updater.go
+++ b/src/setup/update/updater.go
@@ -14,8 +14,8 @@ import (
"syscall"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// githubRelease represents the structure of a GitHub release response
diff --git a/src/steamcmd/getappinfo.go b/src/steamcmd/getappinfo.go
index ab47bda6..5a8bd0a4 100644
--- a/src/steamcmd/getappinfo.go
+++ b/src/steamcmd/getappinfo.go
@@ -13,11 +13,11 @@ import (
"sync"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/commandmgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/commandmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/runfile"
)
var (
diff --git a/src/steamcmd/install.go b/src/steamcmd/install.go
index 7be15268..61536be1 100644
--- a/src/steamcmd/install.go
+++ b/src/steamcmd/install.go
@@ -3,7 +3,7 @@ package steamcmd
import (
"os"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
func installSteamCMD(platform string, steamCMDDir string, downloadURL string, extractFunc ExtractorFunc) (int, error) {
diff --git a/src/steamcmd/steamcmd-helper.go b/src/steamcmd/steamcmd-helper.go
index 4caee4fe..159be7ef 100644
--- a/src/steamcmd/steamcmd-helper.go
+++ b/src/steamcmd/steamcmd-helper.go
@@ -17,7 +17,7 @@ import (
"strings"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// isRelSymlink ensures `link` resolves to a path within `root`.
diff --git a/src/steamcmd/steamcmd.go b/src/steamcmd/steamcmd.go
index ce6c4aa1..be56777e 100644
--- a/src/steamcmd/steamcmd.go
+++ b/src/steamcmd/steamcmd.go
@@ -10,11 +10,11 @@ import (
"strings"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamserverui/runfile"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/runfile"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
var steamMu sync.Mutex
diff --git a/src/steamserverui/gallery/gallery.go b/src/steamserverui/gallery/gallery.go
index 00db95ec..2ddac3bc 100644
--- a/src/steamserverui/gallery/gallery.go
+++ b/src/steamserverui/gallery/gallery.go
@@ -10,8 +10,8 @@ import (
"strings"
"sync"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// GalleryRunfile represents a runfile in the gallery
diff --git a/src/steamserverui/runfile/argexample.go b/src/steamserverui/runfile/argexample.go
index ad248216..7d84a6d8 100644
--- a/src/steamserverui/runfile/argexample.go
+++ b/src/steamserverui/runfile/argexample.go
@@ -3,7 +3,7 @@ package runfile
import (
"fmt"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
// unused
diff --git a/src/steamserverui/runfile/args.go b/src/steamserverui/runfile/args.go
index 9bda8eec..d3c34898 100644
--- a/src/steamserverui/runfile/args.go
+++ b/src/steamserverui/runfile/args.go
@@ -12,8 +12,8 @@ import (
"sync"
"time"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// Package-level mutex for file operations
diff --git a/src/steamserverui/runfile/getters.go b/src/steamserverui/runfile/getters.go
index b6770787..580ef9e2 100644
--- a/src/steamserverui/runfile/getters.go
+++ b/src/steamserverui/runfile/getters.go
@@ -5,7 +5,7 @@ import (
"runtime"
"strings"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
// GetAllArgs returns all GameArgs from the runfile
diff --git a/src/steamserverui/settings/retrieve.go b/src/steamserverui/settings/retrieve.go
index 8cf82c71..da418d99 100644
--- a/src/steamserverui/settings/retrieve.go
+++ b/src/steamserverui/settings/retrieve.go
@@ -5,7 +5,7 @@ import (
"fmt"
"net/http"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
// package settings handles API communication with the config values in package config via getter /setter functions.
diff --git a/src/steamserverui/settings/save.go b/src/steamserverui/settings/save.go
index ca857c27..6b789ad1 100644
--- a/src/steamserverui/settings/save.go
+++ b/src/steamserverui/settings/save.go
@@ -6,7 +6,7 @@ import (
"io"
"net/http"
- "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
)
// package settings handles API communication with the config values in package config via getter /setter functions.
From 0383a7546fd06a3e6d4cdd0cf9f02d4dcd726636 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 03:13:48 +0200
Subject: [PATCH 37/93] adjust executable name to SteamServerUI
---
build/build.go | 4 ++--
src/setup/update/updater.go | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/build/build.go b/build/build.go
index 594fd57a..6848b80c 100644
--- a/build/build.go
+++ b/build/build.go
@@ -62,9 +62,9 @@ func main() {
// Prepare the output file name with the new version, branch, and platform
var outputName string
if config.Branch == "release" {
- outputName = fmt.Sprintf("StationeersServerControlv%s", newVersion)
+ outputName = fmt.Sprintf("SteamServerUIv%s", newVersion)
} else {
- outputName = fmt.Sprintf("StationeersServerControlv%s_%s", newVersion, config.Branch)
+ outputName = fmt.Sprintf("SteamServerUIv%s_%s", newVersion, config.Branch)
}
// Append appropriate extension based on platform
diff --git a/src/setup/update/updater.go b/src/setup/update/updater.go
index 3e37ed8d..a7440752 100644
--- a/src/setup/update/updater.go
+++ b/src/setup/update/updater.go
@@ -100,7 +100,7 @@ func UpdateExecutable() error {
if runtime.GOOS != "windows" {
expectedExt = ".x86_64"
}
- expectedExe := fmt.Sprintf("StationeersServerControl%s%s", latestRelease.TagName, expectedExt)
+ expectedExe := fmt.Sprintf("SteamServerUI%s%s", latestRelease.TagName, expectedExt)
// Find the asset
var downloadURL string
From 16a86d5136670d3cd20b81b5290923aa75080b0a Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 03:16:04 +0200
Subject: [PATCH 38/93] bump version to v7
---
frontend/package-lock.json | 9 +++++++--
src/config/config.go | 4 ++--
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 934708ee..6d326d98 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "steamserverui",
- "version": "v5.5.8",
+ "version": "v7.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "steamserverui",
- "version": "v5.5.8",
+ "version": "v7.0.0",
"license": "proprietary",
"dependencies": {
"https": "^1.0.0"
@@ -882,6 +882,7 @@
"integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^4.0.1",
"debug": "^4.4.1",
@@ -929,6 +930,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -1662,6 +1664,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=12"
},
@@ -1839,6 +1842,7 @@
"integrity": "sha512-UY+OhrWK7WI22bCZ00P/M3HtyWgwJPi9IxSRkoAE2MeAy6kl7ZlZWJZ8RaB+X4KD/G+wjis+cGVnVYaoqbzBqg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
"@jridgewell/sourcemap-codec": "^1.5.0",
@@ -1899,6 +1903,7 @@
"integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
diff --git a/src/config/config.go b/src/config/config.go
index aaba18e9..2033deca 100644
--- a/src/config/config.go
+++ b/src/config/config.go
@@ -11,8 +11,8 @@ import (
var (
// All configuration variables can be found in vars.go
- Version = "5.7.1"
- Branch = "release"
+ Version = "7.0.0"
+ Branch = "v7-nightly"
)
/*
From 70ea29ba909f5a32d480e8b2dabd1b863f0fa8f5 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 03:19:01 +0200
Subject: [PATCH 39/93] update startup banner
---
src/core/loader/terminalmsg.go | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/core/loader/terminalmsg.go b/src/core/loader/terminalmsg.go
index fe1eb952..1c9370ef 100644
--- a/src/core/loader/terminalmsg.go
+++ b/src/core/loader/terminalmsg.go
@@ -15,14 +15,14 @@ func printStartupMessage() {
logger.Core.Cleanf("")
// Main ASCII art logo
- logger.Core.Cleanf(" ███████╗████████╗ █████╗ ████████╗██╗ ██████╗ ███╗ ██╗███████╗███████╗██████╗ ███████╗ ███████╗██╗ ██╗██╗")
- logger.Core.Cleanf(" ██╔════╝╚══██╔══╝██╔══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║██╔════╝██╔════╝██╔══██╗██╔════╝ ██╔════╝██║ ██║██║")
- logger.Core.Cleanf(" ███████╗ ██║ ███████║ ██║ ██║██║ ██║██╔██╗ ██║█████╗ █████╗ ██████╔╝███████╗█████╗███████╗██║ ██║██║")
- logger.Core.Cleanf(" ╚════██║ ██║ ██╔══██║ ██║ ██║██║ ██║██║╚██╗██║██╔══╝ ██╔══╝ ██╔══██╗╚════██║╚════╝╚════██║██║ ██║██║")
- logger.Core.Cleanf(" ███████║ ██║ ██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║███████╗███████╗██║ ██║███████║ ███████║╚██████╔╝██║")
- logger.Core.Cleanf(" ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝ ╚══════╝ ╚═════╝ ╚═╝")
+ logger.Core.Cleanf(" ███████╗████████╗███████╗ █████╗ ███╗ ███╗███████╗███████╗██████╗ ██╗ ██╗███████╗██████╗ ██╗ ██╗██╗")
+ logger.Core.Cleanf(" ██╔════╝╚══██╔══╝██╔════╝██╔══██╗████╗ ████║██╔════╝██╔════╝██╔══██╗██║ ██║██╔════╝██╔══██╗██║ ██║██║")
+ logger.Core.Cleanf(" ███████╗ ██║ █████╗ ███████║██╔████╔██║███████╗█████╗ ██████╔╝██║ ██║█████╗ ██████╔╝██║ ██║██║")
+ logger.Core.Cleanf(" ╚════██║ ██║ ██╔══╝ ██╔══██║██║╚██╔╝██║╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██╔══╝ ██╔══██╗██║ ██║██║")
+ logger.Core.Cleanf(" ███████║ ██║ ███████╗██║ ██║██║ ╚═╝ ██║███████║███████╗██║ ██║ ╚████╔╝ ███████╗██║ ██║╚██████╔╝██║")
+ logger.Core.Cleanf(" ╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝")
logger.Core.Cleanf(" ╔═══════════════════════════════════════════════════════════════════════════════════════════════════╗")
- logger.Core.Cleanf(" ║ 🎮 YOUR ONE-STOP SHOP FOR RUNNING A STATIONEERS SERVER 🎮 ║")
+ logger.Core.Cleanf(" ║ 🎮 YOUR ONE-STOP SHOP FOR RUNNING A STEAMCMD SERVER 🎮 ║")
logger.Core.Cleanf(" ║ 🚀 Version: %s 📅 %s 💻 Runtime: %.3s/%s ║",
config.GetVersion(),
time.Now().Format("2006-01-02 15:04"),
From 6e2d56daaf4d069ae44471424eb9a0662f5dfb0c Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 03:31:07 +0200
Subject: [PATCH 40/93] revert unix socket path to /tmp
---
src/api/socket/examples.md | 8 ++++----
src/api/socket/socket-lin.go | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/api/socket/examples.md b/src/api/socket/examples.md
index f3ee2541..bccf7cbe 100644
--- a/src/api/socket/examples.md
+++ b/src/api/socket/examples.md
@@ -5,7 +5,7 @@ This guide explains how to interact with the socket-based `SSUI-API`, which expo
## Overview
The socket server reuses the HTTP routes defined in `routes.go` but serves them over:
-- **Linux**: A Unix socket at `/var/run/ssui.sock`.
+- **Linux**: A Unix socket at `/tmp/ssui.sock`.
- **Windows**: A named pipe at `\\.\pipe\ssui`.
This is similar to how Docker uses `/var/run/docker.sock` for local API access.
@@ -27,7 +27,7 @@ This is similar to how Docker uses `/var/run/docker.sock` for local API access.
Use `curl` with the `--unix-socket` flag to send HTTP requests to the socket. Example for the `/api/v2/settings` endpoint:
```bash
-curl --unix-socket /var/run/ssui.sock http://localhost/api/v2/settings
+curl --unix-socket /tmp/ssui.sock http://localhost/api/v2/settings
```
**Expected Output**: JSON response from the `settings.RetrieveSettings` handler, e.g.:
@@ -110,14 +110,14 @@ All routes from `routes.go` (e.g., `/api/v2/server/start`, `/api/v2/backups`) ar
- **Linux**:
```bash
- curl --unix-socket /var/run/ssui.sock http://localhost/api/v2/backups
+ curl --unix-socket /tmp/ssui.sock http://localhost/api/v2/backups
```
- **Windows**: Edit `$endpoint` in `test_namedpipe.ps1`, e.g., `$endpoint = "/api/v2/backups"`.
For POST requests (e.g., `/api/v2/server/start`), add a JSON payload:
- **Linux**:
```bash
- curl --unix-socket /var/run/ssui.sock -X POST -H "Content-Type: application/json" -d '{"action":"start"}' http://localhost/api/v2/server/start
+ curl --unix-socket /tmp/ssui.sock -X POST -H "Content-Type: application/json" -d '{"action":"start"}' http://localhost/api/v2/server/start
```
- **Windows**: Update the PowerShell script’s `$request`:
```powershell
diff --git a/src/api/socket/socket-lin.go b/src/api/socket/socket-lin.go
index ea1cdb61..76ef5f8d 100644
--- a/src/api/socket/socket-lin.go
+++ b/src/api/socket/socket-lin.go
@@ -15,7 +15,7 @@ import (
"github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
-const socketPath = "/var/run/ssui.sock"
+const socketPath = "/tmp/ssui.sock"
func StartSocketServer(wg *sync.WaitGroup) {
logger.Socket.Info("Starting Unix socket server...")
From 013c1f23e7a380d6cfc784934a340d0e593f17f8 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 03:39:44 +0200
Subject: [PATCH 41/93] renamed UIMod folder to SSUI
---
.docker/Dockerfile | 6 +--
.docker/compose.yml | 2 +-
.gitignore | 16 +++---
.../onboard_bundled/assets/apiinfo.html | 0
.../onboard_bundled/assets/css/apiinfo.css | 0
.../onboard_bundled/assets/css/background.css | 0
.../onboard_bundled/assets/css/base.css | 0
.../onboard_bundled/assets/css/components.css | 0
.../onboard_bundled/assets/css/config.css | 0
.../assets/css/detectionmanager.css | 0
.../onboard_bundled/assets/css/flags.css | 0
.../onboard_bundled/assets/css/home.css | 0
.../assets/css/info-notice.css | 0
.../onboard_bundled/assets/css/mobile.css | 0
.../onboard_bundled/assets/css/popup.css | 0
.../onboard_bundled/assets/css/sscm.css | 0
.../onboard_bundled/assets/css/style.css | 0
.../onboard_bundled/assets/css/tabs.css | 0
.../onboard_bundled/assets/css/variables.css | 0
.../onboard_bundled/assets/favicon.ico | Bin
.../onboard_bundled/assets/flags/de.webp | Bin
.../onboard_bundled/assets/flags/en.webp | Bin
.../onboard_bundled/assets/flags/sv.webp | Bin
.../assets/js/console-manager.js | 0
.../assets/js/detectionmanager.js | 0
.../onboard_bundled/assets/js/main.js | 0
.../onboard_bundled/assets/js/popup.js | 0
.../onboard_bundled/assets/js/server-api.js | 0
.../onboard_bundled/assets/js/sscm.js | 0
.../onboard_bundled/assets/js/ui-utils.js | 0
.../assets/js/world-gen-config.js | 0
.../assets/playerimages/anna.webp | Bin
.../assets/playerimages/dan.webp | Bin
.../assets/playerimages/darragh.webp | Bin
.../assets/playerimages/david.webp | Bin
.../assets/playerimages/dean.webp | Bin
.../assets/playerimages/garrison.webp | Bin
.../assets/playerimages/ivette.webp | Bin
.../assets/playerimages/john.webp | Bin
.../assets/playerimages/julia.webp | Bin
.../assets/playerimages/ove.webp | Bin
.../assets/playerimages/pierre.webp | Bin
.../assets/playerimages/rolf.webp | Bin
.../assets/playerimages/ronald.webp | Bin
.../onboard_bundled/assets/stationeers.webp | Bin
.../detectionmanager/detectionmanager.html | 0
.../onboard_bundled/localization/de-DE.json | 0
.../onboard_bundled/localization/en-US.json | 0
.../onboard_bundled/localization/sv-SE.json | 0
.../onboard_bundled/twoboxform/twoboxform.css | 0
.../twoboxform/twoboxform.html | 0
.../onboard_bundled/twoboxform/twoboxform.js | 0
.../onboard_bundled/ui/config.html | 0
{UIMod => SSUI}/onboard_bundled/ui/index.html | 0
UIMod/onboard_bundled/scripts/autostart.ps1 | 51 ------------------
UIMod/onboard_bundled/scripts/autostart.sh | 3 --
frontend/electron-builder.yml | 4 +-
frontend/package.json | 2 +-
frontend/vite.config.js | 2 +-
server.go | 2 +-
src/api/TwoBoxForm.go | 2 +-
src/api/configpage.go | 2 +-
src/api/detectionmanagerpage.go | 2 +-
src/api/indexpage.go | 2 +-
src/api/routes.go | 6 +--
src/api/svelteui.go | 2 +-
src/cli/runtimecommands.go | 7 +--
src/config/getters.go | 4 +-
src/config/vars.go | 26 ++++-----
src/core/loader/afterstart.go | 2 +-
src/core/loader/helpers.go | 2 +-
src/core/security/tls.go | 2 +-
src/localization/localization.go | 2 +-
src/setup/cleanup.go | 4 +-
src/setup/install.go | 24 ++++-----
75 files changed, 62 insertions(+), 115 deletions(-)
rename {UIMod => SSUI}/onboard_bundled/assets/apiinfo.html (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/apiinfo.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/background.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/base.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/components.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/config.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/detectionmanager.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/flags.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/home.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/info-notice.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/mobile.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/popup.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/sscm.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/style.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/tabs.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/css/variables.css (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/favicon.ico (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/flags/de.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/flags/en.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/flags/sv.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/js/console-manager.js (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/js/detectionmanager.js (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/js/main.js (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/js/popup.js (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/js/server-api.js (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/js/sscm.js (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/js/ui-utils.js (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/js/world-gen-config.js (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/anna.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/dan.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/darragh.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/david.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/dean.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/garrison.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/ivette.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/john.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/julia.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/ove.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/pierre.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/rolf.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/playerimages/ronald.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/assets/stationeers.webp (100%)
rename {UIMod => SSUI}/onboard_bundled/detectionmanager/detectionmanager.html (100%)
rename {UIMod => SSUI}/onboard_bundled/localization/de-DE.json (100%)
rename {UIMod => SSUI}/onboard_bundled/localization/en-US.json (100%)
rename {UIMod => SSUI}/onboard_bundled/localization/sv-SE.json (100%)
rename {UIMod => SSUI}/onboard_bundled/twoboxform/twoboxform.css (100%)
rename {UIMod => SSUI}/onboard_bundled/twoboxform/twoboxform.html (100%)
rename {UIMod => SSUI}/onboard_bundled/twoboxform/twoboxform.js (100%)
rename {UIMod => SSUI}/onboard_bundled/ui/config.html (100%)
rename {UIMod => SSUI}/onboard_bundled/ui/index.html (100%)
delete mode 100644 UIMod/onboard_bundled/scripts/autostart.ps1
delete mode 100644 UIMod/onboard_bundled/scripts/autostart.sh
diff --git a/.docker/Dockerfile b/.docker/Dockerfile
index 9bd45c49..e6bf47a4 100644
--- a/.docker/Dockerfile
+++ b/.docker/Dockerfile
@@ -10,7 +10,7 @@ COPY ./frontend/package.json ./frontend/package-lock.json* ./
RUN npm install
COPY ./frontend/ ./
-# Build Svelte UI into ../UIMod/onboard_bundled/v2 (as configured in vite.config.js)
+# Build Svelte UI into ../SSUI/onboard_bundled/v2 (as configured in vite.config.js)
RUN npm run build
############
@@ -26,9 +26,9 @@ RUN go mod download
COPY . ./
-COPY --from=frontend-builder /src/UIMod /src/UIMod
+COPY --from=frontend-builder /src/SSUI /src/SSUI
-# Build the server (embed will include UIMod/* at build time)
+# Build the server (embed will include SSUI/* at build time)
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /out/StationeersServerUI ./server.go
############
diff --git a/.docker/compose.yml b/.docker/compose.yml
index a57d4413..920705af 100644
--- a/.docker/compose.yml
+++ b/.docker/compose.yml
@@ -24,7 +24,7 @@ services:
volumes:
- app-data:/app
- ./saves:/app/saves:rw
- - ./UIMod:/app/UIMod:rw
+ - ./SSUI:/app/SSUI:rw
restart: unless-stopped
volumes:
diff --git a/.gitignore b/.gitignore
index afe048f0..cb39947c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,12 +16,12 @@ rocketstation_DedicatedServer*
/saves/*
.github/workflows/nightly-sync.yml
repos.md
-UIMod/*.pem
+SSUI/*.pem
build/StationeersServerControl*
Blacklist.txt
-UIMod/detectionmanager/customdetections.json
-UIMod/tls/cert.pem
-UIMod/tls/key.pem
+SSUI/detectionmanager/customdetections.json
+SSUI/tls/cert.pem
+SSUI/tls/key.pem
steamapps/**
steamcmd/**
Steam/**
@@ -34,14 +34,14 @@ BepInEx/**
run_bepinex.sh
debug.log
modconfig.xml
-UIMod/config/config.json
+SSUI/config/config.json
C:/custom/file.txt
-UIMod/config/customdetections.json
+SSUI/config/customdetections.json
winhttp.dll
autostart*
__debug_bin*
frontend/node_modules
frontend/dist
frontend/build
-UIMod/onboard_bundled/v2
-UIMod/runfiles/**
+SSUI/onboard_bundled/v2
+SSUI/runfiles/**
diff --git a/UIMod/onboard_bundled/assets/apiinfo.html b/SSUI/onboard_bundled/assets/apiinfo.html
similarity index 100%
rename from UIMod/onboard_bundled/assets/apiinfo.html
rename to SSUI/onboard_bundled/assets/apiinfo.html
diff --git a/UIMod/onboard_bundled/assets/css/apiinfo.css b/SSUI/onboard_bundled/assets/css/apiinfo.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/apiinfo.css
rename to SSUI/onboard_bundled/assets/css/apiinfo.css
diff --git a/UIMod/onboard_bundled/assets/css/background.css b/SSUI/onboard_bundled/assets/css/background.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/background.css
rename to SSUI/onboard_bundled/assets/css/background.css
diff --git a/UIMod/onboard_bundled/assets/css/base.css b/SSUI/onboard_bundled/assets/css/base.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/base.css
rename to SSUI/onboard_bundled/assets/css/base.css
diff --git a/UIMod/onboard_bundled/assets/css/components.css b/SSUI/onboard_bundled/assets/css/components.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/components.css
rename to SSUI/onboard_bundled/assets/css/components.css
diff --git a/UIMod/onboard_bundled/assets/css/config.css b/SSUI/onboard_bundled/assets/css/config.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/config.css
rename to SSUI/onboard_bundled/assets/css/config.css
diff --git a/UIMod/onboard_bundled/assets/css/detectionmanager.css b/SSUI/onboard_bundled/assets/css/detectionmanager.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/detectionmanager.css
rename to SSUI/onboard_bundled/assets/css/detectionmanager.css
diff --git a/UIMod/onboard_bundled/assets/css/flags.css b/SSUI/onboard_bundled/assets/css/flags.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/flags.css
rename to SSUI/onboard_bundled/assets/css/flags.css
diff --git a/UIMod/onboard_bundled/assets/css/home.css b/SSUI/onboard_bundled/assets/css/home.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/home.css
rename to SSUI/onboard_bundled/assets/css/home.css
diff --git a/UIMod/onboard_bundled/assets/css/info-notice.css b/SSUI/onboard_bundled/assets/css/info-notice.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/info-notice.css
rename to SSUI/onboard_bundled/assets/css/info-notice.css
diff --git a/UIMod/onboard_bundled/assets/css/mobile.css b/SSUI/onboard_bundled/assets/css/mobile.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/mobile.css
rename to SSUI/onboard_bundled/assets/css/mobile.css
diff --git a/UIMod/onboard_bundled/assets/css/popup.css b/SSUI/onboard_bundled/assets/css/popup.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/popup.css
rename to SSUI/onboard_bundled/assets/css/popup.css
diff --git a/UIMod/onboard_bundled/assets/css/sscm.css b/SSUI/onboard_bundled/assets/css/sscm.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/sscm.css
rename to SSUI/onboard_bundled/assets/css/sscm.css
diff --git a/UIMod/onboard_bundled/assets/css/style.css b/SSUI/onboard_bundled/assets/css/style.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/style.css
rename to SSUI/onboard_bundled/assets/css/style.css
diff --git a/UIMod/onboard_bundled/assets/css/tabs.css b/SSUI/onboard_bundled/assets/css/tabs.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/tabs.css
rename to SSUI/onboard_bundled/assets/css/tabs.css
diff --git a/UIMod/onboard_bundled/assets/css/variables.css b/SSUI/onboard_bundled/assets/css/variables.css
similarity index 100%
rename from UIMod/onboard_bundled/assets/css/variables.css
rename to SSUI/onboard_bundled/assets/css/variables.css
diff --git a/UIMod/onboard_bundled/assets/favicon.ico b/SSUI/onboard_bundled/assets/favicon.ico
similarity index 100%
rename from UIMod/onboard_bundled/assets/favicon.ico
rename to SSUI/onboard_bundled/assets/favicon.ico
diff --git a/UIMod/onboard_bundled/assets/flags/de.webp b/SSUI/onboard_bundled/assets/flags/de.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/flags/de.webp
rename to SSUI/onboard_bundled/assets/flags/de.webp
diff --git a/UIMod/onboard_bundled/assets/flags/en.webp b/SSUI/onboard_bundled/assets/flags/en.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/flags/en.webp
rename to SSUI/onboard_bundled/assets/flags/en.webp
diff --git a/UIMod/onboard_bundled/assets/flags/sv.webp b/SSUI/onboard_bundled/assets/flags/sv.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/flags/sv.webp
rename to SSUI/onboard_bundled/assets/flags/sv.webp
diff --git a/UIMod/onboard_bundled/assets/js/console-manager.js b/SSUI/onboard_bundled/assets/js/console-manager.js
similarity index 100%
rename from UIMod/onboard_bundled/assets/js/console-manager.js
rename to SSUI/onboard_bundled/assets/js/console-manager.js
diff --git a/UIMod/onboard_bundled/assets/js/detectionmanager.js b/SSUI/onboard_bundled/assets/js/detectionmanager.js
similarity index 100%
rename from UIMod/onboard_bundled/assets/js/detectionmanager.js
rename to SSUI/onboard_bundled/assets/js/detectionmanager.js
diff --git a/UIMod/onboard_bundled/assets/js/main.js b/SSUI/onboard_bundled/assets/js/main.js
similarity index 100%
rename from UIMod/onboard_bundled/assets/js/main.js
rename to SSUI/onboard_bundled/assets/js/main.js
diff --git a/UIMod/onboard_bundled/assets/js/popup.js b/SSUI/onboard_bundled/assets/js/popup.js
similarity index 100%
rename from UIMod/onboard_bundled/assets/js/popup.js
rename to SSUI/onboard_bundled/assets/js/popup.js
diff --git a/UIMod/onboard_bundled/assets/js/server-api.js b/SSUI/onboard_bundled/assets/js/server-api.js
similarity index 100%
rename from UIMod/onboard_bundled/assets/js/server-api.js
rename to SSUI/onboard_bundled/assets/js/server-api.js
diff --git a/UIMod/onboard_bundled/assets/js/sscm.js b/SSUI/onboard_bundled/assets/js/sscm.js
similarity index 100%
rename from UIMod/onboard_bundled/assets/js/sscm.js
rename to SSUI/onboard_bundled/assets/js/sscm.js
diff --git a/UIMod/onboard_bundled/assets/js/ui-utils.js b/SSUI/onboard_bundled/assets/js/ui-utils.js
similarity index 100%
rename from UIMod/onboard_bundled/assets/js/ui-utils.js
rename to SSUI/onboard_bundled/assets/js/ui-utils.js
diff --git a/UIMod/onboard_bundled/assets/js/world-gen-config.js b/SSUI/onboard_bundled/assets/js/world-gen-config.js
similarity index 100%
rename from UIMod/onboard_bundled/assets/js/world-gen-config.js
rename to SSUI/onboard_bundled/assets/js/world-gen-config.js
diff --git a/UIMod/onboard_bundled/assets/playerimages/anna.webp b/SSUI/onboard_bundled/assets/playerimages/anna.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/anna.webp
rename to SSUI/onboard_bundled/assets/playerimages/anna.webp
diff --git a/UIMod/onboard_bundled/assets/playerimages/dan.webp b/SSUI/onboard_bundled/assets/playerimages/dan.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/dan.webp
rename to SSUI/onboard_bundled/assets/playerimages/dan.webp
diff --git a/UIMod/onboard_bundled/assets/playerimages/darragh.webp b/SSUI/onboard_bundled/assets/playerimages/darragh.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/darragh.webp
rename to SSUI/onboard_bundled/assets/playerimages/darragh.webp
diff --git a/UIMod/onboard_bundled/assets/playerimages/david.webp b/SSUI/onboard_bundled/assets/playerimages/david.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/david.webp
rename to SSUI/onboard_bundled/assets/playerimages/david.webp
diff --git a/UIMod/onboard_bundled/assets/playerimages/dean.webp b/SSUI/onboard_bundled/assets/playerimages/dean.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/dean.webp
rename to SSUI/onboard_bundled/assets/playerimages/dean.webp
diff --git a/UIMod/onboard_bundled/assets/playerimages/garrison.webp b/SSUI/onboard_bundled/assets/playerimages/garrison.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/garrison.webp
rename to SSUI/onboard_bundled/assets/playerimages/garrison.webp
diff --git a/UIMod/onboard_bundled/assets/playerimages/ivette.webp b/SSUI/onboard_bundled/assets/playerimages/ivette.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/ivette.webp
rename to SSUI/onboard_bundled/assets/playerimages/ivette.webp
diff --git a/UIMod/onboard_bundled/assets/playerimages/john.webp b/SSUI/onboard_bundled/assets/playerimages/john.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/john.webp
rename to SSUI/onboard_bundled/assets/playerimages/john.webp
diff --git a/UIMod/onboard_bundled/assets/playerimages/julia.webp b/SSUI/onboard_bundled/assets/playerimages/julia.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/julia.webp
rename to SSUI/onboard_bundled/assets/playerimages/julia.webp
diff --git a/UIMod/onboard_bundled/assets/playerimages/ove.webp b/SSUI/onboard_bundled/assets/playerimages/ove.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/ove.webp
rename to SSUI/onboard_bundled/assets/playerimages/ove.webp
diff --git a/UIMod/onboard_bundled/assets/playerimages/pierre.webp b/SSUI/onboard_bundled/assets/playerimages/pierre.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/pierre.webp
rename to SSUI/onboard_bundled/assets/playerimages/pierre.webp
diff --git a/UIMod/onboard_bundled/assets/playerimages/rolf.webp b/SSUI/onboard_bundled/assets/playerimages/rolf.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/rolf.webp
rename to SSUI/onboard_bundled/assets/playerimages/rolf.webp
diff --git a/UIMod/onboard_bundled/assets/playerimages/ronald.webp b/SSUI/onboard_bundled/assets/playerimages/ronald.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/playerimages/ronald.webp
rename to SSUI/onboard_bundled/assets/playerimages/ronald.webp
diff --git a/UIMod/onboard_bundled/assets/stationeers.webp b/SSUI/onboard_bundled/assets/stationeers.webp
similarity index 100%
rename from UIMod/onboard_bundled/assets/stationeers.webp
rename to SSUI/onboard_bundled/assets/stationeers.webp
diff --git a/UIMod/onboard_bundled/detectionmanager/detectionmanager.html b/SSUI/onboard_bundled/detectionmanager/detectionmanager.html
similarity index 100%
rename from UIMod/onboard_bundled/detectionmanager/detectionmanager.html
rename to SSUI/onboard_bundled/detectionmanager/detectionmanager.html
diff --git a/UIMod/onboard_bundled/localization/de-DE.json b/SSUI/onboard_bundled/localization/de-DE.json
similarity index 100%
rename from UIMod/onboard_bundled/localization/de-DE.json
rename to SSUI/onboard_bundled/localization/de-DE.json
diff --git a/UIMod/onboard_bundled/localization/en-US.json b/SSUI/onboard_bundled/localization/en-US.json
similarity index 100%
rename from UIMod/onboard_bundled/localization/en-US.json
rename to SSUI/onboard_bundled/localization/en-US.json
diff --git a/UIMod/onboard_bundled/localization/sv-SE.json b/SSUI/onboard_bundled/localization/sv-SE.json
similarity index 100%
rename from UIMod/onboard_bundled/localization/sv-SE.json
rename to SSUI/onboard_bundled/localization/sv-SE.json
diff --git a/UIMod/onboard_bundled/twoboxform/twoboxform.css b/SSUI/onboard_bundled/twoboxform/twoboxform.css
similarity index 100%
rename from UIMod/onboard_bundled/twoboxform/twoboxform.css
rename to SSUI/onboard_bundled/twoboxform/twoboxform.css
diff --git a/UIMod/onboard_bundled/twoboxform/twoboxform.html b/SSUI/onboard_bundled/twoboxform/twoboxform.html
similarity index 100%
rename from UIMod/onboard_bundled/twoboxform/twoboxform.html
rename to SSUI/onboard_bundled/twoboxform/twoboxform.html
diff --git a/UIMod/onboard_bundled/twoboxform/twoboxform.js b/SSUI/onboard_bundled/twoboxform/twoboxform.js
similarity index 100%
rename from UIMod/onboard_bundled/twoboxform/twoboxform.js
rename to SSUI/onboard_bundled/twoboxform/twoboxform.js
diff --git a/UIMod/onboard_bundled/ui/config.html b/SSUI/onboard_bundled/ui/config.html
similarity index 100%
rename from UIMod/onboard_bundled/ui/config.html
rename to SSUI/onboard_bundled/ui/config.html
diff --git a/UIMod/onboard_bundled/ui/index.html b/SSUI/onboard_bundled/ui/index.html
similarity index 100%
rename from UIMod/onboard_bundled/ui/index.html
rename to SSUI/onboard_bundled/ui/index.html
diff --git a/UIMod/onboard_bundled/scripts/autostart.ps1 b/UIMod/onboard_bundled/scripts/autostart.ps1
deleted file mode 100644
index e7e67420..00000000
--- a/UIMod/onboard_bundled/scripts/autostart.ps1
+++ /dev/null
@@ -1,51 +0,0 @@
-# Path to this script file
-$scriptPath = $MyInvocation.MyCommand.Path
-
-# Path to user's startup folder
-$startupFolder = [Environment]::GetFolderPath("Startup")
-
-# Shortcut name in startup
-$shortcutName = "Start-StationeersServerUI.lnk"
-$shortcutPath = Join-Path $startupFolder $shortcutName
-
-# Function to create shortcut
-function New-Shortcut {
- param (
- [string]$targetPath,
- [string]$shortcutPath
- )
-
- $shell = New-Object -ComObject WScript.Shell
- $shortcut = $shell.CreateShortcut($shortcutPath)
- # Set the target to powershell.exe and pass the script as an argument
- $shortcut.TargetPath = "powershell.exe"
- $shortcut.Arguments = "-NoProfile -ExecutionPolicy Bypass -File `"$targetPath`""
- $shortcut.WorkingDirectory = Split-Path $targetPath
- $shortcut.Save()
-}
-
-# Check if shortcut exists in startup folder
-if (-not (Test-Path $shortcutPath)) {
- Write-Output "Shortcut not found in Startup folder. Creating shortcut to enable autostart..."
- New-Shortcut -targetPath $scriptPath -shortcutPath $shortcutPath
- Write-Output "Shortcut created. You may need to restart your session to apply autostart."
- Read-Host "Press Enter to exit"
- exit
-}
-
-# Folder where the executables are located (folder of this script)
-$exeFolder = Split-Path -Parent $scriptPath
-
-# Find latest StationeersServerControl*.exe by last write time as old executables are prefixed _old anyway
-$latestExe = Get-ChildItem -Path $exeFolder -Filter "StationeersServerControl*.exe" |
- Sort-Object LastWriteTime -Descending |
- Select-Object -First 1
-
-if ($null -eq $latestExe) {
- Write-Error "No executable found to start in $exeFolder"
- Read-Host "Press Enter to exit"
- exit 1
-}
-
-Write-Output "Starting $($latestExe.FullName)..."
-Start-Process -FilePath $latestExe.FullName
\ No newline at end of file
diff --git a/UIMod/onboard_bundled/scripts/autostart.sh b/UIMod/onboard_bundled/scripts/autostart.sh
deleted file mode 100644
index b3d220b4..00000000
--- a/UIMod/onboard_bundled/scripts/autostart.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-# not yet implemented
\ No newline at end of file
diff --git a/frontend/electron-builder.yml b/frontend/electron-builder.yml
index 5b261e84..bc501d99 100644
--- a/frontend/electron-builder.yml
+++ b/frontend/electron-builder.yml
@@ -9,8 +9,8 @@ files:
- "package.json"
- "main.cjs"
extraResources:
- - from: "../UIMod/onboard_bundled/v2/"
- to: "UIMod/onboard_bundled/v2/"
+ - from: "../SSUI/onboard_bundled/v2/"
+ to: "SSUI/onboard_bundled/v2/"
win:
target: nsis
icon: ../media/logo.png
diff --git a/frontend/package.json b/frontend/package.json
index eb5f3888..e0e63784 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -2,7 +2,7 @@
"name": "steamserverui",
"main": "main.cjs",
"private": true,
- "version": "v5.7.1",
+ "version": "v7.0.1",
"description": "Svelte UI Interface for Steam Server UI (SSUI) Backend",
"author": {
"name": "JacksonTheMaster",
diff --git a/frontend/vite.config.js b/frontend/vite.config.js
index 90f788e8..79f46f47 100644
--- a/frontend/vite.config.js
+++ b/frontend/vite.config.js
@@ -5,7 +5,7 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [svelte()],
build: {
- outDir: '../UIMod/onboard_bundled/v2', // Change output directory to ../dist
+ outDir: '../SSUI/onboard_bundled/v2', // Change output directory to ../dist
rollupOptions: {
output: {
// Set the name of the JS bundle
diff --git a/server.go b/server.go
index c6f72e8b..b9abcf5d 100644
--- a/server.go
+++ b/server.go
@@ -32,7 +32,7 @@ import (
"github.com/SteamServerUI/SteamServerUI/v7/src/setup"
)
-//go:embed UIMod/onboard_bundled
+//go:embed SSUI/onboard_bundled
var v1uiFS embed.FS
func main() {
diff --git a/src/api/TwoBoxForm.go b/src/api/TwoBoxForm.go
index 7ef16e2b..86ff770e 100644
--- a/src/api/TwoBoxForm.go
+++ b/src/api/TwoBoxForm.go
@@ -58,7 +58,7 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) {
Steps []Step
}
- twoboxformAssetsFS, err := fs.Sub(config.GetV1UIFS(), "UIMod/onboard_bundled/twoboxform")
+ twoboxformAssetsFS, err := fs.Sub(config.GetV1UIFS(), "SSUI/onboard_bundled/twoboxform")
if err != nil {
logger.Web.Error("Failed to get bundled FS")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
diff --git a/src/api/configpage.go b/src/api/configpage.go
index 870c6cbe..cef76a49 100644
--- a/src/api/configpage.go
+++ b/src/api/configpage.go
@@ -13,7 +13,7 @@ import (
func ServeConfigPage(w http.ResponseWriter, r *http.Request) {
- htmlFS, err := fs.Sub(config.V1UIFS, "UIMod/onboard_bundled/ui")
+ htmlFS, err := fs.Sub(config.V1UIFS, "SSUI/onboard_bundled/ui")
if err != nil {
http.Error(w, "Error accessing Virt FS: "+err.Error(), http.StatusInternalServerError)
return
diff --git a/src/api/detectionmanagerpage.go b/src/api/detectionmanagerpage.go
index 19ff5a5b..189658f5 100644
--- a/src/api/detectionmanagerpage.go
+++ b/src/api/detectionmanagerpage.go
@@ -10,7 +10,7 @@ import (
)
func ServeDetectionManager(w http.ResponseWriter, r *http.Request) {
- detectionmanagerFS, err := fs.Sub(config.V1UIFS, "UIMod/onboard_bundled/detectionmanager")
+ detectionmanagerFS, err := fs.Sub(config.V1UIFS, "SSUI/onboard_bundled/detectionmanager")
if err != nil {
http.Error(w, "Error accessing Virt FS: "+err.Error(), http.StatusInternalServerError)
return
diff --git a/src/api/indexpage.go b/src/api/indexpage.go
index db6adcdf..305fb018 100644
--- a/src/api/indexpage.go
+++ b/src/api/indexpage.go
@@ -11,7 +11,7 @@ import (
)
func ServeIndex(w http.ResponseWriter, r *http.Request) {
- htmlFS, err := fs.Sub(config.V1UIFS, "UIMod/onboard_bundled/ui")
+ htmlFS, err := fs.Sub(config.V1UIFS, "SSUI/onboard_bundled/ui")
if err != nil {
http.Error(w, "Error accessing Virt FS: "+err.Error(), http.StatusInternalServerError)
return
diff --git a/src/api/routes.go b/src/api/routes.go
index d6ae6d11..1b718a1a 100644
--- a/src/api/routes.go
+++ b/src/api/routes.go
@@ -17,7 +17,7 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
mux := http.NewServeMux() // Use a mux to apply middleware globally
// Unprotected auth routes
- twoboxformAssetsFS, _ := fs.Sub(config.GetV1UIFS(), "UIMod/onboard_bundled/twoboxform")
+ twoboxformAssetsFS, _ := fs.Sub(config.GetV1UIFS(), "SSUI/onboard_bundled/twoboxform")
mux.Handle("/twoboxform/", http.StripPrefix("/twoboxform/", http.FileServer(http.FS(twoboxformAssetsFS))))
mux.HandleFunc("/auth/login", LoginHandler) // Token issuer
mux.HandleFunc("/auth/logout", LogoutHandler)
@@ -26,7 +26,7 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
// Protected routes (wrapped with middleware)
protectedMux := http.NewServeMux()
- legacyAssetsFS, _ := fs.Sub(config.GetV1UIFS(), "UIMod/onboard_bundled/assets")
+ legacyAssetsFS, _ := fs.Sub(config.GetV1UIFS(), "SSUI/onboard_bundled/assets")
protectedMux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(legacyAssetsFS))))
protectedMux.HandleFunc("/config", ServeConfigPage)
@@ -35,7 +35,7 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
// --- SVELTE UI ---
protectedMux.HandleFunc("/v2", ServeSvelteUI)
- svelteAssetsFS, _ := fs.Sub(config.V1UIFS, "UIMod/onboard_bundled/v2/assets")
+ svelteAssetsFS, _ := fs.Sub(config.V1UIFS, "SSUI/onboard_bundled/v2/assets")
protectedMux.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.FS(svelteAssetsFS))))
protectedMux.HandleFunc("/api/v2/loader/reloadbackend", HandleReloadAll)
diff --git a/src/api/svelteui.go b/src/api/svelteui.go
index 2475dc95..1bfd3631 100644
--- a/src/api/svelteui.go
+++ b/src/api/svelteui.go
@@ -15,7 +15,7 @@ import (
var reloadMu sync.Mutex
func ServeSvelteUI(w http.ResponseWriter, r *http.Request) {
- htmlFS, err := fs.Sub(config.V1UIFS, "UIMod/onboard_bundled/v2")
+ htmlFS, err := fs.Sub(config.V1UIFS, "SSUI/onboard_bundled/v2")
if err != nil {
http.Error(w, "Error accessing Svelte UI: "+err.Error(), http.StatusInternalServerError)
return
diff --git a/src/cli/runtimecommands.go b/src/cli/runtimecommands.go
index 8c617127..2c10e93b 100644
--- a/src/cli/runtimecommands.go
+++ b/src/cli/runtimecommands.go
@@ -14,6 +14,7 @@ import (
"path/filepath"
"runtime"
"sort"
+
"strings"
"sync"
"time"
@@ -253,7 +254,7 @@ func supportPackage() {
zw := zip.NewWriter(zipFile)
defer zw.Close()
- filepath.Walk("./UIMod/logs", func(p string, i os.FileInfo, err error) error {
+ filepath.Walk("./SSUI/logs", func(p string, i os.FileInfo, err error) error {
if err != nil || i.IsDir() {
return nil
}
@@ -264,7 +265,7 @@ func supportPackage() {
return nil
})
- configData, _ := os.ReadFile("./UIMod/config/config.json")
+ configData, _ := os.ReadFile("./SSUI/config/config.json")
var configMap map[string]interface{}
if err := json.Unmarshal(configData, &configMap); err != nil {
@@ -284,7 +285,7 @@ func supportPackage() {
}
// Write sanitized config to zip
- w, _ := zw.Create("UIMod/config/config.json")
+ w, _ := zw.Create("SSUI/config/config.json")
if _, err := w.Write(sanitizedConfig); err != nil {
logger.Core.Error("Failed to write sanitized config to support package")
diff --git a/src/config/getters.go b/src/config/getters.go
index 80b57d0b..ba951bea 100644
--- a/src/config/getters.go
+++ b/src/config/getters.go
@@ -436,10 +436,10 @@ func GetTLSKeyPath() string {
return TLSKeyPath
}
-func GetUIModFolder() string {
+func GetSSUIFolder() string {
ConfigMu.RLock()
defer ConfigMu.RUnlock()
- return UIModFolder
+ return SSUIFolder
}
func GetMaxSSEConnections() int {
diff --git a/src/config/vars.go b/src/config/vars.go
index 3b75400a..e9bcb93b 100644
--- a/src/config/vars.go
+++ b/src/config/vars.go
@@ -137,21 +137,21 @@ var (
// File paths
var (
- TLSCertPath = "./UIMod/tls/cert.pem"
- TLSKeyPath = "./UIMod/tls/key.pem"
- ConfigPath = "./UIMod/config/config.json"
- CustomDetectionsFilePath = "./UIMod/config/customdetections.json"
- LogFolder = "./UIMod/logs/"
- UIModFolder = "./UIMod/"
- TwoBoxFormFolder = "./UIMod/twoboxform/"
- ConfigHtmlPath = "./UIMod/ui/config.html"
- DetectionManagerHtmlPath = "./UIMod/ui/detectionmanager.html"
- TwoBoxFormHtmlPath = "./UIMod/twoboxform/twoboxform.html"
- IndexHtmlPath = "./UIMod/ui/index.html"
- SSCMWebDir = "./UIMod/sscm/"
+ TLSCertPath = "./SSUI/tls/cert.pem"
+ TLSKeyPath = "./SSUI/tls/key.pem"
+ ConfigPath = "./SSUI/config/config.json"
+ CustomDetectionsFilePath = "./SSUI/config/customdetections.json"
+ LogFolder = "./SSUI/logs/"
+ SSUIFolder = "./SSUI/"
+ TwoBoxFormFolder = "./SSUI/twoboxform/"
+ ConfigHtmlPath = "./SSUI/ui/config.html"
+ DetectionManagerHtmlPath = "./SSUI/ui/detectionmanager.html"
+ TwoBoxFormHtmlPath = "./SSUI/twoboxform/twoboxform.html"
+ IndexHtmlPath = "./SSUI/ui/index.html"
+ SSCMWebDir = "./SSUI/sscm/"
SSCMFilePath = "./BepInEx/plugins/SSCM/SSCM.socket"
SSCMPluginDir = "./BepInEx/plugins/SSCM/"
- RunFilesFolder = "./UIMod/runfiles/"
+ RunFilesFolder = "./SSUI/runfiles/"
)
// Bundled Assets
diff --git a/src/core/loader/afterstart.go b/src/core/loader/afterstart.go
index b5dc2d51..a0d21007 100644
--- a/src/core/loader/afterstart.go
+++ b/src/core/loader/afterstart.go
@@ -14,7 +14,7 @@ func AfterStartComplete(wg *sync.WaitGroup) {
wg.Add(1)
defer wg.Done()
config.SetSaveConfig() // Save config after startup through setters
- err := setup.CleanUpOldUIModFolderFiles()
+ err := setup.CleanUpOldSSUIFolderFiles()
if err != nil {
logger.Core.Error("AfterStartComplete: Failed to clean up old pre-v5.5 UI mod folder files: " + err.Error())
}
diff --git a/src/core/loader/helpers.go b/src/core/loader/helpers.go
index 540489c7..5cff29b7 100644
--- a/src/core/loader/helpers.go
+++ b/src/core/loader/helpers.go
@@ -143,7 +143,7 @@ func PrintConfigDetails(logLevel ...string) {
ui := map[string]string{
"SSUIIdentifier": config.GetSSUIIdentifier(),
"SSUIWebPort": config.GetSSUIWebPort(),
- "UIModFolder": config.GetUIModFolder(),
+ "SSUIFolder": config.GetSSUIFolder(),
"MaxSSEConnections": fmt.Sprintf("%d", config.GetMaxSSEConnections()),
"SSEMessageBufferSize": fmt.Sprintf("%d", config.GetSSEMessageBufferSize()),
}
diff --git a/src/core/security/tls.go b/src/core/security/tls.go
index e478ef1e..5dcae2b4 100644
--- a/src/core/security/tls.go
+++ b/src/core/security/tls.go
@@ -31,7 +31,7 @@ func EnsureTLSCerts() error {
keyExists := fileExists(keyPath)
logger.Security.Debug(fmt.Sprintf("Cert exists: %t, Key exists: %t", certExists, keyExists))
- tlsDir := config.GetUIModFolder() + "tls/"
+ tlsDir := config.GetSSUIFolder() + "tls/"
if _, err := os.Stat(tlsDir); os.IsNotExist(err) {
logger.Security.Debug("TLS directory doesn't exist, creating...")
diff --git a/src/localization/localization.go b/src/localization/localization.go
index 7a0e417d..29ad4bcb 100644
--- a/src/localization/localization.go
+++ b/src/localization/localization.go
@@ -32,7 +32,7 @@ func loadTranslations() {
// Clear existing translations
translations = make(map[string]map[string]string)
- virtFS, err := fs.Sub(config.V1UIFS, "UIMod/onboard_bundled/localization")
+ virtFS, err := fs.Sub(config.V1UIFS, "SSUI/onboard_bundled/localization")
if err != nil {
logger.Localization.Error("Failed to access virtual filesystem: " + err.Error())
return
diff --git a/src/setup/cleanup.go b/src/setup/cleanup.go
index 6ef81c57..91ef45d6 100644
--- a/src/setup/cleanup.go
+++ b/src/setup/cleanup.go
@@ -11,8 +11,8 @@ import (
"github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
-func CleanUpOldUIModFolderFiles() error {
- uiModFolder := config.GetUIModFolder()
+func CleanUpOldSSUIFolderFiles() error {
+ uiModFolder := config.GetSSUIFolder()
customdetectionsSourceFile := filepath.Join(uiModFolder, "detectionmanager", "customdetections.json")
customdetectionsDestinationFile := config.GetCustomDetectionsFilePath()
oldUiFolder := filepath.Join(uiModFolder, "ui") // used to test if we need clean up from a structure before v5.5 (since we now have embedded assets)
diff --git a/src/setup/install.go b/src/setup/install.go
index 6cd8fb94..9200c949 100644
--- a/src/setup/install.go
+++ b/src/setup/install.go
@@ -31,10 +31,10 @@ func Install(wg *sync.WaitGroup) {
logger.Install.Error("❌Update check went sideways: " + err.Error())
}
- // Step 1: Check and download the UIMod folder contents
- logger.Install.Debug("🔄Checking UIMod folder...")
- CheckAndDownloadUIMod()
- logger.Install.Debug("✅UIMod folder setup complete.")
+ // Step 1: Check and download the SSUI folder contents
+ logger.Install.Debug("🔄Checking SSUI folder...")
+ CheckAndDownloadSSUI()
+ logger.Install.Debug("✅SSUI folder setup complete.")
// Step 2: Check for Blacklist.txt and create it if it doesn't exist
logger.Install.Info("🔄Checking for Blacklist.txt...")
checkAndCreateBlacklist()
@@ -42,10 +42,10 @@ func Install(wg *sync.WaitGroup) {
logger.Install.Info("✅Setup complete!")
}
-func CheckAndDownloadUIMod() {
- uiModDir := config.GetUIModFolder()
- configDir := config.GetUIModFolder() + "config/"
- tlsDir := config.GetUIModFolder() + "tls/"
+func CheckAndDownloadSSUI() {
+ uiModDir := config.GetSSUIFolder()
+ configDir := config.GetSSUIFolder() + "config/"
+ tlsDir := config.GetSSUIFolder() + "tls/"
requiredDirs := []string{uiModDir, configDir}
@@ -62,7 +62,7 @@ func CheckAndDownloadUIMod() {
// NOTE: Now empty as files are now embedded in the executable. Kept this structure for future use.
// UI - commented out since files are embedded, left here for reference in case we need this funcitonality again
- // "ui/config.html": "https://raw.githubusercontent.com/SteamServerUI/SteamServerUI/{branch}/UIMod/ui/config.html",
+ // "ui/config.html": "https://raw.githubusercontent.com/SteamServerUI/SteamServerUI/{branch}/SSUI/ui/config.html",
}
createRequiredDirs(requiredDirs)
@@ -88,7 +88,7 @@ func CheckAndDownloadUIMod() {
logger.Install.Debug(fmt.Sprintf("IsUpdateEnabled: %v", config.GetIsUpdateEnabled()))
logger.Install.Debug(fmt.Sprintf("IsFirstTimeSetup: %v", config.GetIsFirstTimeSetup()))
if config.GetIsUpdateEnabled() {
- logger.Install.Info("🔍Validating UIMod files for updates...")
+ logger.Install.Info("🔍Validating SSUI files for updates...")
if config.GetBranch() == "release" || config.GetBranch() == "Release" {
downloadBranch = "main"
updateFilesIfDifferent(files)
@@ -97,7 +97,7 @@ func CheckAndDownloadUIMod() {
updateFilesIfDifferent(files)
}
} else {
- logger.Install.Info("♻️Folder ./UIMod already exists. Updates disabled, skipping validation.")
+ logger.Install.Info("♻️Folder ./SSUI already exists. Updates disabled, skipping validation.")
}
}
}
@@ -163,7 +163,7 @@ func checkAndUpdateFile(filepath, url string) {
}
// Extract the necessary parts from URL to build the API call
- // Example URL: https://raw.githubusercontent.com/JacksonTheMaster/StationeersServerUI/main/UIMod/index.html
+ // Example URL: https://raw.githubusercontent.com/JacksonTheMaster/StationeersServerUI/main/SSUI/index.html
urlParts := strings.Split(url, "/")
if len(urlParts) < 7 {
logger.Install.Error("❌Invalid URL format: " + url)
From 35f411aba26df1e1dfbb3f918c479873c6defee6 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster <81807824+JacksonTheMaster@users.noreply.github.com>
Date: Sat, 11 Oct 2025 03:45:32 +0200
Subject: [PATCH 42/93] fix github workflows after renames
---
.github/workflows/auto-release.yaml | 4 ++--
.github/workflows/test-build.yml | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/auto-release.yaml b/.github/workflows/auto-release.yaml
index b36a7157..5252c402 100644
--- a/.github/workflows/auto-release.yaml
+++ b/.github/workflows/auto-release.yaml
@@ -39,7 +39,7 @@ jobs:
- name: Verify build output
run: |
cd build
- if ls StationeersServerControl*; then
+ if ls SteamServerUI*; then
echo "Build succeeded: Executable found."
else
echo "Build failed: No executable found."
@@ -108,7 +108,7 @@ jobs:
prerelease: false
tag_name: ${{ steps.check_tag.outputs.tag }}
name: "Release ${{ steps.check_tag.outputs.tag }}"
- files: ./build/StationeersServerControl*
+ files: ./build/SteamServerUI*
fail_on_unmatched_files: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml
index e26bd219..10f162f3 100644
--- a/.github/workflows/test-build.yml
+++ b/.github/workflows/test-build.yml
@@ -32,7 +32,7 @@ jobs:
- name: Verify build output
run: |
cd build
- if ls StationeersServerControl*; then
+ if ls SteamServerUI*; then
echo "Build succeeded: Executable found."
else
echo "Build failed: No executable found."
@@ -45,4 +45,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: StationeersServerControl
- path: ./build/StationeersServerControl*
\ No newline at end of file
+ path: ./build/SteamServerUI*
\ No newline at end of file
From e1dcf5514df04ab1fcbd3885bbb4c7e8eea32075 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Tue, 14 Oct 2025 02:39:12 +0200
Subject: [PATCH 43/93] refactor: move web's auth handlers to httpauth package
and update route definitions accordingly
---
src/api/httpauth/activate.go | 55 ++++++
src/api/httpauth/login.go | 62 +++++++
src/api/httpauth/logout.go | 32 ++++
src/api/httpauth/register.go | 52 ++++++
src/api/login.go | 242 ---------------------------
src/api/middleware/authmiddleware.go | 76 +++++++++
src/api/routes.go | 11 +-
src/api/start.go | 3 +-
8 files changed, 285 insertions(+), 248 deletions(-)
create mode 100644 src/api/httpauth/activate.go
create mode 100644 src/api/httpauth/login.go
create mode 100644 src/api/httpauth/logout.go
create mode 100644 src/api/httpauth/register.go
delete mode 100644 src/api/login.go
create mode 100644 src/api/middleware/authmiddleware.go
diff --git a/src/api/httpauth/activate.go b/src/api/httpauth/activate.go
new file mode 100644
index 00000000..a807a603
--- /dev/null
+++ b/src/api/httpauth/activate.go
@@ -0,0 +1,55 @@
+package httpauth
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config/configchanger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+)
+
+// ActivateAuthHandler (ex. SetupFinalizeHandler) marks setup as complete
+func ActivateAuthHandler(w http.ResponseWriter, r *http.Request) {
+
+ //check if users map is nil or empty
+ if len(config.GetUsers()) == 0 {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusBadRequest)
+ json.NewEncoder(w).Encode(map[string]string{"error": "No users registered - cannot finalize setup at this time. You should really enable authentication - or click 'Skip authentication'"})
+ return
+ }
+
+ // Load existing config to update it
+ newConfig, err := config.LoadConfig()
+ if err != nil {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error - Failed to load config"})
+ return
+ }
+
+ // Mark setup as complete and enable auth
+ config.SetIsFirstTimeSetup(false)
+ isTrue := true
+ newConfig.AuthEnabled = &isTrue // Set the pointer to true
+
+ // Save the updated config
+ err = configchanger.SaveConfig(newConfig)
+ if err != nil {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error - Failed to save config"})
+ return
+ }
+
+ logger.Web.Info("User Setup finalized successfully")
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(map[string]string{
+ "message": "Setup finalized successfully",
+ "restart_hint": "You will be redirected to the login page...",
+ })
+ loader.ReloadBackend()
+}
diff --git a/src/api/httpauth/login.go b/src/api/httpauth/login.go
new file mode 100644
index 00000000..db69c2c8
--- /dev/null
+++ b/src/api/httpauth/login.go
@@ -0,0 +1,62 @@
+// handlers.go
+package httpauth
+
+import (
+ "encoding/json"
+ "net/http"
+ "time"
+
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/security"
+)
+
+// LoginHandler issues a JWT cookie
+func LoginHandler(w http.ResponseWriter, r *http.Request) {
+ var creds security.UserCredentials
+ err := json.NewDecoder(r.Body).Decode(&creds)
+ if err != nil {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusBadRequest)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Bad Request - Invalid JSON"})
+ return
+ }
+
+ // Check credentials using security package
+ valid, err := security.ValidateCredentials(creds)
+ if err != nil {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error"})
+ return
+ }
+ if !valid {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusUnauthorized)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized - Invalid credentials"})
+ return
+ }
+
+ // Generate JWT
+ tokenString, err := security.GenerateJWT(creds.Username)
+ if err != nil {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error"})
+ return
+ }
+
+ // Set cookie
+ http.SetCookie(w, &http.Cookie{
+ Name: "AuthToken",
+ Value: tokenString,
+ Expires: time.Now().Add(time.Duration(config.GetAuthTokenLifetime()) * time.Minute),
+ HttpOnly: true,
+ Secure: true,
+ Path: "/",
+ SameSite: http.SameSiteStrictMode,
+ })
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(map[string]string{"token": tokenString})
+}
diff --git a/src/api/httpauth/logout.go b/src/api/httpauth/logout.go
new file mode 100644
index 00000000..e0764ae3
--- /dev/null
+++ b/src/api/httpauth/logout.go
@@ -0,0 +1,32 @@
+package httpauth
+
+import (
+ "encoding/json"
+ "net/http"
+ "strings"
+ "time"
+)
+
+func LogoutHandler(w http.ResponseWriter, r *http.Request) {
+ // Clear the cookie by setting it with an expired time
+ http.SetCookie(w, &http.Cookie{
+ Name: "AuthToken",
+ Value: "",
+ Expires: time.Now().Add(-time.Hour), // Set to past time to expire immediately
+ HttpOnly: true,
+ Secure: true,
+ Path: "/",
+ SameSite: http.SameSiteStrictMode,
+ })
+ accept := r.Header.Get("Accept")
+ if accept != "" && strings.Contains(accept, "text/html") {
+ http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
+ return
+ }
+ // For API requests, return success response
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(map[string]string{
+ "message": "Successfully logged out",
+ })
+}
diff --git a/src/api/httpauth/register.go b/src/api/httpauth/register.go
new file mode 100644
index 00000000..5a901248
--- /dev/null
+++ b/src/api/httpauth/register.go
@@ -0,0 +1,52 @@
+package httpauth
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/security"
+)
+
+// RegisterUserHandler registers new users
+func RegisterUserHandler(w http.ResponseWriter, r *http.Request) {
+
+ // Handle preflight OPTIONS requests
+ if r.Method == http.MethodOptions {
+ w.WriteHeader(http.StatusOK)
+ return
+ }
+
+ var creds security.UserCredentials
+ err := json.NewDecoder(r.Body).Decode(&creds)
+ if err != nil {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusBadRequest)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Bad Request - Invalid JSON"})
+ return
+ }
+
+ // Hash the password
+ hashedPassword, err := security.HashPassword(creds.Password)
+ if err != nil {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error"})
+ return
+ }
+
+ // Initialize Users map if nil
+ if config.GetUsers() == nil {
+ config.SetUsers(make(map[string]string))
+ }
+
+ // Add or update the user
+ config.SetUsers(map[string]string{creds.Username: hashedPassword})
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ json.NewEncoder(w).Encode(map[string]string{
+ "message": "User registered successfully",
+ "username": creds.Username,
+ })
+}
diff --git a/src/api/login.go b/src/api/login.go
deleted file mode 100644
index 1423fb6a..00000000
--- a/src/api/login.go
+++ /dev/null
@@ -1,242 +0,0 @@
-// handlers.go
-package api
-
-import (
- "encoding/json"
- "fmt"
- "net/http"
- "strconv"
- "strings"
- "time"
-
- "github.com/SteamServerUI/SteamServerUI/v7/src/config"
- "github.com/SteamServerUI/SteamServerUI/v7/src/config/configchanger"
- "github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
- "github.com/SteamServerUI/SteamServerUI/v7/src/core/security"
- "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
-)
-
-var setupReminderCount = 0 // to limit the number of setup reminders shown to the user
-
-// LoginHandler issues a JWT cookie
-func LoginHandler(w http.ResponseWriter, r *http.Request) {
- var creds security.UserCredentials
- err := json.NewDecoder(r.Body).Decode(&creds)
- if err != nil {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusBadRequest)
- json.NewEncoder(w).Encode(map[string]string{"error": "Bad Request - Invalid JSON"})
- return
- }
-
- // Check credentials using security package
- valid, err := security.ValidateCredentials(creds)
- if err != nil {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusInternalServerError)
- json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error"})
- return
- }
- if !valid {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusUnauthorized)
- json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized - Invalid credentials"})
- return
- }
-
- // Generate JWT
- tokenString, err := security.GenerateJWT(creds.Username)
- if err != nil {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusInternalServerError)
- json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error"})
- return
- }
-
- // Set cookie
- http.SetCookie(w, &http.Cookie{
- Name: "AuthToken",
- Value: tokenString,
- Expires: time.Now().Add(time.Duration(config.GetAuthTokenLifetime()) * time.Minute),
- HttpOnly: true,
- Secure: true,
- Path: "/",
- SameSite: http.SameSiteStrictMode,
- })
-
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusOK)
- json.NewEncoder(w).Encode(map[string]string{"token": tokenString})
-}
-
-// AuthMiddleware protects routes with cookie-based JWT
-func AuthMiddleware(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- // Log request details for debugging
- //logger.Web.Debug("Request Path:" + r.URL.Path) //very spammy
-
- // Check for first-time setup redirect
- if config.GetIsFirstTimeSetup() {
- totalSetupReminderCount := 3 // Defines how often we redirect the users reqests to the setup page
- if setupReminderCount < totalSetupReminderCount {
- if r.URL.Path == "/" && (r.Referer() == "" || r.Referer() != "/setup") {
- remainingReminderCount := totalSetupReminderCount - setupReminderCount
- logger.Web.Warn("🔍Redirecting to setup page, you should really enable authentication...")
- logger.Web.Warn(fmt.Sprintf("You will be remined %s more times.", strconv.Itoa(remainingReminderCount)))
- http.Redirect(w, r, "/setup", http.StatusTemporaryRedirect)
- setupReminderCount++
- return
- }
- }
- }
-
- if !config.GetAuthEnabled() {
- next.ServeHTTP(w, r)
- return
- }
-
- cookie, err := r.Cookie("AuthToken")
- if err != nil {
- // Browser redirect check
- accept := r.Header.Get("Accept")
- if accept != "" && strings.Contains(accept, "text/html") {
- http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
- return
- }
- // API response
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusUnauthorized)
- json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized - No token"})
- return
- }
-
- valid, err := security.ValidateJWT(cookie.Value)
- if err != nil || !valid {
- // Browser redirect check
- accept := r.Header.Get("Accept")
- if accept != "" && strings.Contains(accept, "text/html") {
- http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
- return
- }
- // API response
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusUnauthorized)
- json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized - Invalid token"})
- logger.Security.Warn("Unauthorized Request - Invalid token")
- return
- }
-
- next.ServeHTTP(w, r)
- })
-}
-
-func LogoutHandler(w http.ResponseWriter, r *http.Request) {
- // Clear the cookie by setting it with an expired time
- http.SetCookie(w, &http.Cookie{
- Name: "AuthToken",
- Value: "",
- Expires: time.Now().Add(-time.Hour), // Set to past time to expire immediately
- HttpOnly: true,
- Secure: true,
- Path: "/",
- SameSite: http.SameSiteStrictMode,
- })
- accept := r.Header.Get("Accept")
- if accept != "" && strings.Contains(accept, "text/html") {
- http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
- return
- }
- // For API requests, return success response
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusOK)
- json.NewEncoder(w).Encode(map[string]string{
- "message": "Successfully logged out",
- })
-}
-
-// RegisterUserHandler registers new users
-func RegisterUserHandler(w http.ResponseWriter, r *http.Request) {
-
- // Handle preflight OPTIONS requests
- if r.Method == http.MethodOptions {
- w.WriteHeader(http.StatusOK)
- return
- }
-
- var creds security.UserCredentials
- err := json.NewDecoder(r.Body).Decode(&creds)
- if err != nil {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusBadRequest)
- json.NewEncoder(w).Encode(map[string]string{"error": "Bad Request - Invalid JSON"})
- return
- }
-
- // Hash the password
- hashedPassword, err := security.HashPassword(creds.Password)
- if err != nil {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusInternalServerError)
- json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error"})
- return
- }
-
- // Initialize Users map if nil
- if config.GetUsers() == nil {
- config.SetUsers(make(map[string]string))
- }
-
- // Add or update the user
- config.SetUsers(map[string]string{creds.Username: hashedPassword})
-
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusCreated)
- json.NewEncoder(w).Encode(map[string]string{
- "message": "User registered successfully",
- "username": creds.Username,
- })
-}
-
-// SetupFinalizeHandler marks setup as complete
-func SetupFinalizeHandler(w http.ResponseWriter, r *http.Request) {
-
- //check if users map is nil or empty
- if len(config.GetUsers()) == 0 {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusBadRequest)
- json.NewEncoder(w).Encode(map[string]string{"error": "No users registered - cannot finalize setup at this time. You should really enable authentication - or click 'Skip authentication'"})
- return
- }
-
- // Load existing config to update it
- newConfig, err := config.LoadConfig()
- if err != nil {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusInternalServerError)
- json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error - Failed to load config"})
- return
- }
-
- // Mark setup as complete and enable auth
- config.SetIsFirstTimeSetup(false)
- isTrue := true
- newConfig.AuthEnabled = &isTrue // Set the pointer to true
-
- // Save the updated config
- err = configchanger.SaveConfig(newConfig)
- if err != nil {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusInternalServerError)
- json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error - Failed to save config"})
- return
- }
-
- logger.Web.Info("User Setup finalized successfully")
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusOK)
- json.NewEncoder(w).Encode(map[string]string{
- "message": "Setup finalized successfully",
- "restart_hint": "You will be redirected to the login page...",
- })
- loader.ReloadBackend()
-}
diff --git a/src/api/middleware/authmiddleware.go b/src/api/middleware/authmiddleware.go
new file mode 100644
index 00000000..144687d7
--- /dev/null
+++ b/src/api/middleware/authmiddleware.go
@@ -0,0 +1,76 @@
+package middleware
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/security"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+)
+
+var setupReminderCount = 0 // to limit the number of setup reminders shown to the user
+
+// AuthMiddleware protects routes with cookie-based JWT
+func AuthMiddleware(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Log request details for debugging
+ //logger.Web.Debug("Request Path:" + r.URL.Path) //very spammy
+
+ // Check for first-time setup redirect
+ if config.GetIsFirstTimeSetup() {
+ totalSetupReminderCount := 3 // Defines how often we redirect the users reqests to the setup page
+ if setupReminderCount < totalSetupReminderCount {
+ if r.URL.Path == "/" && (r.Referer() == "" || r.Referer() != "/setup") {
+ remainingReminderCount := totalSetupReminderCount - setupReminderCount
+ logger.Web.Warn("🔍Redirecting to setup page, you should really enable authentication...")
+ logger.Web.Warn(fmt.Sprintf("You will be remined %s more times.", strconv.Itoa(remainingReminderCount)))
+ http.Redirect(w, r, "/setup", http.StatusTemporaryRedirect)
+ setupReminderCount++
+ return
+ }
+ }
+ }
+
+ if !config.GetAuthEnabled() {
+ next.ServeHTTP(w, r)
+ return
+ }
+
+ cookie, err := r.Cookie("AuthToken")
+ if err != nil {
+ // Browser redirect check
+ accept := r.Header.Get("Accept")
+ if accept != "" && strings.Contains(accept, "text/html") {
+ http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
+ return
+ }
+ // API response
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusUnauthorized)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized - No token"})
+ return
+ }
+
+ valid, err := security.ValidateJWT(cookie.Value)
+ if err != nil || !valid {
+ // Browser redirect check
+ accept := r.Header.Get("Accept")
+ if accept != "" && strings.Contains(accept, "text/html") {
+ http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
+ return
+ }
+ // API response
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusUnauthorized)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized - Invalid token"})
+ logger.Security.Warn("Unauthorized Request - Invalid token")
+ return
+ }
+
+ next.ServeHTTP(w, r)
+ })
+}
diff --git a/src/api/routes.go b/src/api/routes.go
index 1b718a1a..c29b0d50 100644
--- a/src/api/routes.go
+++ b/src/api/routes.go
@@ -4,6 +4,7 @@ import (
"io/fs"
"net/http"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api/httpauth"
"github.com/SteamServerUI/SteamServerUI/v7/src/config"
"github.com/SteamServerUI/SteamServerUI/v7/src/config/configchanger"
"github.com/SteamServerUI/SteamServerUI/v7/src/managers/backupmgr"
@@ -19,8 +20,8 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
// Unprotected auth routes
twoboxformAssetsFS, _ := fs.Sub(config.GetV1UIFS(), "SSUI/onboard_bundled/twoboxform")
mux.Handle("/twoboxform/", http.StripPrefix("/twoboxform/", http.FileServer(http.FS(twoboxformAssetsFS))))
- mux.HandleFunc("/auth/login", LoginHandler) // Token issuer
- mux.HandleFunc("/auth/logout", LogoutHandler)
+ mux.HandleFunc("/auth/login", httpauth.LoginHandler) // Token issuer
+ mux.HandleFunc("/auth/logout", httpauth.LogoutHandler)
mux.HandleFunc("/login", ServeTwoBoxFormTemplate)
// Protected routes (wrapped with middleware)
@@ -72,13 +73,13 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
protectedMux.HandleFunc("/api/v2/custom-detections/delete/", detectionmgr.HandleDeleteCustomDetection)
// Authentication
protectedMux.HandleFunc("/changeuser", ServeTwoBoxFormTemplate)
- protectedMux.HandleFunc("/api/v2/auth/adduser", RegisterUserHandler) // user registration and change password
+ protectedMux.HandleFunc("/api/v2/auth/adduser", httpauth.RegisterUserHandler) // user registration and change password
protectedMux.HandleFunc("/api/v2/auth/whoami", WhoAmIHandler)
// Setup
protectedMux.HandleFunc("/setup", ServeTwoBoxFormTemplate)
- protectedMux.HandleFunc("/api/v2/auth/setup/register", RegisterUserHandler) // user registration
- protectedMux.HandleFunc("/api/v2/auth/setup/finalize", SetupFinalizeHandler)
+ protectedMux.HandleFunc("/api/v2/auth/setup/register", httpauth.RegisterUserHandler) // user registration
+ protectedMux.HandleFunc("/api/v2/auth/setup/finalize", httpauth.ActivateAuthHandler)
// SteamServerUI
diff --git a/src/api/start.go b/src/api/start.go
index 5c4b55e2..5074e1fb 100644
--- a/src/api/start.go
+++ b/src/api/start.go
@@ -7,6 +7,7 @@ import (
"net/http/pprof"
"sync"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api/middleware"
"github.com/SteamServerUI/SteamServerUI/v7/src/config"
"github.com/SteamServerUI/SteamServerUI/v7/src/core/security"
"github.com/SteamServerUI/SteamServerUI/v7/src/logger"
@@ -26,7 +27,7 @@ func StartWebServer(wg *sync.WaitGroup) {
mux, protectedMux := SetupRoutes()
// Apply middleware only to protected routes
- mux.Handle("/", AuthMiddleware(protectedMux)) // Wrap protected routes under root
+ mux.Handle("/", middleware.AuthMiddleware(protectedMux)) // Wrap protected routes under root
httpLogger := log.New(&APIServerLogger{}, "", 0)
// Start HTTP server
From c5ff3f6e11ba7fe2e1abd57efd7edfb1775c3c87 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Tue, 14 Oct 2025 03:05:18 +0200
Subject: [PATCH 44/93] refactor: relocate API functions into subpages for
better organization
---
server.go | 4 +-
src/api/handlers.go | 67 ++++++++
src/api/http.go | 144 ------------------
src/api/{ => httpauth}/whoami.go | 2 +-
src/api/{start.go => httpstart.go} | 0
src/api/{ => legacyapi}/connectedplayers.go | 2 +-
src/api/legacyapi/startstop.go | 41 +++++
src/api/{ => pages}/TwoBoxForm.go | 2 +-
src/api/{ => pages}/configpage.go | 2 +-
src/api/{ => pages}/detectionmanagerpage.go | 2 +-
src/api/{ => pages}/indexpage.go | 2 +-
src/api/pages/svelteui.go | 32 ++++
src/api/{ => pages}/templatevars.go | 2 +-
src/api/routes.go | 72 +++++----
src/api/{ => runfileapi}/runfile.go | 7 +-
src/api/{ => runfileapi}/runfilegallery.go | 10 +-
src/api/{socket => socketapi}/examples.md | 0
src/api/{socket => socketapi}/socket-lin.go | 2 +-
src/api/{socket => socketapi}/socket-win.go | 2 +-
src/api/sscmapi/handlers.go | 68 +++++++++
src/api/{commands.go => sscmapi/sscm.go} | 2 +-
src/api/{http-sse.go => sseapi/sse.go} | 2 +-
src/api/svelteui.go | 59 -------
.../{systeminfo.go => sysinfoapi/sysinfo.go} | 2 +-
24 files changed, 267 insertions(+), 261 deletions(-)
create mode 100644 src/api/handlers.go
delete mode 100644 src/api/http.go
rename src/api/{ => httpauth}/whoami.go (96%)
rename src/api/{start.go => httpstart.go} (100%)
rename src/api/{ => legacyapi}/connectedplayers.go (98%)
create mode 100644 src/api/legacyapi/startstop.go
rename src/api/{ => pages}/TwoBoxForm.go (99%)
rename src/api/{ => pages}/configpage.go (99%)
rename src/api/{ => pages}/detectionmanagerpage.go (98%)
rename src/api/{ => pages}/indexpage.go (99%)
create mode 100644 src/api/pages/svelteui.go
rename src/api/{ => pages}/templatevars.go (99%)
rename src/api/{ => runfileapi}/runfile.go (98%)
rename src/api/{ => runfileapi}/runfilegallery.go (89%)
rename src/api/{socket => socketapi}/examples.md (100%)
rename src/api/{socket => socketapi}/socket-lin.go (98%)
rename src/api/{socket => socketapi}/socket-win.go (98%)
create mode 100644 src/api/sscmapi/handlers.go
rename src/api/{commands.go => sscmapi/sscm.go} (99%)
rename src/api/{http-sse.go => sseapi/sse.go} (99%)
delete mode 100644 src/api/svelteui.go
rename src/api/{systeminfo.go => sysinfoapi/sysinfo.go} (98%)
diff --git a/server.go b/server.go
index b9abcf5d..9f2dec7c 100644
--- a/server.go
+++ b/server.go
@@ -25,7 +25,7 @@ import (
"sync"
"github.com/SteamServerUI/SteamServerUI/v7/src/api"
- "github.com/SteamServerUI/SteamServerUI/v7/src/api/socket"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api/socketapi"
"github.com/SteamServerUI/SteamServerUI/v7/src/cli"
"github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
"github.com/SteamServerUI/SteamServerUI/v7/src/logger"
@@ -56,7 +56,7 @@ func main() {
loader.AfterStartComplete(&wg)
wg.Wait()
logger.Main.Debug("Starting socket server...")
- socket.StartSocketServer(&wg)
+ socketapi.StartSocketServer(&wg)
logger.Main.Debug("Starting webserver...")
api.StartWebServer(&wg)
logger.Main.Debug("Initializing SSUICLI...")
diff --git a/src/api/handlers.go b/src/api/handlers.go
new file mode 100644
index 00000000..1928a109
--- /dev/null
+++ b/src/api/handlers.go
@@ -0,0 +1,67 @@
+package api
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamcmd"
+)
+
+func GetGameServerRunState(w http.ResponseWriter, r *http.Request) {
+ runState := gamemgr.InternalIsServerRunning()
+ response := map[string]interface{}{
+ "isRunning": runState,
+ "uuid": gamemgr.GameServerUUID.String(),
+ }
+ w.Header().Set("Content-Type", "application/json")
+ if err := json.NewEncoder(w).Encode(response); err != nil {
+ http.Error(w, "Failed to respond with Game Server status", http.StatusInternalServerError)
+ return
+ }
+}
+
+func HandleRunSteamCMD(w http.ResponseWriter, r *http.Request) {
+
+ // Only allow GET requests
+ if r.Method != http.MethodGet {
+ http.Error(w, "Only GET requests are allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ logger.Core.Info("Running SteamCMD")
+ _, err := steamcmd.InstallAndRunSteamCMD()
+
+ // Update last execution time
+
+ // Success: return 202 Accepted and JSON
+ w.WriteHeader(http.StatusOK)
+ w.Header().Set("Content-Type", "application/json")
+ if err == nil {
+ json.NewEncoder(w).Encode(map[string]string{"statuscode": "202", "status": "Success", "message": "SteamCMD ran successfully, gameserver files are up-to-date!"})
+ return
+ }
+ // Failure: return 202 Accepted and JSON with the error message
+ json.NewEncoder(w).Encode(map[string]string{"statuscode": "202", "status": "Failed", "message": "SteamCMD ran unsuccessfully:" + err.Error()})
+}
+
+func HandleReloadBackend(w http.ResponseWriter, r *http.Request) {
+ logger.API.Debug("Received reloadbackend request from API")
+ // accept only GET requests
+ if r.Method != http.MethodGet {
+ http.Error(w, "Only GET requests are allowed", http.StatusMethodNotAllowed)
+ return
+ }
+ // Reload all loaders
+ loader.ReloadBackend()
+
+ // Set response headers and write JSON response
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusAccepted)
+ if err := json.NewEncoder(w).Encode(map[string]string{"status": "OK"}); err != nil {
+ http.Error(w, "Failed to write response", http.StatusInternalServerError)
+ return
+ }
+}
diff --git a/src/api/http.go b/src/api/http.go
deleted file mode 100644
index cae38d09..00000000
--- a/src/api/http.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package api
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "os"
- "strings"
-
- "github.com/SteamServerUI/SteamServerUI/v7/src/config"
- "github.com/SteamServerUI/SteamServerUI/v7/src/localization"
- "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
- "github.com/SteamServerUI/SteamServerUI/v7/src/managers/commandmgr"
- "github.com/SteamServerUI/SteamServerUI/v7/src/managers/detectionmgr"
- "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
- "github.com/SteamServerUI/SteamServerUI/v7/src/steamcmd"
-)
-
-// StartServer HTTP handler
-func StartServer(w http.ResponseWriter, r *http.Request) {
- logger.API.Debug("Received start request from API")
- if err := gamemgr.InternalStartServer(); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- logger.API.Error("Error starting server: " + err.Error())
- return
- }
- fmt.Fprint(w, localization.GetString("BackendText_ServerStarted"))
- logger.API.Info("Server started.")
-}
-
-// StopServer HTTP handler
-func StopServer(w http.ResponseWriter, r *http.Request) {
- logger.API.Debug("Received stop request from API")
- if err := gamemgr.InternalStopServer(); err != nil {
- if err.Error() == "server not running" {
- fmt.Fprint(w, localization.GetString("BackendText_ServerNotRunningOrAlreadyStopped"))
- logger.API.Warn("Server not running or was already stopped")
- return
- }
- http.Error(w, err.Error(), http.StatusInternalServerError)
- logger.API.Error("Error stopping server: " + err.Error())
- return
- }
- detectionmgr.ClearPlayers(detectionmgr.GetDetector())
- fmt.Fprint(w, localization.GetString("BackendText_ServerStopped"))
- logger.API.Info("Server stopped.")
-}
-
-func GetGameServerRunState(w http.ResponseWriter, r *http.Request) {
- runState := gamemgr.InternalIsServerRunning()
- response := map[string]interface{}{
- "isRunning": runState,
- "uuid": gamemgr.GameServerUUID.String(),
- }
- w.Header().Set("Content-Type", "application/json")
- if err := json.NewEncoder(w).Encode(response); err != nil {
- http.Error(w, "Failed to respond with Game Server status", http.StatusInternalServerError)
- return
- }
-}
-
-// CommandHandler handles POST requests to execute commands via commandmgr.
-// Expects a command in the request body. Returns 204 on success or error details.
-func CommandHandler(w http.ResponseWriter, r *http.Request) {
- // Allow only POST requests
- if r.Method != http.MethodPost {
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
- return
- }
-
- // Read command from request body
- body, err := io.ReadAll(r.Body)
- if err != nil {
- http.Error(w, "Failed to read request body", http.StatusBadRequest)
- return
- }
- command := strings.TrimSpace(string(body))
-
- // Validate command
- if command == "" {
- http.Error(w, "Command cannot be empty", http.StatusBadRequest)
- return
- }
-
- // Execute command via commandmgr
- if err := commandmgr.WriteCommand(command); err != nil {
- switch err {
- case os.ErrNotExist:
- http.Error(w, "Command file path not configured", http.StatusInternalServerError)
- case os.ErrInvalid:
- http.Error(w, "Invalid command", http.StatusBadRequest)
- default:
- http.Error(w, "Failed to write command: "+err.Error(), http.StatusInternalServerError)
- }
- return
- }
-
- // Success: return 204 No Content
- w.WriteHeader(http.StatusNoContent)
-}
-
-func HandleIsSSCMEnabled(w http.ResponseWriter, r *http.Request) {
- // Only allow GET requests
- if r.Method != http.MethodGet {
- http.Error(w, "Only GET requests are allowed", http.StatusMethodNotAllowed)
- return
- }
-
- // Check if SSCM is enabled
- if !config.GetIsSSCMEnabled() {
- http.Error(w, "SSCM is disabled", http.StatusForbidden)
- return
- }
-
- // Success: return 200 OK
- w.WriteHeader(http.StatusOK)
-}
-
-// run SteamCMD from API, but only allow once every 5 minutes to "kinda" prevent concurrent executions although that woluldnt hurn.
-// If the user has a 5mbit connection, I cannot help them anyways.
-func HandleRunSteamCMD(w http.ResponseWriter, r *http.Request) {
-
- // Only allow GET requests
- if r.Method != http.MethodGet {
- http.Error(w, "Only GET requests are allowed", http.StatusMethodNotAllowed)
- return
- }
-
- logger.Core.Info("Running SteamCMD")
- _, err := steamcmd.InstallAndRunSteamCMD()
-
- // Update last execution time
-
- // Success: return 202 Accepted and JSON
- w.WriteHeader(http.StatusOK)
- w.Header().Set("Content-Type", "application/json")
- if err == nil {
- json.NewEncoder(w).Encode(map[string]string{"statuscode": "202", "status": "Success", "message": "SteamCMD ran successfully, gameserver files are up-to-date!"})
- return
- }
- // Failure: return 202 Accepted and JSON with the error message
- json.NewEncoder(w).Encode(map[string]string{"statuscode": "202", "status": "Failed", "message": "SteamCMD ran unsuccessfully:" + err.Error()})
-}
diff --git a/src/api/whoami.go b/src/api/httpauth/whoami.go
similarity index 96%
rename from src/api/whoami.go
rename to src/api/httpauth/whoami.go
index 8b83c285..47176321 100644
--- a/src/api/whoami.go
+++ b/src/api/httpauth/whoami.go
@@ -1,4 +1,4 @@
-package api
+package httpauth
import (
"encoding/json"
diff --git a/src/api/start.go b/src/api/httpstart.go
similarity index 100%
rename from src/api/start.go
rename to src/api/httpstart.go
diff --git a/src/api/connectedplayers.go b/src/api/legacyapi/connectedplayers.go
similarity index 98%
rename from src/api/connectedplayers.go
rename to src/api/legacyapi/connectedplayers.go
index bcb69228..94ba8f07 100644
--- a/src/api/connectedplayers.go
+++ b/src/api/legacyapi/connectedplayers.go
@@ -1,4 +1,4 @@
-package api
+package legacyapi
import (
"encoding/json"
diff --git a/src/api/legacyapi/startstop.go b/src/api/legacyapi/startstop.go
new file mode 100644
index 00000000..62afd0a8
--- /dev/null
+++ b/src/api/legacyapi/startstop.go
@@ -0,0 +1,41 @@
+package legacyapi
+
+import (
+ "fmt"
+ "net/http"
+
+ "github.com/SteamServerUI/SteamServerUI/v7/src/localization"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/detectionmgr"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/gamemgr"
+)
+
+// StartServer HTTP handler
+func StartServer(w http.ResponseWriter, r *http.Request) {
+ logger.API.Debug("Received start request from API")
+ if err := gamemgr.InternalStartServer(); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ logger.API.Error("Error starting server: " + err.Error())
+ return
+ }
+ fmt.Fprint(w, localization.GetString("BackendText_ServerStarted"))
+ logger.API.Info("Server started.")
+}
+
+// StopServer HTTP handler
+func StopServer(w http.ResponseWriter, r *http.Request) {
+ logger.API.Debug("Received stop request from API")
+ if err := gamemgr.InternalStopServer(); err != nil {
+ if err.Error() == "server not running" {
+ fmt.Fprint(w, localization.GetString("BackendText_ServerNotRunningOrAlreadyStopped"))
+ logger.API.Warn("Server not running or was already stopped")
+ return
+ }
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ logger.API.Error("Error stopping server: " + err.Error())
+ return
+ }
+ detectionmgr.ClearPlayers(detectionmgr.GetDetector())
+ fmt.Fprint(w, localization.GetString("BackendText_ServerStopped"))
+ logger.API.Info("Server stopped.")
+}
diff --git a/src/api/TwoBoxForm.go b/src/api/pages/TwoBoxForm.go
similarity index 99%
rename from src/api/TwoBoxForm.go
rename to src/api/pages/TwoBoxForm.go
index 86ff770e..d206acf1 100644
--- a/src/api/TwoBoxForm.go
+++ b/src/api/pages/TwoBoxForm.go
@@ -1,4 +1,4 @@
-package api
+package pages
import (
"io/fs"
diff --git a/src/api/configpage.go b/src/api/pages/configpage.go
similarity index 99%
rename from src/api/configpage.go
rename to src/api/pages/configpage.go
index cef76a49..e6ebbb9f 100644
--- a/src/api/configpage.go
+++ b/src/api/pages/configpage.go
@@ -1,4 +1,4 @@
-package api
+package pages
import (
"fmt"
diff --git a/src/api/detectionmanagerpage.go b/src/api/pages/detectionmanagerpage.go
similarity index 98%
rename from src/api/detectionmanagerpage.go
rename to src/api/pages/detectionmanagerpage.go
index 189658f5..01c59e0b 100644
--- a/src/api/detectionmanagerpage.go
+++ b/src/api/pages/detectionmanagerpage.go
@@ -1,4 +1,4 @@
-package api
+package pages
import (
"fmt"
diff --git a/src/api/indexpage.go b/src/api/pages/indexpage.go
similarity index 99%
rename from src/api/indexpage.go
rename to src/api/pages/indexpage.go
index 305fb018..66b6ecb0 100644
--- a/src/api/indexpage.go
+++ b/src/api/pages/indexpage.go
@@ -1,4 +1,4 @@
-package api
+package pages
import (
"io/fs"
diff --git a/src/api/pages/svelteui.go b/src/api/pages/svelteui.go
new file mode 100644
index 00000000..240e7e2a
--- /dev/null
+++ b/src/api/pages/svelteui.go
@@ -0,0 +1,32 @@
+package pages
+
+import (
+ "io"
+ "io/fs"
+ "net/http"
+
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+)
+
+func ServeSvelteUI(w http.ResponseWriter, r *http.Request) {
+ htmlFS, err := fs.Sub(config.V1UIFS, "SSUI/onboard_bundled/v2")
+ if err != nil {
+ http.Error(w, "Error accessing Svelte UI: "+err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ htmlFile, err := htmlFS.Open("index.html")
+ if err != nil {
+ http.Error(w, "Error reading Svelte UI: "+err.Error(), http.StatusInternalServerError)
+ return
+ }
+ defer htmlFile.Close()
+
+ // Stream the file content to the response
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ _, err = io.Copy(w, htmlFile)
+ if err != nil {
+ http.Error(w, "Error writing Svelte UI: "+err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
diff --git a/src/api/templatevars.go b/src/api/pages/templatevars.go
similarity index 99%
rename from src/api/templatevars.go
rename to src/api/pages/templatevars.go
index 4e0e7c62..4cfe63b1 100644
--- a/src/api/templatevars.go
+++ b/src/api/pages/templatevars.go
@@ -1,4 +1,4 @@
-package api
+package pages
// TemplateData holds data to be passed to templates
type IndexTemplateData struct {
diff --git a/src/api/routes.go b/src/api/routes.go
index c29b0d50..8821672d 100644
--- a/src/api/routes.go
+++ b/src/api/routes.go
@@ -5,6 +5,12 @@ import (
"net/http"
"github.com/SteamServerUI/SteamServerUI/v7/src/api/httpauth"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api/legacyapi"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api/pages"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api/runfileapi"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api/sscmapi"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api/sseapi"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api/sysinfoapi"
"github.com/SteamServerUI/SteamServerUI/v7/src/config"
"github.com/SteamServerUI/SteamServerUI/v7/src/config/configchanger"
"github.com/SteamServerUI/SteamServerUI/v7/src/managers/backupmgr"
@@ -22,7 +28,7 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
mux.Handle("/twoboxform/", http.StripPrefix("/twoboxform/", http.FileServer(http.FS(twoboxformAssetsFS))))
mux.HandleFunc("/auth/login", httpauth.LoginHandler) // Token issuer
mux.HandleFunc("/auth/logout", httpauth.LogoutHandler)
- mux.HandleFunc("/login", ServeTwoBoxFormTemplate)
+ mux.HandleFunc("/login", pages.ServeTwoBoxFormTemplate)
// Protected routes (wrapped with middleware)
protectedMux := http.NewServeMux()
@@ -30,32 +36,32 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
legacyAssetsFS, _ := fs.Sub(config.GetV1UIFS(), "SSUI/onboard_bundled/assets")
protectedMux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(legacyAssetsFS))))
- protectedMux.HandleFunc("/config", ServeConfigPage)
- protectedMux.HandleFunc("/detectionmanager", ServeDetectionManager)
- protectedMux.HandleFunc("/", ServeIndex)
+ protectedMux.HandleFunc("/config", pages.ServeConfigPage)
+ protectedMux.HandleFunc("/detectionmanager", pages.ServeDetectionManager)
+ protectedMux.HandleFunc("/", pages.ServeIndex)
// --- SVELTE UI ---
- protectedMux.HandleFunc("/v2", ServeSvelteUI)
+ protectedMux.HandleFunc("/v2", pages.ServeSvelteUI)
svelteAssetsFS, _ := fs.Sub(config.V1UIFS, "SSUI/onboard_bundled/v2/assets")
protectedMux.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.FS(svelteAssetsFS))))
- protectedMux.HandleFunc("/api/v2/loader/reloadbackend", HandleReloadAll)
+ protectedMux.HandleFunc("/api/v2/loader/reloadbackend", HandleReloadBackend)
// SSE routes
- protectedMux.HandleFunc("/console", GetLogOutput)
- protectedMux.HandleFunc("/events", GetEventOutput)
- protectedMux.HandleFunc("/logs/debug", GetDebugLogOutput)
- protectedMux.HandleFunc("/logs/info", GetInfoLogOutput)
- protectedMux.HandleFunc("/logs/warn", GetWarnLogOutput)
- protectedMux.HandleFunc("/logs/error", GetErrorLogOutput)
- protectedMux.HandleFunc("/logs/backend", GetBackendLogOutput)
+ protectedMux.HandleFunc("/console", sseapi.GetLogOutput)
+ protectedMux.HandleFunc("/events", sseapi.GetEventOutput)
+ protectedMux.HandleFunc("/logs/debug", sseapi.GetDebugLogOutput)
+ protectedMux.HandleFunc("/logs/info", sseapi.GetInfoLogOutput)
+ protectedMux.HandleFunc("/logs/warn", sseapi.GetWarnLogOutput)
+ protectedMux.HandleFunc("/logs/error", sseapi.GetErrorLogOutput)
+ protectedMux.HandleFunc("/logs/backend", sseapi.GetBackendLogOutput)
// Server Control
- protectedMux.HandleFunc("/start", StartServer)
- protectedMux.HandleFunc("/stop", StopServer)
- protectedMux.HandleFunc("/api/v2/server/start", StartServer)
- protectedMux.HandleFunc("/api/v2/server/stop", StopServer)
+ protectedMux.HandleFunc("/start", legacyapi.StartServer)
+ protectedMux.HandleFunc("/stop", legacyapi.StopServer)
+ protectedMux.HandleFunc("/api/v2/server/start", legacyapi.StartServer) // TODO: should return json & get their own functions
+ protectedMux.HandleFunc("/api/v2/server/stop", legacyapi.StopServer) // TODO: should return json & get their own functions
protectedMux.HandleFunc("/api/v2/server/status", GetGameServerRunState)
- protectedMux.HandleFunc("/api/v2/server/status/connectedplayers", HandleConnectedPlayersList)
+ protectedMux.HandleFunc("/api/v2/server/status/connectedplayers", legacyapi.HandleConnectedPlayersList)
backupHandler := backupmgr.NewHTTPHandler(backupmgr.GlobalBackupManager)
protectedMux.HandleFunc("/api/v2/backups", backupHandler.ListBackupsHandler)
@@ -64,42 +70,42 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
// Configuration
protectedMux.HandleFunc("/saveconfigasjson", configchanger.SaveConfigForm) // legacy, used on config page
protectedMux.HandleFunc("/api/v2/saveconfig", configchanger.SaveConfigRestful) // used on twoboxform
- protectedMux.HandleFunc("/api/v2/SSCM/run", HandleCommand) // Command execution via SSCM (needs to be enable, config.IsSSCMEnabled)
- protectedMux.HandleFunc("/api/v2/SSCM/enabled", HandleIsSSCMEnabled) // Check if SSCM is enabled
+ protectedMux.HandleFunc("/api/v2/SSCM/run", sscmapi.HandleCommand) // Command execution via SSCM (needs to be enable, config.IsSSCMEnabled)
+ protectedMux.HandleFunc("/api/v2/SSCM/enabled", sscmapi.HandleIsSSCMEnabled) // Check if SSCM is enabled
protectedMux.HandleFunc("/api/v2/steamcmd/run", HandleRunSteamCMD) // Run SteamCMD
// Custom Detections
protectedMux.HandleFunc("/api/v2/custom-detections", detectionmgr.HandleCustomDetection)
protectedMux.HandleFunc("/api/v2/custom-detections/delete/", detectionmgr.HandleDeleteCustomDetection)
// Authentication
- protectedMux.HandleFunc("/changeuser", ServeTwoBoxFormTemplate)
+ protectedMux.HandleFunc("/changeuser", pages.ServeTwoBoxFormTemplate)
protectedMux.HandleFunc("/api/v2/auth/adduser", httpauth.RegisterUserHandler) // user registration and change password
- protectedMux.HandleFunc("/api/v2/auth/whoami", WhoAmIHandler)
+ protectedMux.HandleFunc("/api/v2/auth/whoami", httpauth.WhoAmIHandler)
// Setup
- protectedMux.HandleFunc("/setup", ServeTwoBoxFormTemplate)
+ protectedMux.HandleFunc("/setup", pages.ServeTwoBoxFormTemplate)
protectedMux.HandleFunc("/api/v2/auth/setup/register", httpauth.RegisterUserHandler) // user registration
protectedMux.HandleFunc("/api/v2/auth/setup/finalize", httpauth.ActivateAuthHandler)
// SteamServerUI
// --- RUNFILE ---
- protectedMux.HandleFunc("/api/v2/runfile/groups", HandleRunfileGroups)
- protectedMux.HandleFunc("/api/v2/runfile/args", HandleRunfileArgs)
- protectedMux.HandleFunc("/api/v2/runfile/args/update", HandleRunfileArgUpdate)
- protectedMux.HandleFunc("/api/v2/runfile", HandleRunfile)
- protectedMux.HandleFunc("/api/v2/runfile/save", HandleRunfileSave)
- protectedMux.HandleFunc("/api/v2/runfile/hardreset", HandleSetRunfileGame)
+ protectedMux.HandleFunc("/api/v2/runfile/groups", runfileapi.HandleRunfileGroups)
+ protectedMux.HandleFunc("/api/v2/runfile/args", runfileapi.HandleRunfileArgs)
+ protectedMux.HandleFunc("/api/v2/runfile/args/update", runfileapi.HandleRunfileArgUpdate)
+ protectedMux.HandleFunc("/api/v2/runfile", runfileapi.HandleRunfile)
+ protectedMux.HandleFunc("/api/v2/runfile/save", runfileapi.HandleRunfileSave)
+ protectedMux.HandleFunc("/api/v2/runfile/hardreset", runfileapi.HandleSetRunfileGame)
// --- LOADER ---
- protectedMux.HandleFunc("/api/v2/loader/reloadrunfile", HandleReloadRunfile)
+ protectedMux.HandleFunc("/api/v2/loader/reloadrunfile", runfileapi.HandleReloadRunfile)
// --- SETTINGS ---
protectedMux.HandleFunc("/api/v2/settings/save", settings.SaveSetting)
protectedMux.HandleFunc("/api/v2/settings", settings.RetrieveSettings)
// --- OS STATS ---
- protectedMux.HandleFunc("/api/v2/osstats", HandleGetOsStats)
+ protectedMux.HandleFunc("/api/v2/osstats", sysinfoapi.HandleGetOsStats)
// --- RUNFILE GALLERY ---
- protectedMux.HandleFunc("/api/v2/gallery", galleryHandler)
- protectedMux.HandleFunc("/api/v2/gallery/select", selectHandler)
+ protectedMux.HandleFunc("/api/v2/gallery", runfileapi.GalleryHandler)
+ protectedMux.HandleFunc("/api/v2/gallery/select", runfileapi.GallerySelectHandler)
return mux, protectedMux
}
diff --git a/src/api/runfile.go b/src/api/runfileapi/runfile.go
similarity index 98%
rename from src/api/runfile.go
rename to src/api/runfileapi/runfile.go
index 5dfdc488..09bd1b83 100644
--- a/src/api/runfile.go
+++ b/src/api/runfileapi/runfile.go
@@ -1,4 +1,4 @@
-package api
+package runfileapi
import (
"encoding/json"
@@ -232,9 +232,6 @@ func HandleRunfileSave(w http.ResponseWriter, r *http.Request) {
// HandleSetRunfileGame reloads the runfile and restarts most of the server. It can also be used to reload the runfile from Disk as a hard reset.
func HandleSetRunfileGame(w http.ResponseWriter, r *http.Request) {
- reloadMu.Lock()
- defer reloadMu.Unlock()
-
// Restrict to POST method
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
@@ -287,8 +284,6 @@ func HandleSetRunfileGame(w http.ResponseWriter, r *http.Request) {
func HandleReloadRunfile(w http.ResponseWriter, r *http.Request) {
logger.API.Debug("Received reloadrunfile request from API")
- reloadMu.Lock()
- defer reloadMu.Unlock()
// accept only GET requests
if r.Method != http.MethodGet {
http.Error(w, "Only GET requests are allowed", http.StatusMethodNotAllowed)
diff --git a/src/api/runfilegallery.go b/src/api/runfileapi/runfilegallery.go
similarity index 89%
rename from src/api/runfilegallery.go
rename to src/api/runfileapi/runfilegallery.go
index 6145185d..d8a7fb49 100644
--- a/src/api/runfilegallery.go
+++ b/src/api/runfileapi/runfilegallery.go
@@ -1,4 +1,4 @@
-package api
+package runfileapi
import (
"encoding/json"
@@ -16,8 +16,8 @@ type response struct {
Error string `json:"error,omitempty"`
}
-// galleryHandler handles GET /api/v2/gallery
-func galleryHandler(w http.ResponseWriter, r *http.Request) {
+// GalleryHandler handles GET /api/v2/gallery
+func GalleryHandler(w http.ResponseWriter, r *http.Request) {
logger.Runfile.Info("Handling GET /api/v2/gallery request")
forceUpdate := strings.ToLower(r.URL.Query().Get("forceUpdate")) == "true"
@@ -32,8 +32,8 @@ func galleryHandler(w http.ResponseWriter, r *http.Request) {
sendResponse(w, http.StatusOK, response{Data: runfiles})
}
-// selectHandler handles POST /api/v2/gallery/select
-func selectHandler(w http.ResponseWriter, r *http.Request) {
+// GallerySelectHandler handles POST /api/v2/gallery/select
+func GallerySelectHandler(w http.ResponseWriter, r *http.Request) {
logger.Runfile.Info("Handling POST /api/v2/gallery/select request")
var req struct {
diff --git a/src/api/socket/examples.md b/src/api/socketapi/examples.md
similarity index 100%
rename from src/api/socket/examples.md
rename to src/api/socketapi/examples.md
diff --git a/src/api/socket/socket-lin.go b/src/api/socketapi/socket-lin.go
similarity index 98%
rename from src/api/socket/socket-lin.go
rename to src/api/socketapi/socket-lin.go
index 76ef5f8d..685a8933 100644
--- a/src/api/socket/socket-lin.go
+++ b/src/api/socketapi/socket-lin.go
@@ -1,7 +1,7 @@
//go:build linux
// +build linux
-package socket
+package socketapi
import (
"context"
diff --git a/src/api/socket/socket-win.go b/src/api/socketapi/socket-win.go
similarity index 98%
rename from src/api/socket/socket-win.go
rename to src/api/socketapi/socket-win.go
index ceed2717..687a1525 100644
--- a/src/api/socket/socket-win.go
+++ b/src/api/socketapi/socket-win.go
@@ -1,7 +1,7 @@
//go:build windows
// +build windows
-package socket
+package socketapi
import (
"context"
diff --git a/src/api/sscmapi/handlers.go b/src/api/sscmapi/handlers.go
new file mode 100644
index 00000000..c69b1cfa
--- /dev/null
+++ b/src/api/sscmapi/handlers.go
@@ -0,0 +1,68 @@
+package sscmapi
+
+import (
+ "io"
+ "net/http"
+ "os"
+ "strings"
+
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/managers/commandmgr"
+)
+
+// CommandHandler handles POST requests to execute commands via commandmgr.
+// Expects a command in the request body. Returns 204 on success or error details.
+func CommandHandler(w http.ResponseWriter, r *http.Request) {
+ // Allow only POST requests
+ if r.Method != http.MethodPost {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ // Read command from request body
+ body, err := io.ReadAll(r.Body)
+ if err != nil {
+ http.Error(w, "Failed to read request body", http.StatusBadRequest)
+ return
+ }
+ command := strings.TrimSpace(string(body))
+
+ // Validate command
+ if command == "" {
+ http.Error(w, "Command cannot be empty", http.StatusBadRequest)
+ return
+ }
+
+ // Execute command via commandmgr
+ if err := commandmgr.WriteCommand(command); err != nil {
+ switch err {
+ case os.ErrNotExist:
+ http.Error(w, "Command file path not configured", http.StatusInternalServerError)
+ case os.ErrInvalid:
+ http.Error(w, "Invalid command", http.StatusBadRequest)
+ default:
+ http.Error(w, "Failed to write command: "+err.Error(), http.StatusInternalServerError)
+ }
+ return
+ }
+
+ // Success: return 204 No Content
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func HandleIsSSCMEnabled(w http.ResponseWriter, r *http.Request) {
+ // Only allow GET requests
+ if r.Method != http.MethodGet {
+ http.Error(w, "Only GET requests are allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ // Check if SSCM is enabled
+ if !config.GetIsSSCMEnabled() {
+ http.Error(w, "SSCM is disabled", http.StatusForbidden)
+ return
+ }
+
+ // Success: return 200 OK
+ w.WriteHeader(http.StatusOK)
+}
diff --git a/src/api/commands.go b/src/api/sscmapi/sscm.go
similarity index 99%
rename from src/api/commands.go
rename to src/api/sscmapi/sscm.go
index 99249861..28ed49c8 100644
--- a/src/api/commands.go
+++ b/src/api/sscmapi/sscm.go
@@ -1,4 +1,4 @@
-package api
+package sscmapi
import (
"encoding/json"
diff --git a/src/api/http-sse.go b/src/api/sseapi/sse.go
similarity index 99%
rename from src/api/http-sse.go
rename to src/api/sseapi/sse.go
index 4d0010b3..d0533438 100644
--- a/src/api/http-sse.go
+++ b/src/api/sseapi/sse.go
@@ -1,4 +1,4 @@
-package api
+package sseapi
import (
"net/http"
diff --git a/src/api/svelteui.go b/src/api/svelteui.go
deleted file mode 100644
index 1bfd3631..00000000
--- a/src/api/svelteui.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package api
-
-import (
- "encoding/json"
- "io"
- "io/fs"
- "net/http"
- "sync"
-
- "github.com/SteamServerUI/SteamServerUI/v7/src/config"
- "github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
- "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
-)
-
-var reloadMu sync.Mutex
-
-func ServeSvelteUI(w http.ResponseWriter, r *http.Request) {
- htmlFS, err := fs.Sub(config.V1UIFS, "SSUI/onboard_bundled/v2")
- if err != nil {
- http.Error(w, "Error accessing Svelte UI: "+err.Error(), http.StatusInternalServerError)
- return
- }
-
- htmlFile, err := htmlFS.Open("index.html")
- if err != nil {
- http.Error(w, "Error reading Svelte UI: "+err.Error(), http.StatusInternalServerError)
- return
- }
- defer htmlFile.Close()
-
- // Stream the file content to the response
- w.Header().Set("Content-Type", "text/html; charset=utf-8")
- _, err = io.Copy(w, htmlFile)
- if err != nil {
- http.Error(w, "Error writing Svelte UI: "+err.Error(), http.StatusInternalServerError)
- return
- }
-}
-
-func HandleReloadAll(w http.ResponseWriter, r *http.Request) {
- logger.API.Debug("Received reloadbackend request from API")
- reloadMu.Lock()
- defer reloadMu.Unlock()
- // accept only GET requests
- if r.Method != http.MethodGet {
- http.Error(w, "Only GET requests are allowed", http.StatusMethodNotAllowed)
- return
- }
- // Reload all loaders
- loader.ReloadBackend()
-
- // Set response headers and write JSON response
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusAccepted)
- if err := json.NewEncoder(w).Encode(map[string]string{"status": "OK"}); err != nil {
- http.Error(w, "Failed to write response", http.StatusInternalServerError)
- return
- }
-}
diff --git a/src/api/systeminfo.go b/src/api/sysinfoapi/sysinfo.go
similarity index 98%
rename from src/api/systeminfo.go
rename to src/api/sysinfoapi/sysinfo.go
index a3386555..b4725059 100644
--- a/src/api/systeminfo.go
+++ b/src/api/sysinfoapi/sysinfo.go
@@ -1,4 +1,4 @@
-package api
+package sysinfoapi
import (
"encoding/json"
From 4b0f06b4d63b12478e4b3dfd989d6c82aa3379b8 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Tue, 14 Oct 2025 03:13:24 +0200
Subject: [PATCH 45/93] refactor activateAuth (FinalizeSetup) to use setters
instead of legacy configchanger
---
src/api/httpauth/activate.go | 21 +++++++--------------
1 file changed, 7 insertions(+), 14 deletions(-)
diff --git a/src/api/httpauth/activate.go b/src/api/httpauth/activate.go
index a807a603..1fbd0647 100644
--- a/src/api/httpauth/activate.go
+++ b/src/api/httpauth/activate.go
@@ -5,7 +5,6 @@ import (
"net/http"
"github.com/SteamServerUI/SteamServerUI/v7/src/config"
- "github.com/SteamServerUI/SteamServerUI/v7/src/config/configchanger"
"github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
"github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
@@ -21,29 +20,24 @@ func ActivateAuthHandler(w http.ResponseWriter, r *http.Request) {
return
}
- // Load existing config to update it
- newConfig, err := config.LoadConfig()
+ // Mark setup as complete and enable auth
+ err := config.SetIsFirstTimeSetup(false)
if err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
- json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error - Failed to load config"})
+ json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error - Failed to SetIsFirstTimeSetup in config"})
return
}
-
- // Mark setup as complete and enable auth
- config.SetIsFirstTimeSetup(false)
- isTrue := true
- newConfig.AuthEnabled = &isTrue // Set the pointer to true
-
- // Save the updated config
- err = configchanger.SaveConfig(newConfig)
+ err = config.SetAuthEnabled(true)
if err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
- json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error - Failed to save config"})
+ json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error - Failed to SetAuthEnabled in config"})
return
}
+ loader.ReloadBackend()
+
logger.Web.Info("User Setup finalized successfully")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
@@ -51,5 +45,4 @@ func ActivateAuthHandler(w http.ResponseWriter, r *http.Request) {
"message": "Setup finalized successfully",
"restart_hint": "You will be redirected to the login page...",
})
- loader.ReloadBackend()
}
From 1b1a4656b7615378365abb1e1983a6aff67f9c68 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Tue, 14 Oct 2025 03:58:37 +0200
Subject: [PATCH 46/93] adds jwt based api keys (long-lived user tokens for
now)
---
src/api/httpauth/registerapikey.go | 102 ++++++++++++++++++
.../httpauth/{register.go => registeruser.go} | 8 ++
src/api/routes.go | 3 +-
src/core/security/auth.go | 11 +-
4 files changed, 121 insertions(+), 3 deletions(-)
create mode 100644 src/api/httpauth/registerapikey.go
rename src/api/httpauth/{register.go => registeruser.go} (84%)
diff --git a/src/api/httpauth/registerapikey.go b/src/api/httpauth/registerapikey.go
new file mode 100644
index 00000000..7fcb1b0a
--- /dev/null
+++ b/src/api/httpauth/registerapikey.go
@@ -0,0 +1,102 @@
+package httpauth
+
+import (
+ "encoding/json"
+ "net/http"
+ "time"
+
+ "github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/security"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/google/uuid"
+)
+
+func RegisterAPIKeyHandler(w http.ResponseWriter, r *http.Request) {
+
+ // Handle preflight OPTIONS requests
+ if r.Method == http.MethodOptions {
+ w.WriteHeader(http.StatusOK)
+ return
+ }
+
+ // Allow only GET or POST methods
+ if r.Method != http.MethodGet && r.Method != http.MethodPost {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Method Not Allowed"})
+ return
+ }
+
+ // Set default duration for GET requests, require duration for POST
+ durationMonths := 1
+ if r.Method == http.MethodPost {
+ var reqBody struct {
+ DurationMonths *int `json:"durationMonths"` // Use pointer to distinguish between 0 and unspecified
+ }
+ err := json.NewDecoder(r.Body).Decode(&reqBody)
+ if err != nil {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusBadRequest)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Bad Request - Invalid JSON"})
+ return
+ }
+ if reqBody.DurationMonths == nil {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusBadRequest)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Bad Request - durationMonths is required for POST"})
+ return
+ }
+ if *reqBody.DurationMonths <= 0 {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusBadRequest)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Bad Request - Duration must be positive"})
+ return
+ }
+ if *reqBody.DurationMonths > 120 {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusBadRequest)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Bad Request - Duration must be less than 10 years"})
+ return
+ }
+ durationMonths = *reqBody.DurationMonths
+ }
+
+ var creds security.UserCredentials
+
+ // Generate a random UUID as the username
+ creds.Username = "apikey-" + uuid.NewString()
+
+ // Hash a random UUID as the password
+ hashedPassword, err := security.HashPassword(uuid.NewString())
+ if err != nil {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error"})
+ return
+ }
+
+ // Initialize Users map if nil
+ if config.GetUsers() == nil {
+ config.SetUsers(make(map[string]string))
+ }
+
+ // Add or update the user
+ config.SetUsers(map[string]string{creds.Username: hashedPassword})
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ apikey, err := security.GenerateJWT(creds.Username, durationMonths)
+ expires := time.Now().AddDate(0, durationMonths, 0)
+ if err != nil {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Internal Server Error"})
+ return
+ }
+ json.NewEncoder(w).Encode(map[string]string{
+ "message": "APIKey registered successfully",
+ "apikey": apikey,
+ "expires": expires.Format(time.RFC3339),
+ })
+ logger.Security.Infof("APIKey %s registered successfully. Expires: %s ", creds.Username, expires.Format(time.RFC3339))
+}
diff --git a/src/api/httpauth/register.go b/src/api/httpauth/registeruser.go
similarity index 84%
rename from src/api/httpauth/register.go
rename to src/api/httpauth/registeruser.go
index 5a901248..fa2fe0d2 100644
--- a/src/api/httpauth/register.go
+++ b/src/api/httpauth/registeruser.go
@@ -3,6 +3,7 @@ package httpauth
import (
"encoding/json"
"net/http"
+ "strings"
"github.com/SteamServerUI/SteamServerUI/v7/src/config"
"github.com/SteamServerUI/SteamServerUI/v7/src/core/security"
@@ -26,6 +27,13 @@ func RegisterUserHandler(w http.ResponseWriter, r *http.Request) {
return
}
+ if strings.HasPrefix(creds.Username, "apikey-") {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusBadRequest)
+ json.NewEncoder(w).Encode(map[string]string{"error": "Bad Request - Invalid Username"})
+ return
+ }
+
// Hash the password
hashedPassword, err := security.HashPassword(creds.Password)
if err != nil {
diff --git a/src/api/routes.go b/src/api/routes.go
index 8821672d..cba56359 100644
--- a/src/api/routes.go
+++ b/src/api/routes.go
@@ -79,7 +79,8 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
protectedMux.HandleFunc("/api/v2/custom-detections/delete/", detectionmgr.HandleDeleteCustomDetection)
// Authentication
protectedMux.HandleFunc("/changeuser", pages.ServeTwoBoxFormTemplate)
- protectedMux.HandleFunc("/api/v2/auth/adduser", httpauth.RegisterUserHandler) // user registration and change password
+ protectedMux.HandleFunc("/api/v2/auth/adduser", httpauth.RegisterUserHandler) // user registration and change password
+ protectedMux.HandleFunc("/api/v2/auth/setup/apikey", httpauth.RegisterAPIKeyHandler) // apikey registration and change password
protectedMux.HandleFunc("/api/v2/auth/whoami", httpauth.WhoAmIHandler)
// Setup
diff --git a/src/core/security/auth.go b/src/core/security/auth.go
index 0194fbef..3aeb57fa 100644
--- a/src/core/security/auth.go
+++ b/src/core/security/auth.go
@@ -4,6 +4,7 @@ package security
//repurposed from a Jacksonthemaster private repo
import (
+ "strings"
"time"
"github.com/SteamServerUI/SteamServerUI/v7/src/config"
@@ -19,8 +20,15 @@ type UserCredentials struct {
}
// GenerateJWT creates a JWT for a given username
-func GenerateJWT(username string) (string, error) {
+func GenerateJWT(username string, apikeyduration ...int) (string, error) {
expirationTime := time.Now().Add(time.Duration(config.GetAuthTokenLifetime()) * time.Minute)
+ if strings.HasPrefix(username, "apikey-") {
+ durationMonths := 1
+ if len(apikeyduration) > 0 {
+ durationMonths = apikeyduration[0]
+ }
+ expirationTime = time.Now().AddDate(0, durationMonths, 0)
+ }
claims := &jwt.MapClaims{
"exp": expirationTime.Unix(),
"iss": "StationeersServerUI",
@@ -37,7 +45,6 @@ func GenerateJWT(username string) (string, error) {
// ValidateCredentials checks username and password against stored users
func ValidateCredentials(creds UserCredentials) (bool, error) {
- // Placeholder: assumes config.Users is a map[string]string (username -> hashed password)
storedHash, exists := config.GetUsers()[creds.Username]
if !exists {
return false, nil
From 704f7c0d08666c755f00adad2beb6fac7cb3311e Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Wed, 15 Oct 2025 16:35:40 +0200
Subject: [PATCH 47/93] added a logger api that can log to logger.Plugin from
an api request
---
src/api/httpstart.go | 7 +++-
src/api/pluginsapi/logs.go | 62 +++++++++++++++++++++++++++++++++
src/api/routes.go | 9 ++++-
src/api/socketapi/socket-lin.go | 5 +--
src/api/socketapi/socket-win.go | 5 +--
src/logger/logger.go | 3 ++
6 files changed, 85 insertions(+), 6 deletions(-)
create mode 100644 src/api/pluginsapi/logs.go
diff --git a/src/api/httpstart.go b/src/api/httpstart.go
index 5074e1fb..0a163c4d 100644
--- a/src/api/httpstart.go
+++ b/src/api/httpstart.go
@@ -24,7 +24,12 @@ func (cl *APIServerLogger) Write(p []byte) (n int, err error) {
func StartWebServer(wg *sync.WaitGroup) {
logger.Web.Info("Starting API services...")
- mux, protectedMux := SetupRoutes()
+ mux, protectedMux := SetupAPIRoutes()
+
+ // if debug mode, add socket only routes to http api for easy testing
+ if config.IsDebugMode {
+ SetupSocketAPIRoutes(protectedMux)
+ }
// Apply middleware only to protected routes
mux.Handle("/", middleware.AuthMiddleware(protectedMux)) // Wrap protected routes under root
diff --git a/src/api/pluginsapi/logs.go b/src/api/pluginsapi/logs.go
new file mode 100644
index 00000000..d3caea93
--- /dev/null
+++ b/src/api/pluginsapi/logs.go
@@ -0,0 +1,62 @@
+package pluginsapi
+
+import (
+ "encoding/json"
+ "net/http"
+ "strings"
+
+ "github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+)
+
+// a http handler that can be used to use logger.Plugin from the api
+func PluginLogHandler(w http.ResponseWriter, r *http.Request) {
+ // Set response header to JSON
+ w.Header().Set("Content-Type", "application/json")
+
+ // Only handle POST requests
+ if r.Method != http.MethodPost {
+ http.Error(w, `{"status":"error","message":"Method not allowed"}`, http.StatusMethodNotAllowed)
+ return
+ }
+
+ // Define struct for incoming JSON
+ type logRequest struct {
+ Level string `json:"level"`
+ PluginName string `json:"pluginname"`
+ Message string `json:"message"`
+ }
+
+ // Decode JSON request body
+ var req logRequest
+ decoder := json.NewDecoder(r.Body)
+ if err := decoder.Decode(&req); err != nil {
+ http.Error(w, `{"status":"error","message":"Invalid JSON format"}`, http.StatusBadRequest)
+ return
+ }
+
+ // Validate input
+ if req.Level == "" || req.PluginName == "" || req.Message == "" {
+ http.Error(w, `{"status":"error","message":"Missing required fields"}`, http.StatusBadRequest)
+ return
+ }
+
+ msg := "<" + req.PluginName + "> " + req.Message
+ // Log based on level
+ switch strings.ToLower(req.Level) {
+ case "info":
+ logger.Plugin.Info(msg)
+ case "warn":
+ logger.Plugin.Warn(msg)
+ case "error":
+ logger.Plugin.Error(msg)
+ case "debug":
+ logger.Plugin.Debug(msg)
+ default:
+ http.Error(w, `{"status":"error","message":"Invalid log level"}`, http.StatusBadRequest)
+ return
+ }
+
+ // Write success response
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(map[string]string{"status": "success"})
+}
diff --git a/src/api/routes.go b/src/api/routes.go
index cba56359..26d912ae 100644
--- a/src/api/routes.go
+++ b/src/api/routes.go
@@ -7,6 +7,7 @@ import (
"github.com/SteamServerUI/SteamServerUI/v7/src/api/httpauth"
"github.com/SteamServerUI/SteamServerUI/v7/src/api/legacyapi"
"github.com/SteamServerUI/SteamServerUI/v7/src/api/pages"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/api/pluginsapi"
"github.com/SteamServerUI/SteamServerUI/v7/src/api/runfileapi"
"github.com/SteamServerUI/SteamServerUI/v7/src/api/sscmapi"
"github.com/SteamServerUI/SteamServerUI/v7/src/api/sseapi"
@@ -18,7 +19,8 @@ import (
"github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/settings"
)
-func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
+// SetupAPIRoutes sets up API routes used by B O T H the web and socket servers
+func SetupAPIRoutes() (*http.ServeMux, *http.ServeMux) {
// Set up handlers with auth middleware
mux := http.NewServeMux() // Use a mux to apply middleware globally
@@ -110,3 +112,8 @@ func SetupRoutes() (*http.ServeMux, *http.ServeMux) {
return mux, protectedMux
}
+
+// SetupSocketAPIRoutes adds routes that are E X C L U S I V E L Y available via sockets (if debug mode is enabled, these routes are added to the http api as well)
+func SetupSocketAPIRoutes(APIMux *http.ServeMux) {
+ APIMux.HandleFunc("/api/v2/plugins/log", pluginsapi.PluginLogHandler)
+}
diff --git a/src/api/socketapi/socket-lin.go b/src/api/socketapi/socket-lin.go
index 685a8933..72a32e29 100644
--- a/src/api/socketapi/socket-lin.go
+++ b/src/api/socketapi/socket-lin.go
@@ -26,8 +26,9 @@ func StartSocketServer(wg *sync.WaitGroup) {
}
// Set up routes
- mux, protectedMux := api.SetupRoutes()
- mux.Handle("/", protectedMux)
+ mux, APIMux := api.SetupAPIRoutes()
+ api.SetupSocketAPIRoutes(APIMux)
+ mux.Handle("/", APIMux)
// Create Unix socket listener
listener, err := net.Listen("unix", socketPath)
diff --git a/src/api/socketapi/socket-win.go b/src/api/socketapi/socket-win.go
index 687a1525..95598bc3 100644
--- a/src/api/socketapi/socket-win.go
+++ b/src/api/socketapi/socket-win.go
@@ -20,8 +20,9 @@ func StartSocketServer(wg *sync.WaitGroup) {
logger.Socket.Info("Starting named pipe server...")
// Set up routes
- mux, protectedMux := api.SetupRoutes()
- mux.Handle("/", protectedMux)
+ mux, httpAPIMux := api.SetupAPIRoutes()
+ api.SetupSocketAPIRoutes(httpAPIMux)
+ mux.Handle("/", httpAPIMux)
// Create named pipe listener
listener, err := winio.ListenPipe(pipePath, nil)
diff --git a/src/logger/logger.go b/src/logger/logger.go
index 1732d345..fa3af1e5 100644
--- a/src/logger/logger.go
+++ b/src/logger/logger.go
@@ -27,6 +27,7 @@ var (
Localization = &Logger{suffix: SYS_LOCALIZATION}
Runfile = &Logger{suffix: SYS_RUNFILE}
Socket = &Logger{suffix: SYS_SOCKET}
+ Plugin = &Logger{suffix: SYS_PLUGIN}
)
// Severity Levels
@@ -54,6 +55,7 @@ const (
SYS_LOCALIZATION = "LOCALIZATION"
SYS_RUNFILE = "RUNFILE"
SYS_SOCKET = "SOCKET"
+ SYS_PLUGIN = "PLUGIN"
)
const (
@@ -81,6 +83,7 @@ var subsystemColors = map[string]string{
SYS_SECURITY: colorRed, // Screams "pay attention"
SYS_LOCALIZATION: colorCyan, // Matches WEB, localization-related
SYS_SOCKET: colorCyan, // Matches WEB, socket-related
+ SYS_PLUGIN: colorCyan, // Matches WEB, plugin-related
}
// Global channels and mutex for all loggers
From 8e3d685471b74c54e0c6a03f35a19d461ef195c7 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Wed, 15 Oct 2025 21:13:43 +0200
Subject: [PATCH 48/93] add GetArgValue method to RunFile for retrieving
runtime values by flag
---
src/steamserverui/runfile/args.go | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/steamserverui/runfile/args.go b/src/steamserverui/runfile/args.go
index d3c34898..be80ec86 100644
--- a/src/steamserverui/runfile/args.go
+++ b/src/steamserverui/runfile/args.go
@@ -145,6 +145,18 @@ func (rf *RunFile) getAllArgs() []GameArg {
return allArgs
}
+func (rf *RunFile) GetArgValue(flag string) string {
+ for category := range rf.Args {
+ for i := range rf.Args[category] {
+ if rf.Args[category][i].Flag != flag {
+ continue
+ }
+ return rf.Args[category][i].RuntimeValue
+ }
+ }
+ return ""
+}
+
// LoadRunfile loads the runfile and stores it in CurrentRunfile
func LoadRunfile(gameName, runFilesFolder string) error {
runfileMutex.Lock()
From 45aa8959692a655b4b60b54187045891b38d910d Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Wed, 15 Oct 2025 21:14:55 +0200
Subject: [PATCH 49/93] remove deprecated staioneers specific server
configuration fields and related getters/setters in favor of runfiles
---
SSUI/onboard_bundled/ui/config.html | 158 -----------------------
src/api/pages/TwoBoxForm.go | 18 +--
src/api/pages/configpage.go | 91 -------------
src/config/config.go | 143 +++------------------
src/config/getters.go | 152 ----------------------
src/config/helpers.go | 8 --
src/config/setters.go | 193 ----------------------------
src/config/vars.go | 47 ++-----
src/core/loader/helpers.go | 25 +---
9 files changed, 35 insertions(+), 800 deletions(-)
diff --git a/SSUI/onboard_bundled/ui/config.html b/SSUI/onboard_bundled/ui/config.html
index b080d61c..3bf8715c 100644
--- a/SSUI/onboard_bundled/ui/config.html
+++ b/SSUI/onboard_bundled/ui/config.html
@@ -53,11 +53,6 @@ {{.UIText_ServerConfig}}
- {{.UIText_BasicSettings}}
- {{if eq .IsNewTerrainAndSaveSystemTrueSelected "selected"}}
- {{.UIText_TerrainSettings}}
- {{end}}
- {{.UIText_NetworkSettings}}
{{.UIText_AdvancedSettings}}
@@ -68,163 +63,10 @@
{{.UIText_PleaseSelectSection}}
{{.UIText_UseWizardAlternative}}
-
-
{{.UIText_BasicServerSettings}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{.UIText_NetworkConfiguration}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{.UIText_AdvancedConfiguration}}
-
-
-
-
-
-
{{.UIText_AutoRestartServerTimer}}:
0 {
- SaveName = parts[0]
- fmt.Println("SaveName: " + SaveName)
- }
- if len(parts) > 1 {
- WorldID = parts[1]
- fmt.Println("WorldID: " + WorldID)
- }
- cfg.SaveInfo = ""
- }
-
- if GameBranch != "public" && GameBranch != "beta" {
- IsNewTerrainAndSaveSystem = false
- } else {
- IsNewTerrainAndSaveSystem = true
- }
+ //if GameBranch != "public" && GameBranch != "beta" {
+ // IsNewTerrainAndSaveSystem = false
+ //} else {
+ // IsNewTerrainAndSaveSystem = true
+ //}
// Set backup paths for old or new style saves
- if IsNewTerrainAndSaveSystem {
- // use new new style autosave folder
- ConfiguredBackupDir = filepath.Join("./saves/", SaveName, "autosave")
- } else {
- // use old style Backups folder
- ConfiguredBackupDir = filepath.Join("./saves/", SaveName, "Backup")
- }
- // use Safebackups folder either way.
- ConfiguredSafeBackupDir = filepath.Join("./saves/", SaveName, "Safebackups")
+ //if IsNewTerrainAndSaveSystem {
+ // // use new new style autosave folder
+ // ConfiguredBackupDir = filepath.Join("./saves/", SaveName, "autosave")
+ //} else {
+ // // use old style Backups folder
+ // ConfiguredBackupDir = filepath.Join("./saves/", SaveName, "Backup")
+ //}
+ //// use Safebackups folder either way.
+ //ConfiguredSafeBackupDir = filepath.Join("./saves/", SaveName, "Safebackups")
safeSaveConfig()
}
@@ -339,30 +249,7 @@ func safeSaveConfig() error {
BackupKeepMonthlyFor: int(BackupKeepMonthlyFor / time.Hour), // Convert to hours
BackupCleanupInterval: int(BackupCleanupInterval / time.Hour), // Convert to hours
BackupWaitTime: int(BackupWaitTime / time.Second), // Convert to seconds
- IsNewTerrainAndSaveSystem: &IsNewTerrainAndSaveSystem,
GameBranch: GameBranch,
- Difficulty: Difficulty,
- StartCondition: StartCondition,
- StartLocation: StartLocation,
- ServerName: ServerName,
- SaveName: SaveName,
- WorldID: WorldID,
- ServerMaxPlayers: ServerMaxPlayers,
- ServerPassword: ServerPassword,
- ServerAuthSecret: ServerAuthSecret,
- AdminPassword: AdminPassword,
- GamePort: GamePort,
- UpdatePort: UpdatePort,
- UPNPEnabled: &UPNPEnabled,
- AutoSave: &AutoSave,
- SaveInterval: SaveInterval,
- AutoPauseServer: &AutoPauseServer,
- LocalIpAddress: LocalIpAddress,
- StartLocalHost: &StartLocalHost,
- ServerVisible: &ServerVisible,
- UseSteamP2P: &UseSteamP2P,
- ExePath: ExePath,
- AdditionalParams: AdditionalParams,
Users: Users,
AuthEnabled: &AuthEnabled,
JwtKey: JwtKey,
diff --git a/src/config/getters.go b/src/config/getters.go
index ba951bea..0e7b93eb 100644
--- a/src/config/getters.go
+++ b/src/config/getters.go
@@ -109,158 +109,12 @@ func GetBackupCleanupInterval() time.Duration {
return BackupCleanupInterval
}
-func GetIsNewTerrainAndSaveSystem() bool {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return IsNewTerrainAndSaveSystem
-}
-
func GetGameBranch() string {
ConfigMu.RLock()
defer ConfigMu.RUnlock()
return GameBranch
}
-func GetDifficulty() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return Difficulty
-}
-
-func GetStartCondition() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return StartCondition
-}
-
-func GetStartLocation() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return StartLocation
-}
-
-func GetServerName() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return ServerName
-}
-
-// special getter for backwards compatibility with SaveInfo
-func GetLegacySaveInfo() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- saveinfo := SaveName + ";" + WorldID
- return saveinfo
-}
-
-func GetSaveName() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return SaveName
-}
-
-func GetWorldID() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return WorldID
-}
-
-func GetServerMaxPlayers() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return ServerMaxPlayers
-}
-
-func GetServerPassword() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return ServerPassword
-}
-
-func GetServerAuthSecret() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return ServerAuthSecret
-}
-
-func GetAdminPassword() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return AdminPassword
-}
-
-func GetGamePort() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return GamePort
-}
-
-func GetUpdatePort() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return UpdatePort
-}
-
-func GetUPNPEnabled() bool {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return UPNPEnabled
-}
-
-func GetAutoSave() bool {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return AutoSave
-}
-
-func GetSaveInterval() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return SaveInterval
-}
-
-func GetAutoPauseServer() bool {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return AutoPauseServer
-}
-
-func GetLocalIpAddress() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return LocalIpAddress
-}
-
-func GetStartLocalHost() bool {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return StartLocalHost
-}
-
-func GetServerVisible() bool {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return ServerVisible
-}
-
-func GetUseSteamP2P() bool {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return UseSteamP2P
-}
-
-func GetExePath() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return ExePath
-}
-
-func GetAdditionalParams() string {
- ConfigMu.RLock()
- defer ConfigMu.RUnlock()
- return AdditionalParams
-}
-
func GetUsers() map[string]string {
ConfigMu.RLock()
defer ConfigMu.RUnlock()
@@ -526,12 +380,6 @@ func GetRunfilesFolder() string {
return RunFilesFolder
}
-func GetIsStationeersMode() bool {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
- return IsStationeersMode
-}
-
// GetRunFilesFolder returns the RunFilesFolder
func GetRunFilesFolder() string {
ConfigMu.Lock()
diff --git a/src/config/helpers.go b/src/config/helpers.go
index 5aaa4d68..d283581d 100644
--- a/src/config/helpers.go
+++ b/src/config/helpers.go
@@ -8,7 +8,6 @@ import (
"encoding/base64"
"fmt"
"os"
- "runtime"
"strconv"
"strings"
)
@@ -89,13 +88,6 @@ func getUsers(jsonValue map[string]string, envKey string, defaultValue map[strin
return defaultValue
}
-func getDefaultExePath() string {
- if runtime.GOOS == "windows" {
- return "./rocketstation_DedicatedServer.exe"
- }
- return "./rocketstation_DedicatedServer.x86_64"
-}
-
func generateJwtKey() string {
// ensure we return JwtKey if it's set
diff --git a/src/config/setters.go b/src/config/setters.go
index 50a42b9b..0791fd9f 100644
--- a/src/config/setters.go
+++ b/src/config/setters.go
@@ -78,22 +78,6 @@ func SetNoSanityCheck(value bool) error {
return nil
}
-func SetSaveName(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- SaveName = value
- return nil
-}
-
-func SetWorldID(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- WorldID = value
- return nil
-}
-
func SetUseRunfiles(value bool) error {
ConfigMu.Lock()
defer ConfigMu.Unlock()
@@ -235,183 +219,6 @@ func SetGameBranch(value string) error {
return safeSaveConfig()
}
-func SetDifficulty(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- Difficulty = value
- return safeSaveConfig()
-}
-
-func SetStartCondition(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- StartCondition = value
- return safeSaveConfig()
-}
-
-func SetStartLocation(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- StartLocation = value
- return safeSaveConfig()
-}
-
-func SetIsNewTerrainAndSaveSystem(value bool) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- IsNewTerrainAndSaveSystem = value
- return safeSaveConfig()
-}
-
-// Server Settings
-func SetServerName(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- ServerName = value
- return safeSaveConfig()
-}
-
-func SetSaveInfo(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- SaveInfo = value
- return safeSaveConfig()
-}
-
-func SetServerMaxPlayers(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- ServerMaxPlayers = value
- return safeSaveConfig()
-}
-
-func SetServerPassword(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- ServerPassword = value
- return safeSaveConfig()
-}
-
-func SetServerAuthSecret(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- ServerAuthSecret = value
- return safeSaveConfig()
-}
-
-func SetAdminPassword(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- AdminPassword = value
- return safeSaveConfig()
-}
-
-func SetGamePort(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- GamePort = value
- return safeSaveConfig()
-}
-
-func SetUpdatePort(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- UpdatePort = value
- return safeSaveConfig()
-}
-
-func SetUPNPEnabled(value bool) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- UPNPEnabled = value
- return safeSaveConfig()
-}
-
-func SetAutoSave(value bool) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- AutoSave = value
- return safeSaveConfig()
-}
-
-func SetSaveInterval(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- SaveInterval = value
- return safeSaveConfig()
-}
-
-func SetAutoPauseServer(value bool) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- AutoPauseServer = value
- return safeSaveConfig()
-}
-
-func SetLocalIpAddress(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- LocalIpAddress = value
- return safeSaveConfig()
-}
-
-func SetStartLocalHost(value bool) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- StartLocalHost = value
- return safeSaveConfig()
-}
-
-func SetServerVisible(value bool) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- ServerVisible = value
- return safeSaveConfig()
-}
-
-func SetUseSteamP2P(value bool) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- UseSteamP2P = value
- return safeSaveConfig()
-}
-
-func SetExePath(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- ExePath = value
- return safeSaveConfig()
-}
-
-func SetAdditionalParams(value string) error {
- ConfigMu.Lock()
- defer ConfigMu.Unlock()
-
- AdditionalParams = value
- return safeSaveConfig()
-}
-
func SetAutoStartServerOnStartup(value bool) error {
ConfigMu.Lock()
defer ConfigMu.Unlock()
diff --git a/src/config/vars.go b/src/config/vars.go
index e9bcb93b..0cee0dfe 100644
--- a/src/config/vars.go
+++ b/src/config/vars.go
@@ -17,32 +17,6 @@ if writes only happen via applyConfig or with ConfigMu locked. Uses getters wher
var ConfigMu sync.RWMutex
-// Game Server configuration
-var (
- ServerName string
- ServerMaxPlayers string
- ServerPassword string
- ServerAuthSecret string
- AdminPassword string
- GamePort string
- UpdatePort string
- LocalIpAddress string
- ServerVisible bool
- UseSteamP2P bool
- AdditionalParams string
- UPNPEnabled bool
- StartLocalHost bool
- SaveInfo string
- SaveName string
- WorldID string
- SaveInterval string
- AutoPauseServer bool
- AutoSave bool
- Difficulty string
- StartCondition string
- StartLocation string
-)
-
// Logging, debugging and misc
var (
IsDebugMode bool //only used for pprof server, keep it like this and check the log level instead. Debug = 10
@@ -52,7 +26,6 @@ var (
SSEMessageBufferSize = 2000
MaxSSEConnections = 20
GameServerAppID = "600760"
- ExePath string
GameBranch string
SubsystemFilters []string
AutoRestartServerTimer string
@@ -67,7 +40,6 @@ var (
var (
UseRunfiles bool
RunfileIdentifier string
- IsStationeersMode bool
)
// Runtime only variables
@@ -99,16 +71,15 @@ var (
// Backup and cleanup settings
var (
- IsCleanupEnabled bool
- BackupKeepLastN int
- BackupKeepDailyFor time.Duration
- BackupKeepWeeklyFor time.Duration
- BackupKeepMonthlyFor time.Duration
- BackupCleanupInterval time.Duration
- ConfiguredBackupDir string
- ConfiguredSafeBackupDir string
- BackupWaitTime time.Duration
- IsNewTerrainAndSaveSystem bool
+ IsCleanupEnabled bool
+ BackupKeepLastN int
+ BackupKeepDailyFor time.Duration
+ BackupKeepWeeklyFor time.Duration
+ BackupKeepMonthlyFor time.Duration
+ BackupCleanupInterval time.Duration
+ ConfiguredBackupDir string
+ ConfiguredSafeBackupDir string
+ BackupWaitTime time.Duration
)
// Authentication and security
diff --git a/src/core/loader/helpers.go b/src/core/loader/helpers.go
index 5cff29b7..4b7e1cc9 100644
--- a/src/core/loader/helpers.go
+++ b/src/core/loader/helpers.go
@@ -48,29 +48,8 @@ func PrintConfigDetails(logLevel ...string) {
// Server Configuration
server := map[string]string{
- "GameBranch": config.GetGameBranch(),
- "ServerName": config.GetServerName(),
- "WorldName": config.GetSaveName(),
- "BackupWorldName": config.GetWorldID(),
- "ServerMaxPlayers": config.GetServerMaxPlayers(),
- "GamePort": config.GetGamePort(),
- "UpdatePort": config.GetUpdatePort(),
- "UPNPEnabled": fmt.Sprintf("%v", config.GetUPNPEnabled()),
- "AutoSave": fmt.Sprintf("%v", config.GetAutoSave()),
- "SaveInterval": config.GetSaveInterval(),
- "AutoPauseServer": fmt.Sprintf("%v", config.GetAutoPauseServer()),
- "LocalIpAddress": config.GetLocalIpAddress(),
- "StartLocalHost": fmt.Sprintf("%v", config.GetStartLocalHost()),
- "ServerVisible": fmt.Sprintf("%v", config.GetServerVisible()),
- "UseSteamP2P": fmt.Sprintf("%v", config.GetUseSteamP2P()),
- "ExePath": config.GetExePath(),
- "AdditionalParams": config.GetAdditionalParams(),
- "GameServerAppID": config.GetGameServerAppID(),
- "Difficulty": config.GetDifficulty(),
- "StartCondition": config.GetStartCondition(),
- "StartLocation": config.GetStartLocation(),
- "SaveInfo": config.GetLegacySaveInfo(),
- "IsNewTerrainAndSaveSystem": fmt.Sprintf("%v", config.GetIsNewTerrainAndSaveSystem()),
+ "GameBranch": config.GetGameBranch(),
+ "GameServerAppID": config.GetGameServerAppID(),
}
printSection("Server Configuration", server)
From 3933d983f7a65f3c1287b09c1fd0fef56b3be3e7 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Wed, 15 Oct 2025 21:16:45 +0200
Subject: [PATCH 50/93] firstpass on making the BMv2 a standalone plugin by
removing config references and replacing them with interim functions to not
throw errors. BMv2 will be removed from the codebase and refactored into a
plugin later.
---
src/core/loader/loader.go | 8 +++-----
src/managers/backupmgr/backupinterface.go | 7 ++++---
src/managers/backupmgr/manager.go | 2 +-
3 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/src/core/loader/loader.go b/src/core/loader/loader.go
index 4d1c15fe..e269ea2c 100644
--- a/src/core/loader/loader.go
+++ b/src/core/loader/loader.go
@@ -65,11 +65,9 @@ func ReloadBepInEx() {
}
func ReloadStationeersBackupManager() {
- if config.GetIsStationeersMode() {
- if err := backupmgr.ReloadBackupManagerFromConfig(); err != nil {
- logger.Backup.Error("Failed to reload backup manager: " + err.Error())
- return
- }
+ if err := backupmgr.ReloadBackupManagerFromConfig(); err != nil {
+ logger.Backup.Error("Failed to reload backup manager: " + err.Error())
+ return
}
}
diff --git a/src/managers/backupmgr/backupinterface.go b/src/managers/backupmgr/backupinterface.go
index 1c3d55f0..a0879b2c 100644
--- a/src/managers/backupmgr/backupinterface.go
+++ b/src/managers/backupmgr/backupinterface.go
@@ -6,6 +6,7 @@ import (
"github.com/SteamServerUI/SteamServerUI/v7/src/config"
"github.com/SteamServerUI/SteamServerUI/v7/src/logger"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/runfile"
"github.com/google/uuid"
)
@@ -62,9 +63,9 @@ func GetBackupConfig() BackupConfig {
id := uuid.New()
bmIdentifier := "[BM" + id.String()[:6] + "]:"
return BackupConfig{
- WorldName: config.GetSaveName(),
- BackupDir: config.GetConfiguredBackupDir(),
- SafeBackupDir: config.GetConfiguredSafeBackupDir(),
+ WorldName: runfile.CurrentRunfile.GetArgValue("SaveName"),
+ BackupDir: "./saves/" + runfile.CurrentRunfile.GetArgValue("SaveName") + "/autosave",
+ SafeBackupDir: "./saves/" + runfile.CurrentRunfile.GetArgValue("SaveName") + "/Safebackups",
WaitTime: 30 * time.Second, // not sure why we are not using config.BackupWaitTime here, but ill not touch it in this commit (config rework)
RetentionPolicy: RetentionPolicy{
KeepLastN: config.GetBackupKeepLastN(),
diff --git a/src/managers/backupmgr/manager.go b/src/managers/backupmgr/manager.go
index 5d61ce12..5ea5de20 100644
--- a/src/managers/backupmgr/manager.go
+++ b/src/managers/backupmgr/manager.go
@@ -148,7 +148,7 @@ func (m *BackupManager) handleNewBackup(filePath string) {
time.Sleep(m.config.WaitTime)
// save the world into Head save too if SSCM is enabled
- if config.GetIsSSCMEnabled() && config.GetIsNewTerrainAndSaveSystem() {
+ if config.GetIsSSCMEnabled() {
commandmgr.WriteCommand("SAVE")
logger.Backup.Debug("HEAD Save triggered via SSCM")
} else {
From 7e17ef8558c0a66021e6f17d1b1300eafe3ad8c7 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Wed, 15 Oct 2025 21:46:23 +0200
Subject: [PATCH 51/93] changed default UI (/) to svelte UI wrapped old ui
under /legacy updated svelte UI -> settings -> legacy views accordingly
---
SSUI/onboard_bundled/assets/js/main.js | 2 --
.../detectionmanager/detectionmanager.html | 2 +-
SSUI/onboard_bundled/ui/config.html | 6 +++---
SSUI/onboard_bundled/ui/index.html | 2 +-
frontend/src/components/settings/ConfigManager.svelte | 6 +++---
.../src/components/settings/DetectionManager.svelte | 6 +++---
frontend/src/components/settings/SettingsView.svelte | 6 +++---
src/api/routes.go | 10 ++++++----
src/config/configchanger/changeconfig.go | 2 +-
9 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/SSUI/onboard_bundled/assets/js/main.js b/SSUI/onboard_bundled/assets/js/main.js
index ab8acda6..469c853f 100644
--- a/SSUI/onboard_bundled/assets/js/main.js
+++ b/SSUI/onboard_bundled/assets/js/main.js
@@ -8,7 +8,6 @@ document.addEventListener('DOMContentLoaded', () => {
resourceSaver(false);
}
typeh1(document.querySelector('h1'), 30);
- if (window.location.pathname == '/') {
setupTabs();
fetchDetectionEvents();
setupLogStreams({
@@ -34,7 +33,6 @@ document.addEventListener('DOMContentLoaded', () => {
createPlanet(planetContainer, 70, 400, 28, 'rgba(200, 150, 200, 0.7)');
}
console.warn("If you see errors for sscm.js or sscm.css, you may want to enable SSCM.");
- }
// Language flag selection
const languageFlags = document.querySelectorAll('#language-flags img');
languageFlags.forEach(flag => {
diff --git a/SSUI/onboard_bundled/detectionmanager/detectionmanager.html b/SSUI/onboard_bundled/detectionmanager/detectionmanager.html
index e298d02e..f0f7158a 100644
--- a/SSUI/onboard_bundled/detectionmanager/detectionmanager.html
+++ b/SSUI/onboard_bundled/detectionmanager/detectionmanager.html
@@ -103,7 +103,7 @@ Regex Detection:
-
+
diff --git a/SSUI/onboard_bundled/ui/config.html b/SSUI/onboard_bundled/ui/config.html
index 3bf8715c..6c556b18 100644
--- a/SSUI/onboard_bundled/ui/config.html
+++ b/SSUI/onboard_bundled/ui/config.html
@@ -37,7 +37,7 @@
{{.UIText_ServerConfig}}
{{.UIText_DiscordIntegration}}
-
+
{{.UIText_DetectionManager}}
@@ -135,7 +135,7 @@
{{.UIText_TerrainSettingsHeader}}
-
+
@@ -217,7 +217,7 @@ {{.UIText_ChannelConfiguration}}
{{.UIText_BannedPlayersListPathInfo}}
-
+
diff --git a/SSUI/onboard_bundled/ui/index.html b/SSUI/onboard_bundled/ui/index.html
index 4e70928e..3602b5c5 100644
--- a/SSUI/onboard_bundled/ui/index.html
+++ b/SSUI/onboard_bundled/ui/index.html
@@ -41,7 +41,7 @@ Stationeers Server UI v{{.Version}}{{.SSUIIdentifier}}
{{.UIText_StartButton}}
{{.UIText_StopButton}}
- {{.UIText_Settings}}
+ {{.UIText_Settings}}
{{.UIText_Update_SteamCMD}}
diff --git a/frontend/src/components/settings/ConfigManager.svelte b/frontend/src/components/settings/ConfigManager.svelte
index 699defb5..56c43777 100644
--- a/frontend/src/components/settings/ConfigManager.svelte
+++ b/frontend/src/components/settings/ConfigManager.svelte
@@ -11,7 +11,7 @@
const backendUrl = getCurrentBackendUrl();
// Construct the full URL with the folder query parameter
- iframeSrc = `${backendUrl}/config`;
+ iframeSrc = `${backendUrl}/legacy/config`;
} catch (error) {
console.error('Error constructing iframe src:', error);
}
@@ -37,7 +37,7 @@
@@ -45,7 +45,7 @@
{#if loading}
-
Loading Config Manager...
+
Loading Legacy Config Manager...
{/if}
diff --git a/frontend/src/components/settings/DetectionManager.svelte b/frontend/src/components/settings/DetectionManager.svelte
index ef0d43bc..a32763ee 100644
--- a/frontend/src/components/settings/DetectionManager.svelte
+++ b/frontend/src/components/settings/DetectionManager.svelte
@@ -11,7 +11,7 @@
const backendUrl = getCurrentBackendUrl();
// Construct the full URL with the folder query parameter
- iframeSrc = `${backendUrl}/detectionmanager`;
+ iframeSrc = `${backendUrl}/legacy/detectionmanager`;
} catch (error) {
console.error('Error constructing iframe src:', error);
}
@@ -37,7 +37,7 @@
@@ -45,7 +45,7 @@
{#if loading}
-
Loading Detection Manager...
+
Loading Legacy Detection Manager...
{/if}
diff --git a/frontend/src/components/settings/SettingsView.svelte b/frontend/src/components/settings/SettingsView.svelte
index a327f474..bc3ea5b9 100644
--- a/frontend/src/components/settings/SettingsView.svelte
+++ b/frontend/src/components/settings/SettingsView.svelte
@@ -26,8 +26,8 @@
class="settings-nav {activeSidebarTab === 'Backends' ? 'active' : ''}"
onclick={() => selectSidebarTab('Backends')}>Backends
+ class="settings-nav {activeSidebarTab === 'Legacy Detection Manager' ? 'active' : ''}"
+ onclick={() => selectSidebarTab('Legacy Detection Manager')}>Legacy Detection Manager
@@ -40,7 +40,7 @@
{:else if activeSidebarTab === 'Backends'}
- {:else if activeSidebarTab === 'Detection Manager'}
+ {:else if activeSidebarTab === 'Legacy Detection Manager'}
{:else if activeSidebarTab === 'Legacy Config Manager'}
diff --git a/src/api/routes.go b/src/api/routes.go
index 26d912ae..f0bf28f6 100644
--- a/src/api/routes.go
+++ b/src/api/routes.go
@@ -38,12 +38,14 @@ func SetupAPIRoutes() (*http.ServeMux, *http.ServeMux) {
legacyAssetsFS, _ := fs.Sub(config.GetV1UIFS(), "SSUI/onboard_bundled/assets")
protectedMux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(legacyAssetsFS))))
- protectedMux.HandleFunc("/config", pages.ServeConfigPage)
- protectedMux.HandleFunc("/detectionmanager", pages.ServeDetectionManager)
- protectedMux.HandleFunc("/", pages.ServeIndex)
+ protectedMux.HandleFunc("/legacy/config", pages.ServeConfigPage)
+ protectedMux.HandleFunc("/legacy/detectionmanager", pages.ServeDetectionManager)
+
+ // Index page(s)
+ protectedMux.HandleFunc("/legacy", pages.ServeIndex)
+ protectedMux.HandleFunc("/", pages.ServeSvelteUI)
// --- SVELTE UI ---
- protectedMux.HandleFunc("/v2", pages.ServeSvelteUI)
svelteAssetsFS, _ := fs.Sub(config.V1UIFS, "SSUI/onboard_bundled/v2/assets")
protectedMux.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.FS(svelteAssetsFS))))
protectedMux.HandleFunc("/api/v2/loader/reloadbackend", HandleReloadBackend)
diff --git a/src/config/configchanger/changeconfig.go b/src/config/configchanger/changeconfig.go
index 073d7b40..6e7e6b2f 100644
--- a/src/config/configchanger/changeconfig.go
+++ b/src/config/configchanger/changeconfig.go
@@ -68,7 +68,7 @@ func SaveConfigForm(w http.ResponseWriter, r *http.Request) {
return
}
- http.Redirect(w, r, "/config", http.StatusSeeOther)
+ http.Redirect(w, r, "/legacy/config", http.StatusSeeOther)
}
func SaveConfigRestful(w http.ResponseWriter, r *http.Request) {
From a4cfa6e5d115d2e5ad5489e39b749b87b7d9d024 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Thu, 16 Oct 2025 01:42:00 +0200
Subject: [PATCH 52/93] disabled BMv2 and removed the IsNewTerrain setting
---
src/config/config.go | 21 ++++++++++-----------
src/core/loader/loader.go | 4 ++--
2 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/src/config/config.go b/src/config/config.go
index 07c3a69d..8722f210 100644
--- a/src/config/config.go
+++ b/src/config/config.go
@@ -40,17 +40,16 @@ type JsonConfig struct {
AuthTokenLifetime int `json:"AuthTokenLifetime"`
// SSUI Settings
- IsNewTerrainAndSaveSystem *bool `json:"IsNewTerrainAndSaveSystem"` // Use new terrain and save system
- LogClutterToConsole *bool `json:"LogClutterToConsole"`
- IsSSCMEnabled *bool `json:"IsSSCMEnabled"`
- IsBepInExEnabled *bool `json:"IsBepInExEnabled"`
- AutoRestartServerTimer string `json:"AutoRestartServerTimer"`
- IsConsoleEnabled *bool `json:"IsConsoleEnabled"`
- LanguageSetting string `json:"LanguageSetting"`
- AutoStartServerOnStartup *bool `json:"AutoStartServerOnStartup"`
- SSUIIdentifier string `json:"SSUIIdentifier"`
- SSUIWebPort string `json:"SSUIWebPort"`
- UseRunfiles *bool `json:"UseRunfiles"`
+ LogClutterToConsole *bool `json:"LogClutterToConsole"`
+ IsSSCMEnabled *bool `json:"IsSSCMEnabled"`
+ IsBepInExEnabled *bool `json:"IsBepInExEnabled"`
+ AutoRestartServerTimer string `json:"AutoRestartServerTimer"`
+ IsConsoleEnabled *bool `json:"IsConsoleEnabled"`
+ LanguageSetting string `json:"LanguageSetting"`
+ AutoStartServerOnStartup *bool `json:"AutoStartServerOnStartup"`
+ SSUIIdentifier string `json:"SSUIIdentifier"`
+ SSUIWebPort string `json:"SSUIWebPort"`
+ UseRunfiles *bool `json:"UseRunfiles"`
// Update Settings
IsUpdateEnabled *bool `json:"IsUpdateEnabled"`
diff --git a/src/core/loader/loader.go b/src/core/loader/loader.go
index e269ea2c..dd4b6f40 100644
--- a/src/core/loader/loader.go
+++ b/src/core/loader/loader.go
@@ -25,7 +25,7 @@ func InitBackend(wg *sync.WaitGroup) {
ReloadConfig()
ReloadRunfile()
ReloadBepInEx()
- ReloadStationeersBackupManager()
+ //ReloadStationeersBackupManager()
ReloadLocalizer()
ReloadAppInfoPoller()
ReloadDiscordBot()
@@ -38,7 +38,7 @@ func ReloadBackend() {
logger.Core.Info("Reloading backend...")
ReloadConfig()
ReloadBepInEx()
- ReloadStationeersBackupManager()
+ //ReloadStationeersBackupManager()
ReloadLocalizer()
ReloadAppInfoPoller()
PrintConfigDetails()
From 6e205d4813a53f2709517a298a30f72a7f41da1b Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Thu, 16 Oct 2025 03:50:37 +0200
Subject: [PATCH 53/93] updated devcontainer to expose socket api file, improve
socket api
---
.devcontainer/Dockerfile | 2 ++
.devcontainer/devcontainer.json | 3 +++
src/api/socketapi/examples.md | 8 ++++----
src/api/socketapi/socket-lin.go | 9 ++++++++-
4 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 9681386c..c87c9209 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -28,5 +28,7 @@ RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
npm install -g npm@latest
# Set up non-root user and workspace
+
+RUN mkdir -p /tmp/ssui && chmod 777 /tmp/ssui
USER vscode
WORKDIR /workspaces
\ No newline at end of file
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 0346bdee..d3b18136 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -8,6 +8,9 @@
},
"workspaceFolder": "/workspaces/project",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/project,type=bind,consistency=cached",
+ "mounts": [
+ "source=ssui-socket-volume,target=/tmp/ssui,type=volume"
+ ],
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.25.0"
diff --git a/src/api/socketapi/examples.md b/src/api/socketapi/examples.md
index bccf7cbe..a0e485cb 100644
--- a/src/api/socketapi/examples.md
+++ b/src/api/socketapi/examples.md
@@ -5,7 +5,7 @@ This guide explains how to interact with the socket-based `SSUI-API`, which expo
## Overview
The socket server reuses the HTTP routes defined in `routes.go` but serves them over:
-- **Linux**: A Unix socket at `/tmp/ssui.sock`.
+- **Linux**: A Unix socket at `/tmp/ssui/ssui.sock`.
- **Windows**: A named pipe at `\\.\pipe\ssui`.
This is similar to how Docker uses `/var/run/docker.sock` for local API access.
@@ -27,7 +27,7 @@ This is similar to how Docker uses `/var/run/docker.sock` for local API access.
Use `curl` with the `--unix-socket` flag to send HTTP requests to the socket. Example for the `/api/v2/settings` endpoint:
```bash
-curl --unix-socket /tmp/ssui.sock http://localhost/api/v2/settings
+curl --unix-socket /tmp/ssui/ssui.sock http://localhost/api/v2/settings
```
**Expected Output**: JSON response from the `settings.RetrieveSettings` handler, e.g.:
@@ -110,14 +110,14 @@ All routes from `routes.go` (e.g., `/api/v2/server/start`, `/api/v2/backups`) ar
- **Linux**:
```bash
- curl --unix-socket /tmp/ssui.sock http://localhost/api/v2/backups
+ curl --unix-socket /tmp/ssui/ssui.sock http://localhost/api/v2/backups
```
- **Windows**: Edit `$endpoint` in `test_namedpipe.ps1`, e.g., `$endpoint = "/api/v2/backups"`.
For POST requests (e.g., `/api/v2/server/start`), add a JSON payload:
- **Linux**:
```bash
- curl --unix-socket /tmp/ssui.sock -X POST -H "Content-Type: application/json" -d '{"action":"start"}' http://localhost/api/v2/server/start
+ curl --unix-socket /tmp/ssui/ssui.sock -X POST -H "Content-Type: application/json" -d '{"action":"start"}' http://localhost/api/v2/server/start
```
- **Windows**: Update the PowerShell script’s `$request`:
```powershell
diff --git a/src/api/socketapi/socket-lin.go b/src/api/socketapi/socket-lin.go
index 72a32e29..52a696cf 100644
--- a/src/api/socketapi/socket-lin.go
+++ b/src/api/socketapi/socket-lin.go
@@ -9,13 +9,14 @@ import (
"net"
"net/http"
"os"
+ "path/filepath"
"sync"
"github.com/SteamServerUI/SteamServerUI/v7/src/api"
"github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
-const socketPath = "/tmp/ssui.sock"
+const socketPath = "/tmp/ssui/ssui.sock"
func StartSocketServer(wg *sync.WaitGroup) {
logger.Socket.Info("Starting Unix socket server...")
@@ -30,6 +31,12 @@ func StartSocketServer(wg *sync.WaitGroup) {
api.SetupSocketAPIRoutes(APIMux)
mux.Handle("/", APIMux)
+ // Create parent directory for the socket
+ parentDir := filepath.Dir(socketPath)
+ if err := os.MkdirAll(parentDir, 0755); err != nil {
+ logger.Socket.Error("Error creating parent directory: " + err.Error())
+ return
+ }
// Create Unix socket listener
listener, err := net.Listen("unix", socketPath)
if err != nil {
From 17c85311381e72684d2fce8dd73be195ba4be8f2 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Fri, 17 Oct 2025 18:48:44 +0200
Subject: [PATCH 54/93] fix runfile loading & improve logging noise
---
src/api/handlers.go | 2 --
src/api/pages/TwoBoxForm.go | 15 +++++++++------
src/api/runfileapi/runfile.go | 3 ---
src/api/runfileapi/runfilegallery.go | 8 ++++----
src/core/loader/loader.go | 1 +
src/core/loader/runfile.go | 4 ++--
src/steamcmd/steamcmd.go | 5 +++++
src/steamserverui/gallery/gallery.go | 19 +++++++++++++++----
src/steamserverui/runfile/args.go | 4 ++--
9 files changed, 38 insertions(+), 23 deletions(-)
diff --git a/src/api/handlers.go b/src/api/handlers.go
index 1928a109..8ac5fc98 100644
--- a/src/api/handlers.go
+++ b/src/api/handlers.go
@@ -30,8 +30,6 @@ func HandleRunSteamCMD(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Only GET requests are allowed", http.StatusMethodNotAllowed)
return
}
-
- logger.Core.Info("Running SteamCMD")
_, err := steamcmd.InstallAndRunSteamCMD()
// Update last execution time
diff --git a/src/api/pages/TwoBoxForm.go b/src/api/pages/TwoBoxForm.go
index 5dd1a495..f560213a 100644
--- a/src/api/pages/TwoBoxForm.go
+++ b/src/api/pages/TwoBoxForm.go
@@ -129,7 +129,7 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) {
SecondaryLabelType: "hidden",
SubmitButtonText: localization.GetString("UIText_PlsRead_SubmitButton"),
SkipButtonText: localization.GetString("UIText_PlsRead_SkipButton"),
- NextStep: "game_branch",
+ NextStep: "admin_account",
},
"game_branch": {
ID: "game_branch",
@@ -364,11 +364,14 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) {
data.Step = "welcome"
}
stepOrder := []string{
- "welcome", "pls_read", "game_branch", "server_name", "save_name", "world_id", "max_players",
- "server_password", "discord_enabled", "discord_token", "control_panel_channel", "save_channel",
- "log_channel", "connection_list_channel", "status_channel", "control_channel",
- "network_config_choice", "game_port", "update_port", "upnp_enabled",
- "local_ip_address", "admin_account", "finalize",
+ "welcome", "pls_read",
+ //"game_branch",
+ //"server_name", "save_name", "world_id", "max_players",
+ //"server_password", "discord_enabled", "discord_token", "control_panel_channel", "save_channel",
+ //"log_channel", "connection_list_channel", "status_channel", "control_channel",
+ //"network_config_choice", "game_port", "update_port", "upnp_enabled",
+ //"local_ip_address",
+ "admin_account", "finalize",
}
var stepSlice []Step
for _, id := range stepOrder {
diff --git a/src/api/runfileapi/runfile.go b/src/api/runfileapi/runfile.go
index 09bd1b83..968b7d17 100644
--- a/src/api/runfileapi/runfile.go
+++ b/src/api/runfileapi/runfile.go
@@ -112,7 +112,6 @@ func HandleRunfileGroups(w http.ResponseWriter, r *http.Request) {
}
groups := runfile.GetUIGroups()
- logger.Runfile.Info("fetched UI groups")
writeJSONResponse(w, http.StatusOK, groups, "")
}
@@ -153,8 +152,6 @@ func HandleRunfileArgs(w http.ResponseWriter, r *http.Request) {
for i, arg := range args {
apiArgs[i] = toAPIGameArg(arg)
}
-
- logger.Runfile.Info(fmt.Sprintf("fetched args for group=%s", group))
writeJSONResponse(w, http.StatusOK, apiArgs, "")
}
diff --git a/src/api/runfileapi/runfilegallery.go b/src/api/runfileapi/runfilegallery.go
index d8a7fb49..82fdbf88 100644
--- a/src/api/runfileapi/runfilegallery.go
+++ b/src/api/runfileapi/runfilegallery.go
@@ -6,6 +6,7 @@ import (
"strconv"
"strings"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
"github.com/SteamServerUI/SteamServerUI/v7/src/logger"
"github.com/SteamServerUI/SteamServerUI/v7/src/steamserverui/gallery"
)
@@ -18,7 +19,6 @@ type response struct {
// GalleryHandler handles GET /api/v2/gallery
func GalleryHandler(w http.ResponseWriter, r *http.Request) {
- logger.Runfile.Info("Handling GET /api/v2/gallery request")
forceUpdate := strings.ToLower(r.URL.Query().Get("forceUpdate")) == "true"
runfiles, err := gallery.GetRunfileGallery(forceUpdate)
@@ -28,13 +28,12 @@ func GalleryHandler(w http.ResponseWriter, r *http.Request) {
return
}
- logger.Runfile.Info("Returning " + strconv.Itoa(len(runfiles)) + " runfiles from gallery")
+ logger.Runfile.Debug("Returning " + strconv.Itoa(len(runfiles)) + " runfiles from gallery")
sendResponse(w, http.StatusOK, response{Data: runfiles})
}
// GallerySelectHandler handles POST /api/v2/gallery/select
func GallerySelectHandler(w http.ResponseWriter, r *http.Request) {
- logger.Runfile.Info("Handling POST /api/v2/gallery/select request")
var req struct {
Identifier string `json:"identifier"`
@@ -57,7 +56,8 @@ func GallerySelectHandler(w http.ResponseWriter, r *http.Request) {
return
}
- logger.Runfile.Info("Successfully saved runfile " + req.Identifier)
+ logger.Runfile.Debug("Successfully saved runfile " + req.Identifier)
+ loader.ReloadBackend()
sendResponse(w, http.StatusOK, response{Data: "Runfile " + req.Identifier + " saved"})
}
diff --git a/src/core/loader/loader.go b/src/core/loader/loader.go
index dd4b6f40..19cc7e6f 100644
--- a/src/core/loader/loader.go
+++ b/src/core/loader/loader.go
@@ -37,6 +37,7 @@ func ReloadBackend() {
logger.Core.Info("Reloading backend...")
ReloadConfig()
+ ReloadRunfile()
ReloadBepInEx()
//ReloadStationeersBackupManager()
ReloadLocalizer()
diff --git a/src/core/loader/runfile.go b/src/core/loader/runfile.go
index bf16600e..648727ac 100644
--- a/src/core/loader/runfile.go
+++ b/src/core/loader/runfile.go
@@ -19,8 +19,8 @@ func InitRunfile(game string) error {
return fmt.Errorf("game cannot be empty")
}
- logger.Runfile.Info("Updating runfile game to " + game)
- logger.Runfile.Info("Stopping server if running")
+ logger.Runfile.Debug("Updating runfile game to " + game)
+ logger.Runfile.Debug("Stopping server if running")
gamemgr.InternalStopServer()
config.SetRunfileIdentifier(game)
diff --git a/src/steamcmd/steamcmd.go b/src/steamcmd/steamcmd.go
index be56777e..48417bf2 100644
--- a/src/steamcmd/steamcmd.go
+++ b/src/steamcmd/steamcmd.go
@@ -122,6 +122,11 @@ func runSteamCMD(steamCMDDir string) (int, error) {
cmd.Env = newEnv
}
+ if config.GetSkipSteamCMD() {
+ logger.Install.Warn("Skipping SteamCMD installation")
+ return 0, nil
+ }
+
// Run the command
if config.GetLogLevel() == 10 {
cmdString := strings.Join(cmd.Args, " ")
diff --git a/src/steamserverui/gallery/gallery.go b/src/steamserverui/gallery/gallery.go
index 2ddac3bc..5fa06626 100644
--- a/src/steamserverui/gallery/gallery.go
+++ b/src/steamserverui/gallery/gallery.go
@@ -11,6 +11,7 @@ import (
"sync"
"github.com/SteamServerUI/SteamServerUI/v7/src/config"
+ "github.com/SteamServerUI/SteamServerUI/v7/src/core/loader"
"github.com/SteamServerUI/SteamServerUI/v7/src/logger"
)
@@ -44,7 +45,7 @@ func GetRunfileGallery(forceUpdate bool) ([]GalleryRunfile, error) {
// Fetch manifest from GitHub Pages
const manifestURL = "https://steamserverui.github.io/runfiles/manifest.ssui"
- logger.Runfile.Info("Fetching runfile gallery from " + manifestURL)
+ logger.Runfile.Debug("Fetching runfile gallery from " + manifestURL)
resp, err := http.Get(manifestURL)
if err != nil {
logger.Runfile.Error(fmt.Sprintf("Failed to fetch manifest: %v", err))
@@ -92,7 +93,7 @@ func SaveRunfileToDisk(identifier string) error {
baseURL := "https://steamserverui.github.io/runfiles"
fileURL := fmt.Sprintf("%s/%s", baseURL, filename)
- logger.Runfile.Info("Fetching runfile from " + fileURL)
+ logger.Runfile.Debug("Fetching runfile from " + fileURL)
resp, err := http.Get(fileURL)
if err != nil {
logger.Runfile.Error(fmt.Sprintf("Failed to fetch runfile %s: %v", filename, err))
@@ -106,7 +107,16 @@ func SaveRunfileToDisk(identifier string) error {
}
saveFilePath := filepath.Join(config.GetRunfilesFolder(), filename)
- logger.Runfile.Info("Saving runfile to " + saveFilePath)
+ logger.Runfile.Debug("Saving runfile to " + saveFilePath)
+
+ // get the dir of the saveFilePath, and os.MkdirAll it if it doesn't exist
+ dir := filepath.Dir(saveFilePath)
+ if _, err := os.Stat(dir); os.IsNotExist(err) {
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ logger.Runfile.Error(fmt.Sprintf("Failed to create runfiles directory %s: %v", dir, err))
+ return fmt.Errorf("couldn't create directory")
+ }
+ }
// Create or overwrite the file
file, err := os.Create(saveFilePath)
@@ -122,7 +132,8 @@ func SaveRunfileToDisk(identifier string) error {
return fmt.Errorf("couldn't save %s, disk's being dramatic", filename)
}
- logger.Runfile.Info("Successfully saved runfile " + filename)
+ logger.Runfile.Debug("Successfully saved runfile " + filename)
+ loader.InitRunfile(identifier)
return nil
}
diff --git a/src/steamserverui/runfile/args.go b/src/steamserverui/runfile/args.go
index be80ec86..d1f19bab 100644
--- a/src/steamserverui/runfile/args.go
+++ b/src/steamserverui/runfile/args.go
@@ -222,7 +222,7 @@ func LoadRunfile(gameName, runFilesFolder string) error {
}
CurrentRunfile = &runfile
- logger.Runfile.Info(fmt.Sprintf("runfile loaded: path=%s", filePath))
+ logger.Runfile.Debug(fmt.Sprintf("runfile loaded: path=%s", filePath))
return nil
}
@@ -276,7 +276,7 @@ func SaveRunfile() error {
break
}
- logger.Runfile.Info(fmt.Sprintf("runfile saved: path=%s", filePath))
+ logger.Runfile.Debug(fmt.Sprintf("runfile saved: path=%s", filePath))
return nil
}
From b79ad6846f45c855668e484c7344982231481b68 Mon Sep 17 00:00:00 2001
From: JacksonTheMaster
Date: Fri, 17 Oct 2025 19:13:43 +0200
Subject: [PATCH 55/93] added small logsCard to main dashboard
---
.../components/Dashboard/DashboardView.svelte | 3 ++
.../Dashboard/cards/LogsCard.svelte | 39 +++++++++++++++++++
frontend/src/components/views/LogsView.svelte | 3 ++
3 files changed, 45 insertions(+)
create mode 100644 frontend/src/components/Dashboard/cards/LogsCard.svelte
diff --git a/frontend/src/components/Dashboard/DashboardView.svelte b/frontend/src/components/Dashboard/DashboardView.svelte
index 0dd620fb..2fe84435 100644
--- a/frontend/src/components/Dashboard/DashboardView.svelte
+++ b/frontend/src/components/Dashboard/DashboardView.svelte
@@ -3,12 +3,14 @@
import ConsoleCard from './cards/ConsoleCard.svelte';
import WarnCard from './cards/WarnCard.svelte';
import SystemInfoCard from './cards/SystemInfoCard.svelte';
+ import LogsCard from './cards/LogsCard.svelte';
+
@@ -17,6 +19,7 @@
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ grid-auto-rows: minmax(0, 400px);
gap: 1.5rem;
padding: 1rem;
}
diff --git a/frontend/src/components/Dashboard/cards/LogsCard.svelte b/frontend/src/components/Dashboard/cards/LogsCard.svelte
new file mode 100644
index 00000000..09beebb5
--- /dev/null
+++ b/frontend/src/components/Dashboard/cards/LogsCard.svelte
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/views/LogsView.svelte b/frontend/src/components/views/LogsView.svelte
index 124d5bd6..cd6a4936 100644
--- a/frontend/src/components/views/LogsView.svelte
+++ b/frontend/src/components/views/LogsView.svelte
@@ -3,6 +3,7 @@
import { apiSSE } from '../../services/api';
// Main state
+ let { hideTimeRange = false } = $props();
let logs = $state([]);
let filteredLogs = $state([]);
let logSources = $state({
@@ -274,6 +275,7 @@
+ {#if !hideTimeRange}
Time Range
@@ -288,6 +290,7 @@
All Time
+ {/if}