Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/JacksonTheMaster/StationeersServerUI/v5

go 1.25.0
go 1.26

require (
github.com/bwmarrin/discordgo v0.28.1
Expand Down
50 changes: 29 additions & 21 deletions src/cli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,35 @@ import (

// init registers default cli commands and their aliases.
func init() {
RegisterCommand("help", helpCommand, "h")
RegisterCommand("dashboard", dashboardCommand, "dash", "d")
RegisterCommand("reloadbackend", WrapNoReturn(loader.ReloadBackend), "rlb", "rb", "r")
RegisterCommand("reloadconfig", WrapNoReturn(loader.ReloadConfig), "rlc", "rc")
RegisterCommand("restartbackend", WrapNoReturn(loader.RestartBackend), "rsb")
RegisterCommand("exit", WrapNoReturn(exitfromcli), "e")
RegisterCommand("deleteconfig", WrapNoReturn(deleteConfig), "delc", "dc")
RegisterCommand("startserver", WrapNoReturn(startServer), "start")
RegisterCommand("stopserver", WrapNoReturn(stopServer), "stop")
RegisterCommand("runsteamcmd", WrapNoReturn(runSteamCMD), "steamcmd", "stcmd")
RegisterCommand("testlocalization", WrapNoReturn(testLocalization), "tl")
RegisterCommand("supportmode", WrapNoReturn(supportMode), "sm")
RegisterCommand("supportpackage", WrapNoReturn(supportPackage), "sp")
RegisterCommand("getbuildid", WrapNoReturn(getBuildID), "gbid")
RegisterCommand("printconfig", WrapNoReturn(printConfig), "pc")
RegisterCommand("update", WrapNoReturn(triggerUpdateCheck), "u")
RegisterCommand("applyupdate", WrapNoReturn(applyUpdate), "au")
RegisterCommand("listmods", WrapNoReturn(listmods), "lm")
RegisterCommand("listworkshophandles", WrapNoReturn(listworkshophandles), "lwh")
RegisterCommand("downloadworkshopupdates", WrapNoReturn(downloadWorkshopUpdates), "dwu")
RegisterCommand("downloadworkshopitemtest", WrapNoReturn(downloadWorkshopItemTest), "dwmodcon")

//Long command, Description, IsDevCommand, Aliases

// User commands
RegisterCommand("help", helpCommand, "Show available commands", false, "h")
RegisterCommand("dashboard", dashboardCommand, "Launch interactive CLI dashboard", false, "dash", "d")
RegisterCommand("startserver", WrapNoReturn(startServer), "Start the game server", false, "start")
RegisterCommand("stopserver", WrapNoReturn(stopServer), "Stop the game server", false, "stop")
RegisterCommand("update", WrapNoReturn(triggerUpdateCheck), "Trigger an SSUI update check", false, "u")
RegisterCommand("applyupdate", WrapNoReturn(applyUpdate), "Apply available SSUI updates", false, "au")
RegisterCommand("reloadbackend", WrapNoReturn(loader.ReloadBackend), "Reload the SSUI backend", false, "rlb", "rb", "r")
RegisterCommand("reloadconfig", WrapNoReturn(loader.ReloadConfig), "Reload the SSUI configuration", false, "rlc", "rc")
RegisterCommand("restartbackend", WrapNoReturn(loader.RestartBackend), "Restart the SSUI backend", false, "rsb")
RegisterCommand("supportmode", WrapNoReturn(supportMode), "Enter support mode", false, "sm")
RegisterCommand("supportpackage", WrapNoReturn(supportPackage), "Create a support package", false, "sp")
RegisterCommand("exit", WrapNoReturn(exitfromcli), "Exit / Shutdown SSUI", false, "e")
RegisterCommand("runsteamcmd", WrapNoReturn(runSteamCMD), "Run SteamCMD to update the Gameserver", false, "steamcmd", "stcmd")
RegisterCommand("downloadworkshopupdates", WrapNoReturn(downloadWorkshopUpdates), "Download Steam workshop Mod updates", true, "dwu")

// Dev commands
RegisterCommand("deleteconfig", WrapNoReturn(deleteConfig), "DELETE the SSUI configuration file", true, "delc", "dc")
RegisterCommand("testlocalization", WrapNoReturn(testLocalization), "Test localization strings", true, "tl")
RegisterCommand("getbuildid", WrapNoReturn(getBuildID), "Get the current game build ID (server must have started once, else empty)", true, "gbid")
RegisterCommand("printconfig", WrapNoReturn(printConfig), "Print the current SSUI configuration", true, "pc")
RegisterCommand("listmods", WrapNoReturn(listmods), "List installed SLP mods", true, "lm")
RegisterCommand("listworkshophandles", WrapNoReturn(listworkshophandles), "List workshop Mod handles", true, "lwh")
RegisterCommand("downloadworkshopitemtest", WrapNoReturn(downloadWorkshopItemTest), "Test downloading a workshop item (ModularConsolesMod)", true, "dwmodcon")
RegisterCommand("dumpheapprofile", WrapNoReturn(dumpHeapProfile), "Dump a pprof heap profile for debugging", true, "dhp")
RegisterCommand("testconnectedplayerslistdiscord", WrapNoReturn(testConnectedPlayersListDiscord), "Send a fake player list to the Discord package to test the connected players panel", true, "tcpd")
}

// dashboardCommand launches the interactive terminal dashboard
Expand Down
37 changes: 37 additions & 0 deletions src/cli/devcommands.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ package cli

import (
"fmt"
"os"
"runtime"
"runtime/pprof"
"time"

"github.com/JacksonTheMaster/StationeersServerUI/v5/src/config"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/discordbot"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/localization"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger"
"github.com/JacksonTheMaster/StationeersServerUI/v5/src/modding"
Expand Down Expand Up @@ -54,3 +59,35 @@ func testLocalization() {
s := localization.GetString("UIText_StartButton")
logger.Core.Info("Start Server Button text (current language: " + currentLanguageSetting + "): " + s)
}

func dumpHeapProfile() {
runtime.GC()
if _, err := os.Stat("heap.pprof"); err == nil {
if err := os.Remove("heap.pprof"); err != nil {
logger.Main.Errorf("could not remove old heap profile", "err", err)
return
}
}
f, err := os.Create("heap.pprof")
if err != nil {
logger.Main.Errorf("could not create heap profile file", "err", err)
return
}
defer f.Close()

if err := pprof.WriteHeapProfile(f); err != nil {
logger.Main.Errorf("could not write heap profile", "err", err)
return
}

logger.Main.Info("Heap profile written to heap.pprof")
}

func testConnectedPlayersListDiscord() {
players := map[string]string{
"76561198334231312": "JacksonTheMaster",
"76561198012262058": "Sebastian - TheNovice",
"76561197995322389": "Non Action Man",
}
discordbot.AddToConnectedPlayers("ThisDataDoesntMatter", "ThisDataDoesntMatter", time.Now(), players)
}
51 changes: 45 additions & 6 deletions src/cli/ssuicli.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,16 @@ var commandRegistry = make(map[string]CommandFunc)
var mu sync.Mutex

var commandAliases = make(map[string][]string)
var commandDescriptions = make(map[string]string)
var commandIsDevOnly = make(map[string]bool)

// RegisterCommand adds a new command and its handler to the registry.
func RegisterCommand(name string, handler CommandFunc, aliases ...string) {
func RegisterCommand(name string, handler CommandFunc, desc string, isDevCommand bool, aliases ...string) {
mu.Lock()
defer mu.Unlock()
commandRegistry[name] = handler
commandDescriptions[name] = desc
commandIsDevOnly[name] = isDevCommand
if len(aliases) > 0 {
commandAliases[name] = append(commandAliases[name], aliases...)
for _, alias := range aliases {
Expand Down Expand Up @@ -127,24 +131,59 @@ func WrapNoReturn(fn func()) CommandFunc {
}
}

// helpCommand displays available commands along with their aliases.
// helpCommand displays available commands along with their aliases and descriptions.
func helpCommand(args []string) error {
mu.Lock()
defer mu.Unlock()
logger.Core.Info("Available commands:")
// Collect primary commands (those in commandAliases keys)
showDev := len(args) > 0 && args[0] == "dev"

// Collect primary commands
primaryCommands := make([]string, 0, len(commandAliases))
for cmd := range commandAliases {
primaryCommands = append(primaryCommands, cmd)
}
sort.Strings(primaryCommands)

// Find the longest command name for alignment
maxLen := 0
for _, cmd := range primaryCommands {
if !showDev && commandIsDevOnly[cmd] {
continue
}
if len(cmd) > maxLen {
maxLen = len(cmd)
}
}

logger.Core.Cleanf("")
if showDev {
logger.Core.Cleanf(" \033[33m═══ SSUICLI Commands (including dev) ═══\033[0m")
} else {
logger.Core.Cleanf(" \033[32m═══ SSUICLI Commands ═══\033[0m")
}
logger.Core.Cleanf("")

for _, cmd := range primaryCommands {
isDev := commandIsDevOnly[cmd]
if !showDev && isDev {
continue
}

desc := commandDescriptions[cmd]
aliases := commandAliases[cmd]

aliasStr := ""
if len(aliases) > 0 {
logger.Core.Info("- " + cmd + " (aliases: " + strings.Join(aliases, ", ") + ")")
aliasStr = " \033[90m(" + strings.Join(aliases, ", ") + ")\033[0m"
}

if isDev {
logger.Core.Cleanf(" \033[33m%-*s\033[0m %s%s", maxLen, cmd, desc, aliasStr)
} else {
logger.Core.Info("- %s" + cmd)
logger.Core.Cleanf(" \033[36m%-*s\033[0m %s%s", maxLen, cmd, desc, aliasStr)
}
}

logger.Core.Cleanf("")
return nil
}