Panel independent of the game server: ServerHost owns port + panel and starts/stops the game server; panel works while stopped/crashed/unconfigured (settings, save ZIP upload, backups, bans offline); crash auto-restart; players told 'offline'; tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
namespace MSCO.Server;
|
||||
|
||||
/// <summary>
|
||||
/// server.json im Web-Panel lesen und ändern - bei laufendem Server (dann sofort wirksam) und wenn er aus ist
|
||||
/// (z.B. Lizenzschlüssel eintragen, damit er überhaupt starten kann).
|
||||
/// </summary>
|
||||
public static class ConfigEditor
|
||||
{
|
||||
public static object Build(ServerConfig config) => new
|
||||
{
|
||||
name = config.Name,
|
||||
description = config.Description,
|
||||
maxPlayers = config.MaxPlayers,
|
||||
isPublic = config.Public,
|
||||
requireSteam = config.RequireSteam,
|
||||
publicAddress = config.PublicAddress,
|
||||
saveSyncMinutes = config.SaveSyncMinutes,
|
||||
autosaveSeconds = config.AutosaveSeconds,
|
||||
port = config.Port,
|
||||
panelEnabled = config.PanelEnabled,
|
||||
panelPort = config.PanelPort,
|
||||
panelBindAddress = config.PanelBindAddress,
|
||||
panelTrustProxy = config.PanelTrustProxy,
|
||||
panelBasePath = config.PanelBasePath,
|
||||
licenseKeySet = !string.IsNullOrWhiteSpace(config.LicenseKey),
|
||||
steamWebApiKeySet = !string.IsNullOrWhiteSpace(config.SteamWebApiKey),
|
||||
environmentOverrides = config.EnvironmentOverrides
|
||||
};
|
||||
|
||||
/// <summary>Einstellungen ändern. Schlüssel (Lizenz, Steam Web API) nur als Owner; leer = unverändert.</summary>
|
||||
public static string Apply(ServerConfig config, string configPath, System.Text.Json.JsonElement b, bool owner, string by)
|
||||
{
|
||||
if (b.ValueKind != System.Text.Json.JsonValueKind.Object) return "Invalid settings";
|
||||
var changed = new List<string>();
|
||||
bool restartNeeded = false;
|
||||
|
||||
string Text(string name, string current, int max)
|
||||
{
|
||||
if (!b.TryGetProperty(name, out var v) || v.ValueKind != System.Text.Json.JsonValueKind.String) return current;
|
||||
string s = v.GetString().Trim();
|
||||
if (s.Length > max) s = s[..max];
|
||||
if (s != current) changed.Add(name);
|
||||
return s;
|
||||
}
|
||||
int Number(string name, int current, int min, int max)
|
||||
{
|
||||
if (!b.TryGetProperty(name, out var v) || !v.TryGetInt32(out int n)) return current;
|
||||
n = Math.Clamp(n, min, max);
|
||||
if (n != current) changed.Add(name);
|
||||
return n;
|
||||
}
|
||||
bool Flag(string name, bool current)
|
||||
{
|
||||
if (!b.TryGetProperty(name, out var v) || (v.ValueKind != System.Text.Json.JsonValueKind.True && v.ValueKind != System.Text.Json.JsonValueKind.False)) return current;
|
||||
bool f = v.GetBoolean();
|
||||
if (f != current) changed.Add(name);
|
||||
return f;
|
||||
}
|
||||
|
||||
string name = Text("name", config.Name, 60);
|
||||
if (name.Length == 0) return "The server name cannot be empty";
|
||||
config.Name = name;
|
||||
config.Description = Text("description", config.Description, 300);
|
||||
config.MaxPlayers = Number("maxPlayers", config.MaxPlayers, 1, 64);
|
||||
config.Public = Flag("isPublic", config.Public);
|
||||
config.RequireSteam = Flag("requireSteam", config.RequireSteam);
|
||||
config.PublicAddress = Text("publicAddress", config.PublicAddress, 200);
|
||||
config.SaveSyncMinutes = Number("saveSyncMinutes", config.SaveSyncMinutes, 0, 240);
|
||||
config.AutosaveSeconds = Number("autosaveSeconds", config.AutosaveSeconds, 10, 3600);
|
||||
|
||||
int before = changed.Count;
|
||||
config.Port = Number("port", config.Port, 1, 65535);
|
||||
config.PanelEnabled = Flag("panelEnabled", config.PanelEnabled);
|
||||
config.PanelPort = Number("panelPort", config.PanelPort, 0, 65535);
|
||||
config.PanelBindAddress = Text("panelBindAddress", config.PanelBindAddress, 60);
|
||||
config.PanelTrustProxy = Flag("panelTrustProxy", config.PanelTrustProxy);
|
||||
config.PanelBasePath = Text("panelBasePath", config.PanelBasePath, 60);
|
||||
if (changed.Count > before) restartNeeded = true;
|
||||
|
||||
foreach (string secret in new[] { "licenseKey", "steamWebApiKey" })
|
||||
{
|
||||
if (!b.TryGetProperty(secret, out var v) || v.ValueKind != System.Text.Json.JsonValueKind.String || v.GetString().Trim().Length == 0) continue;
|
||||
if (!owner) return "Only the owner can change keys";
|
||||
string value = v.GetString().Trim();
|
||||
if (value.Length > 200) return "Key too long";
|
||||
if (secret == "licenseKey") config.LicenseKey = value; else config.SteamWebApiKey = value;
|
||||
changed.Add(secret);
|
||||
restartNeeded = true;
|
||||
}
|
||||
|
||||
if (changed.Count == 0) return "Nothing changed";
|
||||
try { config.Save(configPath); }
|
||||
catch (Exception e) { return "Could not save server.json: " + e.Message; }
|
||||
GameServer.Log($"Settings changed by {by}: {string.Join(", ", changed)}");
|
||||
var overridden = changed.Where(c => config.EnvironmentOverrides.Any(e => e.Replace("_", "").Contains(c.ToUpperInvariant().Replace("ISPUBLIC", "PUBLIC")))).ToList();
|
||||
return "Settings saved" + (restartNeeded ? " - some changes need a restart" : "") +
|
||||
(overridden.Count > 0 ? " (note: " + string.Join(", ", overridden) + " is also set by an environment variable, which wins on restart)" : "");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user