diff --git a/go.mod b/go.mod index 2967a033..e1571ee4 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/src/cli/commands.go b/src/cli/commands.go index 7c7b10ba..716b0afb 100644 --- a/src/cli/commands.go +++ b/src/cli/commands.go @@ -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 diff --git a/src/cli/devcommands.go b/src/cli/devcommands.go index f0aa5f7e..2120e193 100644 --- a/src/cli/devcommands.go +++ b/src/cli/devcommands.go @@ -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" @@ -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) +} diff --git a/src/cli/ssuicli.go b/src/cli/ssuicli.go index 625444b9..a5497da1 100644 --- a/src/cli/ssuicli.go +++ b/src/cli/ssuicli.go @@ -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 { @@ -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 }