diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index dbf2dcea..0ed4e03b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,5 @@ { - "name": "StationeersServerUI (Go+Svelte)", + "name": "StationeersServerUI", "build": { "dockerfile": "Dockerfile", "args": { "TZ": "${localEnv:TZ:Europe/Stockholm}" } diff --git a/UIMod/onboard_bundled/localization/de-DE.json b/UIMod/onboard_bundled/localization/de-DE.json index 311c62dd..5ec5abd6 100644 --- a/UIMod/onboard_bundled/localization/de-DE.json +++ b/UIMod/onboard_bundled/localization/de-DE.json @@ -23,16 +23,17 @@ "UIText_ConfigurationWizard": "Konfigurations-Assistent", "UIText_PleaseSelectSection": "Bitte wähle oben eine Konfigurationssektion aus", "UIText_UseWizardAlternative": "Alternativ nutze den Konfigurations-Assistenten zur Serverkonfiguration.", - "UIText_BasicSettings": "Grundeinstellungen", - "UIText_NetworkSettings": "Netzwerk-Einstellungen", - "UIText_AdvancedSettings": "Erweiterte Einstellungen", - "UIText_BetaSettings": "Beta-Einstellungen", + "UIText_BasicSettings": "Basis", + "UIText_NetworkSettings": "Netzwerk", + "UIText_AdvancedSettings": "Erweitert", + "UIText_BetaSettings": "Beta", "basic": { "UIText_BasicServerSettings": "Grundlegende Servereinstellungen", "UIText_ServerName": "Servername", "UIText_ServerNameInfo": "Name in der Serverliste angezeigt", "UIText_SaveFileName": "Speicherdatei Name", - "UIText_SaveFileNameInfo": "Name des Speicherordners. Muss großgeschrieben sein. Für neue Welt, Welttyp angeben. (MeineVulkanKarte Vulcan) Welttypen im Stationeers Wiki -> Dedicated Server.", + "UIText_SaveFileNameInfo": "Name des Speicherordners. Muss großgeschrieben sein. Für neue Welt, Welttyp angeben. Es wird empfohlen, diesen Wert über den Setup Assistent zu konfigurieren um ihn korrekt zu setzen. ", + "UIText_SaveFileNameUseWizzardButtonText": "Öffne Assistent", "UIText_MaxPlayers": "Max Spieler", "UIText_MaxPlayersInfo": "Maximale Anzahl erlaubter Spieler", "UIText_ServerPassword": "Server Passwort", @@ -57,7 +58,7 @@ "UIText_LocalIpAddress": "Lokale IP Adresse", "UIText_LocalIpAddressInfo": "IP Adresse zum Binden", "UIText_StartLocalHost": "Lokalen Host Starten", - "UIText_StartLocalHostInfo": "Auf TRUE setzen um nur im lokalen Netzwerk zu hören", + "UIText_StartLocalHostInfo": "Auf TRUE setzen. Dies ist erforderlich für den Server um zu funktionieren.", "UIText_ServerVisible": "Server Sichtbar", "UIText_ServerVisibleInfo": "Auf TRUE setzen um Server öffentlich zu listen", "UIText_UseSteamP2P": "Steam P2P Nutzen", @@ -89,7 +90,10 @@ "UIText_StartLocation": "Startort", "UIText_StartLocationInfo": "Startort für Welterstellung. Standard DefaultStartLocation wenn leer.", "UIText_AutoStartServerOnStartup": "Server Auto-Start beim Hochfahren", - "UIText_AutoStartServerOnStartupInfo": "Gameserver automatisch starten wenn SSUI gestartet wird. Standard false." + "UIText_AutoStartServerOnStartupInfo": "Gameserver automatisch starten wenn SSUI gestartet wird. Standard false.", + "UIText_AllowAutoGameServerUpdates": "Automatische Spielserver-Updates aktivieren", + "UIText_AllowAutoGameServerUpdatesInfo": "Erlaubt dem Spielserver, automatisch nach der neuesten Version zu suchen und diese zu installieren. Achtung: Der Server wird neu gestartet, wenn eine neue Version gefunden und installiert wurde. 60 bis 10 Sekunden vor dem Neustart werden mehrere Warnmeldungen mit SAY-Befehlen an den Server gesendet." + }, "discord": { "UIText_DiscordIntegrationTitle": "Discord Integration Vorteile", @@ -123,113 +127,61 @@ }, "setup": { "UIText_FooterText": "Hilfe benötigt? Schaue ins Stationeers Server UI Github Wiki.", + "UIText_FooterTextInfo": "Du kannst das Setup jederzeit beenden, jeder Schritt wird einzeln gespeichert.", "UIText_SSCM_FooterText": "Nutze SSCM für das mächtigste Stationeers Server Management! Du kannst Befehle von der Web-Konsole ausführen ohne Vanilla-Verhalten zu stören!", "UIText_Welcome_Title": "Stationeers Server UI", + "UIText_Welcome_HeaderTitle": "Willkommen!", "UIText_Welcome_SubmitButton": "Setup Starten", "UIText_Welcome_SkipButton": "Setup Überspringen", "UIText_PlsRead_Title": "Bitte lesen!", - "UIText_PlsRead_HeaderTitle": "Wir empfehlen stark, die Texte in diesem Setup-Assistenten zu lesen!", - "UIText_PlsRead_StepMessage": "Die meisten gemeldeten Probleme entstehen durch Fehlkonfiguration.", + "UIText_PlsRead_HeaderTitle": "Hinweis", + "UIText_PlsRead_StepMessage": "Wir empfehlen stark, die Texte in diesem Setup-Assistenten zu lesen! Die meisten gemeldeten Probleme entstehen durch Fehlkonfiguration.", "UIText_PlsRead_SubmitButton": "Verstanden", "UIText_PlsRead_SkipButton": "Verstanden", "UIText_ServerName_Title": "Stationeers Server UI", - "UIText_ServerName_HeaderTitle": "Servername Setup", + "UIText_ServerName_HeaderTitle": "Servername", "UIText_ServerName_StepMessage": "Gib deinem Server einen Namen wie 'Weltraumstation 13'", "UIText_ServerName_PrimaryPlaceholder": "Mein Stationeers Server mit UI", "UIText_ServerName_PrimaryLabel": "Servername", "UIText_ServerName_SubmitButton": "Speichern & Weiter", "UIText_ServerName_SkipButton": "Überspringen", "UIText_SaveIdentifier_Title": "Stationeers Server UI", - "UIText_SaveIdentifier_HeaderTitle": "Speicher-Identifikator Setup", - "UIText_SaveIdentifier_StepMessage": "Setze einen Speicher-Identifikator wie 'Weltraumstation13 Vulcan'. Ersten Buchstaben jedes Wortes groß schreiben. Welttypen im Stationeers Wiki -> Dedicated Server", - "UIText_SaveIdentifier_PrimaryPlaceholder": "Benötigt SaveName und WorldType für ersten Start!", - "UIText_SaveIdentifier_PrimaryLabel": "Speicher-Identifikator", + "UIText_SaveIdentifier_HeaderTitle": "Speicherstand", + "UIText_SaveIdentifier_StepMessage": "Konfiguriere unten den Namen deines Speicherstands und den Welttyp.", + "UIText_SaveIdentifier_PrimaryLabel": "Name deiner Karte", + "UIText_SaveIdentifier_PrimaryPlaceholder": "MeineStationeersKarte", + "UIText_SaveIdentifier_SecondaryLabel": "Stationeers-Welttyp", + "UIText_SaveIdentifier_SecondaryPlaceholder": "Klicke, um einen Welttyp auszuwählen", "UIText_SaveIdentifier_SubmitButton": "Speichern & Weiter", "UIText_SaveIdentifier_SkipButton": "Überspringen", "UIText_MaxPlayers_Title": "Stationeers Server UI", - "UIText_MaxPlayers_HeaderTitle": "Spielerlimit Setup", + "UIText_MaxPlayers_HeaderTitle": "Spielerlimit", "UIText_MaxPlayers_StepMessage": "Wähle die maximale Anzahl Spieler die sich verbinden können.", "UIText_MaxPlayers_PrimaryPlaceholder": "8", "UIText_MaxPlayers_PrimaryLabel": "Max Spieler", "UIText_MaxPlayers_SubmitButton": "Speichern & Weiter", "UIText_MaxPlayers_SkipButton": "Überspringen", "UIText_ServerPassword_Title": "Stationeers Server UI", - "UIText_ServerPassword_HeaderTitle": "Server Passwort Setup", + "UIText_ServerPassword_HeaderTitle": "Server Passwort", "UIText_ServerPassword_StepMessage": "Setze ein Gameserver Passwort oder überspringe diesen Schritt.", "UIText_ServerPassword_PrimaryPlaceholder": "Server Passwort", "UIText_ServerPassword_PrimaryLabel": "Server Passwort", "UIText_ServerPassword_SubmitButton": "Speichern & Weiter", "UIText_ServerPassword_SkipButton": "Überspringen", "UIText_GameBranch_Title": "Stationeers Server UI", - "UIText_GameBranch_HeaderTitle": "Spiel Branch Setup", - "UIText_GameBranch_StepMessage": "Gib einen Beta-Branch ein oder überspringe für Release-Version. Bei Branch-Wechsel SSUI nach Assistenten n e u s t a r t e n.", - "UIText_GameBranch_PrimaryPlaceholder": "beta", - "UIText_GameBranch_PrimaryLabel": "Spiel Branch", + "UIText_GameBranch_HeaderTitle": "Spiel Branch", + "UIText_GameBranch_StepMessage": "Gib einen Beta-Branch ein oder überspringe für Release-Version. Wenn Sie den Zweig wechseln, klicken Sie nach Abschluss dieses Assistenten unbedingt auf „Server aktualisieren“ im Haupt-Dashboard.", + "UIText_GameBranch_SecondaryPlaceholder": "Wähle einen Zweig", + "UIText_GameBranch_SecondaryLabel": "Spielzweig", "UIText_GameBranch_SubmitButton": "Speichern & Weiter", "UIText_GameBranch_SkipButton": "Release Version nutzen", - "UIText_NewTerrainAndSaveSystem_Title": "TERRAINSYSTEM WÄHLEN", - "UIText_NewTerrainAndSaveSystem_HeaderTitle": "Sehr wichtiger Schritt!", + "UIText_NewTerrainAndSaveSystem_Title": "Wichtig", + "UIText_NewTerrainAndSaveSystem_HeaderTitle": "Terrainsystem wählen", "UIText_NewTerrainAndSaveSystem_StepMessage": "Gerade zu Beta gewechselt? Terrain- und Speichersystem umschalten! 'ja' eingeben zum Aktivieren oder 'nein' zum Deaktivieren.", - "UIText_NewTerrainAndSaveSystem_PrimaryPlaceholder": "ja/nein", + "UIText_NewTerrainAndSaveSystem_PrimaryPlaceholder": "nein", "UIText_NewTerrainAndSaveSystem_PrimaryLabel": "Neues System aktivieren", "UIText_NewTerrainAndSaveSystem_SubmitButton": "Speichern & Weiter", "UIText_NewTerrainAndSaveSystem_SkipButton": "Überspringen", - "UIText_DiscordEnabled_Title": "Stationeers Server UI", - "UIText_DiscordEnabled_HeaderTitle": "Discord Integration", - "UIText_DiscordEnabled_StepMessage": "Discord Integration aktivieren? 'ja' eingeben zum Aktivieren oder Überspringen zum Deaktivieren.", - "UIText_DiscordEnabled_PrimaryPlaceholder": "ja", - "UIText_DiscordEnabled_PrimaryLabel": "Discord Aktivieren", - "UIText_DiscordEnabled_SubmitButton": "Speichern & Weiter", - "UIText_DiscordEnabled_SkipButton": "Überspringen (Discord Deaktivieren)", - "UIText_DiscordToken_Title": "Stationeers Server UI", - "UIText_DiscordToken_HeaderTitle": "Discord Bot Token", - "UIText_DiscordToken_StepMessage": "Gib deinen Discord Bot Token für Server Integration ein", - "UIText_DiscordToken_PrimaryPlaceholder": "Discord Bot Token", - "UIText_DiscordToken_PrimaryLabel": "Discord Token", - "UIText_DiscordToken_SubmitButton": "Speichern & Weiter", - "UIText_DiscordToken_SkipButton": "Überspringen", - "UIText_ControlPanelChannel_Title": "Stationeers Server UI", - "UIText_ControlPanelChannel_HeaderTitle": "Discord Channel Setup (1/6)", - "UIText_ControlPanelChannel_StepMessage": "Discord Kontrollpanel Channel ID eingeben", - "UIText_ControlPanelChannel_PrimaryPlaceholder": "Channel ID", - "UIText_ControlPanelChannel_PrimaryLabel": "Kontrollpanel Channel ID", - "UIText_ControlPanelChannel_SubmitButton": "Speichern & Weiter", - "UIText_ControlPanelChannel_SkipButton": "Überspringen", - "UIText_SaveChannel_Title": "Stationeers Server UI", - "UIText_SaveChannel_HeaderTitle": "Discord Channel Setup (2/6)", - "UIText_SaveChannel_StepMessage": "Discord Speicher Channel ID eingeben", - "UIText_SaveChannel_PrimaryPlaceholder": "Channel ID", - "UIText_SaveChannel_PrimaryLabel": "Speicher Channel ID", - "UIText_SaveChannel_SubmitButton": "Speichern & Weiter", - "UIText_SaveChannel_SkipButton": "Überspringen", - "UIText_LogChannel_Title": "Stationeers Server UI", - "UIText_LogChannel_HeaderTitle": "Discord Channel Setup (3/6)", - "UIText_LogChannel_StepMessage": "Discord Log Channel ID eingeben", - "UIText_LogChannel_PrimaryPlaceholder": "Channel ID", - "UIText_LogChannel_PrimaryLabel": "Log Channel ID", - "UIText_LogChannel_SubmitButton": "Speichern & Weiter", - "UIText_LogChannel_SkipButton": "Überspringen", - "UIText_ConnectionListChannel_Title": "Stationeers Server UI", - "UIText_ConnectionListChannel_HeaderTitle": "Discord Channel Setup (4/6)", - "UIText_ConnectionListChannel_StepMessage": "Discord Verbindungslisten Channel ID eingeben", - "UIText_ConnectionListChannel_PrimaryPlaceholder": "Channel ID", - "UIText_ConnectionListChannel_PrimaryLabel": "Verbindungslisten Channel ID", - "UIText_ConnectionListChannel_SubmitButton": "Speichern & Weiter", - "UIText_ConnectionListChannel_SkipButton": "Überspringen", - "UIText_StatusChannel_Title": "Stationeers Server UI", - "UIText_StatusChannel_HeaderTitle": "Discord Channel Setup (5/6)", - "UIText_StatusChannel_StepMessage": "Discord Status Channel ID eingeben", - "UIText_StatusChannel_PrimaryPlaceholder": "Channel ID", - "UIText_StatusChannel_PrimaryLabel": "Status Channel ID", - "UIText_StatusChannel_SubmitButton": "Speichern & Weiter", - "UIText_StatusChannel_SkipButton": "Überspringen", - "UIText_ControlChannel_Title": "Stationeers Server UI", - "UIText_ControlChannel_HeaderTitle": "Discord Channel Setup (6/6)", - "UIText_ControlChannel_StepMessage": "Discord Control Channel ID eingeben", - "UIText_ControlChannel_PrimaryPlaceholder": "Channel ID", - "UIText_ControlChannel_PrimaryLabel": "Control Channel ID", - "UIText_ControlChannel_SubmitButton": "Speichern & Weiter", - "UIText_ControlChannel_SkipButton": "Überspringen", "UIText_NetworkConfigChoice_Title": "Stationeers Server UI", "UIText_NetworkConfigChoice_HeaderTitle": "Netzwerk Konfiguration", "UIText_NetworkConfigChoice_StepMessage": "Netzwerkeinstellungen konfigurieren? 'ja' für Konfiguration oder Überspringen für Standards. Hinweis: Netzwerkkonfiguration besonders wichtig auf Linux Servern.", @@ -238,50 +190,44 @@ "UIText_NetworkConfigChoice_SubmitButton": "Weiter", "UIText_NetworkConfigChoice_SkipButton": "Überspringen (Standards nutzen)", "UIText_GamePort_Title": "Stationeers Server UI", - "UIText_GamePort_HeaderTitle": "Netzwerk Setup (4/4)", + "UIText_GamePort_HeaderTitle": "Netzwerk (1/4)", "UIText_GamePort_StepMessage": "Port-Nummer für Spielverbindungen eingeben", "UIText_GamePort_PrimaryPlaceholder": "27016", "UIText_GamePort_PrimaryLabel": "Spiel Port", "UIText_GamePort_SubmitButton": "Speichern & Weiter", "UIText_GamePort_SkipButton": "Überspringen", "UIText_UpdatePort_Title": "Stationeers Server UI", - "UIText_UpdatePort_HeaderTitle": "Netzwerk Setup (4/4)", + "UIText_UpdatePort_HeaderTitle": "Netzwerk (2/4)", "UIText_UpdatePort_StepMessage": "Port-Nummer für Update-Verbindungen eingeben", "UIText_UpdatePort_PrimaryPlaceholder": "27015", "UIText_UpdatePort_PrimaryLabel": "Update Port", "UIText_UpdatePort_SubmitButton": "Speichern & Weiter", "UIText_UpdatePort_SkipButton": "Überspringen", "UIText_UPnPEnabled_Title": "Stationeers Server UI", - "UIText_UPnPEnabled_HeaderTitle": "Netzwerk Setup (4/4)", + "UIText_UPnPEnabled_HeaderTitle": "Netzwerk (3/4)", "UIText_UPnPEnabled_StepMessage": "UPnP aktivieren? 'ja' zum Aktivieren oder 'nein' zum Deaktivieren.", "UIText_UPnPEnabled_PrimaryPlaceholder": "ja/nein", "UIText_UPnPEnabled_PrimaryLabel": "UPnP Aktivieren", "UIText_UPnPEnabled_SubmitButton": "Speichern & Weiter", "UIText_UPnPEnabled_SkipButton": "Überspringen", "UIText_LocalIPAddress_Title": "Stationeers Server UI", - "UIText_LocalIPAddress_HeaderTitle": "Netzwerk Setup (4/4)", + "UIText_LocalIPAddress_HeaderTitle": "Netzwerk (4/4)", "UIText_LocalIPAddress_StepMessage": "Lokale IP-Adresse des Servers im Format 0.0.0.0 eingeben (keine CIDR Notation)", "UIText_LocalIPAddress_PrimaryPlaceholder": "0.0.0.0", "UIText_LocalIPAddress_PrimaryLabel": "Lokale IP-Adresse", "UIText_LocalIPAddress_SubmitButton": "Speichern & Weiter", "UIText_LocalIPAddress_SkipButton": "Überspringen", "UIText_AdminAccount_Title": "Stationeers Server UI", - "UIText_AdminAccount_HeaderTitle": "Admin Account Setup", - "UIText_AdminAccount_StepMessage": "Richte deinen Admin-Account ein.", + "UIText_AdminAccount_HeaderTitle": "Adminkonto", + "UIText_AdminAccount_StepMessage": "Richte dein Adminkonto ein.", "UIText_AdminAccount_PrimaryPlaceholder": "Benutzername", "UIText_AdminAccount_PrimaryLabel": "Benutzername", "UIText_AdminAccount_SecondaryLabel": "Passwort", "UIText_AdminAccount_SecondaryPlaceholder": "Passwort", "UIText_AdminAccount_SubmitButton": "Speichern & Weiter", "UIText_AdminAccount_SkipButton": "Authentifizierung Überspringen", - "UIText_SSCM_Title": "Stationeers Server Command Manager", - "UIText_SSCM_HeaderTitle": "Einzigartige Funktion", - "UIText_SSCM_StepMessage": "SSCM ist ein maßgeschneidertes Server-Plugin für direkte Serverbefehl-Ausführung aus SSUI. Es ermöglicht Befehle aus der Web-Konsole ohne Störung des Vanilla-Verhaltens, keine Client-seitigen Mods nötig.", - "UIText_SSCM_PrimaryPlaceholder": "'nein' eingeben zum Deaktivieren", - "UIText_SSCM_PrimaryLabel": "Deaktivierung NICHT empfohlen.", - "UIText_SSCM_SubmitButton": "Weiter", - "UIText_SSCM_SkipButton": "Aktiviert lassen", - "UIText_Finalize_Title": "Setup Abschließen", + "UIText_Finalize_Title": "Das wars!", + "UIText_Finalize_HeaderTitle": "Setup Abschließen", "UIText_Finalize_StepMessage": "Bereit zum Abschließen? Deine Konfiguration wurde bereits während des Setups gespeichert. Für Änderungen klicke 'Zurück zum Start' und überspringe was behalten werden soll. Meiste Optionen auch im Config Tab änderbar.", "UIText_Finalize_SubmitButton": "Zurück zum Start", "UIText_Finalize_SkipButton": "Authentifizierung Überspringen", diff --git a/UIMod/onboard_bundled/localization/en-US.json b/UIMod/onboard_bundled/localization/en-US.json index 5cc0140d..4e0b9806 100644 --- a/UIMod/onboard_bundled/localization/en-US.json +++ b/UIMod/onboard_bundled/localization/en-US.json @@ -32,7 +32,8 @@ "UIText_ServerName": "Server Name", "UIText_ServerNameInfo": "Name displayed in server list", "UIText_SaveFileName": "Save File Name", - "UIText_SaveFileNameInfo": "Name of save folder. Must be capitalized. To create a new world, provide the World type to generate. (MyVulcanMap Vulcan) Possible World types: Moon, Mars, Europa, Mimas, Vulcan, Space, Venus -- BETA BRANCH: Mars2, Europa3, MimasHerschel, Vulcan, Venus, Lunar", + "UIText_SaveFileNameInfo": "Name of save folder. Must be capitalized. To create a new world, provide the World type to generate. It is recommended to use the Wizard to configure this value correctly. ", + "UIText_SaveFileNameUseWizzardButtonText": "Open Wizard", "UIText_MaxPlayers": "Max Players", "UIText_MaxPlayersInfo": "Maximum number of players allowed", "UIText_ServerPassword": "Server Password", @@ -57,7 +58,7 @@ "UIText_LocalIpAddress": "Local IP Address", "UIText_LocalIpAddressInfo": "IP address to bind to", "UIText_StartLocalHost": "Start Local Host", - "UIText_StartLocalHostInfo": "Set to TRUE to listen only on local network", + "UIText_StartLocalHostInfo": "Keep this true. This is required for the server to work.", "UIText_ServerVisible": "Server Visible", "UIText_ServerVisibleInfo": "Set to TRUE to list server publicly", "UIText_UseSteamP2P": "Use Steam P2P", @@ -75,7 +76,9 @@ "UIText_AutoRestartServerTimer": "Scheduled Gameserver Restart", "UIText_AutoRestartServerTimerInfo": "

Timeframe in minutes or time format (e.g., 15:04 or 03:04PM) to schedule an automatic gameserver restart. 0 = disabled, 1440 = 24 hours, etc. You will see 'Attention, server is restarting in 30/20/10/5 seconds!' messages ingame before the restart.

", "UIText_GameBranch": "Game Branch", - "UIText_GameBranchInfo": "Branch of the game to use. When changed, requires to restart SSUI!" + "UIText_GameBranchInfo": "Branch of the game to use. When changed, requires to restart SSUI!", + "UIText_AllowAutoGameServerUpdates": "Enable Auto Game Server Updates", + "UIText_AllowAutoGameServerUpdatesInfo": "Allow the gameserver to automatically query for and update to the latest version. Attention: Restarts the server when a new version was found and installed. Will send multiple warning messages to the sever with SAY commands 60-10 seconds before the restart." }, "beta": { "UIText_BetaOnlySettings": "BETA ONLY: NEW TERRAIN AND SAVE SYSTEM SETTINGS", @@ -123,113 +126,61 @@ }, "setup": { "UIText_FooterText": "Need help? Check the Stationeers Server UI Github Wiki.", + "UIText_FooterTextInfo": "You may exit the wizard at any time, each step is saved individually.", "UIText_SSCM_FooterText": "Use SSCM for the most powerful Stationeers server management! You can run commands from the Web console without disrupting vanilla behaviour!", "UIText_Welcome_Title": "Stationeers Server UI", + "UIText_Welcome_HeaderTitle": "Welcome!", "UIText_Welcome_SubmitButton": "Start Setup", "UIText_Welcome_SkipButton": "Skip Setup", "UIText_PlsRead_Title": "Please read!", - "UIText_PlsRead_HeaderTitle": "We strongly recommend you to read the texts in this setup wizard!", - "UIText_PlsRead_StepMessage": "Most reported issues occur because of a misconfiguration.", + "UIText_PlsRead_HeaderTitle": "Disclaimer", + "UIText_PlsRead_StepMessage": "We strongly recommend you to read the texts in this setup wizard! Most reported issues occur because of a misconfiguration.", "UIText_PlsRead_SubmitButton": "I understand", "UIText_PlsRead_SkipButton": "I understand", "UIText_ServerName_Title": "Stationeers Server UI", - "UIText_ServerName_HeaderTitle": "Server Name Setup", + "UIText_ServerName_HeaderTitle": "Server Name", "UIText_ServerName_StepMessage": "Give your server a name like 'Space Station 13'", "UIText_ServerName_PrimaryPlaceholder": "My Stationeers Server with UI", "UIText_ServerName_PrimaryLabel": "Server Name", "UIText_ServerName_SubmitButton": "Save & Continue", "UIText_ServerName_SkipButton": "Skip", "UIText_SaveIdentifier_Title": "Stationeers Server UI", - "UIText_SaveIdentifier_HeaderTitle": "Save Identifier Setup", - "UIText_SaveIdentifier_StepMessage": "Name of save folder, like 'MySave Lunar'. Capitalize the first letter of each word. To create a new world, provide the World type to generate. (MyLunarMap Lunar) Possible World types: Moon, Mars, Europa, Mimas, Vulcan, Space, Venus -- BETA BRANCH: Mars2, Europa3, MimasHerschel, Vulcan, Venus, Lunar", - "UIText_SaveIdentifier_PrimaryPlaceholder": "Requires a SaveName and WorldType for first start!", - "UIText_SaveIdentifier_PrimaryLabel": "Save Identifier", + "UIText_SaveIdentifier_HeaderTitle": "Save Identifier", + "UIText_SaveIdentifier_StepMessage": "Configue your savegame name and world type from the options below.", + "UIText_SaveIdentifier_PrimaryLabel": "Your Map Name", + "UIText_SaveIdentifier_PrimaryPlaceholder": "MyStationeersMap", + "UIText_SaveIdentifier_SecondaryLabel": "Stationeers World Type", + "UIText_SaveIdentifier_SecondaryPlaceholder": "Click to select a world type", "UIText_SaveIdentifier_SubmitButton": "Save & Continue", "UIText_SaveIdentifier_SkipButton": "Skip", "UIText_MaxPlayers_Title": "Stationeers Server UI", - "UIText_MaxPlayers_HeaderTitle": "Player Limit Setup", + "UIText_MaxPlayers_HeaderTitle": "Player Limit", "UIText_MaxPlayers_StepMessage": "Choose the maximum number of players that can connect to the server. Recommended to not exceed 20", "UIText_MaxPlayers_PrimaryPlaceholder": "8", "UIText_MaxPlayers_PrimaryLabel": "Max Players", "UIText_MaxPlayers_SubmitButton": "Save & Continue", "UIText_MaxPlayers_SkipButton": "Skip", "UIText_ServerPassword_Title": "Stationeers Server UI", - "UIText_ServerPassword_HeaderTitle": "Server Password Setup", + "UIText_ServerPassword_HeaderTitle": "Server Password", "UIText_ServerPassword_StepMessage": "Set a gameserver password or skip this step.", "UIText_ServerPassword_PrimaryPlaceholder": "Server Password", "UIText_ServerPassword_PrimaryLabel": "Server Password", "UIText_ServerPassword_SubmitButton": "Save & Continue", "UIText_ServerPassword_SkipButton": "Skip", "UIText_GameBranch_Title": "Stationeers Server UI", - "UIText_GameBranch_HeaderTitle": "Game Branch Setup", + "UIText_GameBranch_HeaderTitle": "Game Branch", "UIText_GameBranch_StepMessage": "Enter a beta branch or skip this to use the release version. If switching branches, make sure to click Update Server on the Main dashboard after completing this wizzard.", - "UIText_GameBranch_PrimaryPlaceholder": "beta", - "UIText_GameBranch_PrimaryLabel": "Game Branch", + "UIText_GameBranch_SecondaryPlaceholder": "Select a branch", + "UIText_GameBranch_SecondaryLabel": "Game Branch", "UIText_GameBranch_SubmitButton": "Save & Continue", "UIText_GameBranch_SkipButton": "Use Normal Version", - "UIText_NewTerrainAndSaveSystem_Title": "CHOOSE TERRAIN SYSTEM", - "UIText_NewTerrainAndSaveSystem_HeaderTitle": "Very important step!", + "UIText_NewTerrainAndSaveSystem_Title": "IMPORTANT", + "UIText_NewTerrainAndSaveSystem_HeaderTitle": "Select Terrain System", "UIText_NewTerrainAndSaveSystem_StepMessage": "Just switched to Beta? If yes, enable handling of the new terrain and save system here. Enter 'yes' to enable or 'no' to use the old system.", - "UIText_NewTerrainAndSaveSystem_PrimaryPlaceholder": "yes/no", + "UIText_NewTerrainAndSaveSystem_PrimaryPlaceholder": "no", "UIText_NewTerrainAndSaveSystem_PrimaryLabel": "Enable new System", "UIText_NewTerrainAndSaveSystem_SubmitButton": "Save & Continue", "UIText_NewTerrainAndSaveSystem_SkipButton": "Skip", - "UIText_DiscordEnabled_Title": "Stationeers Server UI", - "UIText_DiscordEnabled_HeaderTitle": "Discord Integration", - "UIText_DiscordEnabled_StepMessage": "Do you want to enable Discord integration? Enter 'yes' to enable or Skip to disable.", - "UIText_DiscordEnabled_PrimaryPlaceholder": "yes", - "UIText_DiscordEnabled_PrimaryLabel": "Enable Discord", - "UIText_DiscordEnabled_SubmitButton": "Save & Continue", - "UIText_DiscordEnabled_SkipButton": "Skip (Disable Discord)", - "UIText_DiscordToken_Title": "Stationeers Server UI", - "UIText_DiscordToken_HeaderTitle": "Discord Bot Token", - "UIText_DiscordToken_StepMessage": "Enter your Discord bot token for server integration", - "UIText_DiscordToken_PrimaryPlaceholder": "Discord Bot Token", - "UIText_DiscordToken_PrimaryLabel": "Discord Token", - "UIText_DiscordToken_SubmitButton": "Save & Continue", - "UIText_DiscordToken_SkipButton": "Skip", - "UIText_ControlPanelChannel_Title": "Stationeers Server UI", - "UIText_ControlPanelChannel_HeaderTitle": "Discord Channel Setup (1/6)", - "UIText_ControlPanelChannel_StepMessage": "Enter Discord Control Panel Channel ID", - "UIText_ControlPanelChannel_PrimaryPlaceholder": "Channel ID", - "UIText_ControlPanelChannel_PrimaryLabel": "Control Panel Channel ID", - "UIText_ControlPanelChannel_SubmitButton": "Save & Continue", - "UIText_ControlPanelChannel_SkipButton": "Skip", - "UIText_SaveChannel_Title": "Stationeers Server UI", - "UIText_SaveChannel_HeaderTitle": "Discord Channel Setup (2/6)", - "UIText_SaveChannel_StepMessage": "Enter Discord Save Channel ID", - "UIText_SaveChannel_PrimaryPlaceholder": "Channel ID", - "UIText_SaveChannel_PrimaryLabel": "Save Channel ID", - "UIText_SaveChannel_SubmitButton": "Save & Continue", - "UIText_SaveChannel_SkipButton": "Skip", - "UIText_LogChannel_Title": "Stationeers Server UI", - "UIText_LogChannel_HeaderTitle": "Discord Channel Setup (3/6)", - "UIText_LogChannel_StepMessage": "Enter Discord Log Channel ID", - "UIText_LogChannel_PrimaryPlaceholder": "Channel ID", - "UIText_LogChannel_PrimaryLabel": "Log Channel ID", - "UIText_LogChannel_SubmitButton": "Save & Continue", - "UIText_LogChannel_SkipButton": "Skip", - "UIText_ConnectionListChannel_Title": "Stationeers Server UI", - "UIText_ConnectionListChannel_HeaderTitle": "Discord Channel Setup (4/6)", - "UIText_ConnectionListChannel_StepMessage": "Enter Discord Connection List Channel ID", - "UIText_ConnectionListChannel_PrimaryPlaceholder": "Channel ID", - "UIText_ConnectionListChannel_PrimaryLabel": "Connection List Channel ID", - "UIText_ConnectionListChannel_SubmitButton": "Save & Continue", - "UIText_ConnectionListChannel_SkipButton": "Skip", - "UIText_StatusChannel_Title": "Stationeers Server UI", - "UIText_StatusChannel_HeaderTitle": "Discord Channel Setup (5/6)", - "UIText_StatusChannel_StepMessage": "Enter Discord Status Channel ID", - "UIText_StatusChannel_PrimaryPlaceholder": "Channel ID", - "UIText_StatusChannel_PrimaryLabel": "Status Channel ID", - "UIText_StatusChannel_SubmitButton": "Save & Continue", - "UIText_StatusChannel_SkipButton": "Skip", - "UIText_ControlChannel_Title": "Stationeers Server UI", - "UIText_ControlChannel_HeaderTitle": "Discord Channel Setup (6/6)", - "UIText_ControlChannel_StepMessage": "Enter Discord Control Channel ID", - "UIText_ControlChannel_PrimaryPlaceholder": "Channel ID", - "UIText_ControlChannel_PrimaryLabel": "Control Channel ID", - "UIText_ControlChannel_SubmitButton": "Save & Continue", - "UIText_ControlChannel_SkipButton": "Skip", "UIText_NetworkConfigChoice_Title": "Stationeers Server UI", "UIText_NetworkConfigChoice_HeaderTitle": "Network Configuration", "UIText_NetworkConfigChoice_StepMessage": "Do you want to configure network settings? Enter 'yes' to configure or Skip to use defaults. Note: Network configuration is especially important on Linux servers.", @@ -238,35 +189,35 @@ "UIText_NetworkConfigChoice_SubmitButton": "Continue", "UIText_NetworkConfigChoice_SkipButton": "Skip (Use Defaults)", "UIText_GamePort_Title": "Stationeers Server UI", - "UIText_GamePort_HeaderTitle": "Network Setup (1/4)", + "UIText_GamePort_HeaderTitle": "Network (1/4)", "UIText_GamePort_StepMessage": "Enter the port number for game connections", "UIText_GamePort_PrimaryPlaceholder": "27016", "UIText_GamePort_PrimaryLabel": "Game Port", "UIText_GamePort_SubmitButton": "Save & Continue", "UIText_GamePort_SkipButton": "Skip", "UIText_UpdatePort_Title": "Stationeers Server UI", - "UIText_UpdatePort_HeaderTitle": "Network Setup (2/4)", + "UIText_UpdatePort_HeaderTitle": "Network (2/4)", "UIText_UpdatePort_StepMessage": "Enter the port number for update connections", "UIText_UpdatePort_PrimaryPlaceholder": "27015", "UIText_UpdatePort_PrimaryLabel": "Update Port", "UIText_UpdatePort_SubmitButton": "Save & Continue", "UIText_UpdatePort_SkipButton": "Skip", "UIText_UPnPEnabled_Title": "Stationeers Server UI", - "UIText_UPnPEnabled_HeaderTitle": "Network Setup (3/4)", + "UIText_UPnPEnabled_HeaderTitle": "Network (3/4)", "UIText_UPnPEnabled_StepMessage": "Enable UPnP? Enter 'yes' to enable or 'no' to disable.", "UIText_UPnPEnabled_PrimaryPlaceholder": "yes/no", "UIText_UPnPEnabled_PrimaryLabel": "Enable UPnP", "UIText_UPnPEnabled_SubmitButton": "Save & Continue", "UIText_UPnPEnabled_SkipButton": "Skip", "UIText_LocalIPAddress_Title": "Stationeers Server UI", - "UIText_LocalIPAddress_HeaderTitle": "Network Setup (4/4)", + "UIText_LocalIPAddress_HeaderTitle": "Network (4/4)", "UIText_LocalIPAddress_StepMessage": "Enter server's local IP address in format 0.0.0.0 (no CIDR notation)", "UIText_LocalIPAddress_PrimaryPlaceholder": "0.0.0.0", "UIText_LocalIPAddress_PrimaryLabel": "Local IP Address", "UIText_LocalIPAddress_SubmitButton": "Save & Continue", "UIText_LocalIPAddress_SkipButton": "Skip", "UIText_AdminAccount_Title": "Stationeers Server UI", - "UIText_AdminAccount_HeaderTitle": "Admin Account Setup", + "UIText_AdminAccount_HeaderTitle": "Admin Account", "UIText_AdminAccount_StepMessage": "Set up your SSUI admin account.", "UIText_AdminAccount_PrimaryPlaceholder": "Username", "UIText_AdminAccount_PrimaryLabel": "Username", @@ -274,14 +225,8 @@ "UIText_AdminAccount_SecondaryPlaceholder": "Password", "UIText_AdminAccount_SubmitButton": "Save & Continue", "UIText_AdminAccount_SkipButton": "Skip Authentication", - "UIText_SSCM_Title": "Stationeers Server Command Manager", - "UIText_SSCM_HeaderTitle": "Unique Feature", - "UIText_SSCM_StepMessage": "SSCM is a custom server plugin that allows you to execute server commands directly from SSUI. It gives you the ability to run commands from the Web console without disrupting vanlilla behaviour, you dont need any client side mods.", - "UIText_SSCM_PrimaryPlaceholder": "type 'no' to disable", - "UIText_SSCM_PrimaryLabel": "Opting out is NOT recommended.", - "UIText_SSCM_SubmitButton": "Continue", - "UIText_SSCM_SkipButton": "Keep enabled", - "UIText_Finalize_Title": "Finalize Setup", + "UIText_Finalize_Title": "You did it", + "UIText_Finalize_HeaderTitle": "Finalize Setup", "UIText_Finalize_StepMessage": "Ready to finalize? Your configuration has already been saved while you completed this setup. If you want to change any of the settings, you may click Return to Start and skip whatever you want to keep. Most options can also be changed on the config Tab in the UI later.", "UIText_Finalize_SubmitButton": "Return to Start", "UIText_Finalize_SkipButton": "Skip Authentication", diff --git a/UIMod/onboard_bundled/localization/sv-SE.json b/UIMod/onboard_bundled/localization/sv-SE.json index 8ec17c98..ac731882 100644 --- a/UIMod/onboard_bundled/localization/sv-SE.json +++ b/UIMod/onboard_bundled/localization/sv-SE.json @@ -32,7 +32,8 @@ "UIText_ServerName": "Servernamn", "UIText_ServerNameInfo": "Namn som visas i serverlistan", "UIText_SaveFileName": "Sparfilsnamn", - "UIText_SaveFileNameInfo": "Namn på sparmappen. Måste börja med stor bokstav. För att skapa en ny värld, ange världstypen att generera. (MyVulcanMap Vulcan) Världstyper finns på Stationeers Wiki -> Dedicated Server-sidan.", + "UIText_SaveFileNameInfo": "Namn på sparmappen. Måste börja med stor bokstav. För att skapa en ny värld, ange världstypen att generera. Vi rekommenderar att du använder guiden för att konfigurera detta korrekt.", + "UIText_SaveFileNameUseWizzardButtonText": "Öppna guiden", "UIText_MaxPlayers": "Max spelare", "UIText_MaxPlayersInfo": "Maximalt antal tillåtna spelare", "UIText_ServerPassword": "Serverlösenord", @@ -57,7 +58,7 @@ "UIText_LocalIpAddress": "Lokal IP-adress", "UIText_LocalIpAddressInfo": "IP-adress att binda till", "UIText_StartLocalHost": "Starta lokal värd", - "UIText_StartLocalHostInfo": "Sätt till TRUE för att endast lyssna på lokalt nätverk", + "UIText_StartLocalHostInfo": "Sätt till TRUE. Detta är nödvändigt för att servern ska fungera.", "UIText_ServerVisible": "Server synlig", "UIText_ServerVisibleInfo": "Sätt till TRUE för att visa servern offentligt", "UIText_UseSteamP2P": "Använd Steam P2P", @@ -75,7 +76,9 @@ "UIText_AutoRestartServerTimer": "Schemalagd spelserveromstart", "UIText_AutoRestartServerTimerInfo": "Tidsram i minuter för att schemalägga en automatisk spelserveromstart. 0 = inaktiverad, 1440 = 24 timmar, osv. Om SSCM är aktiverat visas meddelanden som \"Varning, servern startar om om 30/20/10/5 sekunder!\" i spelet före omstart.", "UIText_GameBranch": "Spelgren", - "UIText_GameBranchInfo": "Spelgren att använda. Vid ändring krävs omstart av SSUI!" + "UIText_GameBranchInfo": "Spelgren att använda. Vid ändring krävs omstart av SSUI!", + "UIText_AllowAutoGameServerUpdates": "Aktivera automatiska uppdateringar av spelservern", + "UIText_AllowAutoGameServerUpdatesInfo": "Tillåt spelservern att automatiskt söka efter och uppdatera till den senaste versionen. Obs! Servern startas om när en ny version har hittats och installerats. Flera varningsmeddelanden skickas till servern med SAY-kommandon 60–10 sekunder före omstarten." }, "beta": { "UIText_BetaOnlySettings": "ENDAST BETA: INSTÄLLNINGAR FÖR NYTT TERRÄNG- OCH SPARSYSTEM", @@ -123,150 +126,98 @@ }, "setup": { "UIText_FooterText": "Behöver du hjälp? Kolla Stationeers Server UI Github Wiki.", + "UIText_FooterTextInfo": "Du kan avsluta guiden när som helst, varje steg sparas individuellt.", "UIText_SSCM_FooterText": "Använd SSCM för den mest kraftfulla hanteringen av Stationeers-servrar! Du kan köra kommandon från webbkonsolen utan att störa vanliga funktioner!", "UIText_Welcome_Title": "Stationeers Server UI", + "UIText_Welcome_HeaderTitle": "Välkommen!", "UIText_Welcome_SubmitButton": "Börja konfigurera", "UIText_Welcome_SkipButton": "Hoppa över konfiguration", "UIText_PlsRead_Title": "Läs detta!", - "UIText_PlsRead_HeaderTitle": "Viktigt att läsa!", - "UIText_PlsRead_StepMessage": "De flesta rapporterade problemen beror på felkonfiguration.", + "UIText_PlsRead_HeaderTitle": "Viktigt", + "UIText_PlsRead_StepMessage": "Läs texterna ordentligt! De flesta rapporterade problem beror på felaktiga inställningar.", "UIText_PlsRead_SubmitButton": "Jag förstår", "UIText_PlsRead_SkipButton": "Jag förstår", "UIText_ServerName_Title": "Stationeers Server UI", - "UIText_ServerName_HeaderTitle": "Servernamninställning", + "UIText_ServerName_HeaderTitle": "Servernamn", "UIText_ServerName_StepMessage": "Ge din server ett namn, t.ex. 'Rymdstation 13'", "UIText_ServerName_PrimaryPlaceholder": "Min Stationeers-server med UI", "UIText_ServerName_PrimaryLabel": "Servernamn", "UIText_ServerName_SubmitButton": "Spara & fortsätt", "UIText_ServerName_SkipButton": "Hoppa över", "UIText_SaveIdentifier_Title": "Stationeers Server UI", - "UIText_SaveIdentifier_HeaderTitle": "Sparidentifieringsinställning", - "UIText_SaveIdentifier_StepMessage": "Ange en sparidentifierare, t.ex. 'SpaceStation13 Vulcan'. Använd stor bokstav i början av varje ord. Världstyper finns på Stationeers Wiki -> Dedicated Server.", - "UIText_SaveIdentifier_PrimaryPlaceholder": "Kräver ett sparnamn och världstyp vid första start!", - "UIText_SaveIdentifier_PrimaryLabel": "Sparidentifierare", + "UIText_SaveIdentifier_HeaderTitle": "Sparfil", + "UIText_SaveIdentifier_StepMessage": "Ange ett namn för ditt spel och världstyp från alternativen nedan.", + "UIText_SaveIdentifier_PrimaryLabel": "Namn på din karta", + "UIText_SaveIdentifier_PrimaryPlaceholder": "MinStationeersKarta", + "UIText_SaveIdentifier_SecondaryLabel": "Stationeers världstyp", + "UIText_SaveIdentifier_SecondaryPlaceholder": "Klicka för att välja en världstyp", "UIText_SaveIdentifier_SubmitButton": "Spara & fortsätt", "UIText_SaveIdentifier_SkipButton": "Hoppa över", "UIText_MaxPlayers_Title": "Stationeers Server UI", - "UIText_MaxPlayers_HeaderTitle": "Spelargränsinställning", + "UIText_MaxPlayers_HeaderTitle": "Spelargräns", "UIText_MaxPlayers_StepMessage": "Välj maximalt antal spelare som kan ansluta till servern.", "UIText_MaxPlayers_PrimaryPlaceholder": "8", "UIText_MaxPlayers_PrimaryLabel": "Max spelare", "UIText_MaxPlayers_SubmitButton": "Spara & fortsätt", "UIText_MaxPlayers_SkipButton": "Hoppa över", "UIText_ServerPassword_Title": "Stationeers Server UI", - "UIText_ServerPassword_HeaderTitle": "Serverlösenordsinställning", + "UIText_ServerPassword_HeaderTitle": "Serverlösenord", "UIText_ServerPassword_StepMessage": "Ange ett serverlösenord eller hoppa över detta steg.", "UIText_ServerPassword_PrimaryPlaceholder": "Serverlösenord", "UIText_ServerPassword_PrimaryLabel": "Serverlösenord", "UIText_ServerPassword_SubmitButton": "Spara & fortsätt", "UIText_ServerPassword_SkipButton": "Hoppa över", "UIText_GameBranch_Title": "Stationeers Server UI", - "UIText_GameBranch_HeaderTitle": "Spelgreninställning", - "UIText_GameBranch_StepMessage": "Ange en betagren eller hoppa över för att använda standardversionen. Vid byte av gren, starta om SSUI efter guiden.", - "UIText_GameBranch_PrimaryPlaceholder": "beta", - "UIText_GameBranch_PrimaryLabel": "Spelgren", + "UIText_GameBranch_HeaderTitle": "Spel-branch", + "UIText_GameBranch_StepMessage": "Ange en betagren eller hoppa över för att använda standardversionen. Om du byter grenar, se till att klicka på Uppdatera server på huvudpanelen efter att du har slutfört den här guiden.", + "UIText_GameBranch_SecondaryPlaceholder": "Välj en gren", + "UIText_GameBranch_SecondaryLabel": "Spelgren", "UIText_GameBranch_SubmitButton": "Spara & fortsätt", "UIText_GameBranch_SkipButton": "Använd standardversion", - "UIText_NewTerrainAndSaveSystem_Title": "VÄLJ TERRÄNGSYSTEM", - "UIText_NewTerrainAndSaveSystem_HeaderTitle": "Viktigt steg!", + "UIText_NewTerrainAndSaveSystem_Title": "Viktigt", + "UIText_NewTerrainAndSaveSystem_HeaderTitle": "Välj terrängsystem", "UIText_NewTerrainAndSaveSystem_StepMessage": "Bytt till beta? Aktivera terräng- och sparsystem för att stödja det! Ange 'ja' för att aktivera eller 'nej' för att inaktivera.", - "UIText_NewTerrainAndSaveSystem_PrimaryPlaceholder": "yes/no", + "UIText_NewTerrainAndSaveSystem_PrimaryPlaceholder": "nej", "UIText_NewTerrainAndSaveSystem_PrimaryLabel": "Aktivera nytt system", "UIText_NewTerrainAndSaveSystem_SubmitButton": "Spara & fortsätt", "UIText_NewTerrainAndSaveSystem_SkipButton": "Hoppa över", - "UIText_DiscordEnabled_Title": "Stationeers Server UI", - "UIText_DiscordEnabled_HeaderTitle": "Discord-integration", - "UIText_DiscordEnabled_StepMessage": "Vill du aktivera Discord-integration? Ange 'ja' för att aktivera eller hoppa över för att inaktivera.", - "UIText_DiscordEnabled_PrimaryPlaceholder": "yes/no", - "UIText_DiscordEnabled_PrimaryLabel": "Aktivera Discord", - "UIText_DiscordEnabled_SubmitButton": "Spara & fortsätt", - "UIText_DiscordEnabled_SkipButton": "Hoppa över (inaktivera Discord)", - "UIText_DiscordToken_Title": "Stationeers Server UI", - "UIText_DiscordToken_HeaderTitle": "Discord-bot-token", - "UIText_DiscordToken_StepMessage": "Ange din Discord-bot-token för serverintegration", - "UIText_DiscordToken_PrimaryPlaceholder": "Discord-bot-token", - "UIText_DiscordToken_PrimaryLabel": "Discord-token", - "UIText_DiscordToken_SubmitButton": "Spara & fortsätt", - "UIText_DiscordToken_SkipButton": "Hoppa över", - "UIText_ControlPanelChannel_Title": "Stationeers Server UI", - "UIText_ControlPanelChannel_HeaderTitle": "Discord-kanalinställning (1/6)", - "UIText_ControlPanelChannel_StepMessage": "Ange Discord-kontrollpanelkanalens ID", - "UIText_ControlPanelChannel_PrimaryPlaceholder": "Kanal-ID", - "UIText_ControlPanelChannel_PrimaryLabel": "Kontrollpanelkanal-ID", - "UIText_ControlPanelChannel_SubmitButton": "Spara & fortsätt", - "UIText_ControlPanelChannel_SkipButton": "Hoppa över", - "UIText_SaveChannel_Title": "Stationeers Server UI", - "UIText_SaveChannel_HeaderTitle": "Discord-kanalinställning (2/6)", - "UIText_SaveChannel_StepMessage": "Ange Discord-sparfilskanalens ID", - "UIText_SaveChannel_PrimaryPlaceholder": "Kanal-ID", - "UIText_SaveChannel_PrimaryLabel": "Sparkanal-ID", - "UIText_SaveChannel_SubmitButton": "Spara & fortsätt", - "UIText_SaveChannel_SkipButton": "Hoppa över", - "UIText_LogChannel_Title": "Stationeers Server UI", - "UIText_LogChannel_HeaderTitle": "Discord-kanalinställning (3/6)", - "UIText_LogChannel_StepMessage": "Ange Discord-loggkanalens ID", - "UIText_LogChannel_PrimaryPlaceholder": "Kanal-ID", - "UIText_LogChannel_PrimaryLabel": "Loggkanal-ID", - "UIText_LogChannel_SubmitButton": "Spara & fortsätt", - "UIText_LogChannel_SkipButton": "Hoppa över", - "UIText_ConnectionListChannel_Title": "Stationeers Server UI", - "UIText_ConnectionListChannel_HeaderTitle": "Discord-kanalinställning (4/6)", - "UIText_ConnectionListChannel_StepMessage": "Ange Discord-anslutningslistkanalens ID", - "UIText_ConnectionListChannel_PrimaryPlaceholder": "Kanal-ID", - "UIText_ConnectionListChannel_PrimaryLabel": "Anslutningslistkanal-ID", - "UIText_ConnectionListChannel_SubmitButton": "Spara & fortsätt", - "UIText_ConnectionListChannel_SkipButton": "Hoppa över", - "UIText_StatusChannel_Title": "Stationeers Server UI", - "UIText_StatusChannel_HeaderTitle": "Discord-kanalinställning (5/6)", - "UIText_StatusChannel_StepMessage": "Ange Discord-statuskanalens ID", - "UIText_StatusChannel_PrimaryPlaceholder": "Kanal-ID", - "UIText_StatusChannel_PrimaryLabel": "Statuskanal-ID", - "UIText_StatusChannel_SubmitButton": "Spara & fortsätt", - "UIText_StatusChannel_SkipButton": "Hoppa över", - "UIText_ControlChannel_Title": "Stationeers Server UI", - "UIText_ControlChannel_HeaderTitle": "Discord-kanalinställning (6/6)", - "UIText_ControlChannel_StepMessage": "Ange Discord-kontrollkanalens ID", - "UIText_ControlChannel_PrimaryPlaceholder": "Kanal-ID", - "UIText_ControlChannel_PrimaryLabel": "Kontrollkanal-ID", - "UIText_ControlChannel_SubmitButton": "Spara & fortsätt", - "UIText_ControlChannel_SkipButton": "Hoppa över", "UIText_NetworkConfigChoice_Title": "Stationeers Server UI", - "UIText_NetworkConfigChoice_HeaderTitle": "Nätverkskonfiguration", + "UIText_NetworkConfigChoice_HeaderTitle": "Nätverk", "UIText_NetworkConfigChoice_StepMessage": "Vill du konfigurera nätverksinställningar? Ange 'ja' för att konfigurera eller hoppa över för att använda standardvärden. Obs: Nätverkskonfiguration är särskilt viktigt på Linux-servrar.", "UIText_NetworkConfigChoice_PrimaryPlaceholder": "ja", "UIText_NetworkConfigChoice_PrimaryLabel": "Konfigurera nätverk", "UIText_NetworkConfigChoice_SubmitButton": "Fortsätt", "UIText_NetworkConfigChoice_SkipButton": "Hoppa över (använd standardvärden)", "UIText_GamePort_Title": "Stationeers Server UI", - "UIText_GamePort_HeaderTitle": "Nätverksinställning (1/4)", + "UIText_GamePort_HeaderTitle": "Nätverk (1/4)", "UIText_GamePort_StepMessage": "Ange portnummer för spelanslutningar", "UIText_GamePort_PrimaryPlaceholder": "27016", "UIText_GamePort_PrimaryLabel": "Spelport", "UIText_GamePort_SubmitButton": "Spara & fortsätt", "UIText_GamePort_SkipButton": "Hoppa över", "UIText_UpdatePort_Title": "Stationeers Server UI", - "UIText_UpdatePort_HeaderTitle": "Nätverksinställning (2/4)", + "UIText_UpdatePort_HeaderTitle": "Nätverk (2/4)", "UIText_UpdatePort_StepMessage": "Ange portnummer för uppdateringsanslutningar", "UIText_UpdatePort_PrimaryPlaceholder": "27015", "UIText_UpdatePort_PrimaryLabel": "Uppdateringsport", "UIText_UpdatePort_SubmitButton": "Spara & fortsätt", "UIText_UpdatePort_SkipButton": "Hoppa över", "UIText_UPnPEnabled_Title": "Stationeers Server UI", - "UIText_UPnPEnabled_HeaderTitle": "Nätverksinställning (3/4)", + "UIText_UPnPEnabled_HeaderTitle": "Nätverk (3/4)", "UIText_UPnPEnabled_StepMessage": "Aktivera UPnP? Ange 'ja' för att aktivera eller 'nej' för att inaktivera.", - "UIText_UPnPEnabled_PrimaryPlaceholder": "yes/no", + "UIText_UPnPEnabled_PrimaryPlaceholder": "ja/nej", "UIText_UPnPEnabled_PrimaryLabel": "Aktivera UPnP", "UIText_UPnPEnabled_SubmitButton": "Spara & fortsätt", "UIText_UPnPEnabled_SkipButton": "Hoppa över", "UIText_LocalIPAddress_Title": "Stationeers Server UI", - "UIText_LocalIPAddress_HeaderTitle": "Nätverksinställning (4/4)", + "UIText_LocalIPAddress_HeaderTitle": "Nätverk (4/4)", "UIText_LocalIPAddress_StepMessage": "Ange serverns lokala IP-adress i formatet 0.0.0.0 (ingen CIDR-notation)", "UIText_LocalIPAddress_PrimaryPlaceholder": "0.0.0.0", "UIText_LocalIPAddress_PrimaryLabel": "Lokal IP-adress", "UIText_LocalIPAddress_SubmitButton": "Spara & fortsätt", "UIText_LocalIPAddress_SkipButton": "Hoppa över", "UIText_AdminAccount_Title": "Stationeers Server UI", - "UIText_AdminAccount_HeaderTitle": "Admin-kontoinställning", + "UIText_AdminAccount_HeaderTitle": "Admin-konto", "UIText_AdminAccount_StepMessage": "Konfigurera ditt admin-konto.", "UIText_AdminAccount_PrimaryPlaceholder": "Användarnamn", "UIText_AdminAccount_PrimaryLabel": "Användarnamn", @@ -274,14 +225,8 @@ "UIText_AdminAccount_SecondaryPlaceholder": "Lösenord", "UIText_AdminAccount_SubmitButton": "Spara & fortsätt", "UIText_AdminAccount_SkipButton": "Hoppa över autentisering", - "UIText_SSCM_Title": "Stationeers Server Command Manager", - "UIText_SSCM_HeaderTitle": "Unik funktion", - "UIText_SSCM_StepMessage": "SSCM är ett anpassat serverplugin som låter dig köra serverkommandon direkt från SSUI. Det ger dig möjlighet att köra kommandon från webbkonsolen utan att störa vanliga funktioner, inga klientsidemoddar behövs.", - "UIText_SSCM_PrimaryPlaceholder": "skriv 'nej' för att inaktivera", - "UIText_SSCM_PrimaryLabel": "Att välja bort rekommenderas INTE.", - "UIText_SSCM_SubmitButton": "Fortsätt", - "UIText_SSCM_SkipButton": "Behåll aktiverad", - "UIText_Finalize_Title": "Slutför konfiguration", + "UIText_Finalize_Title": "Det var allt", + "UIText_Finalize_HeaderTitle": "Slutför konfiguration", "UIText_Finalize_StepMessage": "Redo att slutföra? Din konfiguration har redan sparats under guiden. Om du vill ändra inställningar kan du klicka på Gå tillbaka till start och hoppa över det du vill behålla. De flesta inställningar kan också ändras i konfigurationsfliken i gränssnittet.", "UIText_Finalize_SubmitButton": "Gå tillbaka till start", "UIText_Finalize_SkipButton": "Hoppa över autentisering", diff --git a/UIMod/onboard_bundled/twoboxform/twoboxform.css b/UIMod/onboard_bundled/twoboxform/twoboxform.css index 1df32dca..5a5d7795 100644 --- a/UIMod/onboard_bundled/twoboxform/twoboxform.css +++ b/UIMod/onboard_bundled/twoboxform/twoboxform.css @@ -482,4 +482,147 @@ footer { h1 { font-size: 1.2rem; } -} \ No newline at end of file + +} +@media (max-width: 800px) { + .progress-bar { + display: none !important; + } +} +@media (max-height: 1050px) { + .progress-bar { + display: none !important; + } +} + +.progress-bar { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 10px; + margin-top: 20px; + padding-top: 10px; + margin-bottom: 20px; +} + +.progress-step { + display: flex; + flex-direction: column; + align-items: center; + width: 80px; + text-align: center; +} + +.progress-circle { + display: block; + width: 20px; + height: 20px; + border-radius: 50%; + opacity: 0.7; + border: 2px solid var(--primary); + transition: all 0.3s ease; +} + +.progress-circle.active { + background-color: var(--primary-dim); + scale: 1.2; +} + +.progress-step.active .progress-circle { + background-color: var(--primary); + opacity: 1; + box-shadow: 0 0 10px var(--primary-glow); +} + +.progress-circle:hover { + background-color: var(--primary); + transform: scale(1.2); +} + +.progress-label { + font-size: 0.7rem; + color: var(--primary-dim); + margin-top: 5px; + text-wrap: balance; + line-height: 1.2; +} + +.progress-step.active .progress-label { + color: var(--text-bright); +} + +#progress-bar-container { + position: fixed; + bottom: 0; + left: 0; + width: 100%; + padding: 10px 0; + z-index: 100; + display: flex; + justify-content: center; +} + +select { + width: 100%; + padding: 12px; + background-color: rgba(0, 0, 0, 0.6); + color: var(--primary); + border: 2px solid var(--primary-dim); + border-radius: 4px; + font-family: 'Share Tech Mono', monospace; + transition: all 0.3s ease; + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: no-repeat; + background-position: right 12px top 50%; +} + +select:focus { + border-color: var(--primary); + box-shadow: 0 0 20px var(--primary-glow); + outline: none; +} + +option { + background-color: var(--bg-panel); + color: var(--primary); + padding: 10px; + font-family: 'Share Tech Mono', monospace; + font-size: 1rem; +} + +option:hover { + background-color: var(--primary-dim); + color: var(--text-bright); +} + + +#exit-button-container { + position: absolute; + top: 15px; + right: 15px; + display: flex; + gap: 10px; + cursor: pointer; + font-variant-emoji: text; + color: #00ffab; +} + +#exit-button-container img{ + width: 30px; + height: 20px; + cursor: pointer; + transition: transform 0.5s ease; +} + +#exit-button-container img { + opacity: 0.7; +} + +#exit-button-container img:hover { + scale: 1.1; + animation: wave 3s infinite; + opacity: 1; +} \ No newline at end of file diff --git a/UIMod/onboard_bundled/twoboxform/twoboxform.html b/UIMod/onboard_bundled/twoboxform/twoboxform.html index abd09bc1..984c9c0f 100644 --- a/UIMod/onboard_bundled/twoboxform/twoboxform.html +++ b/UIMod/onboard_bundled/twoboxform/twoboxform.html @@ -20,7 +20,7 @@

Preparing...

Preparing {{.Title}}

- {{if ne .Step "welcome"}} + {{if and (eq .Mode "setup") (ne .Step "welcome")}}
English German @@ -28,6 +28,9 @@

Preparing...

{{end}}
+ {{if and (eq .Mode "setup") (ne .Step "welcome")}} +
+ {{end}}

{{.Title}}


@@ -67,13 +70,22 @@

{{.HeaderTitle}}

{{if ne .SecondaryLabel ""}}
+ {{if eq .SecondaryLabelType "dropdown"}} + + {{else}} + type="{{.SecondaryLabelType}}" + id="secondary-field" + name="secondary-field" + placeholder="{{.SecondaryPlaceholderText}}" + required + > + {{end}}
{{end}} @@ -107,10 +119,24 @@

{{.HeaderTitle}}

+ {{if and (eq .Mode "setup") (ne .Step "welcome")}} +
+
+ {{range .Steps}} +
+ + {{.HeaderTitle}} +
+ {{end}} +
+ {{end}} + \ No newline at end of file diff --git a/UIMod/onboard_bundled/twoboxform/twoboxform.js b/UIMod/onboard_bundled/twoboxform/twoboxform.js index d329e72b..3423361b 100644 --- a/UIMod/onboard_bundled/twoboxform/twoboxform.js +++ b/UIMod/onboard_bundled/twoboxform/twoboxform.js @@ -67,9 +67,9 @@ document.addEventListener('DOMContentLoaded', () => { function booleanToConfig(value) { if (typeof value === 'string') { value = value.trim().toLowerCase(); - if (value === 'yes' || value === 'true' || value === '1') { + if (value === 'yes' || value === 'true' || value === '1' || value === 'ja') { return true; - } else if (value === 'no' || value === 'false' || value === '0') { + } else if (value === 'no' || value === 'false' || value === '0' || value === 'nej') { return false; } } @@ -135,6 +135,29 @@ document.addEventListener('DOMContentLoaded', () => { body = JSON.stringify({ [configField]: booleanToConfig(document.getElementById('primary-field').value) }); + + } else if (configField === "SaveInfo") { + const primaryValue = document.getElementById('primary-field').value.trim(); + const secondaryValue = document.getElementById('secondary-field').value.trim(); + if (secondaryValue === '' || secondaryValue === document.getElementById('secondary-field').placeholder) { + showNotification('Please select a world type!', 'error'); + hidePreloader(); + return; // Prevent submission + } + const joinedValue = `${primaryValue} ${secondaryValue}`; + body = JSON.stringify({ + [configField]: joinedValue + }); + } else if (configField === "gameBranch") { + const secondaryValue = document.getElementById('secondary-field').value.trim(); + if (secondaryValue === '' || secondaryValue === document.getElementById('secondary-field').placeholder) { + showNotification('Please select a world type!', 'error'); + hidePreloader(); + return; // Prevent submission + } + body = JSON.stringify({ + [configField]: secondaryValue + }); } else { body = JSON.stringify({ [configField]: document.getElementById('primary-field').value @@ -170,13 +193,15 @@ document.addEventListener('DOMContentLoaded', () => { }); const data = await response.json(); - if (response.ok) { + if (response.ok || response.status === 201) { if (configField || step === "admin_account") { hidePreloader(); showNotification(step === "admin_account" ? 'Admin account saved!' : 'Config saved!', 'success'); // Wait for backend response to complete before redirecting try { await response.json(); } catch (e) {} // Ensure backend response is fully processed - window.location.href = `/setup?step=${nextStep}`; + setTimeout(() => { + window.location.href = `/setup?step=${nextStep}`; + }, 1000); } else if (mode === 'login') { showNotification('Login Successful!', 'success'); await preloadNextPage(); @@ -184,7 +209,6 @@ document.addEventListener('DOMContentLoaded', () => { window.location.href = '/'; } else { // changeuser hidePreloader(); - const data = await response.json(); // Ensure backend response is processed showNotification(data.message || 'User updated!', 'success'); form.reset(); } diff --git a/UIMod/onboard_bundled/ui/config.html b/UIMod/onboard_bundled/ui/config.html index 3a36a9f3..3ceef7a3 100644 --- a/UIMod/onboard_bundled/ui/config.html +++ b/UIMod/onboard_bundled/ui/config.html @@ -80,6 +80,7 @@

{{.UIText_BasicServerSettings}}

{{.UIText_SaveFileNameInfo}}
+
{{.UIText_SaveFileNameUseWizzardButtonText}}
@@ -240,6 +241,15 @@

{{.UIText_AdvancedConfiguration}}

{{.UIText_AutoStartServerOnStartupInfo}}
+ +
+ + +
{{.UIText_AllowAutoGameServerUpdatesInfo}}
+
diff --git a/UIMod/onboard_bundled/ui/index.html b/UIMod/onboard_bundled/ui/index.html index 0a753410..3ffbd622 100644 --- a/UIMod/onboard_bundled/ui/index.html +++ b/UIMod/onboard_bundled/ui/index.html @@ -152,7 +152,7 @@

- SSUI Discord diff --git a/media/discord-panel.png b/media/discord-panel.png index 49cfac04..9c38cf9f 100644 Binary files a/media/discord-panel.png and b/media/discord-panel.png differ diff --git a/server.go b/server.go index 90a4ecb0..7c5c6677 100644 --- a/server.go +++ b/server.go @@ -39,6 +39,7 @@ func main() { logger.ConfigureConsole() logger.Install.Info("Starting setup...") loader.ReloadConfig() // Load the config file before starting the setup process + loader.LoadCmdArgs() setup.Install(&wg) wg.Wait() logger.Main.Debug("Initializing resources...") diff --git a/src/cli/runtimecommands.go b/src/cli/runtimecommands.go index 39c3a91b..f495a287 100644 --- a/src/cli/runtimecommands.go +++ b/src/cli/runtimecommands.go @@ -23,7 +23,7 @@ import ( "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/setup" + "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd" ) // ANSI escape codes for green text and reset @@ -160,6 +160,8 @@ func init() { 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") } func startServer() { @@ -191,13 +193,20 @@ func deleteConfig() { } func runSteamCMD() { - if gamemgr.InternalIsServerRunning() { - logger.Core.Warn("Server is running, stopping server first...") - gamemgr.InternalStopServer() - time.Sleep(10000 * time.Millisecond) + steamcmd.InstallAndRunSteamCMD() +} + +func printConfig() { + loader.PrintConfigDetails("Info") +} + +func getBuildID() { + buildID := config.GetCurrentBranchBuildID() + if buildID == "" { + logger.Core.Error("Build ID not found, empty string returned") + return } - logger.Core.Info("Running SteamCMD") - setup.InstallAndRunSteamCMD() + logger.Core.Info("Build ID: " + buildID) } func testLocalization() { diff --git a/src/config/config.go b/src/config/config.go index a6fcccd6..f1b304e7 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -11,71 +11,94 @@ import ( var ( // All configuration variables can be found in vars.go - Version = "5.6.2" + Version = "5.6.4" Branch = "release" ) +/* +If you read this, you are likely a developer. I sincerly apologize for the way the config works. +While I would love to refactor the config to not write to file then read the file every time a config value is changed, +I have not found the time to do so. So, for now, we save to file, then read the file and rely on whatever the file says. Although this is not ideal, it works for now. Deal with it. +JacksonTheMaster +*/ + type JsonConfig struct { - DiscordToken string `json:"discordToken"` - ControlChannelID string `json:"controlChannelID"` - StatusChannelID string `json:"statusChannelID"` - ConnectionListChannelID string `json:"connectionListChannelID"` - LogChannelID string `json:"logChannelID"` - SaveChannelID string `json:"saveChannelID"` - ControlPanelChannelID string `json:"controlPanelChannelID"` - DiscordCharBufferSize int `json:"DiscordCharBufferSize"` - BlackListFilePath string `json:"blackListFilePath"` - IsDiscordEnabled *bool `json:"isDiscordEnabled"` - ErrorChannelID string `json:"errorChannelID"` - BackupKeepLastN int `json:"backupKeepLastN"` // Number of most recent backups to keep (default: 2000) - IsCleanupEnabled *bool `json:"isCleanupEnabled"` // Enable automatic cleanup of backups (default: false) - BackupKeepDailyFor int `json:"backupKeepDailyFor"` // Retention period in hours for daily backups - BackupKeepWeeklyFor int `json:"backupKeepWeeklyFor"` // Retention period in hours for weekly backups - BackupKeepMonthlyFor int `json:"backupKeepMonthlyFor"` // Retention period in hours for monthly backups - BackupCleanupInterval int `json:"backupCleanupInterval"` // Hours between backup cleanup operations - BackupWaitTime int `json:"backupWaitTime"` // Seconds to wait before copying backups - IsNewTerrainAndSaveSystem *bool `json:"IsNewTerrainAndSaveSystem"` // Use new terrain and save system - GameBranch string `json:"gameBranch"` - Difficulty string `json:"Difficulty"` - StartCondition string `json:"StartCondition"` - StartLocation string `json:"StartLocation"` - ServerName string `json:"ServerName"` - SaveInfo string `json:"SaveInfo"` - ServerMaxPlayers string `json:"ServerMaxPlayers"` - ServerPassword string `json:"ServerPassword"` - ServerAuthSecret string `json:"ServerAuthSecret"` - AdminPassword string `json:"AdminPassword"` - GamePort string `json:"GamePort"` - 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"` - ExePath string `json:"ExePath"` - AdditionalParams string `json:"AdditionalParams"` - Users map[string]string `json:"users"` // Map of username to hashed password - AuthEnabled *bool `json:"authEnabled"` // Toggle for enabling/disabling auth - JwtKey string `json:"JwtKey"` - AuthTokenLifetime int `json:"AuthTokenLifetime"` - Debug *bool `json:"Debug"` - CreateSSUILogFile *bool `json:"CreateSSUILogFile"` - LogLevel int `json:"LogLevel"` - LogClutterToConsole *bool `json:"LogClutterToConsole"` - SubsystemFilters []string `json:"subsystemFilters"` - IsUpdateEnabled *bool `json:"IsUpdateEnabled"` - IsSSCMEnabled *bool `json:"IsSSCMEnabled"` - AutoRestartServerTimer string `json:"AutoRestartServerTimer"` - AllowPrereleaseUpdates *bool `json:"AllowPrereleaseUpdates"` - AllowMajorUpdates *bool `json:"AllowMajorUpdates"` - IsConsoleEnabled *bool `json:"IsConsoleEnabled"` - LanguageSetting string `json:"LanguageSetting"` - AutoStartServerOnStartup *bool `json:"AutoStartServerOnStartup"` - SSUIIdentifier string `json:"SSUIIdentifier"` - SSUIWebPort string `json:"SSUIWebPort"` + // 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"` + 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"` + CreateSSUILogFile *bool `json:"CreateSSUILogFile"` + LogLevel int `json:"LogLevel"` + SubsystemFilters []string `json:"subsystemFilters"` + + // Authentication Settings + Users map[string]string `json:"users"` // Map of username to hashed password + AuthEnabled *bool `json:"authEnabled"` // Toggle for enabling/disabling auth + JwtKey string `json:"JwtKey"` + AuthTokenLifetime int `json:"AuthTokenLifetime"` + + // SSUI Settings + IsNewTerrainAndSaveSystem *bool `json:"IsNewTerrainAndSaveSystem"` // Use new terrain and save system + ExePath string `json:"ExePath"` + LogClutterToConsole *bool `json:"LogClutterToConsole"` + IsSSCMEnabled *bool `json:"IsSSCMEnabled"` + AutoRestartServerTimer string `json:"AutoRestartServerTimer"` + IsConsoleEnabled *bool `json:"IsConsoleEnabled"` + LanguageSetting string `json:"LanguageSetting"` + AutoStartServerOnStartup *bool `json:"AutoStartServerOnStartup"` + SSUIIdentifier string `json:"SSUIIdentifier"` + SSUIWebPort string `json:"SSUIWebPort"` + + // Update Settings + IsUpdateEnabled *bool `json:"IsUpdateEnabled"` + AllowPrereleaseUpdates *bool `json:"AllowPrereleaseUpdates"` + AllowMajorUpdates *bool `json:"AllowMajorUpdates"` + AllowAutoGameServerUpdates *bool `json:"AllowAutoGameServerUpdates"` + + // Discord Settings + DiscordToken string `json:"discordToken"` + ControlChannelID string `json:"controlChannelID"` + StatusChannelID string `json:"statusChannelID"` + ConnectionListChannelID string `json:"connectionListChannelID"` + LogChannelID string `json:"logChannelID"` + SaveChannelID string `json:"saveChannelID"` + ControlPanelChannelID string `json:"controlPanelChannelID"` + DiscordCharBufferSize int `json:"DiscordCharBufferSize"` + BlackListFilePath string `json:"blackListFilePath"` + IsDiscordEnabled *bool `json:"isDiscordEnabled"` + ErrorChannelID string `json:"errorChannelID"` + + //Backup Settings + BackupKeepLastN int `json:"backupKeepLastN"` // Number of most recent backups to keep (default: 2000) + IsCleanupEnabled *bool `json:"isCleanupEnabled"` // Enable automatic cleanup of backups (default: false) + BackupKeepDailyFor int `json:"backupKeepDailyFor"` // Retention period in hours for daily backups + BackupKeepWeeklyFor int `json:"backupKeepWeeklyFor"` // Retention period in hours for weekly backups + BackupKeepMonthlyFor int `json:"backupKeepMonthlyFor"` // Retention period in hours for monthly backups + BackupCleanupInterval int `json:"backupCleanupInterval"` // Hours between backup cleanup operations + BackupWaitTime int `json:"backupWaitTime"` // Seconds to wait before copying backups } // LoadConfig loads and initializes the configuration @@ -216,6 +239,10 @@ func applyConfig(cfg *JsonConfig) { AllowMajorUpdates = allowMajorUpdatesVal cfg.AllowMajorUpdates = &allowMajorUpdatesVal + allowAutoGameServerUpdatesVal := getBool(cfg.AllowAutoGameServerUpdates, "ALLOW_AUTO_GAME_SERVER_UPDATES", false) + AllowAutoGameServerUpdates = allowAutoGameServerUpdatesVal + cfg.AllowAutoGameServerUpdates = &allowAutoGameServerUpdatesVal + SubsystemFilters = getStringSlice(cfg.SubsystemFilters, "SUBSYSTEM_FILTERS", []string{}) AutoRestartServerTimer = getString(cfg.AutoRestartServerTimer, "AUTO_RESTART_SERVER_TIMER", "0") isSSCMEnabledVal := getBool(cfg.IsSSCMEnabled, "IS_SSCM_ENABLED", true) @@ -260,66 +287,67 @@ func applyConfig(cfg *JsonConfig) { func safeSaveConfig() error { cfg := JsonConfig{ - DiscordToken: DiscordToken, - ControlChannelID: ControlChannelID, - StatusChannelID: StatusChannelID, - ConnectionListChannelID: ConnectionListChannelID, - LogChannelID: LogChannelID, - SaveChannelID: SaveChannelID, - ControlPanelChannelID: ControlPanelChannelID, - DiscordCharBufferSize: DiscordCharBufferSize, - BlackListFilePath: BlackListFilePath, - IsDiscordEnabled: &IsDiscordEnabled, - ErrorChannelID: ErrorChannelID, - BackupKeepLastN: BackupKeepLastN, - IsCleanupEnabled: &IsCleanupEnabled, - BackupKeepDailyFor: int(BackupKeepDailyFor / time.Hour), // Convert to hours - BackupKeepWeeklyFor: int(BackupKeepWeeklyFor / time.Hour), // Convert to hours - 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, - SaveInfo: SaveInfo, - 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, - AuthTokenLifetime: AuthTokenLifetime, - Debug: &IsDebugMode, - CreateSSUILogFile: &CreateSSUILogFile, - LogLevel: LogLevel, - LogClutterToConsole: &LogClutterToConsole, - SubsystemFilters: SubsystemFilters, - IsUpdateEnabled: &IsUpdateEnabled, - IsSSCMEnabled: &IsSSCMEnabled, - AutoRestartServerTimer: AutoRestartServerTimer, - AllowPrereleaseUpdates: &AllowPrereleaseUpdates, - AllowMajorUpdates: &AllowMajorUpdates, - IsConsoleEnabled: &IsConsoleEnabled, - LanguageSetting: LanguageSetting, - AutoStartServerOnStartup: &AutoStartServerOnStartup, - SSUIIdentifier: SSUIIdentifier, - SSUIWebPort: SSUIWebPort, + DiscordToken: DiscordToken, + ControlChannelID: ControlChannelID, + StatusChannelID: StatusChannelID, + ConnectionListChannelID: ConnectionListChannelID, + LogChannelID: LogChannelID, + SaveChannelID: SaveChannelID, + ControlPanelChannelID: ControlPanelChannelID, + DiscordCharBufferSize: DiscordCharBufferSize, + BlackListFilePath: BlackListFilePath, + IsDiscordEnabled: &IsDiscordEnabled, + ErrorChannelID: ErrorChannelID, + BackupKeepLastN: BackupKeepLastN, + IsCleanupEnabled: &IsCleanupEnabled, + BackupKeepDailyFor: int(BackupKeepDailyFor / time.Hour), // Convert to hours + BackupKeepWeeklyFor: int(BackupKeepWeeklyFor / time.Hour), // Convert to hours + 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, + SaveInfo: SaveInfo, + 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, + AuthTokenLifetime: AuthTokenLifetime, + Debug: &IsDebugMode, + CreateSSUILogFile: &CreateSSUILogFile, + LogLevel: LogLevel, + LogClutterToConsole: &LogClutterToConsole, + SubsystemFilters: SubsystemFilters, + IsUpdateEnabled: &IsUpdateEnabled, + IsSSCMEnabled: &IsSSCMEnabled, + AutoRestartServerTimer: AutoRestartServerTimer, + AllowPrereleaseUpdates: &AllowPrereleaseUpdates, + AllowMajorUpdates: &AllowMajorUpdates, + AllowAutoGameServerUpdates: &AllowAutoGameServerUpdates, + IsConsoleEnabled: &IsConsoleEnabled, + LanguageSetting: LanguageSetting, + AutoStartServerOnStartup: &AutoStartServerOnStartup, + SSUIIdentifier: SSUIIdentifier, + SSUIWebPort: SSUIWebPort, } file, err := os.Create(ConfigPath) diff --git a/src/config/getters.go b/src/config/getters.go index c7220d69..f07be86a 100644 --- a/src/config/getters.go +++ b/src/config/getters.go @@ -475,3 +475,15 @@ func GetGameServerAppID() string { defer ConfigMu.RUnlock() return GameServerAppID } + +func GetCurrentBranchBuildID() string { + ConfigMu.RLock() + defer ConfigMu.RUnlock() + return CurrentBranchBuildID +} + +func GetAllowAutoGameServerUpdates() bool { + ConfigMu.RLock() + defer ConfigMu.RUnlock() + return AllowAutoGameServerUpdates +} diff --git a/src/config/setters.go b/src/config/setters.go index 19e58065..10bf86fd 100644 --- a/src/config/setters.go +++ b/src/config/setters.go @@ -30,6 +30,14 @@ func SetIsSSCMEnabled(value bool) error { return safeSaveConfig() } +func SetCurrentBranchBuildID(value string) error { + ConfigMu.Lock() + defer ConfigMu.Unlock() + + CurrentBranchBuildID = value + return nil +} + // 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 @@ -630,3 +638,11 @@ func SetIsConsoleEnabled(value bool) error { IsConsoleEnabled = value return safeSaveConfig() } + +func SetAllowAutoGameServerUpdates(value bool) error { + ConfigMu.Lock() + defer ConfigMu.Unlock() + + AllowAutoGameServerUpdates = value + return safeSaveConfig() +} diff --git a/src/config/vars.go b/src/config/vars.go index e8e3cda8..3c37b51d 100644 --- a/src/config/vars.go +++ b/src/config/vars.go @@ -61,6 +61,7 @@ var ( LanguageSetting string AutoStartServerOnStartup bool SSUIIdentifier string + CurrentBranchBuildID string // ONLY RUNTIME ) // Discord integration @@ -103,11 +104,12 @@ var ( SSUIWebPort string ) -// SSUI Updates +// SSUI Updates and Game Server Updates var ( - IsUpdateEnabled bool - AllowPrereleaseUpdates bool - AllowMajorUpdates bool + IsUpdateEnabled bool + AllowPrereleaseUpdates bool + AllowMajorUpdates bool + AllowAutoGameServerUpdates bool ) // SSCM (Stationeers Server Command Manager) settings diff --git a/src/core/loader/cmdargs.go b/src/core/loader/cmdargs.go new file mode 100644 index 00000000..787d63e9 --- /dev/null +++ b/src/core/loader/cmdargs.go @@ -0,0 +1,95 @@ +package loader + +import ( + "flag" + "fmt" + "strings" + + "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config" + "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/security" + "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger" +) + +// LoadCmdArgs parses command-line arguments ONCE at startup (called from func main) and applies them using the config setters. +// Because this is using the config rather than adding features to it, it is a part of the loader package. +func LoadCmdArgs() { + // Define flags matching the config variable names + var backendEndpointPort string + var gameBranch string + var logLevel int + var isDebugMode bool + var createSSUILogFile bool + var recoveryPassword string + var devMode bool + + flag.StringVar(&backendEndpointPort, "BackendEndpointPort", "", "Override the backend endpoint port (e.g., 8080)") + flag.StringVar(&backendEndpointPort, "p", "", "(Alias) Override the backend endpoint port (e.g., 8080)") + flag.StringVar(&gameBranch, "GameBranch", "", "Override the game branch (e.g., beta)") + flag.StringVar(&gameBranch, "b", "", "(Alias) Override the game branch (e.g., beta)") + flag.StringVar(&recoveryPassword, "RecoveryPassword", "", "Adds a 'recovery' user (expects password as argument)") + flag.StringVar(&recoveryPassword, "r", "", "(Alias) Adds a 'recovery' user (expects password as argument)") + flag.BoolVar(&devMode, "dev", false, "Enable dev mode: Auth, and enables cli-console. For development only.") + flag.IntVar(&logLevel, "LogLevel", 0, "Override the log level (e.g., 10)") + flag.IntVar(&logLevel, "ll", 0, "(Alias) Override the log level (e.g., 10)") + flag.BoolVar(&isDebugMode, "IsDebugMode", false, "Enable debug mode") + flag.BoolVar(&isDebugMode, "debug", false, "(Alias) Enable debug mode") + flag.BoolVar(&createSSUILogFile, "CreateSSUILogFile", false, "Create a log file for SSUI") + flag.BoolVar(&createSSUILogFile, "lf", false, "(Alias) Create a log file for SSUI") + + // Parse command-line flags + flag.Parse() + + if devMode { + config.SetAuthEnabled(true) + config.SetIsFirstTimeSetup(false) + config.SetUsers(map[string]string{"admin": "$2a$10$7QQhPkNAfT.MXhJhnnodXOyn3KKE/1eu7nYb0y2O1UBoAWc0Y/fda"}) // admin:admin + config.SetIsConsoleEnabled(true) + logger.Main.Info("Dev mode enabled: Auth enabled, admin user set to admin:admin:superadmin, console enabled") + } + + if backendEndpointPort != "" && backendEndpointPort != "8443" { + oldPort := config.GetSSUIWebPort() + config.SetSSUIWebPort(backendEndpointPort) + logger.Main.Info(fmt.Sprintf("Overriding SetSSUIWebPort from command line: Before=%s, Now=%s", oldPort, backendEndpointPort)) + } + + if gameBranch != "" { + oldBranch := config.GetGameBranch() + config.SetGameBranch(gameBranch) + logger.Main.Info(fmt.Sprintf("Overriding GameBranch from command line: Before=%s, Now=%s", oldBranch, gameBranch)) + } + + if recoveryPassword != "" { + recoveryPassword = strings.TrimSpace(recoveryPassword) + if recoveryPassword == "" { + logger.Main.Error("Recovery flag provided but password is empty. Skipping recovery user creation.") + } else { + hashedPassword, err := security.HashPassword(recoveryPassword) + if err != nil { + logger.Main.Error(fmt.Sprintf("Failed to hash recovery password: %v", err)) + return + } + config.SetUsers(map[string]string{"recovery": hashedPassword}) + logger.Main.Warn(fmt.Sprintf("Recovery user added with access level superadmin. Login with username 'recovery' and password '%s'", recoveryPassword)) + } + } + + if logLevel != 0 { + oldLevel := config.GetLogLevel() + config.SetLogLevel(logLevel) + logger.Main.Info(fmt.Sprintf("Overriding LogLevel from command line: Before=%d, Now=%d", oldLevel, logLevel)) + } + + if isDebugMode { + oldDebug := config.GetIsDebugMode() + config.SetIsDebugMode(true) + config.SetLogLevel(10) + logger.Main.Info(fmt.Sprintf("Overriding IsDebugMode from command line: Before=%t, Now=true", oldDebug)) + } + + if createSSUILogFile { + oldCreateSSUILogFile := config.GetCreateSSUILogFile() + config.SetCreateSSUILogFile(true) + logger.Main.Info(fmt.Sprintf("Overriding CreateSSUILogFile from command line: Before=%t, Now=true", oldCreateSSUILogFile)) + } +} diff --git a/src/core/loader/helpers.go b/src/core/loader/helpers.go index 7a02792a..92d6f897 100644 --- a/src/core/loader/helpers.go +++ b/src/core/loader/helpers.go @@ -8,15 +8,25 @@ import ( "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger" ) -func PrintConfigDetails() { +func PrintConfigDetails(logLevel ...string) { logger.Config.Debug("=== Game Server Configuration Details ===") // Helper function to print sections printSection := func(title string, fields map[string]string) { - logger.Config.Debug(fmt.Sprintf("\n%s", title)) - logger.Config.Debug(strings.Repeat("-", len(title))) - for key, value := range fields { - logger.Config.Debug(fmt.Sprintf("%-30s: %s", key, value)) + + if logLevel == nil { + logger.Config.Debug(fmt.Sprintf("\n%s", title)) + logger.Config.Debug(strings.Repeat("-", len(title))) + for key, value := range fields { + logger.Config.Debug(fmt.Sprintf("%-30s: %s", key, value)) + } + } + if len(logLevel) > 0 && logLevel[0] == "Info" { + logger.Config.Info(fmt.Sprintf("\n%s", title)) + logger.Config.Info(strings.Repeat("-", len(title))) + for key, value := range fields { + logger.Config.Info(fmt.Sprintf("%-30s: %s", key, value)) + } } } @@ -112,6 +122,8 @@ func PrintConfigDetails() { "AllowPrereleaseUpdates": fmt.Sprintf("%v", config.GetAllowPrereleaseUpdates()), "AllowMajorUpdates": fmt.Sprintf("%v", config.GetAllowMajorUpdates()), "AutoRestartServerTimer": config.GetAutoRestartServerTimer(), + "AutoGameServerUpdates": fmt.Sprintf("%v", config.GetAllowAutoGameServerUpdates()), + "CurrentBranchBuildID": config.GetCurrentBranchBuildID(), } printSection("Updater Configuration", updater) diff --git a/src/discordbot/controlpanel.go b/src/discordbot/controlpanel.go new file mode 100644 index 00000000..323e472d --- /dev/null +++ b/src/discordbot/controlpanel.go @@ -0,0 +1,150 @@ +package discordbot + +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/bwmarrin/discordgo" +) + +func sendControlPanel() { + if !config.GetIsDiscordEnabled() { + return + } + + // Create an embed for the control panel + embed := &discordgo.MessageEmbed{ + Title: "🚀 SSUI Control Panel", + Description: "Use the reactions below to manage the server:", + Color: 0x1e90ff, // Vibrant blue color + Fields: []*discordgo.MessageEmbedField{ + { + Name: "🟢 Start", + Value: "Launch the gameserver", + Inline: false, + }, + { + Name: "🔴 Stop", + Value: "Stop the gameserver", + Inline: false, + }, + { + Name: "🔄 Restart", + Value: "Restart the gameserver", + Inline: false, + }, + { + Name: "♻️ Update", + Value: "Update the gameserver via SteamCMD", + Inline: false, + }, + }, + Timestamp: time.Now().Format(time.RFC3339), + } + + // Send the embed message + msg, err := config.DiscordSession.ChannelMessageSendEmbed(config.GetControlPanelChannelID(), embed) + if err != nil { + logger.Discord.Error("Error sending control panel embed: " + err.Error()) + return + } + + clearMessagesAboveLastN(config.GetControlPanelChannelID(), 1) // Clear all old control panel messages + + // Add reactions (acting as buttons) to the control message + config.DiscordSession.MessageReactionAdd(config.GetControlPanelChannelID(), msg.ID, "🟢") // Start + config.DiscordSession.MessageReactionAdd(config.GetControlPanelChannelID(), msg.ID, "🔴") // Stop + config.DiscordSession.MessageReactionAdd(config.GetControlPanelChannelID(), msg.ID, "🔄") // Restart + config.DiscordSession.MessageReactionAdd(config.GetControlPanelChannelID(), msg.ID, "♻️") // Update + ControlMessageID = msg.ID +} + +func handleControlReactions(s *discordgo.Session, r *discordgo.MessageReactionAdd) { + // Ignore reactions from the bot itself + if r.UserID == s.State.User.ID { + return + } + + var actionMessage string + + switch r.Emoji.Name { + case "🟢": // Start action + gamemgr.InternalStartServer() + actionMessage = "🟢 Server is Starting..." + case "🔴": // Stop action + gamemgr.InternalStopServer() + actionMessage = "🔴 Server is Stopping..." + case "🔄": // Restart action + actionMessage = "🔄 Server is restarting..." + go func() { + // Perform stop operation + gamemgr.InternalStopServer() + + // Non-blocking delay using channel and goroutine + delayChan := make(chan bool) + go func() { + time.Sleep(5 * time.Second) + delayChan <- true + }() + + // Wait for delay to complete + <-delayChan + + // Start server after delay + gamemgr.InternalStartServer() + }() + case "♻️": // Update action + actionMessage = "♻️ Server is updating, this may take a while..." + go func() { + // Perform stop operation + gamemgr.InternalStopServer() + + // Non-blocking delay using channel and goroutine + delayChan := make(chan bool) + go func() { + time.Sleep(5 * time.Second) + delayChan <- true + }() + + // Wait for delay to complete + <-delayChan + + _, err := steamcmd.InstallAndRunSteamCMD() + + Value := map[bool]string{true: "🟢 Success", false: "🔴 Failed"}[err == nil] + SendMessageToStatusChannel(fmt.Sprintf("SteamCMD Update status: %s", Value)) + sendTemporaryMessage(s, config.GetControlPanelChannelID(), fmt.Sprintf("SteamCMD Update status: %s", Value), 30*time.Second) + if err != nil { + SendMessageToStatusChannel(fmt.Sprintf("Update failed: %v", err.Error())) + } + }() + + default: + logger.Discord.Debug("Unknown reaction: " + r.Emoji.Name) + return + } + + // Get the user who triggered the action + user, err := s.User(r.UserID) + if err != nil { + logger.Discord.Error("Error fetching user details: " + err.Error()) + return + } + username := user.Username + + // Send a temporary confirmation message to the control panel channel + sendTemporaryMessage(s, config.GetControlPanelChannelID(), actionMessage, 30*time.Second) + + // Send the action message to the status channel + SendMessageToStatusChannel(fmt.Sprintf("%s triggered by %s.", actionMessage, username)) + + // Remove the reaction after processing + err = s.MessageReactionRemove(config.GetControlPanelChannelID(), r.MessageID, r.Emoji.APIName(), r.UserID) + if err != nil { + logger.Discord.Error("Error removing reaction: " + err.Error()) + } +} diff --git a/src/discordbot/discordgo-license.md b/src/discordbot/discordgo-license.md new file mode 100644 index 00000000..f24cf6f5 --- /dev/null +++ b/src/discordbot/discordgo-license.md @@ -0,0 +1,27 @@ +Copyright (c) 2015, Bruce Marriner +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of discordgo nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/src/discordbot/handleReactions.go b/src/discordbot/handleReactions.go index c46cab44..351c9d2b 100644 --- a/src/discordbot/handleReactions.go +++ b/src/discordbot/handleReactions.go @@ -33,60 +33,6 @@ func listenToDiscordReactions(s *discordgo.Session, r *discordgo.MessageReaction // Optionally, we could add more message-specific handlers here for other features } -func handleControlReactions(s *discordgo.Session, r *discordgo.MessageReactionAdd) { - // handleControlReactions - Handles reactions for server control actions - var actionMessage string - - switch r.Emoji.Name { - case "▶️": // Start action - gamemgr.InternalStartServer() - actionMessage = "🕛Server is Starting..." - case "⏹️": // Stop action - gamemgr.InternalStopServer() - actionMessage = "🛑Server is Stopping..." - case "♻️": // Restart action - actionMessage = "♻️Server is restarting..." - go func() { - // Perform stop operation - gamemgr.InternalStopServer() - - // Non-blocking delay using channel and goroutine - delayChan := make(chan bool) - go func() { - time.Sleep(5 * time.Second) - delayChan <- true - }() - - // Wait for delay to complete - <-delayChan - - // Start server after delay - gamemgr.InternalStartServer() - }() - - default: - logger.Discord.Debug("Unknown reaction: " + r.Emoji.Name) - return - } - - // Get the user who triggered the action - user, err := s.User(r.UserID) - if err != nil { - logger.Discord.Error("Error fetching user details: " + err.Error()) - return - } - username := user.Username - - // Send the action message to the control channel - SendMessageToStatusChannel(fmt.Sprintf("%s triggered by %s.", actionMessage, username)) - - // Remove the reaction after processing - err = s.MessageReactionRemove(config.GetControlPanelChannelID(), r.MessageID, r.Emoji.APIName(), r.UserID) - if err != nil { - logger.Discord.Error("Error removing reaction: " + err.Error()) - } -} - // v4 FIXED, Unused in v4.3 func handleExceptionReactions(s *discordgo.Session, r *discordgo.MessageReactionAdd) { var actionMessage string diff --git a/src/discordbot/handleSlashcommands.go b/src/discordbot/handleSlashcommands.go index fd110b89..20d315aa 100644 --- a/src/discordbot/handleSlashcommands.go +++ b/src/discordbot/handleSlashcommands.go @@ -12,7 +12,7 @@ import ( "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/setup" + "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd" "github.com/bwmarrin/discordgo" ) @@ -97,24 +97,40 @@ func handleStatus(s *discordgo.Session, i *discordgo.InteractionCreate, data Emb } func handleUpdate(s *discordgo.Session, i *discordgo.InteractionCreate, data EmbedData) error { - data.Title = "🎮 Gameserver Update" - data.Description = "Updating the gameserver via SteamCMD..." - data.Color = 0xFFA500 - if gamemgr.InternalIsServerRunning() { - SendMessageToControlChannel("❗ Server is running, stopping server first...") - gamemgr.InternalStopServer() - time.Sleep(10000 * time.Millisecond) + thinkingData := EmbedData{ + Title: "🎮 Gameserver Update", + Description: "The Backend is processing the gameserver update via SteamCMD. Please wait, this may take a while...", + Color: 0xFFA500, // Orange color for in-progress + } + + err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{generateEmbed(thinkingData)}, + }, + }) + if err != nil { + return err } + data.Title = "🎮 Gameserver Update" + data.Description = "Gameserver update completed." + data.Color = 0x00FF00 // Green for completion (will adjust if error) - _, err := setup.InstallAndRunSteamCMD() + _, err = steamcmd.InstallAndRunSteamCMD() data.Fields = []EmbedField{ {Name: "Update Status:", Value: map[bool]string{true: "🟢 Success", false: "🔴 Failed"}[err == nil], Inline: true}, } if err != nil { + data.Color = 0xFF0000 // Red for error data.Fields = append(data.Fields, EmbedField{Name: "Error:", Value: err.Error(), Inline: true}) } - return respond(s, i, data) + + // Edit the original message with "update completed" embed + _, err = s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{ + Embeds: &[]*discordgo.MessageEmbed{generateEmbed(data)}, + }) + return err } func handleHelp(s *discordgo.Session, i *discordgo.InteractionCreate, data EmbedData) error { @@ -122,12 +138,14 @@ func handleHelp(s *discordgo.Session, i *discordgo.InteractionCreate, data Embed data.Fields = []EmbedField{ {Name: "/start", Value: "Starts the server"}, {Name: "/stop", Value: "Stops the server"}, - {Name: "/restore ", Value: "Restores a backup"}, + {Name: "/status", Value: "Gets the running status of the gameserver process"}, + {Name: "/update", Value: "Updates the gameserver via SteamCMD"}, {Name: "/list [limit]", Value: "Lists recent backups (default: 5)"}, - {Name: "/help", Value: "Shows this help"}, + {Name: "/restore ", Value: "Restores a backup"}, {Name: "/bansteamid ", Value: "Bans a player"}, {Name: "/unbansteamid ", Value: "Unbans a player"}, - {Name: "/update", Value: "Updates the gameserver via SteamCMD"}, + {Name: "/command ", Value: "Sends a command to the gameserver console"}, + {Name: "/help", Value: "Shows this help"}, } return respond(s, i, data) } diff --git a/src/discordbot/sendMessage.go b/src/discordbot/sendMessage.go index 367c1a24..f91ff83c 100644 --- a/src/discordbot/sendMessage.go +++ b/src/discordbot/sendMessage.go @@ -2,6 +2,7 @@ package discordbot import ( "strings" + "time" "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger" @@ -149,29 +150,6 @@ func sendMessageToErrorChannel(message string) []*discordgo.Message { return sentMessages } -func sendControlPanel() { - if !config.GetIsDiscordEnabled() { - return - } - messageContent := "Control Panel:\n\nReact with the following to perform actions:\n" + - "▶️ Start the server\n\n" + - "⏹️ Stop the server\n\n" + - "♻️ Restart the server\n\n" - - msg, err := config.DiscordSession.ChannelMessageSend(config.GetControlPanelChannelID(), messageContent) - if err != nil { - logger.Discord.Error("Error sending control panel: " + err.Error()) - return - } - - // Add reactions (acting as buttons) to the control message - config.DiscordSession.MessageReactionAdd(config.GetControlPanelChannelID(), msg.ID, "▶️") // Start - config.DiscordSession.MessageReactionAdd(config.GetControlPanelChannelID(), msg.ID, "⏹️") // Stop - config.DiscordSession.MessageReactionAdd(config.GetControlPanelChannelID(), msg.ID, "♻️") // Restart - ControlMessageID = msg.ID - clearMessagesAboveLastN(config.GetControlPanelChannelID(), 1) // Clear all old control panel messages -} - // This function is used to clear messages above the last N messages in a channel. If you call this with 5, it will clear all messages in the channel besides the most recent 5. func clearMessagesAboveLastN(channelID string, keep int) { go func() { @@ -201,3 +179,22 @@ func clearMessagesAboveLastN(channelID string, keep int) { } }() } + +// sendTemporaryMessage sends a message to the specified channel and deletes it after the given duration. +func sendTemporaryMessage(s *discordgo.Session, channelID, message string, duration time.Duration) { + // Send the message + msg, err := s.ChannelMessageSend(channelID, message) + if err != nil { + logger.Discord.Error("Error sending temporary message: " + err.Error()) + return + } + + // Schedule deletion after the specified duration + go func() { + time.Sleep(duration) + err := s.ChannelMessageDelete(channelID, msg.ID) + if err != nil { + logger.Discord.Error("Error deleting temporary message: " + err.Error()) + } + }() +} diff --git a/src/setup/install.go b/src/setup/install.go index fd66e9d2..12a33e80 100644 --- a/src/setup/install.go +++ b/src/setup/install.go @@ -17,6 +17,7 @@ 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 @@ -32,16 +33,18 @@ func Install(wg *sync.WaitGroup) { } // Step 1: Check and download the UIMod folder contents - logger.Install.Info("🔄Checking UIMod folder...") + logger.Install.Debug("🔄Checking UIMod folder...") CheckAndDownloadUIMod() - logger.Install.Info("✅UIMod folder setup complete.") + logger.Install.Debug("✅UIMod 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() logger.Install.Info("✅Blacklist.txt verified or created.") // Step 3: Install and run SteamCMD logger.Install.Info("🔄Installing and running SteamCMD...") - InstallAndRunSteamCMD() + if config.GetBranch() != "indev-no-steamcmd" { + steamcmd.InstallAndRunSteamCMD() + } logger.Install.Info("✅Setup complete!") } diff --git a/src/setup/sscm.go b/src/setup/sscm.go index fde685f4..a41cb8f3 100644 --- a/src/setup/sscm.go +++ b/src/setup/sscm.go @@ -8,6 +8,7 @@ import ( "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config" "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger" + "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd" ) // BepInEx version: 5.4.23.2 or v5-lts @@ -136,7 +137,7 @@ func downloadAndInstallBepInEx(url string) error { // Extract the zip file to the current directory logger.Install.Info("📦Extracting BepInEx to current directory") - err = unzip(zipFile, fileInfo.Size(), ".") + err = steamcmd.Unzip(zipFile, fileInfo.Size(), ".") if err != nil { return fmt.Errorf("failed to extract BepInEx: %w", err) } @@ -149,10 +150,10 @@ func downloadAndInstallBepInEx(url string) error { } } - if runtime.GOOS == "linux" { - // make sure run_bepinex.sh is executable - if err := os.Chmod("./run_bepinex.sh", os.ModePerm); err != nil { - logger.Install.Warn(fmt.Sprintf("⚠️Failed to make run_bepinex.sh executable: %v", err)) + if runtime.GOOS != "linux" { + err = os.Remove("./run_bepinex.sh") + if err != nil { + logger.Install.Warn(fmt.Sprintf("⚠️Failed to remove obsoleterun_bepinex.sh: %v", err)) } } diff --git a/src/steamcmd/getappinfo.go b/src/steamcmd/getappinfo.go new file mode 100644 index 00000000..d693693d --- /dev/null +++ b/src/steamcmd/getappinfo.go @@ -0,0 +1,175 @@ +package steamcmd + +import ( + "bytes" + "fmt" + "maps" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "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" +) + +var ( + branches = make(map[string]string) + branchesLock sync.RWMutex // Protects branches map for concurrent access + stopPoller = make(chan struct{}) // Channel to signal poller cancellation +) + +// InitAppInfoPoller starts a goroutine that periodically fetches app info and stops any previous poller. +func AppInfoPoller() { + // Signal previous poller to stop + select { + case stopPoller <- struct{}{}: + // Previous poller was signaled to stop + default: + // No previous poller running + } + + // if AutoGameServerUpdates is disabled, dont start the poller. + if !config.GetAllowAutoGameServerUpdates() { + return + } + // Start new poller + go func() { + for { + select { + case <-stopPoller: + logger.Install.Debug("🛑 Previous app info poller stopped") + return + default: + err := getAppInfo() + if err != nil { + logger.Install.Warn("❌ Failed to get Update info: " + err.Error()) + } + select { + case <-stopPoller: + logger.Install.Debug("🛑 App info poller stopped") + return + case <-time.After(10 * time.Minute): + // Continue to next iteration + } + } + } + }() +} + +// getAppInfo fetches the branches and their build IDs for the specified app ID using SteamCMD +// and stores them in the package-level branches map. +func getAppInfo() error { + steamcmddir := SteamCMDLinuxDir + executable := "steamcmd.sh" + appid := config.GetGameServerAppID() + + if runtime.GOOS == "windows" { + executable = "steamcmd.exe" + steamcmddir = SteamCMDWindowsDir + } + + // Build SteamCMD command with +app_info_update to ensure fresh data + cmd := exec.Command(filepath.Join(steamcmddir, executable), "+login", "anonymous", "+app_info_update", "1", "+app_info_print", appid, "+quit") + + // Capture output instead of printing directly + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + // Log the command + //if config.GetLogLevel() == 10 { + // cmdString := strings.Join(cmd.Args, " ") + // logger.Install.Debug("🕑 Running SteamCMD for app info: " + cmdString) + //} + + // Run the command + err := cmd.Run() + if err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + logger.Install.Errorf("❌ SteamCMD app info failed (code %d): %s\n", exitErr.ExitCode(), stderr.String()) + return fmt.Errorf("SteamCMD app info failed with exit code %d: %w", exitErr.ExitCode(), err) + } + logger.Install.Errorf("❌ Error running SteamCMD app info: %s\n", err.Error()) + return fmt.Errorf("failed to run SteamCMD app info: %w", err) + } + + // Extract branches and build IDs + newBranches, err := extractBranches(stdout.String()) + if err != nil { + logger.Install.Debug("❌ Failed to extract branches: " + err.Error() + "\n") + return err + } + + // Update package-level branches map + branchesLock.Lock() + maps.Copy(branches, newBranches) + branchesLock.Unlock() + + currentBranch := config.GetGameBranch() + if buildID, ok := branches[currentBranch]; ok { + if config.GetCurrentBranchBuildID() != "" && config.GetCurrentBranchBuildID() != buildID { + logger.Install.Info("❗New gameserver update detected!") + if config.GetAllowAutoGameServerUpdates() { + logger.Install.Info("🔍 Updating gameserver via SteamCMD...") + if gamemgr.InternalIsServerRunning() { + commandmgr.WriteCommand("say Update found, stopping server in 60 seconds...") + logger.Install.Info("❗Stopping server in 60 seconds...") + time.Sleep(10 * time.Second) + commandmgr.WriteCommand("say Update found, stopping server in 50 seconds...") + time.Sleep(10 * time.Second) + commandmgr.WriteCommand("say Update found, stopping server in 40 seconds...") + time.Sleep(10 * time.Second) + commandmgr.WriteCommand("say Update found, stopping server in 30 seconds...") + time.Sleep(3 * time.Second) + commandmgr.WriteCommand("SAVE") + time.Sleep(7 * time.Second) + commandmgr.WriteCommand("say Update found, stopping server in 20 seconds. World was Saved. ") + time.Sleep(10 * time.Second) + commandmgr.WriteCommand("say Update found, stopping server in 10 seconds...") + time.Sleep(10 * time.Second) + gamemgr.InternalStopServer() + } + _, err := InstallAndRunSteamCMD() + if err != nil { + logger.Install.Error("❌ Failed to update gameserver: " + err.Error() + "\n") + } + gamemgr.InternalStartServer() + } + } + config.SetCurrentBranchBuildID(buildID) + } + + return nil +} + +// extractBranches uses regex to extract branch names and their build IDs from SteamCMD output. +func extractBranches(output string) (map[string]string, error) { + // Regex to match the entire branches section, handling nested braces + pattern := regexp.MustCompile(`"branches"\s*\{([\s\S]*?)\}\s*\}`) + branchSection := pattern.FindStringSubmatch(output) + if len(branchSection) < 2 { + return nil, fmt.Errorf("branches section not found in output") + } + + // Regex to match individual branch blocks + branchPattern := regexp.MustCompile(`"([^"]+)"\s*\{[\s\S]*?"buildid"\s*"(\d+)"[\s\S]*?\}`) + matches := branchPattern.FindAllStringSubmatch(branchSection[1], -1) + + branches := make(map[string]string) + for _, match := range matches { + if len(match) >= 3 { + branches[match[1]] = match[2] + } + } + + if len(branches) == 0 { + return nil, fmt.Errorf("no branches with build IDs found") + } + + return branches, nil +} diff --git a/src/steamcmd/install.go b/src/steamcmd/install.go new file mode 100644 index 00000000..7be15268 --- /dev/null +++ b/src/steamcmd/install.go @@ -0,0 +1,72 @@ +package steamcmd + +import ( + "os" + + "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger" +) + +func installSteamCMD(platform string, steamCMDDir string, downloadURL string, extractFunc ExtractorFunc) (int, error) { + // Check if SteamCMD is already installed + if _, err := os.Stat(steamCMDDir); os.IsNotExist(err) { + logger.Install.Warn("⚠️ SteamCMD not found for " + platform + ", downloading...\n") + + // Create SteamCMD directory + if err := createSteamCMDDirectory(steamCMDDir); err != nil { + logger.Install.Error("❌ Error creating SteamCMD directory: " + err.Error() + "\n") + return -1, err + } + + // Ensure cleanup on failure + success := false + defer func() { + if !success { + logger.Install.Warn("⚠️ Cleaning up due to failure...\n") + os.RemoveAll(steamCMDDir) + } + }() + + // Install required libraries + if err := installRequiredLibraries(); err != nil { + logger.Install.Error("❌ Error installing required libraries: " + err.Error() + "\n") + return -1, err + } + + // Download and extract SteamCMD + if err := downloadAndExtractSteamCMD(downloadURL, steamCMDDir, extractFunc); err != nil { + logger.Install.Error("❌ " + err.Error() + "\n") + return -1, err + } + + // Set executable permissions for SteamCMD files + if err := setExecutablePermissions(steamCMDDir); err != nil { + logger.Install.Error("❌ Error setting executable permissions: " + err.Error() + "\n") + return -1, err + } + + // Verify the steamcmd binary + if err := verifySteamCMDBinary(steamCMDDir); err != nil { + logger.Install.Error("❌ " + err.Error() + "\n") + return -1, err + } + + // Mark installation as successful + success = true + logger.Install.Info("✅ SteamCMD installed successfully.\n") + } else { + logger.Install.Info("✅ SteamCMD is already installed.") + } + + // Run SteamCMD and return its exit status and error + return runSteamCMD(steamCMDDir) +} + +// installSteamCMDLinux downloads and installs SteamCMD on Linux. +func installSteamCMDLinux() (int, error) { + return installSteamCMD("Linux", SteamCMDLinuxDir, SteamCMDLinuxURL, untarWrapper) +} + +// installSteamCMDWindows downloads and installs SteamCMD on Windows. +func installSteamCMDWindows() (int, error) { + return installSteamCMD("Windows", SteamCMDWindowsDir, SteamCMDWindowsURL, Unzip) +} diff --git a/src/setup/steamcmd-helper.go b/src/steamcmd/steamcmd-helper.go similarity index 99% rename from src/setup/steamcmd-helper.go rename to src/steamcmd/steamcmd-helper.go index 84cd1577..23e2b977 100644 --- a/src/setup/steamcmd-helper.go +++ b/src/steamcmd/steamcmd-helper.go @@ -1,4 +1,4 @@ -package setup +package steamcmd import ( "archive/tar" @@ -181,7 +181,7 @@ func untar(dest string, r io.Reader) error { } // unzip extracts a zip archive. -func unzip(zipReader io.ReaderAt, size int64, dest string) error { +func Unzip(zipReader io.ReaderAt, size int64, dest string) error { reader, err := zip.NewReader(zipReader, size) if err != nil { return fmt.Errorf("failed to create zip reader: %w", err) diff --git a/src/setup/steamcmd.go b/src/steamcmd/steamcmd.go similarity index 58% rename from src/setup/steamcmd.go rename to src/steamcmd/steamcmd.go index 1364ce29..e6dbb0fc 100644 --- a/src/setup/steamcmd.go +++ b/src/steamcmd/steamcmd.go @@ -1,4 +1,4 @@ -package setup +package steamcmd import ( "fmt" @@ -10,6 +10,7 @@ import ( "strings" "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger" + "github.com/JacksonTheMaster/StationeersServerUI/v5/src/managers/gamemgr" "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config" ) @@ -29,87 +30,28 @@ 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.GetBranch() == "indev-no-steamcmd" || config.GetIsDebugMode() { - logger.Install.Info("🔍 Detected indev-no-steamcmd branch or debug=true, skipping SteamCMD run") - return 0, nil + + if gamemgr.InternalIsServerRunning() { + logger.Core.Warn("Server is running, stopping server first...") + err := gamemgr.InternalStopServer() + if err != nil { + logger.Core.Error("Error stopping server before running Steamcmd: " + err.Error()) + } } + logger.Core.Info("Running SteamCMD") - if runtime.GOOS == "windows" { + switch runtime.GOOS { + case "windows": return installSteamCMDWindows() - } else if runtime.GOOS == "linux" { + case "linux": return installSteamCMDLinux() - } else { + default: err := fmt.Errorf("SteamCMD installation is not supported on this OS") logger.Install.Error("❌ " + err.Error() + "\n") return -1, err } } -func installSteamCMD(platform string, steamCMDDir string, downloadURL string, extractFunc ExtractorFunc) (int, error) { - // Check if SteamCMD is already installed - if _, err := os.Stat(steamCMDDir); os.IsNotExist(err) { - logger.Install.Warn("⚠️ SteamCMD not found for " + platform + ", downloading...\n") - - // Create SteamCMD directory - if err := createSteamCMDDirectory(steamCMDDir); err != nil { - logger.Install.Error("❌ Error creating SteamCMD directory: " + err.Error() + "\n") - return -1, err - } - - // Ensure cleanup on failure - success := false - defer func() { - if !success { - logger.Install.Warn("⚠️ Cleaning up due to failure...\n") - os.RemoveAll(steamCMDDir) - } - }() - - // Install required libraries - if err := installRequiredLibraries(); err != nil { - logger.Install.Error("❌ Error installing required libraries: " + err.Error() + "\n") - return -1, err - } - - // Download and extract SteamCMD - if err := downloadAndExtractSteamCMD(downloadURL, steamCMDDir, extractFunc); err != nil { - logger.Install.Error("❌ " + err.Error() + "\n") - return -1, err - } - - // Set executable permissions for SteamCMD files - if err := setExecutablePermissions(steamCMDDir); err != nil { - logger.Install.Error("❌ Error setting executable permissions: " + err.Error() + "\n") - return -1, err - } - - // Verify the steamcmd binary - if err := verifySteamCMDBinary(steamCMDDir); err != nil { - logger.Install.Error("❌ " + err.Error() + "\n") - return -1, err - } - - // Mark installation as successful - success = true - logger.Install.Info("✅ SteamCMD installed successfully.\n") - } else { - logger.Install.Info("✅ SteamCMD is already installed.") - } - - // Run SteamCMD and return its exit status and error - return runSteamCMD(steamCMDDir) -} - -// installSteamCMDLinux downloads and installs SteamCMD on Linux. -func installSteamCMDLinux() (int, error) { - return installSteamCMD("Linux", SteamCMDLinuxDir, SteamCMDLinuxURL, untarWrapper) -} - -// installSteamCMDWindows downloads and installs SteamCMD on Windows. -func installSteamCMDWindows() (int, error) { - return installSteamCMD("Windows", SteamCMDWindowsDir, SteamCMDWindowsURL, unzip) -} - // runSteamCMD runs the SteamCMD command to update the game and returns its exit status and any error. func runSteamCMD(steamCMDDir string) (int, error) { currentDir, err := os.Getwd() diff --git a/src/web/TwoBoxForm.go b/src/web/TwoBoxForm.go index bbee909f..1b4e32bf 100644 --- a/src/web/TwoBoxForm.go +++ b/src/web/TwoBoxForm.go @@ -12,13 +12,17 @@ import ( func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) { type Step struct { - ID string - Title string - HeaderTitle string - StepMessage string - PrimaryLabel string - SecondaryLabel string - SecondaryLabelType string + ID string + Title string + HeaderTitle string + StepMessage string + PrimaryLabel string + SecondaryLabel string + SecondaryLabelType string + SecondaryOptions []struct { + Display string // Text shown in dropdown + Value string // Value sent on submission + } SubmitButtonText string SkipButtonText string PrimaryPlaceholderText string @@ -28,24 +32,30 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) { } type TemplateData struct { - IsFirstTimeSetup bool - Path string - Title string - HeaderTitle string - StepMessage string - PrimaryLabel string - SecondaryLabel string - SecondaryLabelType string + IsFirstTimeSetup bool + Path string + Title string + HeaderTitle string + StepMessage string + PrimaryLabel string + SecondaryLabel string + SecondaryLabelType string + SecondaryOptions []struct { + Display string + Value string + } SubmitButtonText string SkipButtonText string Mode string ShowExtraButtons bool FooterText string + FooterTextInfo string Step string ConfigField string NextStep string PrimaryPlaceholderText string SecondaryPlaceholderText string + Steps []Step } twoboxformAssetsFS, err := fs.Sub(config.GetV1UIFS(), "UIMod/onboard_bundled/twoboxform") @@ -68,12 +78,43 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) { stepID = "welcome" // Start with welcome page for first-time setup } + var gameBranchOptions = []struct{ Display, Value string }{ + {Display: "Stable branch (default)", Value: "public"}, + {Display: "Beta branch", Value: "beta"}, + {Display: "Pre-terrain rework update", Value: "preterrain"}, + {Display: "Pre-rocket refactor update", Value: "prerocket"}, + {Display: "Version before the latest update", Value: "previous"}, + } + + var worldOptions = []struct{ Display, Value string }{ + {Display: "Moon", Value: "Moon"}, + {Display: "Vulcan", Value: "Vulcan"}, + {Display: "Venus", Value: "Venus"}, + {Display: "Mars", Value: "Mars"}, + {Display: "Europa", Value: "Europa"}, + {Display: "Mimas", Value: "Mimas"}, + } + + if config.GetIsNewTerrainAndSaveSystem() { + worldOptions = []struct{ Display, Value string }{ + {Display: "Lunar", Value: "Lunar"}, + {Display: "Vulcan", Value: "Vulcan"}, + {Display: "Venus", Value: "Venus"}, + {Display: "Mars", Value: "Mars2"}, + {Display: "Europa", Value: "Europa3"}, + {Display: "Mimas Herschel", Value: "MimasHerschel"}, + {Display: "Mars", Value: "Mars"}, + {Display: "Europa", Value: "Europa"}, + {Display: "Mimas", Value: "Mimas"}, + } + } + // Define all steps in a map for easy access and modification steps := map[string]Step{ "welcome": { ID: "welcome", Title: localization.GetString("UIText_Welcome_Title"), - HeaderTitle: "", + HeaderTitle: localization.GetString("UIText_Welcome_HeaderTitle"), StepMessage: "", PrimaryLabel: "", SecondaryLabel: "", @@ -92,7 +133,35 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) { SecondaryLabelType: "hidden", SubmitButtonText: localization.GetString("UIText_PlsRead_SubmitButton"), SkipButtonText: localization.GetString("UIText_PlsRead_SkipButton"), - NextStep: "server_name", + NextStep: "game_branch", + }, + "game_branch": { + ID: "game_branch", + Title: localization.GetString("UIText_GameBranch_Title"), + HeaderTitle: localization.GetString("UIText_GameBranch_HeaderTitle"), + StepMessage: localization.GetString("UIText_GameBranch_StepMessage"), + SecondaryPlaceholderText: localization.GetString("UIText_GameBranch_SecondaryPlaceholder"), + SecondaryLabel: localization.GetString("UIText_GameBranch_SecondaryLabel"), + SecondaryLabelType: "dropdown", + SecondaryOptions: gameBranchOptions, + SubmitButtonText: localization.GetString("UIText_GameBranch_SubmitButton"), + SkipButtonText: localization.GetString("UIText_GameBranch_SkipButton"), + ConfigField: "gameBranch", + NextStep: "newterrain_and_savesystem", + }, + "newterrain_and_savesystem": { + ID: "newterrain_and_savesystem", + Title: localization.GetString("UIText_NewTerrainAndSaveSystem_Title"), + HeaderTitle: localization.GetString("UIText_NewTerrainAndSaveSystem_HeaderTitle"), + StepMessage: localization.GetString("UIText_NewTerrainAndSaveSystem_StepMessage"), + PrimaryPlaceholderText: localization.GetString("UIText_NewTerrainAndSaveSystem_PrimaryPlaceholder"), + PrimaryLabel: localization.GetString("UIText_NewTerrainAndSaveSystem_PrimaryLabel"), + SecondaryLabel: "", + SecondaryLabelType: "hidden", + SubmitButtonText: localization.GetString("UIText_NewTerrainAndSaveSystem_SubmitButton"), + SkipButtonText: localization.GetString("UIText_NewTerrainAndSaveSystem_SkipButton"), + ConfigField: "IsNewTerrainAndSaveSystem", + NextStep: "server_name", }, "server_name": { ID: "server_name", @@ -109,18 +178,20 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) { NextStep: "save_identifier", }, "save_identifier": { - ID: "save_identifier", - Title: localization.GetString("UIText_SaveIdentifier_Title"), - HeaderTitle: localization.GetString("UIText_SaveIdentifier_HeaderTitle"), - StepMessage: localization.GetString("UIText_SaveIdentifier_StepMessage"), - PrimaryPlaceholderText: localization.GetString("UIText_SaveIdentifier_PrimaryPlaceholder"), - PrimaryLabel: localization.GetString("UIText_SaveIdentifier_PrimaryLabel"), - SecondaryLabel: "", - SecondaryLabelType: "hidden", - SubmitButtonText: localization.GetString("UIText_SaveIdentifier_SubmitButton"), - SkipButtonText: localization.GetString("UIText_SaveIdentifier_SkipButton"), - ConfigField: "SaveInfo", - NextStep: "max_players", + ID: "save_identifier", + Title: localization.GetString("UIText_SaveIdentifier_Title"), + HeaderTitle: localization.GetString("UIText_SaveIdentifier_HeaderTitle"), + StepMessage: localization.GetString("UIText_SaveIdentifier_StepMessage"), + PrimaryPlaceholderText: localization.GetString("UIText_SaveIdentifier_PrimaryPlaceholder"), + PrimaryLabel: localization.GetString("UIText_SaveIdentifier_PrimaryLabel"), + SecondaryLabel: localization.GetString("UIText_SaveIdentifier_SecondaryLabel"), + SecondaryLabelType: "dropdown", + SecondaryPlaceholderText: localization.GetString("UIText_SaveIdentifier_SecondaryPlaceholder"), + SecondaryOptions: worldOptions, + SubmitButtonText: localization.GetString("UIText_SaveIdentifier_SubmitButton"), + SkipButtonText: localization.GetString("UIText_SaveIdentifier_SkipButton"), + ConfigField: "SaveInfo", + NextStep: "max_players", }, "max_players": { ID: "max_players", @@ -148,147 +219,6 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) { SubmitButtonText: localization.GetString("UIText_ServerPassword_SubmitButton"), SkipButtonText: localization.GetString("UIText_ServerPassword_SkipButton"), ConfigField: "ServerPassword", - NextStep: "game_branch", - }, - "game_branch": { - ID: "game_branch", - Title: localization.GetString("UIText_GameBranch_Title"), - HeaderTitle: localization.GetString("UIText_GameBranch_HeaderTitle"), - StepMessage: localization.GetString("UIText_GameBranch_StepMessage"), - PrimaryPlaceholderText: localization.GetString("UIText_GameBranch_PrimaryPlaceholder"), - PrimaryLabel: localization.GetString("UIText_GameBranch_PrimaryLabel"), - SecondaryLabel: "", - SecondaryLabelType: "hidden", - SubmitButtonText: localization.GetString("UIText_GameBranch_SubmitButton"), - SkipButtonText: localization.GetString("UIText_GameBranch_SkipButton"), - ConfigField: "gameBranch", - NextStep: "newterrain_and_savesystem", - }, - "newterrain_and_savesystem": { - ID: "newterrain_and_savesystem", - Title: localization.GetString("UIText_NewTerrainAndSaveSystem_Title"), - HeaderTitle: localization.GetString("UIText_NewTerrainAndSaveSystem_HeaderTitle"), - StepMessage: localization.GetString("UIText_NewTerrainAndSaveSystem_StepMessage"), - PrimaryPlaceholderText: localization.GetString("UIText_NewTerrainAndSaveSystem_PrimaryPlaceholder"), - PrimaryLabel: localization.GetString("UIText_NewTerrainAndSaveSystem_PrimaryLabel"), - SecondaryLabel: "", - SecondaryLabelType: "hidden", - SubmitButtonText: localization.GetString("UIText_NewTerrainAndSaveSystem_SubmitButton"), - SkipButtonText: localization.GetString("UIText_NewTerrainAndSaveSystem_SkipButton"), - ConfigField: "IsNewTerrainAndSaveSystem", - NextStep: "network_config_choice", - }, - "discord_enabled": { - ID: "discord_enabled", - Title: localization.GetString("UIText_DiscordEnabled_Title"), - HeaderTitle: localization.GetString("UIText_DiscordEnabled_HeaderTitle"), - StepMessage: localization.GetString("UIText_DiscordEnabled_StepMessage"), - PrimaryPlaceholderText: localization.GetString("UIText_DiscordEnabled_PrimaryPlaceholder"), - PrimaryLabel: localization.GetString("UIText_DiscordEnabled_PrimaryLabel"), - SecondaryLabel: "", - SecondaryLabelType: "hidden", - SubmitButtonText: localization.GetString("UIText_DiscordEnabled_SubmitButton"), - SkipButtonText: localization.GetString("UIText_DiscordEnabled_SkipButton"), - ConfigField: "isDiscordEnabled", // We'll handle the boolean conversion in JS - NextStep: "discord_token", // Default next step if enabled - // The actual next step will be determined by JS based on the answer - }, - "discord_token": { - ID: "discord_token", - Title: localization.GetString("UIText_DiscordToken_Title"), - HeaderTitle: localization.GetString("UIText_DiscordToken_HeaderTitle"), - StepMessage: localization.GetString("UIText_DiscordToken_StepMessage"), - PrimaryPlaceholderText: localization.GetString("UIText_DiscordToken_PrimaryPlaceholder"), - PrimaryLabel: localization.GetString("UIText_DiscordToken_PrimaryLabel"), - SecondaryLabel: "", - SecondaryLabelType: "hidden", - SubmitButtonText: localization.GetString("UIText_DiscordToken_SubmitButton"), - SkipButtonText: localization.GetString("UIText_DiscordToken_SkipButton"), - ConfigField: "discordToken", - NextStep: "control_panel_channel", - }, - "control_panel_channel": { - ID: "control_panel_channel", - Title: localization.GetString("UIText_ControlPanelChannel_Title"), - HeaderTitle: localization.GetString("UIText_ControlPanelChannel_HeaderTitle"), - StepMessage: localization.GetString("UIText_ControlPanelChannel_StepMessage"), - PrimaryPlaceholderText: localization.GetString("UIText_ControlPanelChannel_PrimaryPlaceholder"), - PrimaryLabel: localization.GetString("UIText_ControlPanelChannel_PrimaryLabel"), - SecondaryLabel: "", - SecondaryLabelType: "hidden", - SubmitButtonText: localization.GetString("UIText_ControlPanelChannel_SubmitButton"), - SkipButtonText: localization.GetString("UIText_ControlPanelChannel_SkipButton"), - ConfigField: "controlPanelChannelID", - NextStep: "save_channel", - }, - "save_channel": { - ID: "save_channel", - Title: localization.GetString("UIText_SaveChannel_Title"), - HeaderTitle: localization.GetString("UIText_SaveChannel_HeaderTitle"), - StepMessage: localization.GetString("UIText_SaveChannel_StepMessage"), - PrimaryPlaceholderText: localization.GetString("UIText_SaveChannel_PrimaryPlaceholder"), - PrimaryLabel: localization.GetString("UIText_SaveChannel_PrimaryLabel"), - SecondaryLabel: "", - SecondaryLabelType: "hidden", - SubmitButtonText: localization.GetString("UIText_SaveChannel_SubmitButton"), - SkipButtonText: localization.GetString("UIText_SaveChannel_SkipButton"), - ConfigField: "saveChannelID", - NextStep: "log_channel", - }, - "log_channel": { - ID: "log_channel", - Title: localization.GetString("UIText_LogChannel_Title"), - HeaderTitle: localization.GetString("UIText_LogChannel_HeaderTitle"), - StepMessage: localization.GetString("UIText_LogChannel_StepMessage"), - PrimaryPlaceholderText: localization.GetString("UIText_LogChannel_PrimaryPlaceholder"), - PrimaryLabel: localization.GetString("UIText_LogChannel_PrimaryLabel"), - SecondaryLabel: "", - SecondaryLabelType: "hidden", - SubmitButtonText: localization.GetString("UIText_LogChannel_SubmitButton"), - SkipButtonText: localization.GetString("UIText_LogChannel_SkipButton"), - ConfigField: "logChannelID", - NextStep: "connection_list_channel", - }, - "connection_list_channel": { - ID: "connection_list_channel", - Title: localization.GetString("UIText_ConnectionListChannel_Title"), - HeaderTitle: localization.GetString("UIText_ConnectionListChannel_HeaderTitle"), - StepMessage: localization.GetString("UIText_ConnectionListChannel_StepMessage"), - PrimaryPlaceholderText: localization.GetString("UIText_ConnectionListChannel_PrimaryPlaceholder"), - PrimaryLabel: localization.GetString("UIText_ConnectionListChannel_PrimaryLabel"), - SecondaryLabel: "", - SecondaryLabelType: "hidden", - SubmitButtonText: localization.GetString("UIText_ConnectionListChannel_SubmitButton"), - SkipButtonText: localization.GetString("UIText_ConnectionListChannel_SkipButton"), - ConfigField: "connectionListChannelID", - NextStep: "status_channel", - }, - "status_channel": { - ID: "status_channel", - Title: localization.GetString("UIText_StatusChannel_Title"), - HeaderTitle: localization.GetString("UIText_StatusChannel_HeaderTitle"), - StepMessage: localization.GetString("UIText_StatusChannel_StepMessage"), - PrimaryPlaceholderText: localization.GetString("UIText_StatusChannel_PrimaryPlaceholder"), - PrimaryLabel: localization.GetString("UIText_StatusChannel_PrimaryLabel"), - SecondaryLabel: "", - SecondaryLabelType: "hidden", - SubmitButtonText: localization.GetString("UIText_StatusChannel_SubmitButton"), - SkipButtonText: localization.GetString("UIText_StatusChannel_SkipButton"), - ConfigField: "statusChannelID", - NextStep: "control_channel", - }, - "control_channel": { - ID: "control_channel", - Title: localization.GetString("UIText_ControlChannel_Title"), - HeaderTitle: localization.GetString("UIText_ControlChannel_HeaderTitle"), - StepMessage: localization.GetString("UIText_ControlChannel_StepMessage"), - PrimaryPlaceholderText: localization.GetString("UIText_ControlChannel_PrimaryPlaceholder"), - PrimaryLabel: localization.GetString("UIText_ControlChannel_PrimaryLabel"), - SecondaryLabel: "", - SecondaryLabelType: "hidden", - SubmitButtonText: localization.GetString("UIText_ControlChannel_SubmitButton"), - SkipButtonText: localization.GetString("UIText_ControlChannel_SkipButton"), - ConfigField: "controlChannelID", NextStep: "network_config_choice", }, "network_config_choice": { @@ -378,24 +308,10 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) { ConfigField: "", NextStep: "finalize", }, - "sscm": { - ID: "sscm", - Title: localization.GetString("UIText_SSCM_Title"), - HeaderTitle: localization.GetString("UIText_SSCM_HeaderTitle"), - StepMessage: localization.GetString("UIText_SSCM_StepMessage"), - PrimaryPlaceholderText: localization.GetString("UIText_SSCM_PrimaryPlaceholder"), - PrimaryLabel: localization.GetString("UIText_SSCM_PrimaryLabel"), - SecondaryLabel: "", - SecondaryLabelType: "hidden", - SubmitButtonText: localization.GetString("UIText_SSCM_SubmitButton"), - SkipButtonText: localization.GetString("UIText_SSCM_SkipButton"), - ConfigField: "IsSSCMEnabled", - NextStep: "finalize", - }, "finalize": { ID: "finalize", Title: localization.GetString("UIText_Finalize_Title"), - HeaderTitle: "", + HeaderTitle: localization.GetString("UIText_Finalize_HeaderTitle"), StepMessage: localization.GetString("UIText_Finalize_StepMessage"), PrimaryLabel: "", SecondaryLabel: "", @@ -410,6 +326,7 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) { Path: path, Step: stepID, FooterText: localization.GetString("UIText_FooterText"), + FooterTextInfo: localization.GetString("UIText_FooterTextInfo"), } switch { @@ -436,9 +353,7 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) { data.NextStep = step.NextStep data.PrimaryPlaceholderText = step.PrimaryPlaceholderText data.SecondaryPlaceholderText = step.SecondaryPlaceholderText - if stepID == "sscm" { - data.FooterText = localization.GetString("UIText_SSCM_FooterText") - } + data.SecondaryOptions = step.SecondaryOptions } else { // Default to welcome page if step is invalid welcomeStep := steps["welcome"] @@ -454,6 +369,21 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) { data.NextStep = welcomeStep.NextStep data.Step = "welcome" } + stepOrder := []string{ + "welcome", "pls_read", "game_branch", "newterrain_and_savesystem", "server_name", "save_identifier", "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 { + if step, exists := steps[id]; exists { + stepSlice = append(stepSlice, step) + } + } + data.Steps = stepSlice case path == "/changeuser": data.Title = localization.GetString("UIText_ChangeUser_Title") @@ -476,6 +406,7 @@ func ServeTwoBoxFormTemplate(w http.ResponseWriter, r *http.Request) { data.SecondaryLabelType = "password" data.SubmitButtonText = localization.GetString("UIText_Login_SubmitButton") data.Mode = "login" + data.Step = "" data.ShowExtraButtons = false } diff --git a/src/web/configpage.go b/src/web/configpage.go index 705b45ea..225ab365 100644 --- a/src/web/configpage.go +++ b/src/web/configpage.go @@ -100,61 +100,72 @@ func ServeConfigPage(w http.ResponseWriter, r *http.Request) { steamP2PFalseSelected = "selected" } + autoGameServerUpdatesTrueSelected := "" + autoGameServerUpdatesFalseSelected := "" + if config.GetAllowAutoGameServerUpdates() { + autoGameServerUpdatesTrueSelected = "selected" + } else { + autoGameServerUpdatesFalseSelected = "selected" + } + data := ConfigTemplateData{ // Config values - DiscordToken: config.GetDiscordToken(), - ControlChannelID: config.GetControlChannelID(), - StatusChannelID: config.GetStatusChannelID(), - ConnectionListChannelID: config.GetConnectionListChannelID(), - LogChannelID: config.GetLogChannelID(), - SaveChannelID: config.GetSaveChannelID(), - ControlPanelChannelID: config.GetControlPanelChannelID(), - BlackListFilePath: config.GetBlackListFilePath(), - ErrorChannelID: config.GetErrorChannelID(), - IsDiscordEnabled: fmt.Sprintf("%v", config.GetIsDiscordEnabled()), - IsDiscordEnabledTrueSelected: discordTrueSelected, - IsDiscordEnabledFalseSelected: discordFalseSelected, - GameBranch: config.GetGameBranch(), - Difficulty: config.GetDifficulty(), - StartCondition: config.GetStartCondition(), - StartLocation: config.GetStartLocation(), - ServerName: config.GetServerName(), - SaveInfo: config.GetSaveInfo(), - ServerMaxPlayers: config.GetServerMaxPlayers(), - ServerPassword: config.GetServerPassword(), - ServerAuthSecret: config.GetServerAuthSecret(), - AdminPassword: config.GetAdminPassword(), - GamePort: config.GetGamePort(), - UpdatePort: config.GetUpdatePort(), - UPNPEnabled: fmt.Sprintf("%v", config.GetUPNPEnabled()), - UPNPEnabledTrueSelected: upnpTrueSelected, - UPNPEnabledFalseSelected: upnpFalseSelected, - AutoSave: fmt.Sprintf("%v", config.GetAutoSave()), - AutoSaveTrueSelected: autoSaveTrueSelected, - AutoSaveFalseSelected: autoSaveFalseSelected, - SaveInterval: config.GetSaveInterval(), - AutoPauseServer: fmt.Sprintf("%v", config.GetAutoPauseServer()), - AutoPauseServerTrueSelected: autoPauseTrueSelected, - AutoPauseServerFalseSelected: autoPauseFalseSelected, - LocalIpAddress: config.GetLocalIpAddress(), - StartLocalHost: fmt.Sprintf("%v", config.GetStartLocalHost()), - StartLocalHostTrueSelected: startLocalTrueSelected, - StartLocalHostFalseSelected: startLocalFalseSelected, - ServerVisible: fmt.Sprintf("%v", config.GetServerVisible()), - ServerVisibleTrueSelected: serverVisibleTrueSelected, - ServerVisibleFalseSelected: serverVisibleFalseSelected, - UseSteamP2P: fmt.Sprintf("%v", config.GetUseSteamP2P()), - UseSteamP2PTrueSelected: steamP2PTrueSelected, - UseSteamP2PFalseSelected: steamP2PFalseSelected, - ExePath: config.GetExePath(), - AdditionalParams: config.GetAdditionalParams(), - AutoRestartServerTimer: config.GetAutoRestartServerTimer(), - IsNewTerrainAndSaveSystem: fmt.Sprintf("%v", config.GetIsNewTerrainAndSaveSystem()), - IsNewTerrainAndSaveSystemTrueSelected: isNewTerrainAndSaveSystemTrueSelected, - IsNewTerrainAndSaveSystemFalseSelected: isNewTerrainAndSaveSystemFalseSelected, - AutoStartServerOnStartup: fmt.Sprintf("%v", config.GetAutoStartServerOnStartup()), - AutoStartServerOnStartupTrueSelected: autoStartServerTrueSelected, - AutoStartServerOnStartupFalseSelected: autoStartServerFalseSelected, + DiscordToken: config.GetDiscordToken(), + ControlChannelID: config.GetControlChannelID(), + StatusChannelID: config.GetStatusChannelID(), + ConnectionListChannelID: config.GetConnectionListChannelID(), + LogChannelID: config.GetLogChannelID(), + SaveChannelID: config.GetSaveChannelID(), + ControlPanelChannelID: config.GetControlPanelChannelID(), + BlackListFilePath: config.GetBlackListFilePath(), + ErrorChannelID: config.GetErrorChannelID(), + IsDiscordEnabled: fmt.Sprintf("%v", config.GetIsDiscordEnabled()), + IsDiscordEnabledTrueSelected: discordTrueSelected, + IsDiscordEnabledFalseSelected: discordFalseSelected, + GameBranch: config.GetGameBranch(), + Difficulty: config.GetDifficulty(), + StartCondition: config.GetStartCondition(), + StartLocation: config.GetStartLocation(), + ServerName: config.GetServerName(), + SaveInfo: config.GetSaveInfo(), + ServerMaxPlayers: config.GetServerMaxPlayers(), + ServerPassword: config.GetServerPassword(), + ServerAuthSecret: config.GetServerAuthSecret(), + AdminPassword: config.GetAdminPassword(), + GamePort: config.GetGamePort(), + UpdatePort: config.GetUpdatePort(), + UPNPEnabled: fmt.Sprintf("%v", config.GetUPNPEnabled()), + UPNPEnabledTrueSelected: upnpTrueSelected, + UPNPEnabledFalseSelected: upnpFalseSelected, + AutoSave: fmt.Sprintf("%v", config.GetAutoSave()), + AutoSaveTrueSelected: autoSaveTrueSelected, + AutoSaveFalseSelected: autoSaveFalseSelected, + SaveInterval: config.GetSaveInterval(), + AutoPauseServer: fmt.Sprintf("%v", config.GetAutoPauseServer()), + AutoPauseServerTrueSelected: autoPauseTrueSelected, + AutoPauseServerFalseSelected: autoPauseFalseSelected, + LocalIpAddress: config.GetLocalIpAddress(), + StartLocalHost: fmt.Sprintf("%v", config.GetStartLocalHost()), + StartLocalHostTrueSelected: startLocalTrueSelected, + StartLocalHostFalseSelected: startLocalFalseSelected, + ServerVisible: fmt.Sprintf("%v", config.GetServerVisible()), + ServerVisibleTrueSelected: serverVisibleTrueSelected, + ServerVisibleFalseSelected: serverVisibleFalseSelected, + UseSteamP2P: fmt.Sprintf("%v", config.GetUseSteamP2P()), + UseSteamP2PTrueSelected: steamP2PTrueSelected, + UseSteamP2PFalseSelected: steamP2PFalseSelected, + ExePath: config.GetExePath(), + AdditionalParams: config.GetAdditionalParams(), + AutoRestartServerTimer: config.GetAutoRestartServerTimer(), + IsNewTerrainAndSaveSystem: fmt.Sprintf("%v", config.GetIsNewTerrainAndSaveSystem()), + IsNewTerrainAndSaveSystemTrueSelected: isNewTerrainAndSaveSystemTrueSelected, + IsNewTerrainAndSaveSystemFalseSelected: isNewTerrainAndSaveSystemFalseSelected, + AutoStartServerOnStartup: fmt.Sprintf("%v", config.GetAutoStartServerOnStartup()), + AutoStartServerOnStartupTrueSelected: autoStartServerTrueSelected, + AutoStartServerOnStartupFalseSelected: autoStartServerFalseSelected, + AllowAutoGameServerUpdates: fmt.Sprintf("%v", config.GetAllowAutoGameServerUpdates()), + AllowAutoGameServerUpdatesTrueSelected: autoGameServerUpdatesTrueSelected, + AllowAutoGameServerUpdatesFalseSelected: autoGameServerUpdatesFalseSelected, // Localized UI text UIText_ServerConfig: localization.GetString("UIText_ServerConfig"), @@ -169,61 +180,64 @@ func ServeConfigPage(w http.ResponseWriter, r *http.Request) { UIText_BetaSettings: localization.GetString("UIText_BetaSettings"), UIText_BasicServerSettings: localization.GetString("UIText_BasicServerSettings"), - UIText_ServerName: localization.GetString("UIText_ServerName"), - UIText_ServerNameInfo: localization.GetString("UIText_ServerNameInfo"), - UIText_SaveFileName: localization.GetString("UIText_SaveFileName"), - UIText_SaveFileNameInfo: localization.GetString("UIText_SaveFileNameInfo"), - UIText_MaxPlayers: localization.GetString("UIText_MaxPlayers"), - UIText_MaxPlayersInfo: localization.GetString("UIText_MaxPlayersInfo"), - UIText_ServerPassword: localization.GetString("UIText_ServerPassword"), - UIText_ServerPasswordInfo: localization.GetString("UIText_ServerPasswordInfo"), - UIText_AdminPassword: localization.GetString("UIText_AdminPassword"), - UIText_AdminPasswordInfo: localization.GetString("UIText_AdminPasswordInfo"), - UIText_AutoSave: localization.GetString("UIText_AutoSave"), - UIText_AutoSaveInfo: localization.GetString("UIText_AutoSaveInfo"), - UIText_SaveInterval: localization.GetString("UIText_SaveInterval"), - UIText_SaveIntervalInfo: localization.GetString("UIText_SaveIntervalInfo"), - UIText_AutoPauseServer: localization.GetString("UIText_AutoPauseServer"), - UIText_AutoPauseServerInfo: localization.GetString("UIText_AutoPauseServerInfo"), - UIText_NetworkConfiguration: localization.GetString("UIText_NetworkConfiguration"), - UIText_GamePort: localization.GetString("UIText_GamePort"), - UIText_GamePortInfo: localization.GetString("UIText_GamePortInfo"), - UIText_UpdatePort: localization.GetString("UIText_UpdatePort"), - UIText_UpdatePortInfo: localization.GetString("UIText_UpdatePortInfo"), - UIText_UPNPEnabled: localization.GetString("UIText_UPNPEnabled"), - UIText_UPNPEnabledInfo: localization.GetString("UIText_UPNPEnabledInfo"), - UIText_LocalIpAddress: localization.GetString("UIText_LocalIpAddress"), - UIText_LocalIpAddressInfo: localization.GetString("UIText_LocalIpAddressInfo"), - UIText_StartLocalHost: localization.GetString("UIText_StartLocalHost"), - UIText_StartLocalHostInfo: localization.GetString("UIText_StartLocalHostInfo"), - UIText_ServerVisible: localization.GetString("UIText_ServerVisible"), - UIText_ServerVisibleInfo: localization.GetString("UIText_ServerVisibleInfo"), - UIText_UseSteamP2P: localization.GetString("UIText_UseSteamP2P"), - UIText_UseSteamP2PInfo: localization.GetString("UIText_UseSteamP2PInfo"), - UIText_AdvancedConfiguration: localization.GetString("UIText_AdvancedConfiguration"), - UIText_ServerAuthSecret: localization.GetString("UIText_ServerAuthSecret"), - UIText_ServerAuthSecretInfo: localization.GetString("UIText_ServerAuthSecretInfo"), - UIText_ServerExePath: localization.GetString("UIText_ServerExePath"), - UIText_ServerExePathInfo: localization.GetString("UIText_ServerExePathInfo"), - UIText_ServerExePathInfo2: localization.GetString("UIText_ServerExePathInfo2"), - UIText_AdditionalParams: localization.GetString("UIText_AdditionalParams"), - UIText_AdditionalParamsInfo: localization.GetString("UIText_AdditionalParamsInfo"), - UIText_AutoRestartServerTimer: localization.GetString("UIText_AutoRestartServerTimer"), - UIText_AutoRestartServerTimerInfo: localization.GetString("UIText_AutoRestartServerTimerInfo"), - UIText_GameBranch: localization.GetString("UIText_GameBranch"), - UIText_GameBranchInfo: localization.GetString("UIText_GameBranchInfo"), - UIText_BetaOnlySettings: localization.GetString("UIText_BetaOnlySettings"), - UIText_BetaWarning: localization.GetString("UIText_BetaWarning"), - UIText_UseNewTerrainAndSave: localization.GetString("UIText_UseNewTerrainAndSave"), - UIText_UseNewTerrainAndSaveInfo: localization.GetString("UIText_UseNewTerrainAndSaveInfo"), - UIText_Difficulty: localization.GetString("UIText_Difficulty"), - UIText_DifficultyInfo: localization.GetString("UIText_DifficultyInfo"), - UIText_StartCondition: localization.GetString("UIText_StartCondition"), - UIText_StartConditionInfo: localization.GetString("UIText_StartConditionInfo"), - UIText_StartLocation: localization.GetString("UIText_StartLocation"), - UIText_StartLocationInfo: localization.GetString("UIText_StartLocationInfo"), - UIText_AutoStartServerOnStartup: localization.GetString("UIText_AutoStartServerOnStartup"), - UIText_AutoStartServerOnStartupInfo: localization.GetString("UIText_AutoStartServerOnStartupInfo"), + UIText_ServerName: localization.GetString("UIText_ServerName"), + UIText_ServerNameInfo: localization.GetString("UIText_ServerNameInfo"), + UIText_SaveFileName: localization.GetString("UIText_SaveFileName"), + UIText_SaveFileNameInfo: localization.GetString("UIText_SaveFileNameInfo"), + UIText_SaveFileNameUseWizzardButtonText: localization.GetString("UIText_SaveFileNameUseWizzardButtonText"), + UIText_MaxPlayers: localization.GetString("UIText_MaxPlayers"), + UIText_MaxPlayersInfo: localization.GetString("UIText_MaxPlayersInfo"), + UIText_ServerPassword: localization.GetString("UIText_ServerPassword"), + UIText_ServerPasswordInfo: localization.GetString("UIText_ServerPasswordInfo"), + UIText_AdminPassword: localization.GetString("UIText_AdminPassword"), + UIText_AdminPasswordInfo: localization.GetString("UIText_AdminPasswordInfo"), + UIText_AutoSave: localization.GetString("UIText_AutoSave"), + UIText_AutoSaveInfo: localization.GetString("UIText_AutoSaveInfo"), + UIText_SaveInterval: localization.GetString("UIText_SaveInterval"), + UIText_SaveIntervalInfo: localization.GetString("UIText_SaveIntervalInfo"), + UIText_AutoPauseServer: localization.GetString("UIText_AutoPauseServer"), + UIText_AutoPauseServerInfo: localization.GetString("UIText_AutoPauseServerInfo"), + UIText_NetworkConfiguration: localization.GetString("UIText_NetworkConfiguration"), + UIText_GamePort: localization.GetString("UIText_GamePort"), + UIText_GamePortInfo: localization.GetString("UIText_GamePortInfo"), + UIText_UpdatePort: localization.GetString("UIText_UpdatePort"), + UIText_UpdatePortInfo: localization.GetString("UIText_UpdatePortInfo"), + UIText_UPNPEnabled: localization.GetString("UIText_UPNPEnabled"), + UIText_UPNPEnabledInfo: localization.GetString("UIText_UPNPEnabledInfo"), + UIText_LocalIpAddress: localization.GetString("UIText_LocalIpAddress"), + UIText_LocalIpAddressInfo: localization.GetString("UIText_LocalIpAddressInfo"), + UIText_StartLocalHost: localization.GetString("UIText_StartLocalHost"), + UIText_StartLocalHostInfo: localization.GetString("UIText_StartLocalHostInfo"), + UIText_ServerVisible: localization.GetString("UIText_ServerVisible"), + UIText_ServerVisibleInfo: localization.GetString("UIText_ServerVisibleInfo"), + UIText_UseSteamP2P: localization.GetString("UIText_UseSteamP2P"), + UIText_UseSteamP2PInfo: localization.GetString("UIText_UseSteamP2PInfo"), + UIText_AdvancedConfiguration: localization.GetString("UIText_AdvancedConfiguration"), + UIText_ServerAuthSecret: localization.GetString("UIText_ServerAuthSecret"), + UIText_ServerAuthSecretInfo: localization.GetString("UIText_ServerAuthSecretInfo"), + UIText_ServerExePath: localization.GetString("UIText_ServerExePath"), + UIText_ServerExePathInfo: localization.GetString("UIText_ServerExePathInfo"), + UIText_ServerExePathInfo2: localization.GetString("UIText_ServerExePathInfo2"), + UIText_AdditionalParams: localization.GetString("UIText_AdditionalParams"), + UIText_AdditionalParamsInfo: localization.GetString("UIText_AdditionalParamsInfo"), + UIText_AutoRestartServerTimer: localization.GetString("UIText_AutoRestartServerTimer"), + UIText_AutoRestartServerTimerInfo: localization.GetString("UIText_AutoRestartServerTimerInfo"), + UIText_GameBranch: localization.GetString("UIText_GameBranch"), + UIText_GameBranchInfo: localization.GetString("UIText_GameBranchInfo"), + UIText_BetaOnlySettings: localization.GetString("UIText_BetaOnlySettings"), + UIText_BetaWarning: localization.GetString("UIText_BetaWarning"), + UIText_UseNewTerrainAndSave: localization.GetString("UIText_UseNewTerrainAndSave"), + UIText_UseNewTerrainAndSaveInfo: localization.GetString("UIText_UseNewTerrainAndSaveInfo"), + UIText_Difficulty: localization.GetString("UIText_Difficulty"), + UIText_DifficultyInfo: localization.GetString("UIText_DifficultyInfo"), + UIText_StartCondition: localization.GetString("UIText_StartCondition"), + UIText_StartConditionInfo: localization.GetString("UIText_StartConditionInfo"), + UIText_StartLocation: localization.GetString("UIText_StartLocation"), + UIText_StartLocationInfo: localization.GetString("UIText_StartLocationInfo"), + UIText_AutoStartServerOnStartup: localization.GetString("UIText_AutoStartServerOnStartup"), + UIText_AutoStartServerOnStartupInfo: localization.GetString("UIText_AutoStartServerOnStartupInfo"), + UIText_AllowAutoGameServerUpdates: localization.GetString("UIText_AllowAutoGameServerUpdates"), + UIText_AllowAutoGameServerUpdatesInfo: localization.GetString("UIText_AllowAutoGameServerUpdatesInfo"), UIText_DiscordIntegrationTitle: localization.GetString("UIText_DiscordIntegrationTitle"), UIText_DiscordBotToken: localization.GetString("UIText_DiscordBotToken"), diff --git a/src/web/http.go b/src/web/http.go index 1eb4a324..5f99f9fe 100644 --- a/src/web/http.go +++ b/src/web/http.go @@ -15,7 +15,7 @@ import ( "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/setup" + "github.com/JacksonTheMaster/StationeersServerUI/v5/src/steamcmd" ) // StartServer HTTP handler @@ -137,13 +137,8 @@ func HandleRunSteamCMD(w http.ResponseWriter, r *http.Request) { return } - if gamemgr.InternalIsServerRunning() { - logger.Core.Warn("Server is running, stopping server first...") - gamemgr.InternalStopServer() - time.Sleep(10000 * time.Millisecond) - } logger.Core.Info("Running SteamCMD") - _, err := setup.InstallAndRunSteamCMD() + _, err := steamcmd.InstallAndRunSteamCMD() // Update last execution time lastSteamCMDExecution = time.Now() diff --git a/src/web/templatevars.go b/src/web/templatevars.go index a3eb24ba..13cd36c2 100644 --- a/src/web/templatevars.go +++ b/src/web/templatevars.go @@ -23,59 +23,62 @@ type IndexTemplateData struct { // ConfigTemplateData holds data for the config page template type ConfigTemplateData struct { // Config values - DiscordToken string - ControlChannelID string - StatusChannelID string - ConnectionListChannelID string - LogChannelID string - SaveChannelID string - ControlPanelChannelID string - BlackListFilePath string - ErrorChannelID string - IsDiscordEnabled string - IsDiscordEnabledTrueSelected string - IsDiscordEnabledFalseSelected string - GameBranch string - Difficulty string - StartCondition string - StartLocation string - ServerName string - SaveInfo string - ServerMaxPlayers string - ServerPassword string - ServerAuthSecret string - AdminPassword string - GamePort string - UpdatePort string - UPNPEnabled string - UPNPEnabledTrueSelected string - UPNPEnabledFalseSelected string - AutoSave string - AutoSaveTrueSelected string - AutoSaveFalseSelected string - SaveInterval string - AutoPauseServer string - AutoPauseServerTrueSelected string - AutoPauseServerFalseSelected string - LocalIpAddress string - StartLocalHost string - StartLocalHostTrueSelected string - StartLocalHostFalseSelected string - ServerVisible string - ServerVisibleTrueSelected string - ServerVisibleFalseSelected string - UseSteamP2P string - UseSteamP2PTrueSelected string - UseSteamP2PFalseSelected string - ExePath string - AdditionalParams string - AutoRestartServerTimer string - IsNewTerrainAndSaveSystem string - IsNewTerrainAndSaveSystemTrueSelected string - IsNewTerrainAndSaveSystemFalseSelected string - AutoStartServerOnStartup string - AutoStartServerOnStartupTrueSelected string - AutoStartServerOnStartupFalseSelected string + DiscordToken string + ControlChannelID string + StatusChannelID string + ConnectionListChannelID string + LogChannelID string + SaveChannelID string + ControlPanelChannelID string + BlackListFilePath string + ErrorChannelID string + IsDiscordEnabled string + IsDiscordEnabledTrueSelected string + IsDiscordEnabledFalseSelected string + GameBranch string + Difficulty string + StartCondition string + StartLocation string + ServerName string + SaveInfo string + ServerMaxPlayers string + ServerPassword string + ServerAuthSecret string + AdminPassword string + GamePort string + UpdatePort string + UPNPEnabled string + UPNPEnabledTrueSelected string + UPNPEnabledFalseSelected string + AutoSave string + AutoSaveTrueSelected string + AutoSaveFalseSelected string + SaveInterval string + AutoPauseServer string + AutoPauseServerTrueSelected string + AutoPauseServerFalseSelected string + LocalIpAddress string + StartLocalHost string + StartLocalHostTrueSelected string + StartLocalHostFalseSelected string + ServerVisible string + ServerVisibleTrueSelected string + ServerVisibleFalseSelected string + UseSteamP2P string + UseSteamP2PTrueSelected string + UseSteamP2PFalseSelected string + ExePath string + AdditionalParams string + AutoRestartServerTimer string + IsNewTerrainAndSaveSystem string + IsNewTerrainAndSaveSystemTrueSelected string + IsNewTerrainAndSaveSystemFalseSelected string + AutoStartServerOnStartup string + AutoStartServerOnStartupTrueSelected string + AutoStartServerOnStartupFalseSelected string + AllowAutoGameServerUpdates string + AllowAutoGameServerUpdatesTrueSelected string + AllowAutoGameServerUpdatesFalseSelected string UIText_ServerConfig string UIText_DiscordIntegration string @@ -89,61 +92,64 @@ type ConfigTemplateData struct { UIText_BetaSettings string UIText_BasicServerSettings string - UIText_ServerName string - UIText_ServerNameInfo string - UIText_SaveFileName string - UIText_SaveFileNameInfo string - UIText_MaxPlayers string - UIText_MaxPlayersInfo string - UIText_ServerPassword string - UIText_ServerPasswordInfo string - UIText_AdminPassword string - UIText_AdminPasswordInfo string - UIText_AutoSave string - UIText_AutoSaveInfo string - UIText_SaveInterval string - UIText_SaveIntervalInfo string - UIText_AutoPauseServer string - UIText_AutoPauseServerInfo string - UIText_NetworkConfiguration string - UIText_GamePort string - UIText_GamePortInfo string - UIText_UpdatePort string - UIText_UpdatePortInfo string - UIText_UPNPEnabled string - UIText_UPNPEnabledInfo string - UIText_LocalIpAddress string - UIText_LocalIpAddressInfo string - UIText_StartLocalHost string - UIText_StartLocalHostInfo string - UIText_ServerVisible string - UIText_ServerVisibleInfo string - UIText_UseSteamP2P string - UIText_UseSteamP2PInfo string - UIText_AdvancedConfiguration string - UIText_ServerAuthSecret string - UIText_ServerAuthSecretInfo string - UIText_ServerExePath string - UIText_ServerExePathInfo string - UIText_ServerExePathInfo2 string - UIText_AdditionalParams string - UIText_AdditionalParamsInfo string - UIText_AutoRestartServerTimer string - UIText_AutoRestartServerTimerInfo string - UIText_GameBranch string - UIText_GameBranchInfo string - UIText_BetaOnlySettings string - UIText_BetaWarning string - UIText_UseNewTerrainAndSave string - UIText_UseNewTerrainAndSaveInfo string - UIText_Difficulty string - UIText_DifficultyInfo string - UIText_StartCondition string - UIText_StartConditionInfo string - UIText_StartLocation string - UIText_StartLocationInfo string - UIText_AutoStartServerOnStartup string - UIText_AutoStartServerOnStartupInfo string + UIText_ServerName string + UIText_ServerNameInfo string + UIText_SaveFileName string + UIText_SaveFileNameInfo string + UIText_SaveFileNameUseWizzardButtonText string + UIText_MaxPlayers string + UIText_MaxPlayersInfo string + UIText_ServerPassword string + UIText_ServerPasswordInfo string + UIText_AdminPassword string + UIText_AdminPasswordInfo string + UIText_AutoSave string + UIText_AutoSaveInfo string + UIText_SaveInterval string + UIText_SaveIntervalInfo string + UIText_AutoPauseServer string + UIText_AutoPauseServerInfo string + UIText_NetworkConfiguration string + UIText_GamePort string + UIText_GamePortInfo string + UIText_UpdatePort string + UIText_UpdatePortInfo string + UIText_UPNPEnabled string + UIText_UPNPEnabledInfo string + UIText_LocalIpAddress string + UIText_LocalIpAddressInfo string + UIText_StartLocalHost string + UIText_StartLocalHostInfo string + UIText_ServerVisible string + UIText_ServerVisibleInfo string + UIText_UseSteamP2P string + UIText_UseSteamP2PInfo string + UIText_AdvancedConfiguration string + UIText_ServerAuthSecret string + UIText_ServerAuthSecretInfo string + UIText_ServerExePath string + UIText_ServerExePathInfo string + UIText_ServerExePathInfo2 string + UIText_AdditionalParams string + UIText_AdditionalParamsInfo string + UIText_AutoRestartServerTimer string + UIText_AutoRestartServerTimerInfo string + UIText_GameBranch string + UIText_GameBranchInfo string + UIText_BetaOnlySettings string + UIText_BetaWarning string + UIText_UseNewTerrainAndSave string + UIText_UseNewTerrainAndSaveInfo string + UIText_Difficulty string + UIText_DifficultyInfo string + UIText_StartCondition string + UIText_StartConditionInfo string + UIText_StartLocation string + UIText_StartLocationInfo string + UIText_AutoStartServerOnStartup string + UIText_AutoStartServerOnStartupInfo string + UIText_AllowAutoGameServerUpdates string + UIText_AllowAutoGameServerUpdatesInfo string UIText_DiscordIntegrationTitle string UIText_DiscordBotToken string diff --git a/sscm/LICENSE b/sscm/LICENSE index 46e41b50..48aadff9 100644 --- a/sscm/LICENSE +++ b/sscm/LICENSE @@ -76,6 +76,10 @@ Unless permitted by applicable law, the Licensee shall not: 10.3 Severability: If any provision is unenforceable, the remaining provisions remain in effect. 10.4 Language: This Agreement is in English. +## Open source licenses + +This project uses Discordgo, Copyright (c) 2015 Bruce Marriner, under the BSD 3-Clause License. The copyright notice, conditions, and disclaimer are reproduced in the discordbot package. + ## CONTACT For inquiries, contact the Licensor at github.com/JacksonTheMaster.