Fewer hitches: FSM scene scans less often, damage safety check one mesh at a time; hitch detector (per-subsystem timing, reports MSCO part or game) in the F7 console
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -183,6 +183,7 @@ namespace MSCO
|
||||
public void Host(int port)
|
||||
{
|
||||
Disconnect();
|
||||
PartStart();
|
||||
try
|
||||
{
|
||||
session = NetSession.StartHost(port);
|
||||
@@ -944,6 +945,7 @@ namespace MSCO
|
||||
return;
|
||||
}
|
||||
savingMessageUntil = Time.realtimeSinceStartup + 2.5f; // "SAVING GAME..." in der Bildschirmmitte
|
||||
lastSaveTriggered = Time.realtimeSinceStartup;
|
||||
PlayMakerFSM.BroadcastEvent("SAVEGAME");
|
||||
MscoLog.Info("[MSCO] Game saved.");
|
||||
}
|
||||
@@ -952,6 +954,7 @@ namespace MSCO
|
||||
|
||||
void Update()
|
||||
{
|
||||
CheckHitch();
|
||||
MeasureFps();
|
||||
// Menü-Buttons des Spiels nicht durch unser Fenster hindurch auslösen (z.B. Changelog)
|
||||
// Solange das Multiplayer-Fenster offen ist, reagiert das Spielmenü (CONTINUE, QUIT ...) gar nicht;
|
||||
@@ -1866,62 +1869,89 @@ namespace MSCO
|
||||
if (sendTimer >= SendInterval)
|
||||
{
|
||||
sendTimer = 0f;
|
||||
PartStart();
|
||||
try { SendGame(MsgType.Player, BuildLocalState().Serialize()); }
|
||||
catch (System.Exception e) { ReportError("player state", e); }
|
||||
PartEnd("player state");
|
||||
}
|
||||
// Eigene Bedürfnisse (Hunger, Durst ...) pro Spieler auf dem Server sichern
|
||||
if (dedicated && worldSynced && Time.realtimeSinceStartup >= nextNeedsSend)
|
||||
{
|
||||
nextNeedsSend = Time.realtimeSinceStartup + NeedsInterval;
|
||||
PartStart();
|
||||
try
|
||||
{
|
||||
byte[] needs = PlayerNeeds.Build();
|
||||
if (needs != null) session.SendTo(NetSession.HostId, MsgType.PlayerNeeds, needs);
|
||||
}
|
||||
catch (System.Exception e) { ReportError("player needs", e); }
|
||||
PartEnd("player needs");
|
||||
}
|
||||
PartStart();
|
||||
try { objects.Update(localPlayer, Time.deltaTime); }
|
||||
catch (System.Exception e) { ReportError("object sync", e); }
|
||||
PartStart();
|
||||
PartEnd("object sync");
|
||||
try { vehicleFx.Update(objects, SendGame, Time.deltaTime, IsWorldAuthority); }
|
||||
catch (System.Exception e) { ReportError("vehicle lights/sound", e); }
|
||||
PartStart();
|
||||
PartEnd("vehicle lights/sound");
|
||||
try { worldSync.Update(IsWorldAuthority, SendGame, Time.unscaledDeltaTime); }
|
||||
catch (System.Exception e) { ReportError("time/weather sync", e); }
|
||||
PartEnd("time/weather sync");
|
||||
|
||||
// Geld erst beobachten, wenn das Spiel fertig geladen ist
|
||||
PartStart();
|
||||
try
|
||||
{
|
||||
if (objects.ScanComplete) moneySync.Begin();
|
||||
moneySync.Update(SendGame);
|
||||
}
|
||||
catch (System.Exception e) { ReportError("money sync", e); }
|
||||
PartEnd("money sync");
|
||||
|
||||
// Türen, Tore, Hauben (z.B. Garagentor)
|
||||
PartStart();
|
||||
try { if (objects.ScanComplete) doorSync.Update(SendGame); }
|
||||
catch (System.Exception e) { ReportError("door sync", e); }
|
||||
PartEnd("door sync");
|
||||
|
||||
// Gesamtzustand der Fahrzeuge (Tank, Öl, Verschleiß ...)
|
||||
PartStart();
|
||||
try { if (objects.ScanComplete) vehicleState.Update(objects, localId, SendGame); }
|
||||
catch (System.Exception e) { ReportError("vehicle state sync", e); }
|
||||
PartEnd("vehicle state sync");
|
||||
// Unfallschäden (verbeulte Karosserie)
|
||||
PartStart();
|
||||
try { if (objects.ScanComplete) damageSync.Update(objects, localId, SendGame); }
|
||||
catch (System.Exception e) { ReportError("crash damage sync", e); }
|
||||
PartEnd("crash damage sync");
|
||||
// Zündung, Motor, Licht, Choke ... der Fahrzeuge
|
||||
PartStart();
|
||||
try { if (objects.ScanComplete && localPlayer != null) controls.Update(objects, localId, localPlayer.position, SendGame); }
|
||||
catch (System.Exception e) { ReportError("vehicle controls sync", e); }
|
||||
PartEnd("vehicle controls sync");
|
||||
// Türen, Motorhaube, Kofferraum der Fahrzeuge
|
||||
PartStart();
|
||||
try { if (objects.ScanComplete && localPlayer != null) hinges.Update(objects, localId, localPlayer.position, Time.deltaTime, SendGame); }
|
||||
catch (System.Exception e) { ReportError("car door sync", e); }
|
||||
PartEnd("car door sync");
|
||||
// Jobs, Aufträge, Laden
|
||||
PartStart();
|
||||
try { if (objects.ScanComplete) worldVars.Update(SendGame); }
|
||||
catch (System.Exception e) { ReportError("jobs/shop sync", e); }
|
||||
PartEnd("jobs/shop sync");
|
||||
// Verkehr: die Welt-Autorität simuliert, alle anderen fahren nach
|
||||
PartStart();
|
||||
try { if (objects.ScanComplete) traffic.Update(IsWorldAuthority, Time.deltaTime, SendGame); }
|
||||
catch (System.Exception e) { ReportError("traffic sync", e); }
|
||||
PartEnd("traffic sync");
|
||||
}
|
||||
|
||||
foreach (PlayerInfo p in players.Values)
|
||||
{
|
||||
if (p.Avatar == null) continue;
|
||||
PartStart();
|
||||
try
|
||||
{
|
||||
// Sitzt der Mitspieler in einem Fahrzeug, die Figur an unsere Kopie dieses Fahrzeugs hängen
|
||||
@@ -1937,6 +1967,7 @@ namespace MSCO
|
||||
objects.ApplyAvatarHold(p.Id, p.Avatar.HeldId, pos, rot);
|
||||
}
|
||||
catch (System.Exception e) { ReportError("player figure", e); }
|
||||
PartEnd("player figure");
|
||||
}
|
||||
|
||||
// Abgefangene Schraub-/Montage-Aktionen verschicken
|
||||
@@ -1947,12 +1978,52 @@ namespace MSCO
|
||||
foreach (FsmEventMsg msg in FsmSync.Outbox) SendGame(MsgType.FsmEvent, msg.Serialize());
|
||||
}
|
||||
catch (System.Exception e) { ReportError("part/bolt sync", e); }
|
||||
PartEnd("part/bolt sync");
|
||||
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>
|
||||
// ---------------------------------------------------------------- Hänger erkennen
|
||||
// Jeder Sync-Teil wird gemessen. Dauert ein Bild sehr lange, steht in der F7-Konsole, ob MSCO schuld war
|
||||
// (und welcher Teil) oder das Spiel selbst (z.B. beim Speichern).
|
||||
readonly System.Diagnostics.Stopwatch partWatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
long partStart;
|
||||
double frameMsco, slowestMs;
|
||||
string slowestPart;
|
||||
float nextHitchReport, lastSaveTriggered = -100f;
|
||||
const float HitchSeconds = 0.2f;
|
||||
|
||||
void PartStart() { partStart = partWatch.ElapsedTicks; }
|
||||
|
||||
void PartEnd(string name)
|
||||
{
|
||||
double ms = (partWatch.ElapsedTicks - partStart) * 1000.0 / System.Diagnostics.Stopwatch.Frequency;
|
||||
frameMsco += ms;
|
||||
if (ms > slowestMs) { slowestMs = ms; slowestPart = name; }
|
||||
}
|
||||
|
||||
/// <summary>Zu Beginn jedes Bilds: war das letzte Bild ein Hänger? Dann Ursache melden (höchstens alle 20 s).</summary>
|
||||
void CheckHitch()
|
||||
{
|
||||
float dt = Time.unscaledDeltaTime;
|
||||
if (dt > HitchSeconds && MenuHelper.InGame && Time.realtimeSinceStartup >= nextHitchReport)
|
||||
{
|
||||
nextHitchReport = Time.realtimeSinceStartup + 20f;
|
||||
int total = Mathf.RoundToInt(dt * 1000f);
|
||||
if (frameMsco > dt * 1000f * 0.4f)
|
||||
MscoLog.Warning("[MSCO] Hitch " + total + " ms - caused by MSCO: " + slowestPart + " " + slowestMs.ToString("0") +
|
||||
" ms (MSCO total " + frameMsco.ToString("0") + " ms)");
|
||||
else
|
||||
MscoLog.Info("[MSCO] Hitch " + total + " ms - not MSCO (MSCO used " + frameMsco.ToString("0") + " ms)" +
|
||||
(Time.realtimeSinceStartup - lastSaveTriggered < 5f ? " - the game was saving" : " - the game itself (loading, saving, physics)"));
|
||||
}
|
||||
frameMsco = 0;
|
||||
slowestMs = 0;
|
||||
slowestPart = null;
|
||||
}
|
||||
|
||||
void ReportError(string what, System.Exception e)
|
||||
{
|
||||
float now = Time.realtimeSinceStartup, last;
|
||||
@@ -1965,25 +2036,37 @@ namespace MSCO
|
||||
void FixedUpdate()
|
||||
{
|
||||
if (!IsPeerReady || !MenuHelper.InGame) return;
|
||||
PartStart();
|
||||
try { objects.FixedUpdate(); }
|
||||
catch (System.Exception e) { ReportError("object movement", e); }
|
||||
PartEnd("object movement");
|
||||
PartStart();
|
||||
try { traffic.FixedUpdate(); }
|
||||
catch (System.Exception e) { ReportError("traffic movement", e); }
|
||||
PartEnd("traffic movement");
|
||||
PartStart();
|
||||
try { hinges.FixedUpdate(); }
|
||||
catch (System.Exception e) { ReportError("car door movement", e); }
|
||||
PartEnd("car door movement");
|
||||
}
|
||||
|
||||
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
|
||||
PartStart();
|
||||
try { if (IsPeerReady && MenuHelper.InGame) vehicleFx.Apply(objects); }
|
||||
catch (System.Exception e) { ReportError("vehicle lights/sound", e); }
|
||||
PartEnd("vehicle lights/sound");
|
||||
PartStart();
|
||||
try { if (IsPeerReady && MenuHelper.InGame) wheelSpin.Apply(objects, Time.deltaTime); }
|
||||
catch (System.Exception e) { ReportError("wheels", e); }
|
||||
PartEnd("wheels");
|
||||
// Beifahrer nach der Fahrzeugbewegung auf den Sitz setzen
|
||||
PartStart();
|
||||
try { if (localPlayer != null && MenuHelper.InGame) passenger.LateUpdate(localPlayer, playerCamera, objects); }
|
||||
catch (System.Exception e) { ReportError("passenger", e); }
|
||||
PartEnd("passenger");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2122,6 +2205,7 @@ namespace MSCO
|
||||
System.Collections.IEnumerator UploadSave(int requestId)
|
||||
{
|
||||
if (!MenuHelper.InGame || !dedicated || session == null) yield break;
|
||||
lastSaveTriggered = Time.realtimeSinceStartup;
|
||||
PlayMakerFSM.BroadcastEvent("SAVEGAME");
|
||||
float until = Time.realtimeSinceStartup + 3f; // das Spiel schreibt die Dateien über mehrere Frames
|
||||
while (Time.realtimeSinceStartup < until) yield return null;
|
||||
|
||||
Reference in New Issue
Block a user