Server gets the current save game from a player in the game (on join and every few minutes) instead of replaying actions; vehicle fire/smoke synced, also for parked cars

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 11:15:18 +02:00
co-authored by Claude Opus 5.5
parent bdaedcc1e8
commit b3405d3752
5 changed files with 110 additions and 5 deletions
+46 -1
View File
@@ -1134,6 +1134,11 @@ namespace MSCO
break;
}
case MsgType.SaveRequest:
// Server möchte den aktuellen Spielstand (für neue Spieler / zum Sichern)
StartCoroutine(UploadSave(r.ReadInt32()));
break;
case MsgType.Reconnect:
{
// Server hat die Welt zurückgesetzt o.ä.: Meldung zeigen, ins Menü, dann automatisch neu verbinden
@@ -1445,7 +1450,7 @@ namespace MSCO
}
try { objects.Update(localPlayer, Time.deltaTime); }
catch (System.Exception e) { ReportError("object sync", e); }
try { vehicleFx.Update(objects, SendGame, Time.deltaTime); }
try { vehicleFx.Update(objects, SendGame, Time.deltaTime, IsWorldAuthority); }
catch (System.Exception e) { ReportError("vehicle lights/sound", e); }
try { worldSync.Update(IsWorldAuthority, SendGame, Time.unscaledDeltaTime); }
catch (System.Exception e) { ReportError("time/weather sync", e); }
@@ -1627,6 +1632,46 @@ namespace MSCO
public Quaternion Rotation;
}
/// <summary>
/// Im Spiel speichern (wie auf der Toilette) und den Spielstand an den Server schicken. So bekommen neue Spieler
/// genau die aktuelle Welt - Schrauben, Teile, alles - statt eines alten Stands mit nachgespielten Aktionen.
/// Der eigene Spielstand ist davon nicht betroffen: er liegt gesichert im Backup und kommt im Hauptmenü zurück.
/// </summary>
System.Collections.IEnumerator UploadSave(int requestId)
{
if (!MenuHelper.InGame || !dedicated || session == null) yield break;
PlayMakerFSM.BroadcastEvent("SAVEGAME");
float until = Time.realtimeSinceStartup + 3f; // das Spiel schreibt die Dateien über mehrere Frames
while (Time.realtimeSinceStartup < until) yield return null;
if (session == null) yield break;
List<KeyValuePair<string, byte[]>> files;
try { files = SaveSync.ReadLocalSave(); }
catch (System.Exception e) { MscoLog.Warning("Could not read the save game for the server: " + e.Message); yield break; }
const int Chunk = 256 * 1024; // unter der Paketgrenze des Servers
foreach (KeyValuePair<string, byte[]> f in files)
{
for (int offset = 0; offset < f.Value.Length || offset == 0; offset += Chunk)
{
if (session == null) yield break;
int start = offset, length = Mathf.Min(Chunk, f.Value.Length - offset);
string name = f.Key;
byte[] data = f.Value;
session.SendTo(NetSession.HostId, MsgType.SaveUploadPart, NetSession.Build(w =>
{
w.Write(requestId); w.Write(name); w.Write(data.Length); w.Write(start); w.Write(length);
w.Write(data, start, length);
}));
yield return null;
if (data.Length == 0) break;
}
}
int count = files.Count;
session.SendTo(NetSession.HostId, MsgType.SaveUploadDone, NetSession.Build(w => { w.Write(requestId); w.Write(count); }));
MscoLog.Info("Sent the current save game to the server (" + count + " files)");
}
/// <summary>Sobald die erste Position des Hosts da ist: seitlich neben ihn stellen.</summary>
void TryTeleportToHost()
{