Pause game input while typing (chat/console/admin); isolate sync subsystems so one error doesn't stop everything
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+45
-1
@@ -74,11 +74,15 @@ namespace MSCO
|
|||||||
Cursor.lockState = CursorLockMode.None;
|
Cursor.lockState = CursorLockMode.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>PlayMaker-Aktionen, die den Mauszeiger setzen, überspringen, solange ein Fenster offen ist.</summary>
|
/// <summary>
|
||||||
|
/// 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 ...).
|
||||||
|
/// </summary>
|
||||||
static void PatchCursorActions()
|
static void PatchCursorActions()
|
||||||
{
|
{
|
||||||
if (patched) return;
|
if (patched) return;
|
||||||
patched = true;
|
patched = true;
|
||||||
|
PatchGameInput();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Type action = null;
|
Type action = null;
|
||||||
@@ -104,5 +108,45 @@ namespace MSCO
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool SkipWhileLocked() { return !locked; }
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-22
@@ -1233,7 +1233,7 @@ namespace MSCO
|
|||||||
StartCoroutine(ApplyWorldSnapshot(snapshot));
|
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);
|
string result = passenger.Toggle(localPlayer, playerCamera, objects, players);
|
||||||
if (result != null)
|
if (result != null)
|
||||||
@@ -1246,58 +1246,94 @@ namespace MSCO
|
|||||||
bool ready = IsPeerReady;
|
bool ready = IsPeerReady;
|
||||||
if (ready)
|
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
|
sendTimer += Time.unscaledDeltaTime; // läuft auch im Pausenmenü (timeScale = 0) weiter
|
||||||
if (sendTimer >= SendInterval)
|
if (sendTimer >= SendInterval)
|
||||||
{
|
{
|
||||||
sendTimer = 0f;
|
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);
|
try { objects.Update(localPlayer, Time.deltaTime); }
|
||||||
vehicleFx.Update(objects, SendGame, Time.deltaTime);
|
catch (System.Exception e) { ReportError("object sync", e); }
|
||||||
worldSync.Update(IsWorldAuthority, SendGame, Time.unscaledDeltaTime);
|
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
|
// Geld erst beobachten, wenn das Spiel fertig geladen ist
|
||||||
if (objects.ScanComplete) moneySync.Begin();
|
try
|
||||||
moneySync.Update(SendGame);
|
{
|
||||||
|
if (objects.ScanComplete) moneySync.Begin();
|
||||||
|
moneySync.Update(SendGame);
|
||||||
|
}
|
||||||
|
catch (System.Exception e) { ReportError("money sync", e); }
|
||||||
|
|
||||||
// Türen, Tore, Hauben (z.B. Garagentor)
|
// 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ß ...)
|
// Gesamtzustand der Fahrzeuge (Tank, Öl, Verschleiß ...)
|
||||||
if (objects.ScanComplete) vehicleState.Update(objects, localId, SendGame);
|
try { if (objects.ScanComplete) vehicleState.Update(objects, localId, SendGame); }
|
||||||
|
catch (System.Exception e) { ReportError("vehicle state sync", e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (PlayerInfo p in players.Values)
|
foreach (PlayerInfo p in players.Values)
|
||||||
{
|
{
|
||||||
if (p.Avatar == null) continue;
|
if (p.Avatar == null) continue;
|
||||||
p.Avatar.Tick(Time.unscaledDeltaTime);
|
try
|
||||||
|
{
|
||||||
|
p.Avatar.Tick(Time.unscaledDeltaTime);
|
||||||
|
|
||||||
// Gehaltenes Item an die Hand der Figur setzen
|
// Gehaltenes Item an die Hand der Figur setzen
|
||||||
Vector3 pos;
|
Vector3 pos;
|
||||||
Quaternion rot;
|
Quaternion rot;
|
||||||
if (p.Avatar.TryGetHeldPose(out pos, out rot))
|
if (p.Avatar.TryGetHeldPose(out pos, out rot))
|
||||||
objects.ApplyAvatarHold(p.Id, p.Avatar.HeldId, pos, rot);
|
objects.ApplyAvatarHold(p.Id, p.Avatar.HeldId, pos, rot);
|
||||||
|
}
|
||||||
|
catch (System.Exception e) { ReportError("player figure", e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abgefangene Schraub-/Montage-Aktionen verschicken
|
// Abgefangene Schraub-/Montage-Aktionen verschicken
|
||||||
FsmSync.Enabled = ready;
|
try
|
||||||
FsmSync.Tick();
|
{
|
||||||
foreach (FsmEventMsg msg in FsmSync.Outbox) SendGame(MsgType.FsmEvent, msg.Serialize());
|
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();
|
FsmSync.Outbox.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly Dictionary<string, float> lastErrorReport = new Dictionary<string, float>();
|
||||||
|
|
||||||
|
/// <summary>Fehler eines Teilsystems melden (höchstens alle 30 s pro Teilsystem, damit das Log nicht vollläuft).</summary>
|
||||||
|
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()
|
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()
|
void LateUpdate()
|
||||||
{
|
{
|
||||||
InputLock.KeepCursorFree(); // das Spiel sperrt den Zeiger im selben Frame evtl. wieder
|
InputLock.KeepCursorFree(); // das Spiel sperrt den Zeiger im selben Frame evtl. wieder
|
||||||
// Nach den Spiel-FSMs, damit Lichter/Motorgeräusch der Fahrzeuge anderer gewinnen
|
// 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
|
// 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); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user