Join: replay part actions first, then rescan and place objects (detached parts no longer stuck in the car); rescan right after remote disassembly; lift out bodies that keep getting flung

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 08:52:53 +02:00
co-authored by Claude Opus 5.5
parent 8346cd9b09
commit 647acda46c
2 changed files with 58 additions and 7 deletions
+30 -3
View File
@@ -41,6 +41,7 @@ namespace MSCO
bool dedicated; // verbunden mit einem dedizierten Server
bool connectingToServer; // Verbindungsaufbau zu einem dedizierten Server läuft
bool readySent; // Server: Weltzustand schon angefordert
bool rescanSoon; // nach einer Abbau-Aktion neue lose Teile sofort erfassen
byte[] pendingSnapshot; // Server: Weltzustand, wartet auf Objekt-Suchlauf
const int SnapshotEventsPerFrame = 20;
readonly ServerBrowser browser = new ServerBrowser();
@@ -1041,8 +1042,14 @@ namespace MSCO
objects.Handle(from, type, r);
break;
case MsgType.FsmEvent:
FsmSync.Apply(FsmEventMsg.Deserialize(r));
{
FsmEventMsg msg = FsmEventMsg.Deserialize(r);
FsmSync.Apply(msg);
// Ein Teil wurde abgebaut: gleich als loses Objekt erfassen, damit es der Hand des Mitspielers
// folgt, statt Sekunden lang im Auto zu stecken (die Physik würde es samt Auto wegschleudern)
if (msg.EventName == "DISASSEMBLE" || msg.EventName == "REMOVE" || msg.EventName == "UNINSTALL") rescanSoon = true;
break;
}
case MsgType.WorldTime:
worldSync.Apply(r);
break;
@@ -1218,6 +1225,11 @@ namespace MSCO
}
if (teleportToHost) TryTeleportToHost();
if (rescanSoon && objects.ScanComplete)
{
rescanSoon = false;
objects.ScanNow();
}
// Dedizierter Server: nach dem ersten Objekt-Suchlauf den Weltzustand anfordern und anwenden
if (dedicated && handshakeDone && !readySent && objects.ScanComplete)
@@ -1365,14 +1377,16 @@ namespace MSCO
if (localPlayer != null) localPlayer.position = spawn + Vector3.up * 0.2f;
}
// Positionen erst merken: sie werden NACH den Montage-Aktionen gesetzt. Sonst steckt ein Teil, das eine
// nachgespielte Aktion vom Auto löst, noch im Auto - die Physik schleudert es dann samt Auto weg.
int objectCount = r.ReadInt32();
int applied = 0;
var poses = new List<KeyValuePair<int, Pose>>(objectCount);
for (int i = 0; i < objectCount; i++)
{
int id = r.ReadInt32();
var pos = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
var rot = new Quaternion(r.ReadSingle(), r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
if (objects.ApplySnapshotPose(id, pos, rot)) applied++;
poses.Add(new KeyValuePair<int, Pose>(id, new Pose { Position = pos, Rotation = rot }));
}
int eventCount = r.ReadInt32();
@@ -1383,6 +1397,13 @@ namespace MSCO
if ((i + 1) % SnapshotEventsPerFrame == 0) yield return null; // nicht alles in einem Frame
}
// Abgebaute Teile sind jetzt eigene Objekte: erfassen, dann alle Positionen setzen
yield return null;
objects.ScanNow();
int applied = 0;
foreach (KeyValuePair<int, Pose> p in poses)
if (objects.ApplySnapshotPose(p.Key, p.Value.Position, p.Value.Rotation)) applied++;
// Geld: Summe aller Änderungen seit dem Spielstand des Servers
if (r.BaseStream.Length - r.BaseStream.Position >= 4) moneySync.Apply(r.ReadSingle());
@@ -1400,6 +1421,12 @@ namespace MSCO
SetStatus("Connected");
}
struct Pose
{
public Vector3 Position;
public Quaternion Rotation;
}
/// <summary>Sobald die erste Position des Hosts da ist: seitlich neben ihn stellen.</summary>
void TryTeleportToHost()
{