diff --git a/InputLock.cs b/InputLock.cs index 62b458c..f29999d 100644 --- a/InputLock.cs +++ b/InputLock.cs @@ -74,11 +74,15 @@ namespace MSCO Cursor.lockState = CursorLockMode.None; } - /// PlayMaker-Aktionen, die den Mauszeiger setzen, überspringen, solange ein Fenster offen ist. + /// + /// PlayMaker-Aktionen, die den Mauszeiger setzen, überspringen, solange ein Fenster offen ist. Außerdem die + /// Tastenabfragen des Spiels (cInput) sperren: sonst löst Tippen im Chat Spielaktionen aus (Benutzen, Hupe ...). + /// static void PatchCursorActions() { if (patched) return; patched = true; + PatchGameInput(); try { Type action = null; @@ -104,5 +108,45 @@ namespace MSCO } static bool SkipWhileLocked() { return !locked; } + + static void PatchGameInput() + { + try + { + Type input = typeof(cInput); + HarmonyInstance harmony = HarmonyInstance.Create("mbm.msco.inputlock.keys"); + var boolPrefix = new HarmonyMethod(typeof(InputLock).GetMethod("NoKeyWhileLocked", BindingFlags.Static | BindingFlags.NonPublic)); + var floatPrefix = new HarmonyMethod(typeof(InputLock).GetMethod("NoAxisWhileLocked", BindingFlags.Static | BindingFlags.NonPublic)); + int count = 0; + foreach (MethodInfo m in input.GetMethods(BindingFlags.Static | BindingFlags.Public)) + { + bool keyQuery = m.Name == "GetKey" || m.Name == "GetKeyDown" || m.Name == "GetKeyUp" || + m.Name == "GetButton" || m.Name == "GetButtonDown" || m.Name == "GetButtonUp"; + bool axisQuery = m.Name == "GetAxis" || m.Name == "GetAxisRaw"; + if (keyQuery && m.ReturnType == typeof(bool)) { harmony.Patch(m, boolPrefix, null); count++; } + else if (axisQuery && m.ReturnType == typeof(float)) { harmony.Patch(m, floatPrefix, null); count++; } + } + ModConsole.Print("[MSCO] Game input is paused while typing (" + count + " cInput queries)"); + } + catch (Exception e) + { + ModConsole.Warning("[MSCO] Could not pause game input while typing: " + e.Message); + } + } + + // Harmony-Prefixe: solange ein Eingabefenster offen ist, meldet cInput "nichts gedrückt" + static bool NoKeyWhileLocked(ref bool __result) + { + if (!locked) return true; + __result = false; + return false; + } + + static bool NoAxisWhileLocked(ref float __result) + { + if (!locked) return true; + __result = 0f; + return false; + } } } diff --git a/MultiplayerManager.cs b/MultiplayerManager.cs index 57a5506..109f8db 100644 --- a/MultiplayerManager.cs +++ b/MultiplayerManager.cs @@ -1233,7 +1233,7 @@ namespace MSCO StartCoroutine(ApplyWorldSnapshot(snapshot)); } - if (MSCO.PassengerKey != null && MSCO.PassengerKey.GetKeybindDown()) + if (MSCO.PassengerKey != null && !overlays.AnyOpen && MSCO.PassengerKey.GetKeybindDown()) { string result = passenger.Toggle(localPlayer, playerCamera, objects, players); if (result != null) @@ -1246,58 +1246,94 @@ namespace MSCO bool ready = IsPeerReady; if (ready) { + // Jedes Teilsystem einzeln abgesichert: ein Fehler in einem (z.B. an einem gerade zerstörten Objekt) + // soll nicht den ganzen Sync stoppen sendTimer += Time.unscaledDeltaTime; // läuft auch im Pausenmenü (timeScale = 0) weiter if (sendTimer >= SendInterval) { sendTimer = 0f; - SendGame(MsgType.Player, BuildLocalState().Serialize()); + try { SendGame(MsgType.Player, BuildLocalState().Serialize()); } + catch (System.Exception e) { ReportError("player state", e); } } - objects.Update(localPlayer, Time.deltaTime); - vehicleFx.Update(objects, SendGame, Time.deltaTime); - worldSync.Update(IsWorldAuthority, SendGame, Time.unscaledDeltaTime); + try { objects.Update(localPlayer, Time.deltaTime); } + catch (System.Exception e) { ReportError("object sync", e); } + try { vehicleFx.Update(objects, SendGame, Time.deltaTime); } + 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); } // Geld erst beobachten, wenn das Spiel fertig geladen ist - if (objects.ScanComplete) moneySync.Begin(); - moneySync.Update(SendGame); + try + { + if (objects.ScanComplete) moneySync.Begin(); + moneySync.Update(SendGame); + } + catch (System.Exception e) { ReportError("money sync", e); } // Türen, Tore, Hauben (z.B. Garagentor) - if (objects.ScanComplete) doorSync.Update(SendGame); + try { if (objects.ScanComplete) doorSync.Update(SendGame); } + catch (System.Exception e) { ReportError("door sync", e); } - // Gesamtzustand der Fahrzeuge (Tank, Öl, Schrauben, Verschleiß ...) - if (objects.ScanComplete) vehicleState.Update(objects, localId, SendGame); + // Gesamtzustand der Fahrzeuge (Tank, Öl, Verschleiß ...) + try { if (objects.ScanComplete) vehicleState.Update(objects, localId, SendGame); } + catch (System.Exception e) { ReportError("vehicle state sync", e); } } foreach (PlayerInfo p in players.Values) { if (p.Avatar == null) continue; - p.Avatar.Tick(Time.unscaledDeltaTime); + try + { + p.Avatar.Tick(Time.unscaledDeltaTime); - // Gehaltenes Item an die Hand der Figur setzen - Vector3 pos; - Quaternion rot; - if (p.Avatar.TryGetHeldPose(out pos, out rot)) - objects.ApplyAvatarHold(p.Id, p.Avatar.HeldId, pos, rot); + // Gehaltenes Item an die Hand der Figur setzen + Vector3 pos; + Quaternion rot; + if (p.Avatar.TryGetHeldPose(out pos, out rot)) + objects.ApplyAvatarHold(p.Id, p.Avatar.HeldId, pos, rot); + } + catch (System.Exception e) { ReportError("player figure", e); } } // Abgefangene Schraub-/Montage-Aktionen verschicken - FsmSync.Enabled = ready; - FsmSync.Tick(); - foreach (FsmEventMsg msg in FsmSync.Outbox) SendGame(MsgType.FsmEvent, msg.Serialize()); + try + { + FsmSync.Enabled = ready; + FsmSync.Tick(); + foreach (FsmEventMsg msg in FsmSync.Outbox) SendGame(MsgType.FsmEvent, msg.Serialize()); + } + catch (System.Exception e) { ReportError("part/bolt sync", e); } FsmSync.Outbox.Clear(); } + readonly Dictionary lastErrorReport = new Dictionary(); + + /// Fehler eines Teilsystems melden (höchstens alle 30 s pro Teilsystem, damit das Log nicht vollläuft). + void ReportError(string what, System.Exception e) + { + float now = Time.realtimeSinceStartup, last; + if (lastErrorReport.TryGetValue(what, out last) && now - last < 30f) return; + lastErrorReport[what] = now; + ModConsole.Error("[MSCO] " + what + " failed: " + e); + overlays.Log("Error in " + what + ": " + e.Message); + } + void FixedUpdate() { - if (IsPeerReady && MenuHelper.InGame) objects.FixedUpdate(); + if (!IsPeerReady || !MenuHelper.InGame) return; + try { objects.FixedUpdate(); } + catch (System.Exception e) { ReportError("object movement", e); } } void LateUpdate() { InputLock.KeepCursorFree(); // das Spiel sperrt den Zeiger im selben Frame evtl. wieder // Nach den Spiel-FSMs, damit Lichter/Motorgeräusch der Fahrzeuge anderer gewinnen - if (IsPeerReady && MenuHelper.InGame) vehicleFx.Apply(objects); + try { if (IsPeerReady && MenuHelper.InGame) vehicleFx.Apply(objects); } + catch (System.Exception e) { ReportError("vehicle lights/sound", e); } // Beifahrer nach der Fahrzeugbewegung auf den Sitz setzen - if (localPlayer != null && MenuHelper.InGame) passenger.LateUpdate(localPlayer, playerCamera, objects); + try { if (localPlayer != null && MenuHelper.InGame) passenger.LateUpdate(localPlayer, playerCamera, objects); } + catch (System.Exception e) { ReportError("passenger", e); } } ///