Clear message instead of crash when data/ or the save is not readable
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+33
-3
@@ -73,19 +73,30 @@ public sealed class GameServer
|
|||||||
|
|
||||||
public bool LoadSave()
|
public bool LoadSave()
|
||||||
{
|
{
|
||||||
saveFiles = new List<KeyValuePair<string, byte[]>>();
|
// Erst alles lesen, dann übernehmen: schlägt "reload-save" fehl, bleibt der bisherige Spielstand aktiv
|
||||||
|
var loaded = new List<KeyValuePair<string, byte[]>>();
|
||||||
|
try
|
||||||
|
{
|
||||||
Directory.CreateDirectory(savePath);
|
Directory.CreateDirectory(savePath);
|
||||||
foreach (string name in SaveFiles)
|
foreach (string name in SaveFiles)
|
||||||
{
|
{
|
||||||
string file = Path.Combine(savePath, name);
|
string file = Path.Combine(savePath, name);
|
||||||
if (File.Exists(file)) saveFiles.Add(new(name, File.ReadAllBytes(file)));
|
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("No save game found in " + savePath);
|
||||||
Log("Copy your My Summer Car save files there, or start the server with --import-save.");
|
Log("Copy your My Summer Car save files there, or start the server with --import-save.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
saveFiles = loaded;
|
||||||
Log("Loaded save game (" + saveFiles.Count + " files, " + saveFiles.Sum(f => f.Value.Length) / 1024 + " KB)");
|
Log("Loaded save game (" + saveFiles.Count + " files, " + saveFiles.Sum(f => f.Value.Length) / 1024 + " KB)");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -507,6 +518,25 @@ public sealed class GameServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hinweis bei fehlenden Dateirechten (Linux/Docker: Dateien gehören einem anderen Benutzer,
|
||||||
|
/// z.B. root oder per SFTP mit anderem Konto hochgeladen).
|
||||||
|
/// </summary>
|
||||||
|
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()
|
void SaveWorld()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
+28
-1
@@ -26,7 +26,34 @@ GameServer.Log("MSCO dedicated server " + GameServer.ModVersion + " (" + Runtime
|
|||||||
GameServer.Log("Data folder: " + dataDir);
|
GameServer.Log("Data folder: " + dataDir);
|
||||||
GameServer.Log("Master server: " + MasterServer.Url);
|
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();
|
var fromEnvironment = config.ApplyEnvironment();
|
||||||
if (fromEnvironment.Count > 0)
|
if (fromEnvironment.Count > 0)
|
||||||
GameServer.Log("Settings overridden by environment (.env): " + string.Join(", ", fromEnvironment));
|
GameServer.Log("Settings overridden by environment (.env): " + string.Join(", ", fromEnvironment));
|
||||||
|
|||||||
@@ -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/<benutzer>/MSCO-Dedicated-Server/data/save/`.
|
Die Dateien einfach nach `data/save/` im Server-Ordner hochladen, z.B. `/home/<benutzer>/MSCO-Dedicated-Server/data/save/`.
|
||||||
Das geht auch, während der Server läuft. Kommt dabei **„Permission denied“**, gehört der Ordner noch root
|
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.
|
(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
|
### Neuen Spielstand übernehmen
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user