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); }
}
///