From 266a462fc37f7c2f5c8898b1dc9e253b0ad7fb2d Mon Sep 17 00:00:00 2001 From: MrMaho Date: Sat, 3 Oct 2026 17:41:24 +0200 Subject: [PATCH] Server 2.10.2: warn when an uploaded save has no items.txt Bought parts (oil filter, spark plugs, fan belt, battery ...) and whether they are installed are stored in items.txt. Without it they are missing on the car - e.g. the finished Satsuma of a downloaded save loses oil on the server (in singleplayer an old local items.txt hides the problem). The upload result now says so. 2 new tests (148 pass). Co-Authored-By: Claude Opus 5.5 --- GameServer.cs | 2 +- MSCO.Server.csproj | 2 +- SaveStore.cs | 12 +++++++++++- Tests/Program.cs | 5 ++++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/GameServer.cs b/GameServer.cs index be556c3..3619d59 100644 --- a/GameServer.cs +++ b/GameServer.cs @@ -1392,7 +1392,7 @@ public sealed class GameServer SaveWorld(); Log($"New save game uploaded by {by} ({found.Count} files) - it is also the new reset point"); ReconnectAll($"{by} uploaded a new save game - reconnecting..."); - return $"Save game uploaded ({found.Count} files) - everyone reconnects"; + return $"Save game uploaded ({found.Count} files) - everyone reconnects" + SaveStore.MissingItemsWarning(found); } /// Aktuellen Spielstand als ZIP. diff --git a/MSCO.Server.csproj b/MSCO.Server.csproj index 302889d..524fbb6 100644 --- a/MSCO.Server.csproj +++ b/MSCO.Server.csproj @@ -7,7 +7,7 @@ enable MSCO.Server MSCO.Server - 2.10.1 + 2.10.2 MBM MSCO Dedicated Server true diff --git a/SaveStore.cs b/SaveStore.cs index bb999c0..80ceb95 100644 --- a/SaveStore.cs +++ b/SaveStore.cs @@ -216,6 +216,16 @@ public static class SaveStore return null; } + /// + /// Hinweis, wenn im Spielstand items.txt fehlt: dort stehen alle gekauften Teile und Gegenstände (Ölfilter, + /// Zündkerzen, Keilriemen, Batterie ...) samt "eingebaut". Ohne die Datei fehlen sie am Auto - z.B. läuft Öl aus, + /// weil kein Ölfilter da ist. Im Einzelspieler fällt das nicht auf, wenn dort noch eine alte items.txt liegt. + /// + public static string MissingItemsWarning(IReadOnlyDictionary files) => + files.ContainsKey("items.txt") ? "" + : " - WARNING: items.txt is missing in this save. Bought parts (oil filter, spark plugs, fan belt, battery ...) are " + + "stored there, so they are missing on the car (e.g. oil runs out without the oil filter). Upload the save with items.txt."; + /// Aktuellen Spielstand als ZIP. public static byte[] ExportZip(string dataDir) { @@ -247,7 +257,7 @@ public static class SaveStore } catch (Exception e) { return "Could not write the save game: " + e.Message; } GameServer.Log($"New save game uploaded by {by} ({files.Count} files, server stopped) - it is also the new reset point"); - return $"Save game uploaded ({files.Count} files) - start the server to use it"; + return $"Save game uploaded ({files.Count} files) - start the server to use it" + MissingItemsWarning(files); } /// Server aus: Backup wiederherstellen. diff --git a/Tests/Program.cs b/Tests/Program.cs index 37d33f4..5bf4bd6 100644 --- a/Tests/Program.cs +++ b/Tests/Program.cs @@ -537,8 +537,10 @@ async Task PanelTests() up.Headers.Add("X-MSCO-Panel", "1"); up.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip"); var upRes = await owner.SendAsync(up); + string upText = await upRes.Content.ReadAsStringAsync(); Check(upRes.IsSuccessStatusCode && File.ReadAllText(Path.Combine(dir, "save", "defaultES2File.txt")) == "ZIPPED SAVE", - "A save game ZIP can be uploaded: " + await upRes.Content.ReadAsStringAsync()); + "A save game ZIP can be uploaded: " + upText); + Check(upText.Contains("items.txt is missing"), "Uploading a save without items.txt warns (bought parts like the oil filter would be missing)"); Check(!File.Exists(Path.Combine(dir, "..", "evil.txt")) && !File.Exists(Path.Combine(dir, "evil.txt")), "Paths inside the ZIP are never used"); Check(File.ReadAllText(Path.Combine(dir, "save_original", "defaultES2File.txt")) == "ZIPPED SAVE", "The uploaded save is the new reset point"); // RAR (mit WinRAR gepackt, "solid", Unterordner, zusätzliche fremde Datei) @@ -550,6 +552,7 @@ async Task PanelTests() Check(rarRes.IsSuccessStatusCode && File.ReadAllText(Path.Combine(dir, "save", "defaultES2File.txt")) == "RAR SAVE" && File.ReadAllText(Path.Combine(dir, "save", "items.txt")) == "rar items" && !File.Exists(Path.Combine(dir, "save", "readme.txt")), "A RAR archive can be uploaded (only the save files are taken): " + await rarRes.Content.ReadAsStringAsync()); + Check(!(await rarRes.Content.ReadAsStringAsync()).Contains("items.txt is missing"), "No items.txt warning when the save has items.txt"); var junk = new HttpRequestMessage(HttpMethod.Post, "api/save/upload") { Content = new ByteArrayContent(System.Text.Encoding.UTF8.GetBytes("this is no archive")) }; junk.Headers.Add("X-MSCO-Panel", "1"); var junkRes = await owner.SendAsync(junk);