diff --git a/MultiplayerManager.cs b/MultiplayerManager.cs index 9e9c533..0a17ce8 100644 --- a/MultiplayerManager.cs +++ b/MultiplayerManager.cs @@ -545,6 +545,9 @@ namespace MSCO string lastServerHost, lastServerName; int lastServerPort, lastServerMax; string reconnectMessage; // gesetzt, wenn der Server uns neu verbinden lässt + float reconnectHoldUntil; // so lange die Meldung noch im Spiel zeigen + string centerMessage; // große Meldung in der Bildmitte + float centerMessageUntil; float reconnectAt; void ConnectToServer(string host, int port, string displayName, int maxPlayers) @@ -811,6 +814,7 @@ namespace MSCO if (session.IsClosed) { + if (Time.realtimeSinceStartup < reconnectHoldUntil) return; // Meldung erst lesen lassen OnSessionEnded(session.CloseReason); return; } @@ -1144,8 +1148,11 @@ namespace MSCO // Server hat die Welt zurückgesetzt o.ä.: Meldung zeigen, ins Menü, dann automatisch neu verbinden reconnectMessage = r.ReadString(); overlays.AddChat("", reconnectMessage, true); + // Groß in der Bildmitte zeigen und erst nach kurzer Zeit ins Menü wechseln - sonst sieht man nichts + centerMessage = reconnectMessage; + centerMessageUntil = Time.realtimeSinceStartup + 3f; + reconnectHoldUntil = Time.realtimeSinceStartup + 2.5f; session.Close("Reconnecting"); - OnSessionEnded(session.CloseReason); return; } @@ -1774,6 +1781,13 @@ namespace MSCO if (Time.realtimeSinceStartup < savingMessageUntil) GUI.Label(new Rect(0, Screen.height / 2f - 40, Screen.width, 80), "SAVING GAME...", savingStyle); + if (centerMessage != null && Time.realtimeSinceStartup < centerMessageUntil) + { + int oldSize = savingStyle.fontSize; + savingStyle.fontSize = 30; + GUI.Label(new Rect(0, Screen.height / 2f - 40, Screen.width, 80), centerMessage.ToUpper(), savingStyle); + savingStyle.fontSize = oldSize; + } if (session != null) DrawHud(); DrawPing(); diff --git a/ObjectSync.cs b/ObjectSync.cs index 41e405c..92f58bc 100644 --- a/ObjectSync.cs +++ b/ObjectSync.cs @@ -34,6 +34,7 @@ namespace MSCO // Wie oft kurz hintereinander weggeschleudert (steckt es in etwas fest?) public int RunawayCount; public float RunawayWindowStart; + public float LastRunawayLog = -100f; public Vector3 TargetPos; public Quaternion TargetRot; @@ -554,27 +555,43 @@ namespace MSCO if (rb.isKinematic || rb.velocity.sqrMagnitude < RunawaySpeed * RunawaySpeed) return; rb.velocity = Vector3.zero; rb.angularVelocity = Vector3.zero; + b.NoMotionClaimUntil = Time.time + HandoverCooldown; - // Mehrmals kurz hintereinander: das Objekt steckt in etwas fest (z.B. abgebautes Teil im Auto). - // Dann nicht wieder an dieselbe Stelle setzen, sondern nach oben heraus heben. + // Wird es kurz hintereinander wieder weggeschleudert, steckt es in etwas fest (z.B. loses Teil im Auto). + // NICHT an die alte Stelle zurücksetzen (dort steckt es ja) - sondern von oben auf die nächste freie Fläche. if (Time.time - b.RunawayWindowStart > 5f) { b.RunawayWindowStart = Time.time; b.RunawayCount = 0; } b.RunawayCount++; - bool stuck = b.RunawayCount >= 3; + bool stuck = b.RunawayCount >= 2; if (stuck) { - Bounds bounds = new Bounds(rb.worldCenterOfMass, Vector3.zero); - foreach (Collider c in rb.GetComponentsInChildren()) bounds.Encapsulate(c.bounds); - rb.transform.position += Vector3.up * (bounds.size.y + 1.5f); + PlaceOnTop(rb); b.RunawayCount = 0; } - else if (IsLocal(b) && b.LastSentTime > 0f && IsValidPose(b.LastSentPos, b.LastSentRot)) + rb.Sleep(); + + // Nicht das Log fluten: pro Objekt höchstens alle 30 s melden + if (Time.time - b.LastRunawayLog > 30f) { - rb.transform.position = b.LastSentPos; - rb.transform.rotation = b.LastSentRot; + b.LastRunawayLog = Time.time; + MscoLog.Warning("Stopped " + b.RootName + " - it was flung away by the physics" + (stuck ? " (was stuck, placed on top)" : "")); } - b.NoMotionClaimUntil = Time.time + HandoverCooldown; - if (b.RunawayCount <= 1 || stuck) - MscoLog.Warning("[MSCO] Stopped " + b.RootName + " - it was flung away by the physics" + (stuck ? " (stuck, lifted out)" : "")); + } + + /// Objekt von oben auf die höchste Fläche an seiner Stelle legen (eigene Collider und Spieler ignoriert). + static void PlaceOnTop(Rigidbody rb) + { + Bounds bounds = new Bounds(rb.worldCenterOfMass, Vector3.zero); + foreach (Collider c in rb.GetComponentsInChildren()) bounds.Encapsulate(c.bounds); + Vector3 from = new Vector3(bounds.center.x, bounds.max.y + 50f, bounds.center.z); + float top = float.MinValue; + foreach (RaycastHit hit in Physics.RaycastAll(from, Vector3.down, 120f)) + { + if (hit.collider.isTrigger || hit.transform.IsChildOf(rb.transform) || hit.transform.root.name == "PLAYER") continue; + if (hit.point.y > top) top = hit.point.y; + } + if (top == float.MinValue) return; + float lift = top + bounds.extents.y + 0.3f - bounds.center.y; + rb.transform.position += Vector3.up * lift; } ///