From b449f1c5a3a49087c1baa95eb0a847db2abeef37 Mon Sep 17 00:00:00 2001 From: dan51588 Date: Wed, 28 Oct 2026 23:05:54 -0700 Subject: [PATCH] fix: read SClient+0x1198 as the one-byte bool it is The loading/transition flag at SClient+0x1198 was read as a uint32. In client.exe the field is a bool: every access is a byte move (5 reads, 3 writes), and +0x1199..+0x119B are padding the client never writes. When that padding holds stale heap data the flag never reads as zero, so a pilot who is in game is treated as still loading: the in-game Client Manager tab, add-on overlays, Auto Pilot and the other features gated on LoadingOrTransitionFlag == 0 never appear. Read one byte in ClientSessionObserver and ClientNavigationStateObserver (the same field, FrameProcessingGateFlag). The model types stay uint. Co-Authored-By: Claude Opus 5.5 --- .../ClientSessionObserver.cs | 5 ++++- .../ClientNavigationStateObservation.cs | 2 +- .../ClientNavigationStateObserver.cs | 7 ++++--- .../ProcessMemoryReader.cs | 19 +++++++++++++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Source/Net7ClientManager.Observations/ClientSessionObserver.cs b/Source/Net7ClientManager.Observations/ClientSessionObserver.cs index dd40ec8..18823fb 100644 --- a/Source/Net7ClientManager.Observations/ClientSessionObserver.cs +++ b/Source/Net7ClientManager.Observations/ClientSessionObserver.cs @@ -208,7 +208,10 @@ private bool TryReadDirectClientState( ClientContextTargetObjectId); } - if (!memory.TryReadUInt32( + // A bool: client.exe only reads and writes this field with byte moves. + // The three bytes after it are padding the client never initializes, + // so a 32-bit read can see stale heap data and never reach zero. + if (!memory.TryReadByte( clientContextAddress + ClientContextLoadingOrTransitionFlag, out var loadingOrTransitionFlag)) diff --git a/Source/Net7ClientManager.Observations/Models/ClientNavigationStateObservation.cs b/Source/Net7ClientManager.Observations/Models/ClientNavigationStateObservation.cs index d0df371..31f3762 100644 --- a/Source/Net7ClientManager.Observations/Models/ClientNavigationStateObservation.cs +++ b/Source/Net7ClientManager.Observations/Models/ClientNavigationStateObservation.cs @@ -41,7 +41,7 @@ public sealed record ClientNavigationStateObservation public uint CurrentClientTime { get; init; } /// - /// Raw SClient +0x1198 gate around part of frame processing. Its broader + /// SClient +0x1198 gate (a bool) around part of frame processing. Its broader /// semantic meaning is intentionally not named as a loading flag. /// public uint FrameProcessingGateFlag { get; init; } diff --git a/Source/Net7ClientManager.Observations/Observers/ClientNavigationStateObserver.cs b/Source/Net7ClientManager.Observations/Observers/ClientNavigationStateObserver.cs index 14bf008..6ea39ef 100644 --- a/Source/Net7ClientManager.Observations/Observers/ClientNavigationStateObserver.cs +++ b/Source/Net7ClientManager.Observations/Observers/ClientNavigationStateObserver.cs @@ -113,9 +113,10 @@ public ClientNavigationStateObservation Observe( ClientContextLocalPlayerObjectId, out var localPlayerObjectId); - _ = TryReadUInt32( - memory, - clientContextAddress, + // A bool, the same field as ClientSessionObserver's + // LoadingOrTransitionFlag; see there. + _ = memory.TryReadByte( + clientContextAddress + ClientContextFrameProcessingGate, out var frameProcessingGateFlag); diff --git a/Source/Net7ClientManager.Observations/ProcessMemoryReader.cs b/Source/Net7ClientManager.Observations/ProcessMemoryReader.cs index 415c241..8c41e29 100644 --- a/Source/Net7ClientManager.Observations/ProcessMemoryReader.cs +++ b/Source/Net7ClientManager.Observations/ProcessMemoryReader.cs @@ -69,6 +69,25 @@ public bool TryReadUInt32( return true; } + public bool TryReadByte( + uint address, + out byte value) + { + value = 0; + + if (!this.TryReadBytes( + address, + 1, + out var bytes)) + { + return false; + } + + value = bytes[0]; + + return true; + } + public bool TryReadBytes( uint address, int length,