LCOV - code coverage report
Current view: top level - io - GameConnector.h (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 9 9
Test Date: 2026-03-18 19:01:10 Functions: 100.0 % 6 6
Branches: 100.0 % 6 6

             Branch data     Line data    Source code
       1                 :             : #ifndef GAMECONNECTOR_H
       2                 :             : #define GAMECONNECTOR_H
       3                 :             : 
       4                 :             : #ifdef _WIN32
       5                 :             : #include <windows.h>
       6                 :             : #else
       7                 :             : #include "io/lmu_sm_interface/LinuxMock.h"
       8                 :             : #endif
       9                 :             : 
      10                 :             : #include "io/lmu_sm_interface/LmuSharedMemoryWrapper.h"
      11                 :             : #include "io/lmu_sm_interface/SafeSharedMemoryLock.h"
      12                 :             : #include <mutex>
      13                 :             : #include <atomic>
      14                 :             : #include <cstring>
      15                 :             : 
      16                 :             : namespace FFBEngineTests { class GameConnectorTestAccessor; }
      17                 :             : 
      18                 :             : class GameConnector {
      19                 :             : public:
      20                 :             :     static GameConnector& Get();
      21                 :             :     
      22                 :             :     // Attempt to connect to LMU Shared Memory
      23                 :             :     bool TryConnect();
      24                 :             :     
      25                 :             :     // Disconnect and clean up resources
      26                 :             :     void Disconnect();
      27                 :             :     
      28                 :             :     // Check for Legacy rFactor 2 Plugin conflict
      29                 :             :     bool CheckLegacyConflict();
      30                 :             :     
      31                 :             :     // Is connected to LMU SM?
      32                 :             :     bool IsConnected() const;
      33                 :             :     
      34                 :             :     // Thread-safe copy of telemetry data
      35                 :             :     // Returns true if in realtime (driving) mode, false if in menu/replay
      36                 :             :     bool CopyTelemetry(SharedMemoryObjectOut& dest);
      37                 :             : 
      38                 :             :     // Returns true if telemetry data hasn't changed for more than timeout (v0.7.15)
      39                 :             :     bool IsStale(long timeoutMs = 100) const;
      40                 :             : 
      41                 :             :     // Robust State Accessors (#267)
      42                 :        3240 :     bool IsSessionActive() const { return m_sessionActive.load(std::memory_order_relaxed); }
      43                 :        6402 :     bool IsInRealtime() const { return m_inRealtime.load(std::memory_order_relaxed); }
      44                 :       12788 :     long GetSessionType() const { return m_currentSessionType.load(std::memory_order_relaxed); }
      45                 :          10 :     unsigned char GetGamePhase() const { return m_currentGamePhase.load(std::memory_order_relaxed); }
      46                 :        6466 :     signed char GetPlayerControl() const { return m_playerControl.load(std::memory_order_relaxed); }
      47                 :             : 
      48                 :             :     // Composite predicate: true only when the player is physically behind the wheel
      49                 :             :     // and actively in control (not paused, not in garage UI, not AI-controlled).
      50                 :             :     // Addresses the ESC-menu-while-on-track edge case (session transition.md).
      51                 :        3169 :     bool IsPlayerActivelyDriving() const {
      52                 :        3169 :         return m_inRealtime.load(std::memory_order_relaxed)
      53         [ +  + ]:           8 :             && m_playerControl.load(std::memory_order_relaxed) == 0  // Player (not AI/replay/nobody)
      54   [ +  +  +  + ]:        3176 :             && m_currentGamePhase.load(std::memory_order_relaxed) != 9; // 9 == Paused
      55                 :             :     }
      56                 :             : 
      57                 :             : private:
      58                 :             :     struct TransitionState {
      59                 :             :         unsigned char optionsLocation = 255;
      60                 :             :         bool inRealtime = false;
      61                 :             :         unsigned char gamePhase = 255;
      62                 :             :         long session = -1;
      63                 :             :         signed char control = -2;
      64                 :             :         unsigned char pitState = 255;
      65                 :             :         char vehicleName[64] = { 0 };
      66                 :             :         char trackName[64] = { 0 };
      67                 :             :         char optionsPage[32] = { 0 };
      68                 :             :         float steeringRange = -1.0f;
      69                 :             :         bool playerHasVehicle = false;  // investigation: quit-to-menu detection (#7.4a)
      70                 :             :         int  numVehicles = -1;          // investigation: quit-to-menu detection (#7.4b)
      71                 :             :         uint32_t eventState[SME_MAX] = { 0 };
      72                 :             :         std::chrono::steady_clock::time_point lastEventLogTime[SME_MAX];
      73                 :             :     } m_prevState;
      74                 :             : 
      75                 :             :     // CheckTransitions orchestrates the two-phase update:
      76                 :             :     //   1. _UpdateStateFromSnapshot  — unconditionally syncs atomics from the SM buffer
      77                 :             :     //   2. _LogTransitions           — detects changes vs m_prevState and emits log lines
      78                 :             :     void CheckTransitions(const SharedMemoryObjectOut& current);
      79                 :             :     void _UpdateStateFromSnapshot(const SharedMemoryObjectOut& current);
      80                 :             :     void _LogTransitions(const SharedMemoryObjectOut& current);
      81                 :             : 
      82                 :             :     // String-lookup helpers (extracted for reuse and testability)
      83                 :             :     static const char* SmeEventName(int eventIndex);
      84                 :             :     static const char* GamePhaseName(unsigned char phase);
      85                 :             :     static const char* SessionTypeName(long session);
      86                 :             :     static const char* ControlModeName(signed char control);
      87                 :             :     static const char* PitStateName(unsigned char pitState);
      88                 :             : 
      89                 :             :     friend class FFBEngineTests::GameConnectorTestAccessor;
      90                 :             : 
      91                 :             :     GameConnector();
      92                 :             :     ~GameConnector();
      93                 :             :     
      94                 :             :     SharedMemoryLayout* m_pSharedMemLayout = nullptr;
      95                 :             :     mutable std::optional<SafeSharedMemoryLock> m_smLock;
      96                 :             :     HANDLE m_hMapFile = NULL;
      97                 :             :     mutable HWND m_hwndGame = NULL;
      98                 :             :     DWORD m_processId = 0;
      99                 :             : 
     100                 :             :     std::atomic<bool> m_connected{false};
     101                 :             :     mutable std::recursive_mutex m_mutex;
     102                 :             : 
     103                 :             :     // Robust State Machine (#267)
     104                 :             :     std::atomic<bool> m_sessionActive{ false };
     105                 :             :     std::atomic<bool> m_inRealtime{ false };
     106                 :             :     std::atomic<long> m_currentSessionType{ -1 };
     107                 :             :     std::atomic<unsigned char> m_currentGamePhase{ 255 };
     108                 :             :     std::atomic<signed char> m_playerControl{ -2 };
     109                 :             : 
     110                 :             :     // Heartbeat for staleness detection (v0.7.15)
     111                 :             :     double m_lastElapsedTime = -1.0;
     112                 :             :     double m_lastSteering = 0.0; // Issue #184: Steering heartbeat
     113                 :             :     mutable std::chrono::steady_clock::time_point m_lastUpdateLocalTime;
     114                 :             : 
     115                 :             :     // Quit-to-menu detection (#7.5): armed when mInRealtime goes true→false.
     116                 :             :     // If SME_ENTER fires within the deadline window, we know the user quit to
     117                 :             :     // the main menu (not just returned to the garage monitor).
     118                 :             :     bool m_pendingMenuCheck = false;
     119                 :             :     std::chrono::steady_clock::time_point m_menuCheckDeadline;
     120                 :             : 
     121                 :             :     void _DisconnectLocked();
     122                 :             : };
     123                 :             : #endif // GAMECONNECTOR_H
        

Generated by: LCOV version 2.0-1