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,