diff --git a/GameServer.cs b/GameServer.cs index 987b1fa..5012fc9 100644 --- a/GameServer.cs +++ b/GameServer.cs @@ -73,19 +73,30 @@ public sealed class GameServer public bool LoadSave() { - saveFiles = new List>(); - Directory.CreateDirectory(savePath); - foreach (string name in SaveFiles) + // Erst alles lesen, dann übernehmen: schlägt "reload-save" fehl, bleibt der bisherige Spielstand aktiv + var loaded = new List>(); + try { - string file = Path.Combine(savePath, name); - if (File.Exists(file)) saveFiles.Add(new(name, File.ReadAllBytes(file))); + Directory.CreateDirectory(savePath); + foreach (string name in SaveFiles) + { + string file = Path.Combine(savePath, name); + if (File.Exists(file)) loaded.Add(new(name, File.ReadAllBytes(file))); + } } - if (saveFiles.All(f => f.Key != "defaultES2File.txt")) + catch (Exception e) when (e is UnauthorizedAccessException or IOException) + { + Log("Cannot read the save game: " + e.Message); + Log(PermissionHint()); + return false; + } + if (loaded.All(f => f.Key != "defaultES2File.txt")) { Log("No save game found in " + savePath); Log("Copy your My Summer Car save files there, or start the server with --import-save."); return false; } + saveFiles = loaded; Log("Loaded save game (" + saveFiles.Count + " files, " + saveFiles.Sum(f => f.Value.Length) / 1024 + " KB)"); return true; } @@ -507,6 +518,25 @@ public sealed class GameServer } } + /// + /// Hinweis bei fehlenden Dateirechten (Linux/Docker: Dateien gehören einem anderen Benutzer, + /// z.B. root oder per SFTP mit anderem Konto hochgeladen). + /// + public static string PermissionHint() + { + string uid = "?"; + try + { + string line = File.ReadLines("/proc/self/status").FirstOrDefault(l => l.StartsWith("Uid:")); + if (line != null) uid = line.Split(new[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries)[1]; + } + catch { } // nicht Linux + return uid == "?" + ? "Check the file permissions of the data folder." + : $"The server runs as user id {uid}. Fix on the host: sudo chown -R {uid}:{uid} data " + + "(or set MSCO_UID / MSCO_GID in .env to your own id: `id -u`) - see README \"Permission denied\"."; + } + void SaveWorld() { try diff --git a/Program.cs b/Program.cs index 31d7df8..74a0726 100644 --- a/Program.cs +++ b/Program.cs @@ -26,7 +26,34 @@ GameServer.Log("MSCO dedicated server " + GameServer.ModVersion + " (" + Runtime GameServer.Log("Data folder: " + dataDir); GameServer.Log("Master server: " + MasterServer.Url); -var config = ServerConfig.LoadOrCreate(configPath, out bool created); +// Datenordner beschreibbar? (Linux/Docker: gehört er einem anderen Benutzer, gleich klar melden statt abzustürzen) +try +{ + string probe = Path.Combine(dataDir, ".write-test"); + File.WriteAllText(probe, "ok"); + File.Delete(probe); +} +catch (Exception e) when (e is UnauthorizedAccessException or IOException) +{ + GameServer.Log("No write access to the data folder " + dataDir + ": " + e.Message); + GameServer.Log(GameServer.PermissionHint()); + Pause(); + return 1; +} + +ServerConfig config; +bool created; +try +{ + config = ServerConfig.LoadOrCreate(configPath, out created); +} +catch (Exception e) when (e is UnauthorizedAccessException or IOException) +{ + GameServer.Log("Cannot read " + configPath + ": " + e.Message); + GameServer.Log(GameServer.PermissionHint()); + Pause(); + return 1; +} var fromEnvironment = config.ApplyEnvironment(); if (fromEnvironment.Count > 0) GameServer.Log("Settings overridden by environment (.env): " + string.Join(", ", fromEnvironment)); diff --git a/README.md b/README.md index 25328df..efe4027 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,9 @@ In `data/save/` gehören diese Dateien aus dem My-Summer-Car-Spielstand: Die Dateien einfach nach `data/save/` im Server-Ordner hochladen, z.B. `/home//MSCO-Dedicated-Server/data/save/`. Das geht auch, während der Server läuft. Kommt dabei **„Permission denied“**, gehört der Ordner noch root (siehe Abschnitt „Linux: Permission denied“ oben) – einmal `sudo chown -R $(id -u):$(id -g) data` ausführen. +Meldet der **Server** nach dem Hochladen `Cannot read the save game … Permission denied`, gehören die neuen +Dateien einem anderen Benutzer (z.B. mit einem anderen SFTP-Konto oder als root hochgeladen) oder deine +Benutzer-ID ist nicht 1000. Der Server nennt im Log seine Benutzer-ID und den passenden `chown`-Befehl. ### Neuen Spielstand übernehmen