Doors on all vehicles: no more part-logic FSMs in DoorSync, single sender for hinges

- DoorSync matched every FSM with door/hood/lid/trunk in its name, including
  the door parts' removal/assembly/bolt/paint FSMs and trigger_ objects, and
  forced their states at the other players: doors hung crooked, snapped back
  or came off. Those are excluded now (assembly runs via FsmSync).
- HingeSync: on a parked vehicle everyone nearby sent the angle, so doors
  ping-ponged. Now only the player who clicked near the door (4 s) sends;
  on a driven vehicle the driver sends; received angles are held 3 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 17:44:03 +02:00
co-authored by Claude Opus 5.5
parent dd31587d61
commit f707e67e5c
4 changed files with 57 additions and 4 deletions
+6
View File
@@ -17,6 +17,12 @@
- Konsole (F7): `model classic` zeigt wieder die MSCO-Figur, `model game` die MSC-Menschen; `rig` zeigt, wer welches - Konsole (F7): `model classic` zeigt wieder die MSCO-Figur, `model game` die MSC-Menschen; `rig` zeigt, wer welches
Modell hat. Findet MSCO den Körper nicht, bleibt automatisch die MSCO-Figur Modell hat. Findet MSCO den Körper nicht, bleibt automatisch die MSCO-Figur
**Türen an allen Fahrzeugen**
- Türen hingen schief, sprangen zurück oder lösten sich: der Tür-Sync erfasste auch die FSMs zum An-/Abbauen,
Festschrauben und Lackieren der Tür-Teile und erzwang deren Zustände beim Mitspieler. Jetzt nur noch Öffnen/Schließen
- Türen schaukelten hin und her: an einem stehenden Auto schickte jeder in der Nähe den Türwinkel zurück. Jetzt schickt
nur, wer an der Tür geklickt hat (bzw. beim fahrenden Auto der Fahrer); empfangene Winkel werden länger gehalten
**Mitfahren** **Mitfahren**
- **Wer im Fahrzeug ist, fährt mit - genau an seiner Stelle**: Kopf in der Kabine, in der offenen Tür oder auf der - **Wer im Fahrzeug ist, fährt mit - genau an seiner Stelle**: Kopf in der Kabine, in der offenen Tür oder auf der
Ladefläche -> die Position im Fahrzeug wird festgehalten, kein Umsetzen auf einen berechneten Sitz. Geht auch, wenn Ladefläche -> die Position im Fahrzeug wird festgehalten, kein Umsetzen auf einen berechneten Sitz. Geht auch, wenn
+16
View File
@@ -16,6 +16,13 @@ namespace MSCO
public class DoorSync public class DoorSync
{ {
static readonly string[] Keywords = { "door", "gate", "hood", "bonnet", "trunk", "lid", "hatch" }; static readonly string[] Keywords = { "door", "gate", "hood", "bonnet", "trunk", "lid", "hatch" };
// Nicht die FSMs zum An-/Abbauen, Festschrauben, Lackieren, Speichern ... der Tür-TEILE: deren Zustände wurden
// beim Mitspieler erzwungen - Türen hingen schief, lösten sich oder sprangen zurück. Montage läuft über FsmSync.
static readonly string[] ExcludedFsms =
{
"removal", "assembly", "assemble", "boltcheck", "bolt", "screw", "paint", "data", "save", "load", "wiring",
"setup", "damage", "tightness", "detach", "attach", "install"
};
const float Cooldown = 1.5f; // Folge-Zustände der Animation nicht nochmal senden const float Cooldown = 1.5f; // Folge-Zustände der Animation nicht nochmal senden
const float RescanInterval = 10f; // neue Objekte (z.B. angebaute Autotüren) aufnehmen const float RescanInterval = 10f; // neue Objekte (z.B. angebaute Autotüren) aufnehmen
@@ -102,6 +109,7 @@ namespace MSCO
string root = fsm.transform.root.name; string root = fsm.transform.root.name;
if (root == "PLAYER" || root.StartsWith("MSCO_")) continue; if (root == "PLAYER" || root.StartsWith("MSCO_")) continue;
if (!Matches(fsm.gameObject.name) && !Matches(fsm.FsmName) && !Matches(fsm.transform.parent != null ? fsm.transform.parent.name : "")) continue; if (!Matches(fsm.gameObject.name) && !Matches(fsm.FsmName) && !Matches(fsm.transform.parent != null ? fsm.transform.parent.name : "")) continue;
if (IsPartLogic(fsm)) continue;
string key = FsmSync.PathOf(fsm.transform) + "|" + fsm.FsmName; string key = FsmSync.PathOf(fsm.transform) + "|" + fsm.FsmName;
Tracked t; Tracked t;
@@ -110,6 +118,14 @@ namespace MSCO
} }
} }
static bool IsPartLogic(PlayMakerFSM fsm)
{
string f = fsm.FsmName.ToLower(), o = fsm.gameObject.name.ToLower();
if (o.StartsWith("trigger_") || o.StartsWith("bolt")) return true;
foreach (string x in ExcludedFsms) if (f.Contains(x)) return true;
return false;
}
static bool Matches(string name) static bool Matches(string name)
{ {
if (string.IsNullOrEmpty(name)) return false; if (string.IsNullOrEmpty(name)) return false;
+32 -4
View File
@@ -19,7 +19,12 @@ namespace MSCO
const float SendInterval = 0.1f; const float SendInterval = 0.1f;
const float MinAngle = 2f; // Grad - kleiner ist Zittern const float MinAngle = 2f; // Grad - kleiner ist Zittern
const float NearRange = 4f; // so nah muss man an einer Tür stehen, um sie zu bewegen const float NearRange = 4f; // so nah muss man an einer Tür stehen, um sie zu bewegen
const float ApplyHold = 1.5f; // so lange nach einer Nachricht die Feder halten und selbst nicht senden const float ApplyHold = 3f; // so lange nach einer Nachricht die Feder halten und selbst nicht senden
// Steht das Fahrzeug, schickt nur, wer die Tür gerade selbst bewegt hat (Klick in der Nähe). Vorher schickte
// jeder in der Nähe: setzte sich die Tür beim anderen nach dem Übernehmen etwas anders, schickte er zurück -
// die Türen schaukelten hin und her.
const float InteractRange = 2.5f;
const float InteractAuthority = 4f; // so lange nach dem Klick (Tür schwingt aus)
const float SpringForce = 120f; const float SpringForce = 120f;
const float SpringDamper = 12f; const float SpringDamper = 12f;
@@ -34,6 +39,7 @@ namespace MSCO
public float TargetAngle; public float TargetAngle;
public float TargetTime = -100f; public float TargetTime = -100f;
public bool Driving; // Feder gerade von uns gesetzt public bool Driving; // Feder gerade von uns gesetzt
public float LocalAuthorityUntil = -100f;
public bool OrigUseSpring; public bool OrigUseSpring;
public JointSpring OrigSpring; public JointSpring OrigSpring;
} }
@@ -69,10 +75,13 @@ namespace MSCO
foreach (Hinge h in hinges.Values) foreach (Hinge h in hinges.Values)
{ {
if (h.Joint == null || h.Rb == null || !h.Rb.gameObject.activeInHierarchy) continue; if (h.Joint == null || h.Rb == null || !h.Rb.gameObject.activeInHierarchy) continue;
if (Time.time - h.TargetTime < ApplyHold) continue; // gerade von anderen gesetzt bool mine = Time.time < h.LocalAuthorityUntil;
if (!mine && Time.time - h.TargetTime < ApplyHold) continue; // gerade von anderen gesetzt
int owner = objects.VehicleOwner(h.Root); int owner = objects.VehicleOwner(h.Root);
if (owner != SyncedBody.NoOwner && owner != localId) continue; // der Fahrer schickt // Fährt jemand: der Fahrer schickt (Türen schwingen während der Fahrt). Hat man selbst gerade an der Tür
if (owner == SyncedBody.NoOwner && (h.Rb.position - playerPos).sqrMagnitude > NearRange * NearRange) continue; // geklickt, schickt man auch - sonst würde die Feder sie beim Fahrer gleich wieder zudrücken.
if (!mine && owner != SyncedBody.NoOwner && owner != localId) continue;
if (!mine && owner == SyncedBody.NoOwner) { h.SentAngle = SafeAngle(h, h.SentAngle); continue; } // stehendes Auto: nur wer klickt
float angle = h.Joint.angle; float angle = h.Joint.angle;
if (float.IsNaN(angle) || float.IsInfinity(angle)) continue; if (float.IsNaN(angle) || float.IsInfinity(angle)) continue;
@@ -90,6 +99,24 @@ namespace MSCO
})); }));
} }
/// <summary>Jedes Frame: Klick (linke Maustaste / Benutzen) in der Nähe einer Tür = wir bewegen sie gerade.</summary>
public void NoteInteraction(Vector3 playerPos)
{
if (!Input.GetMouseButtonDown(0) && !Input.GetKeyDown(KeyCode.F)) return;
foreach (Hinge h in hinges.Values)
{
if (h.Rb == null || (h.Rb.worldCenterOfMass - playerPos).sqrMagnitude > InteractRange * InteractRange) continue;
h.LocalAuthorityUntil = Time.time + InteractAuthority;
h.TargetTime = -100f; // eigene Bewegung: keine Feder mehr auf den alten Winkel
}
}
static float SafeAngle(Hinge h, float fallback)
{
float a = h.Joint.angle;
return float.IsNaN(a) || float.IsInfinity(a) ? fallback : a;
}
public void Handle(BinaryReader r) public void Handle(BinaryReader r)
{ {
int count = r.ReadInt32(); int count = r.ReadInt32();
@@ -100,6 +127,7 @@ namespace MSCO
Hinge h; Hinge h;
if (!hinges.TryGetValue(key, out h)) { nextScan = 0f; continue; } if (!hinges.TryGetValue(key, out h)) { nextScan = 0f; continue; }
if (float.IsNaN(angle) || float.IsInfinity(angle) || Mathf.Abs(angle) > 180f) continue; if (float.IsNaN(angle) || float.IsInfinity(angle) || Mathf.Abs(angle) > 180f) continue;
if (Time.time < h.LocalAuthorityUntil) continue; // wir bewegen sie gerade selbst
h.TargetAngle = angle; h.TargetAngle = angle;
h.TargetTime = Time.time; h.TargetTime = Time.time;
h.SentAngle = angle; // nicht als eigene Bewegung zurückschicken h.SentAngle = angle; // nicht als eigene Bewegung zurückschicken
+3
View File
@@ -1883,6 +1883,9 @@ namespace MSCO
} }
else if (IsPeerReady && MenuHelper.InGame && localPlayer != null && !overlays.AnyOpen) else if (IsPeerReady && MenuHelper.InGame && localPlayer != null && !overlays.AnyOpen)
passengerResult = passenger.AutoUpdate(localPlayer, playerCamera, objects, players); // in die Beifahrertür treten passengerResult = passenger.AutoUpdate(localPlayer, playerCamera, objects, players); // in die Beifahrertür treten
// Klick an einer Fahrzeugtür: wir bewegen sie (nur dann wird ihr Winkel geschickt, siehe HingeSync)
if (MenuHelper.InGame && localPlayer != null && !overlays.AnyOpen)
hinges.NoteInteraction(playerCamera != null ? playerCamera.transform.position : localPlayer.position);
if (passengerResult != null) if (passengerResult != null)
{ {
SetStatus(passengerResult); SetStatus(passengerResult);