diff --git a/UIMod/onboard_bundled/assets/css/config.css b/UIMod/onboard_bundled/assets/css/config.css index ff3d3d2e..abb25d50 100644 --- a/UIMod/onboard_bundled/assets/css/config.css +++ b/UIMod/onboard_bundled/assets/css/config.css @@ -599,6 +599,7 @@ select option { } .manage-left, +.manage-center, .manage-right { flex: 1; display: flex; @@ -610,6 +611,7 @@ select option { } .manage-left p, +.manage-center p, .manage-right p { flex: 1; margin-bottom: 15px; @@ -617,6 +619,7 @@ select option { } .manage-left .slp-button, +.manage-center .slp-button, .manage-right .slp-button { width: 100%; margin-top: auto; diff --git a/UIMod/onboard_bundled/assets/js/slp.js b/UIMod/onboard_bundled/assets/js/slp.js index f4588d19..6fb4ceac 100644 --- a/UIMod/onboard_bundled/assets/js/slp.js +++ b/UIMod/onboard_bundled/assets/js/slp.js @@ -110,6 +110,31 @@ function uninstallSLP() { }); } +function reinstallSLP() { + if (!confirm('Are you sure you want to reinstall SLP? This will re-download and reinstall the SLP plugin. Your mods and modconfig will be preserved.')) { + return; + } + setButtonLoading('reinstallSLPBtn', true); + showPopup('info', 'Reinstalling Stationeers Launch Pad...\n\nThis will re-download the latest version while keeping your mods intact.'); + + fetch('/api/v2/slp/reinstall') + .then(response => response.json()) + .then(data => { + if (data.success) { + showPopup('success', 'Stationeers Launch Pad reinstalled successfully!' + (data.version ? ' (Version: ' + data.version + ')' : '') + ' The page will refresh automatically.'); + setButtonLoading('reinstallSLPBtn', false); + setTimeout(() => window.location.reload(), 3000); + } else { + showPopup('error', 'Failed to reinstall SLP:\n\n' + (data.error || 'Unknown error')); + setButtonLoading('reinstallSLPBtn', false); + } + }) + .catch(error => { + showPopup('error', 'Failed to reinstall SLP:\n\n' + (error.message || 'Network error')); + setButtonLoading('reinstallSLPBtn', false); + }); +} + function updateWorkshopMods() { setButtonLoading('updateWorkshopModsBtn', true); showPopup('info', 'Updating workshop mods...\n\nThis may take some time depending on the number of mods. Please wait.'); diff --git a/UIMod/onboard_bundled/ui/config.html b/UIMod/onboard_bundled/ui/config.html index 329daeb3..ae6c2f08 100644 --- a/UIMod/onboard_bundled/ui/config.html +++ b/UIMod/onboard_bundled/ui/config.html @@ -630,6 +630,10 @@
⚠️ {{.UIText_SLP_UninstallWarning}}
+Re-download and reinstall SLP without removing your mods.
+ +{{.UIText_SLP_UpdateWorkshopModsDesc}}
diff --git a/src/config/config.go b/src/config/config.go index 8f448602..533bf32d 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -11,7 +11,7 @@ import ( var ( // All configuration variables can be found in vars.go - Version = "5.13.1" + Version = "5.13.2" Branch = "release" ) diff --git a/src/config/getters.go b/src/config/getters.go index b7ace57f..315a2434 100644 --- a/src/config/getters.go +++ b/src/config/getters.go @@ -358,6 +358,12 @@ func GetAutoRestartServerTimer() string { return AutoRestartServerTimer } +func GetNextAutoRestartTime() time.Time { + ConfigMu.RLock() + defer ConfigMu.RUnlock() + return NextAutoRestartTime +} + func GetAllowPrereleaseUpdates() bool { ConfigMu.RLock() defer ConfigMu.RUnlock() diff --git a/src/config/setters.go b/src/config/setters.go index 7839e082..39009092 100644 --- a/src/config/setters.go +++ b/src/config/setters.go @@ -413,6 +413,14 @@ func SetAutoRestartServerTimer(value string) error { return safeSaveConfig() } +func SetNextAutoRestartTime(value time.Time) error { + ConfigMu.Lock() + defer ConfigMu.Unlock() + + NextAutoRestartTime = value + return nil +} + // Backup Settings func SetBackupKeepLastN(value int) error { ConfigMu.Lock() diff --git a/src/config/vars.go b/src/config/vars.go index 692fbaa6..05d2f745 100644 --- a/src/config/vars.go +++ b/src/config/vars.go @@ -72,12 +72,13 @@ var ( // Runtime only variables var ( - CurrentBranchBuildID string // ONLY RUNTIME - ExtractedGameVersion string // ONLY RUNTIME - SkipSteamCMD bool // ONLY RUNTIME - IsDockerContainer bool // ONLY RUNTIME - NoSanityCheck bool // ONLY RUNTIME - IsGameServerRunning bool // ONLY RUNTIME + CurrentBranchBuildID string // ONLY RUNTIME + ExtractedGameVersion string // ONLY RUNTIME + SkipSteamCMD bool // ONLY RUNTIME + IsDockerContainer bool // ONLY RUNTIME + NoSanityCheck bool // ONLY RUNTIME + IsGameServerRunning bool // ONLY RUNTIME + NextAutoRestartTime time.Time // ONLY RUNTIME ) // Discord integration diff --git a/src/discordbot/serverstatuspanel.go b/src/discordbot/serverstatuspanel.go index 7b364299..e5942249 100644 --- a/src/discordbot/serverstatuspanel.go +++ b/src/discordbot/serverstatuspanel.go @@ -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, + }) + + 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("