-
Notifications
You must be signed in to change notification settings - Fork 2
v5.13.2 - SLP fix and discord improvements #161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b62bf59
7d5fb09
2830ae3
79773d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,8 @@ import ( | |
| // Button custom IDs for the server & players panel | ||
| const ( | ||
| ButtonGetPassword = "ssui_get_password" | ||
| ButtonGetGameVersion = "ssui_get_game_version" | ||
| ButtonGetNextRestart = "ssui_get_next_restart" | ||
| ButtonDownloadBackupPfx = "ssui_download_backup_" // Prefix for download backup button | ||
| ) | ||
|
|
||
|
|
@@ -119,19 +121,37 @@ func buildStatusPanelEmbed(players map[string]string) *discordgo.MessageEmbed { | |
|
|
||
| // buildPanelComponents returns the action row with interactive buttons | ||
| func buildPanelComponents() []discordgo.MessageComponent { | ||
| var buttons []discordgo.MessageComponent | ||
|
|
||
| if config.GetServerPassword() == "" { | ||
| if config.GetServerPassword() != "" { | ||
| buttons = append(buttons, discordgo.Button{ | ||
| Label: "🔑 Get Server Password", | ||
| Style: discordgo.PrimaryButton, | ||
| CustomID: ButtonGetPassword, | ||
| }) | ||
| } | ||
|
|
||
| buttons = append(buttons, discordgo.Button{ | ||
| Label: "🎮 Get Game Version", | ||
| Style: discordgo.SecondaryButton, | ||
| CustomID: ButtonGetGameVersion, | ||
| }) | ||
|
Comment on lines
+134
to
+138
|
||
|
|
||
| if config.GetAutoRestartServerTimer() != "0" && config.GetAutoRestartServerTimer() != "" { | ||
| buttons = append(buttons, discordgo.Button{ | ||
| Label: "🔄 Next Auto Restart", | ||
| Style: discordgo.SecondaryButton, | ||
| CustomID: ButtonGetNextRestart, | ||
| }) | ||
| } | ||
|
|
||
| if len(buttons) == 0 { | ||
| return nil | ||
| } | ||
|
|
||
| return []discordgo.MessageComponent{ | ||
| discordgo.ActionsRow{ | ||
| Components: []discordgo.MessageComponent{ | ||
| discordgo.Button{ | ||
| Label: "🔑 Get Server Password", | ||
| Style: discordgo.PrimaryButton, | ||
| CustomID: ButtonGetPassword, | ||
| }, | ||
| }, | ||
| Components: buttons, | ||
| }, | ||
| } | ||
| } | ||
|
|
@@ -191,6 +211,10 @@ func handlePanelButtonInteraction(s *discordgo.Session, i *discordgo.Interaction | |
| switch customID { | ||
| case ButtonGetPassword: | ||
| handleGetPasswordButton(s, i) | ||
| case ButtonGetGameVersion: | ||
| handleGetGameVersionButton(s, i) | ||
| case ButtonGetNextRestart: | ||
| handleGetNextRestartButton(s, i) | ||
| default: | ||
| return | ||
| } | ||
|
|
@@ -249,3 +273,86 @@ func handleGetPasswordButton(s *discordgo.Session, i *discordgo.InteractionCreat | |
| } | ||
| }() | ||
| } | ||
|
|
||
| // handleGetGameVersionButton sends the current game server version as an ephemeral message | ||
| func handleGetGameVersionButton(s *discordgo.Session, i *discordgo.InteractionCreate) { | ||
| version := config.GetExtractedGameVersion() | ||
|
|
||
| var embed *discordgo.MessageEmbed | ||
| if version == "" { | ||
| embed = &discordgo.MessageEmbed{ | ||
| Title: "🎮 Game Version Unknown", | ||
| Description: "The game server version has not been detected yet.", | ||
| Color: 0xFFA500, | ||
| } | ||
| } else { | ||
| embed = &discordgo.MessageEmbed{ | ||
| Title: "🎮 Game Server Version", | ||
| Color: 0x5865F2, | ||
| Fields: []*discordgo.MessageEmbedField{ | ||
| { | ||
| Name: "Version", | ||
| Value: "```" + version + "```", | ||
| Inline: false, | ||
| }, | ||
| }, | ||
| Timestamp: time.Now().Format(time.RFC3339), | ||
| } | ||
| } | ||
|
|
||
| err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ | ||
| Type: discordgo.InteractionResponseChannelMessageWithSource, | ||
| Data: &discordgo.InteractionResponseData{ | ||
| Embeds: []*discordgo.MessageEmbed{embed}, | ||
| Flags: discordgo.MessageFlagsEphemeral, | ||
| }, | ||
| }) | ||
| if err != nil { | ||
| logger.Discord.Error("Error responding to game version button: " + err.Error()) | ||
| } | ||
| } | ||
|
|
||
| // handleGetNextRestartButton sends the next scheduled auto-restart time as an ephemeral message | ||
| func handleGetNextRestartButton(s *discordgo.Session, i *discordgo.InteractionCreate) { | ||
| nextRestart := config.GetNextAutoRestartTime() | ||
|
|
||
| var embed *discordgo.MessageEmbed | ||
| if nextRestart.IsZero() { | ||
| embed = &discordgo.MessageEmbed{ | ||
| Title: "🔄 No Restart Scheduled", | ||
| Description: "No auto-restart is currently scheduled.", | ||
| Color: 0xFFA500, | ||
| } | ||
| } else { | ||
| unixTS := nextRestart.Unix() | ||
| embed = &discordgo.MessageEmbed{ | ||
| Title: "🔄 Next Auto Restart", | ||
| Description: "Times below are shown in your local (Discord) timezone.", | ||
| Color: 0x5865F2, | ||
| Fields: []*discordgo.MessageEmbedField{ | ||
| { | ||
| Name: "Scheduled Time", | ||
| Value: fmt.Sprintf("<t:%d>", unixTS), | ||
| Inline: true, | ||
| }, | ||
| { | ||
| Name: "Countdown", | ||
| Value: fmt.Sprintf("<t:%d:R>", unixTS), | ||
| Inline: true, | ||
| }, | ||
| }, | ||
| Timestamp: time.Now().Format(time.RFC3339), | ||
| } | ||
| } | ||
|
|
||
| err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ | ||
| Type: discordgo.InteractionResponseChannelMessageWithSource, | ||
| Data: &discordgo.InteractionResponseData{ | ||
| Embeds: []*discordgo.MessageEmbed{embed}, | ||
| Flags: discordgo.MessageFlagsEphemeral, | ||
| }, | ||
| }) | ||
| if err != nil { | ||
| logger.Discord.Error("Error responding to next restart button: " + err.Error()) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -20,13 +20,15 @@ func startAutoRestart(schedule string, done chan struct{}) { | |||||||||
| // Try parsing as a time in HH:MM format | ||||||||||
| if t, err := time.Parse("15:04", schedule); err == nil { | ||||||||||
| // Valid HH:MM format, schedule daily restart | ||||||||||
| setNextDailyRestartTime(t) | ||||||||||
| go scheduleDailyRestart(t, done) | ||||||||||
| return | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Try parsing as a time in HH:MMAM/PM format | ||||||||||
| if t, err := time.Parse("03:04PM", schedule); err == nil { | ||||||||||
| // Valid HH:MMAM/PM format, schedule daily restart | ||||||||||
| setNextDailyRestartTime(t) | ||||||||||
| go scheduleDailyRestart(t, done) | ||||||||||
| return | ||||||||||
| } | ||||||||||
|
|
@@ -42,6 +44,8 @@ func startAutoRestart(schedule string, done chan struct{}) { | |||||||||
| return | ||||||||||
| } | ||||||||||
|
|
||||||||||
| config.SetNextAutoRestartTime(time.Now().Add(time.Duration(minutesInt) * time.Minute)) | ||||||||||
|
|
||||||||||
| ticker := time.NewTicker(time.Duration(minutesInt) * time.Minute) | ||||||||||
| defer ticker.Stop() | ||||||||||
|
|
||||||||||
|
|
@@ -88,6 +92,7 @@ func startAutoRestart(schedule string, done chan struct{}) { | |||||||||
| return | ||||||||||
| } | ||||||||||
| case <-done: | ||||||||||
| config.SetNextAutoRestartTime(time.Time{}) | ||||||||||
| return | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
@@ -115,6 +120,8 @@ func scheduleDailyRestart(t time.Time, done chan struct{}) { | |||||||||
| if !internalIsServerRunningNoLock() { | ||||||||||
| mu.Unlock() | ||||||||||
| logger.Core.Info("Auto-restart skipped: server is not running") | ||||||||||
| // Schedule next day | ||||||||||
| setNextDailyRestartTime(t) | ||||||||||
| continue | ||||||||||
| } | ||||||||||
| mu.Unlock() | ||||||||||
|
|
@@ -133,6 +140,8 @@ func scheduleDailyRestart(t time.Time, done chan struct{}) { | |||||||||
| logger.Core.Info("Daily auto-restart triggered: stopping server") | ||||||||||
| if err := InternalStopServer(); err != nil { | ||||||||||
| logger.Core.Error("Daily auto-restart failed to stop server: " + err.Error()) | ||||||||||
| // Schedule next day | ||||||||||
| setNextDailyRestartTime(t) | ||||||||||
| continue | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
|
@@ -146,7 +155,19 @@ func scheduleDailyRestart(t time.Time, done chan struct{}) { | |||||||||
| } | ||||||||||
|
||||||||||
| } | |
| } | |
| // Schedule next day after a successful restart | |
| setNextDailyRestartTime(t) |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -172,6 +172,27 @@ func UninstallSLP() (string, error) { | |||||||||||||
| return "success", nil | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // ReinstallSLP removes only the SLP plugin directory (preserving mods and modconfig.xml), | ||||||||||||||
| // then downloads and installs the latest version. | ||||||||||||||
| // Returns: (installed version tag or "", error) | ||||||||||||||
| func ReinstallSLP() (string, error) { | ||||||||||||||
| pluginsDir := "BepInEx/plugins" | ||||||||||||||
| slpDir := filepath.Join(pluginsDir, "StationeersLaunchPad") | ||||||||||||||
|
|
||||||||||||||
| // Remove only the SLP plugin directory, keep mods & modconfig.xml | ||||||||||||||
| if _, err := os.Stat(slpDir); err == nil { | ||||||||||||||
| logger.Install.Info("🔄 Removing existing SLP installation for reinstall...") | ||||||||||||||
| if err := os.RemoveAll(slpDir); err != nil { | ||||||||||||||
| return "", fmt.Errorf("failed to remove SLP folder for reinstall: %w", err) | ||||||||||||||
| } | ||||||||||||||
| } else { | ||||||||||||||
| logger.Install.Info("SLP not currently installed; proceeding with fresh install") | ||||||||||||||
|
Comment on lines
+188
to
+189
|
||||||||||||||
| } else { | |
| logger.Install.Info("SLP not currently installed; proceeding with fresh install") | |
| } else if os.IsNotExist(err) { | |
| logger.Install.Info("SLP not currently installed; proceeding with fresh install") | |
| } else { | |
| return "", fmt.Errorf("failed to check existing SLP installation: %w", err) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -11,6 +11,7 @@ import ( | |||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config" | ||||||||||||||||||||||||||||||
| "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger" | ||||||||||||||||||||||||||||||
| "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr" | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // runAndExit launches the new executable and terminates the current process | ||||||||||||||||||||||||||||||
|
|
@@ -61,7 +62,13 @@ func runAndExitLinux(newExe string) error { | |||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| func RestartMySelf() { | ||||||||||||||||||||||||||||||
| func RestartMySelf() { // Stop the game server before restarting to prevent detached processes | ||||||||||||||||||||||||||||||
| if config.GetIsGameServerRunning() { | ||||||||||||||||||||||||||||||
| logger.Install.Info("🛑 Stopping game server before restart...") | ||||||||||||||||||||||||||||||
| if err := gamemgr.InternalStopServer(); err != nil { | ||||||||||||||||||||||||||||||
| logger.Install.Warn(fmt.Sprintf("⚠️ Failed to stop game server before restart: %v. Proceeding anyway.", err)) | ||||||||||||||||||||||||||||||
|
Comment on lines
+66
to
+69
|
||||||||||||||||||||||||||||||
| if config.GetIsGameServerRunning() { | |
| logger.Install.Info("🛑 Stopping game server before restart...") | |
| if err := gamemgr.InternalStopServer(); err != nil { | |
| logger.Install.Warn(fmt.Sprintf("⚠️ Failed to stop game server before restart: %v. Proceeding anyway.", err)) | |
| wasRunning := config.GetIsGameServerRunning() | |
| if wasRunning { | |
| logger.Install.Info("🛑 Stopping game server before restart...") | |
| if err := gamemgr.InternalStopServer(); err != nil { | |
| // Re-check running state: if still running, abort restart to avoid detached/duplicate server | |
| if config.GetIsGameServerRunning() { | |
| logger.Install.Error(fmt.Sprintf("⚠️ Failed to stop game server before restart and server still appears to be running: %v. Aborting restart.", err)) | |
| return | |
| } | |
| logger.Install.Warn(fmt.Sprintf("⚠️ Failed to stop game server before restart: %v, but server no longer appears to be running. Proceeding with restart.", err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The UI calls
/api/v2/slp/reinstallwith a default GET. The release notes (and typical HTTP semantics) suggest this should be a POST since it changes server state. Consider changing this fetch tomethod: 'POST'(and handling non-2xx responses), in sync with the backend method enforcement.