Branch data Line data Source code
1 : : #include "GameConnector.h"
2 : : #include "Logger.h"
3 : : #ifndef _WIN32
4 : : #include "lmu_sm_interface/LinuxMock.h"
5 : : #endif
6 : : #include "lmu_sm_interface/SafeSharedMemoryLock.h"
7 : : #include <iostream>
8 : :
9 : : #define LEGACY_SHARED_MEMORY_NAME "$rFactor2SMMP_Telemetry$"
10 : :
11 : 55699 : GameConnector& GameConnector::Get() {
12 [ + + + - : 55699 : static GameConnector instance;
+ - - - ]
13 : 55701 : return instance;
14 : : }
15 : :
16 : 1 : GameConnector::GameConnector() {}
17 : :
18 : 1 : GameConnector::~GameConnector() {
19 : 1 : Disconnect();
20 : 1 : }
21 : :
22 : 12411 : void GameConnector::Disconnect() {
23 [ + - ]: 12411 : std::lock_guard<std::mutex> lock(m_mutex);
24 : 12411 : _DisconnectLocked();
25 : 12411 : }
26 : :
27 : 24822 : void GameConnector::_DisconnectLocked() {
28 : : #if defined(_WIN32) || defined(HEADLESS_GUI)
29 [ + + ]: 24822 : if (m_pSharedMemLayout) {
30 : 12409 : UnmapViewOfFile(m_pSharedMemLayout);
31 : 12409 : m_pSharedMemLayout = nullptr;
32 : : }
33 [ + + ]: 24822 : if (m_hMapFile) {
34 : 12409 : CloseHandle(m_hMapFile);
35 : 12409 : m_hMapFile = NULL;
36 : : }
37 : 24822 : m_hwndGame = NULL;
38 : : #endif
39 : 24822 : m_smLock.reset();
40 : 24822 : m_connected = false;
41 : 24822 : m_processId = 0;
42 : 24822 : }
43 : :
44 : 12415 : bool GameConnector::TryConnect() {
45 [ + - ]: 12415 : std::lock_guard<std::mutex> lock(m_mutex);
46 [ + + ]: 12415 : if (m_connected) return true;
47 : :
48 : : // Ensure we don't leak handles from a previous partial/failed attempt
49 : 12409 : _DisconnectLocked();
50 : :
51 : : #if defined(_WIN32) || defined(HEADLESS_GUI)
52 [ + - ]: 12409 : m_hMapFile = OpenFileMappingA(FILE_MAP_READ, FALSE, LMU_SHARED_MEMORY_FILE);
53 : :
54 [ - + ]: 12409 : if (m_hMapFile == NULL) {
55 : 0 : return false;
56 : : }
57 : :
58 [ + - ]: 12409 : m_pSharedMemLayout = (SharedMemoryLayout*)MapViewOfFile(m_hMapFile, FILE_MAP_READ, 0, 0, sizeof(SharedMemoryLayout));
59 [ - + ]: 12409 : if (m_pSharedMemLayout == NULL) {
60 [ # # # # ]: 0 : std::cerr << "[GameConnector] Could not map view of file." << std::endl;
61 [ # # # # ]: 0 : Logger::Get().LogWin32Error("MapViewOfFile", GetLastError());
62 : 0 : _DisconnectLocked();
63 : 0 : return false;
64 : : }
65 : :
66 [ + - + - ]: 12409 : m_smLock = SafeSharedMemoryLock::MakeSafeSharedMemoryLock();
67 [ + + ]: 12409 : if (!m_smLock.has_value()) {
68 [ + - + - ]: 1 : std::cerr << "[GameConnector] Failed to init LMU Shared Memory Lock" << std::endl;
69 [ + - + - ]: 1 : Logger::Get().Log("Failed to init SafeSharedMemoryLock.");
70 : 1 : _DisconnectLocked();
71 : 1 : return false;
72 : : }
73 : :
74 : 12408 : HWND hwnd = m_pSharedMemLayout->data.generic.appInfo.mAppWindow;
75 [ + + ]: 12408 : if (hwnd) {
76 : 4 : m_hwndGame = hwnd; // Store HWND for liveness check (IsWindow)
77 : : // Note: multiple threads might access shared memory, but HWND is usually stable during session.
78 : : // We use IsWindow(m_hwndGame) instead of OpenProcess to avoid AV heuristics flagging "Process Access".
79 : : }
80 : :
81 : 12408 : m_connected = true;
82 : 12408 : m_lastUpdateLocalTime = std::chrono::steady_clock::now();
83 [ + - + - ]: 12408 : std::cout << "[GameConnector] Connected to LMU Shared Memory." << std::endl;
84 [ + - + - ]: 12408 : Logger::Get().Log("Connected to LMU Shared Memory.");
85 : 12408 : return true;
86 : : #else
87 : : return false;
88 : : #endif
89 : 12415 : }
90 : :
91 : 7 : bool GameConnector::CheckLegacyConflict() {
92 : : #if defined(_WIN32) || defined(HEADLESS_GUI)
93 : 7 : HANDLE hLegacy = OpenFileMappingA(FILE_MAP_READ, FALSE, LEGACY_SHARED_MEMORY_NAME);
94 [ + + ]: 7 : if (hLegacy) {
95 : 2 : std::cout << "[Warning] Legacy rFactor 2 Shared Memory Plugin detected. This may conflict with LMU 1.2 data." << std::endl;
96 : 2 : CloseHandle(hLegacy);
97 : 2 : return true;
98 : : }
99 : : #endif
100 : 5 : return false;
101 : : }
102 : :
103 : 5154 : bool GameConnector::IsConnected() const {
104 [ + + ]: 5154 : if (!m_connected.load(std::memory_order_acquire)) return false;
105 : :
106 [ + - ]: 4942 : std::lock_guard<std::mutex> lock(m_mutex);
107 [ - + ]: 4942 : if (!m_connected.load(std::memory_order_relaxed)) return false;
108 : :
109 : : #if defined(_WIN32) || defined(HEADLESS_GUI)
110 [ + + ]: 4942 : if (m_hwndGame) {
111 [ + + ]: 4766 : if (!IsWindow(m_hwndGame)) {
112 : : // Window is gone, game likely exited
113 : 1 : const_cast<GameConnector*>(this)->_DisconnectLocked();
114 : 1 : return false;
115 : : }
116 : : }
117 : : #endif
118 : :
119 [ + - + - : 4941 : return m_connected.load(std::memory_order_relaxed) && m_pSharedMemLayout && m_smLock.has_value();
+ - ]
120 : 4942 : }
121 : :
122 : 20974 : bool GameConnector::CopyTelemetry(SharedMemoryObjectOut& dest) {
123 [ + + ]: 20974 : if (!m_connected.load(std::memory_order_acquire)) return false;
124 : :
125 [ + - ]: 10137 : std::lock_guard<std::mutex> lock(m_mutex);
126 [ + + + - : 10137 : if (!m_connected.load(std::memory_order_relaxed) || !m_pSharedMemLayout || !m_smLock.has_value()) return false;
- + + + ]
127 : :
128 [ + + ]: 9919 : if (m_smLock->Lock(50)) {
129 : 9918 : CopySharedMemoryObj(dest, m_pSharedMemLayout->data);
130 : :
131 [ + + ]: 9918 : if (dest.telemetry.playerHasVehicle) {
132 : 4 : uint8_t idx = dest.telemetry.playerVehicleIdx;
133 [ + - ]: 4 : if (idx < 104) {
134 : 4 : double currentET = dest.telemetry.telemInfo[idx].mElapsedTime;
135 [ + + ]: 4 : if (currentET != m_lastElapsedTime) {
136 : 2 : m_lastElapsedTime = currentET;
137 : 2 : m_lastUpdateLocalTime = std::chrono::steady_clock::now();
138 : : }
139 : : }
140 : : } else {
141 : 9914 : m_lastUpdateLocalTime = std::chrono::steady_clock::now();
142 : : }
143 : :
144 : 9918 : bool isRealtime = (m_pSharedMemLayout->data.scoring.scoringInfo.mInRealtime != 0);
145 : 9918 : m_smLock->Unlock();
146 : 9918 : return isRealtime;
147 : : } else {
148 : 1 : return false;
149 : : }
150 : 10137 : }
151 : :
152 : 4766 : bool GameConnector::IsStale(long timeoutMs) const {
153 [ + + ]: 4766 : if (!m_connected.load(std::memory_order_acquire)) return true;
154 : :
155 : 4765 : auto now = std::chrono::steady_clock::now();
156 [ + - + - ]: 4765 : auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_lastUpdateLocalTime).count();
157 : 4765 : return (diff > timeoutMs);
158 : : }
|