fix(builder): SteamCMD is the only option for Linux Steam servers; settings must reach the server
Validate game templates / validate (push) Successful in 20s
Validate game templates / boot-test (push) Skipped
Validate game templates / publish (push) Skipped

This commit is contained in:
Greg Reed committed 2026-11-07 01:58:27 +00:00
1 parent 1fcf05cc27
commit 010d7a57a1
3 files changed
+70 -10

No files matched your search

+25 -7
View File
@@ -174,9 +174,10 @@ const researchPrompt = "You research how to host a game's dedicated server for G
"the ways this game's dedicated server can be run (variants): separate editions of the game with their own server, " +
"crossplay builds, or notably different server images. For each variant give:\n" +
"- images: how to run this server, best first. When the game has a dedicated server on Steam that runs on Linux, " +
"cm2network/steamcmd (the plain SteamCMD route, installing the server into /server) always comes first and that " +
"variant is the recommended one. Then Docker Hub images that appear in the pages and run as a non-root user " +
"(needed for servers that only exist for Windows).\n" +
"cm2network/steamcmd (the plain SteamCMD route, installing the server into /server) is its only image, and " +
"there is no separate variant for a community image of the same server: those keep their saves outside /server. " +
"Docker Hub images that appear in the pages and run as a non-root user are only for servers that aren't on Steam " +
"for Linux (Windows-only servers that need Proton or Wine, games not on Steam).\n" +
"- steamAppId: the Steam app id of the DEDICATED SERVER (not the game itself), if installed via SteamCMD; else \"\".\n" +
"- startupHint: the command that starts the server from its install folder /server, with the launch options " +
"for name, port, world and save location exactly as the pages document them; \"\" if unknown.\n" +
@@ -311,7 +312,7 @@ func verifyVariants(res *researchResult) {
}
}
apps := steamApps(appIDs)
anyRunnable := false
anyRunnable, linuxSteam := false, false
for i := range res.Variants {
v := &res.Variants[i]
var images []string
@@ -333,7 +334,8 @@ func verifyVariants(res *researchResult) {
v.Verified = append(v.Verified, fmt.Sprintf("Steam app %s is %q", v.SteamAppID, app.name))
v.Images = withSteamCMD(v.Images, app.linux)
if app.linux {
v.Verified = append(v.Verified, "it ships a Linux server, so the SteamCMD option (installing it into /server) is recommended")
linuxSteam = true
v.Verified = append(v.Verified, "it ships a Linux server, so it is installed with SteamCMD into /server")
}
} else {
v.Problems = append(v.Problems, "Steam app "+v.SteamAppID+" not found for anonymous SteamCMD")
@@ -352,10 +354,26 @@ func verifyVariants(res *researchResult) {
res.Reason = "unknown"
res.Message = "No variant has a Docker image or Steam app that could be confirmed. " + res.Message
}
if linuxSteam {
res.Variants = steamCMDOnly(res.Variants)
}
res.Variants = oneOptionPerImage(res.Variants)
recommend(res.Variants)
}
// steamCMDOnly keeps only the SteamCMD options once the game has a Linux server on Steam. Ready-made images
// of such servers keep their files and saves in their own folders (/config, /home/...), outside /server,
// and read settings from their own variables, so they can't be made or repaired into a working template.
func steamCMDOnly(variants []variant) []variant {
var out []variant
for _, v := range variants {
if len(v.Images) > 0 && isSteamCMDImage(v.Images[0]) && v.SteamAppID != "" {
out = append(out, v)
}
}
return out
}
// recommend marks exactly one variant recommended, among the runnable ones. A variant that installs a Linux
// server with SteamCMD comes before the model's choice: it keeps everything under /server, as nodes need.
func recommend(variants []variant) {
@@ -422,7 +440,7 @@ func steamApps(apps []string) map[string]steamApp {
}
// withSteamCMD adds the plain SteamCMD route to a variant's images. When the Steam app ships a Linux server
// it goes first: it installs into /server, which nodes keep, while ready-made images often keep their
// it is the only route: it installs into /server, which nodes keep, while ready-made images keep their
// files elsewhere. For Windows-only servers it stays last, behind images that bring Proton or Wine.
func withSteamCMD(images []string, linux bool) []string {
var others []string
@@ -432,7 +450,7 @@ func withSteamCMD(images []string, linux bool) []string {
}
}
if linux {
return append([]string{"cm2network/steamcmd"}, others...)
return []string{"cm2network/steamcmd"}
}
return append(others, "cm2network/steamcmd")
}