diff --git a/code/components/gta-net-five/include/netTimeSync.h b/code/components/gta-net-five/include/netTimeSync.h new file mode 100644 index 0000000000..712e3aab00 --- /dev/null +++ b/code/components/gta-net-five/include/netTimeSync.h @@ -0,0 +1,74 @@ +#pragma once + +struct TrustAddressData +{ + uint32_t m_addr; + uint16_t m_port; +}; + +struct TrustAddress1604 +{ + TrustAddressData m_addr1; + TrustAddressData m_addr2; +}; + +struct TrustAddress2060 +{ + TrustAddressData m_addr1; + TrustAddressData m_addr2; + TrustAddressData m_addr3; +}; + +struct TrustAddress2372 +{ + uint32_t m_addr; +}; + +template +using TrustAddress = std::conditional_t<(Build >= 2372), TrustAddress2372, std::conditional_t<(Build >= 2060), TrustAddress2060, TrustAddress1604>>; + +namespace rage +{ +class netConnectionManager; + +template +class netTimeSync +{ +public: + virtual ~netTimeSync() = 0; + + void Update(); + void HandleTimeSync(net::Buffer& buffer); + bool IsInitialized(); + void SetConnectionManager(netConnectionManager* mgr); + +private: + netConnectionManager* m_connectionMgr; // +8 (1604) + TrustAddress m_trustAddr; // +16 + uint32_t m_sessionKey; // +32 + int32_t m_timeDelta; // +36 + struct + { + void* self; + void* cb; + } m_messageDelegate; // +40 + char m_pad_56[32]; + uint32_t m_nextSync; // +88 + uint32_t m_configTimeBetweenSyncs; // +92 + uint32_t m_configMaxBackoff; // +96, usually 60000 + uint32_t m_effectiveTimeBetweenSyncs; // +100 + uint32_t m_lastRtt; // +104 + uint32_t m_retryCount; // +108 + uint32_t m_requestSequence; // +112 + uint32_t m_replySequence; // +116 + uint32_t m_flags; // +120 + uint32_t m_packetFlags; // +124 + uint32_t m_lastTime; // +128, used to prevent time from going backwards + uint8_t m_applyFlags; // +132 + uint8_t m_disabled; // +133 +}; +} + +static_assert(sizeof(rage::netTimeSync<1604>) == 136); +static_assert(sizeof(rage::netTimeSync<2060>) == 144); +static_assert(sizeof(rage::netTimeSync<2372>) == 128); diff --git a/code/components/gta-net-five/src/CloneExperiments.cpp b/code/components/gta-net-five/src/CloneExperiments.cpp index 46a0948341..1ebd8de4b2 100644 --- a/code/components/gta-net-five/src/CloneExperiments.cpp +++ b/code/components/gta-net-five/src/CloneExperiments.cpp @@ -2392,6 +2392,8 @@ static HookFunction hookFunction([]() // memset in CNetworkDamageTracker::ctor hook::put(hook::get_pattern("48 89 11 48 8B D9 41 B8 80 00 00 00", 8), sizeof(float) * 256); } + + g_CNetPlayerOffset_PlayerInfo = *hook::get_pattern("48 8B 81 ? 00 00 00 48 83 C0 20 C3", 3);; #endif MH_EnableHook(MH_ALL_HOOKS); @@ -3798,532 +3800,6 @@ static HookFunction hookFunction2([]() } }); -#include - -static hook::cdecl_stub _getConnectionManager([]() -{ -#ifdef GTA_FIVE - return hook::get_call(hook::get_pattern("E8 ? ? ? ? C7 44 24 40 60 EA 00 00")); -#elif IS_RDR3 - return hook::get_call(hook::get_pattern("48 8B D0 8B 46 ? 44 8B CD C7", -13)); -#endif -}); - -static bool (*g_origInitializeTime)(void* timeSync, void* connectionMgr, int flags, void* trustHost, - uint32_t sessionSeed, int* deltaStart, int packetFlags, int initialBackoff, int maxBackoff); - -static bool g_initedTimeSync; - -struct EmptyStruct -{ - -}; - -#ifdef GTA_FIVE -struct TrustAddressData -{ - uint32_t addr; - uint16_t port; -}; - -struct TrustAddress1604 -{ - TrustAddressData m_addr1; - TrustAddressData m_addr2; -}; - -struct TrustAddress2060 -{ - TrustAddressData m_addr1; - TrustAddressData m_addr2; - TrustAddressData m_addr3; -}; - -struct TrustAddress2372 -{ - uint32_t m_addr; -}; - -template -using TrustAddress = std::conditional_t<(Build >= 2372), TrustAddress2372, std::conditional_t<(Build >= 2060), TrustAddress2060, TrustAddress1604>>; - -template -class netTimeSync -{ -public: - void Update() - { - if (!icgi->OneSyncEnabled) - { - return; - } - - if (/*m_connectionMgr /*&& m_flags & 2 && */ !m_disabled) - { - uint32_t curTime = timeGetTime(); - - if (!m_nextSync || int32_t(timeGetTime() - m_nextSync) >= 0) - { - m_requestSequence++; - - net::Buffer outBuffer; - outBuffer.Write(curTime); // request time - outBuffer.Write(m_requestSequence); // request sequence - - g_netLibrary->SendReliableCommand("msgTimeSyncReq", (const char*)outBuffer.GetData().data(), outBuffer.GetCurOffset()); - - m_nextSync = (curTime + m_effectiveTimeBetweenSyncs) | 1; - } - } - } - - void HandleTimeSync(net::Buffer& buffer) - { - auto reqTime = buffer.Read(); - auto reqSequence = buffer.Read(); - auto resDelta = buffer.Read(); - - if (m_disabled) - { - return; - } - - /*if (!(m_flags & 2)) - { - return; - }*/ - - // out of order? - if (int32_t(reqSequence - m_replySequence) <= 0) - { - return; - } - - auto rtt = timeGetTime() - reqTime; - - // bad timestamp, negative time passed - if (int32_t(rtt) <= 0) - { - return; - } - - int32_t timeDelta = resDelta + (rtt / 2) - timeGetTime(); - - // is this a low RTT, or did we retry often enough? - if (rtt <= 300 || m_retryCount >= 10) - { - if (!m_lastRtt) - { - m_lastRtt = rtt; - } - - // is RTT within variance, low, or retried? - if (rtt <= 100 || (rtt / m_lastRtt) < 2 || m_retryCount >= 10) - { - m_timeDelta = timeDelta; - m_replySequence = reqSequence; - - // progressive backoff once we've established a valid time base - if (m_effectiveTimeBetweenSyncs < m_configMaxBackoff) - { - m_effectiveTimeBetweenSyncs = std::min(m_configMaxBackoff, m_effectiveTimeBetweenSyncs * 2); - } - - m_retryCount = 0; - - // use flag 4 to reset time at least once, even if game session code has done so to a higher value - if (!(m_applyFlags & 4)) - { - m_lastTime = m_timeDelta + timeGetTime(); - } - - m_applyFlags |= 7; - } - else - { - m_nextSync = 0; - m_retryCount++; - } - - // update average RTT - m_lastRtt = (rtt + m_lastRtt) / 2; - } - else - { - m_nextSync = 0; - m_retryCount++; - } - } - - bool IsInitialized() - { - if (!g_initedTimeSync) - { - g_origInitializeTime(this, _getConnectionManager(), 1, nullptr, 0, nullptr, 7, 2000, 60000); - - // to make the game not try to get time from us - m_connectionMgr = nullptr; - - g_initedTimeSync = true; - - return false; - } - - return (m_applyFlags & 4) != 0; - } - - inline void SetConnectionManager(void* mgr) - { - m_connectionMgr = mgr; - } - -private: - void* m_vtbl; // 0 - void* m_connectionMgr; // 8 - TrustAddress m_trustAddr; // 16 - uint32_t m_sessionKey; // 32 - int32_t m_timeDelta; // 36 - struct - { - void* self; - void* cb; - } messageDelegate; // 40 - char m_pad_38[32]; // 56 - uint32_t m_nextSync; // 88 - uint32_t m_configTimeBetweenSyncs; // 92 - uint32_t m_configMaxBackoff; // 96, usually 60000 - uint32_t m_effectiveTimeBetweenSyncs; // 100 - uint32_t m_lastRtt; // 104 - uint32_t m_retryCount; // 108 - uint32_t m_requestSequence; // 112 - uint32_t m_replySequence; // 116 - uint32_t m_flags; // 120 - uint32_t m_packetFlags; // 124 - uint32_t m_lastTime; // 128, used to prevent time from going backwards - uint8_t m_applyFlags; // 132 - uint8_t m_disabled; // 133 -}; -#elif IS_RDR3 -struct ExtraPaddingData -{ - char extra_padding[24]; -}; - -template -using ExtraPadding = std::conditional_t; - - -template -class netTimeSync -{ -public: - void Update() - { - if (!icgi->OneSyncEnabled) - { - return; - } - - if (/*m_connectionMgr /*&& m_flags & 2 && */ !m_disabled) - { - uint32_t curTime = timeGetTime(); - - if (!m_nextSync || int32_t(timeGetTime() - m_nextSync) >= 0) - { - m_requestSequence++; - - net::Buffer outBuffer; - outBuffer.Write(curTime); // request time - outBuffer.Write(m_requestSequence); // request sequence - - g_netLibrary->SendReliableCommand("msgTimeSyncReq", (const char*)outBuffer.GetData().data(), outBuffer.GetCurOffset()); - - m_nextSync = (curTime + m_effectiveTimeBetweenSyncs) | 1; - } - } - } - - void HandleTimeSync(net::Buffer& buffer) - { - auto reqTime = buffer.Read(); - auto reqSequence = buffer.Read(); - auto resDelta = buffer.Read(); - - if (m_disabled) - { - return; - } - - /*if (!(m_flags & 2)) - { - return; - }*/ - - // out of order? - if (int32_t(reqSequence - m_replySequence) <= 0) - { - return; - } - - auto rtt = timeGetTime() - reqTime; - - // bad timestamp, negative time passed - if (int32_t(rtt) <= 0) - { - return; - } - - int32_t timeDelta = resDelta + (rtt / 2) - timeGetTime(); - - // is this a low RTT, or did we retry often enough? - if (rtt <= 300 || m_retryCount >= 10) - { - if (!m_lastRtt) - { - m_lastRtt = rtt; - } - - // is RTT within variance, low, or retried? - if (rtt <= 100 || (rtt / m_lastRtt) < 2 || m_retryCount >= 10) - { - m_timeDelta = timeDelta; - m_replySequence = reqSequence; - - // progressive backoff once we've established a valid time base - if (m_effectiveTimeBetweenSyncs < m_configMaxBackoff) - { - m_effectiveTimeBetweenSyncs = std::min(m_configMaxBackoff, m_effectiveTimeBetweenSyncs * 2); - } - - m_retryCount = 0; - - // use flag 4 to reset time at least once, even if game session code has done so to a higher value - if (!(m_applyFlags & 4)) - { - m_lastTime = m_timeDelta + timeGetTime(); - } - - m_applyFlags |= 7; - } - else - { - m_nextSync = 0; - m_retryCount++; - } - - // update average RTT - m_lastRtt = (rtt + m_lastRtt) / 2; - } - else - { - m_nextSync = 0; - m_retryCount++; - } - } - - bool IsInitialized() - { - if (!g_initedTimeSync) - { - // we don't want to use cloud time - m_useCloudTime = false; - - g_origInitializeTime(this, _getConnectionManager(), 1, nullptr, 0, nullptr, 7, 2000, 60000); - - // to make the game not try to get time from us - m_connectionMgr = nullptr; - - g_initedTimeSync = true; - - return false; - } - - return (m_applyFlags & 4) != 0; - } - - inline void SetConnectionManager(void* mgr) - { - m_connectionMgr = mgr; - } - -public: - void* m_vtbl; // +0 - void* m_connectionMgr; // +8 - uint32_t m_unkTrust; // +16 - uint32_t m_sessionKey; // +20 - char m_pad_24[44]; // +24 - ExtraPadding<(Build >= 1355)> m_pad_72; - uint32_t m_nextSync; // +72 - uint32_t m_configTimeBetweenSyncs; // +76 - uint32_t m_configMaxBackoff; // +80, usually 60000 - uint32_t m_effectiveTimeBetweenSyncs; // +84 - uint32_t m_lastRtt; // +88 - uint32_t m_retryCount; // +92 - uint32_t m_requestSequence; // +96 - uint32_t m_replySequence; // +100 - uint32_t m_flags; // +104 - uint32_t m_packetFlags; // +108 - int32_t m_timeDelta; // +112 - char m_pad_116[28]; - uint32_t m_lastTime; // +144, used to prevent time from going backwards - uint8_t m_applyFlags; // +148 - uint8_t m_unk5; // +149 - uint8_t m_disabled; // +150 - uint8_t m_useCloudTime; // +151 -}; -#endif - -template -static netTimeSync** g_netTimeSync; - -template -bool netTimeSync__InitializeTimeStub(netTimeSync* timeSync, void* connectionMgr, int flags, void* trustHost, - uint32_t sessionSeed, int* deltaStart, int packetFlags, int initialBackoff, int maxBackoff) -{ - if (!icgi->OneSyncEnabled) - { - return g_origInitializeTime(timeSync, connectionMgr, flags, trustHost, sessionSeed, deltaStart, packetFlags, initialBackoff, maxBackoff); - } - - timeSync->SetConnectionManager(connectionMgr); - - return true; -} - -bool IsWaitingForTimeSync() -{ -#ifdef GTA_FIVE - if (xbr::IsGameBuildOrGreater<2372>()) - { - return !(*g_netTimeSync<2372>)->IsInitialized(); - } - else if (xbr::IsGameBuildOrGreater<2060>()) - { - return !(*g_netTimeSync<2060>)->IsInitialized(); - } - - return !(*g_netTimeSync<1604>)->IsInitialized(); -#elif IS_RDR3 - if (xbr::IsGameBuildOrGreater<1355>()) - { - return !(*g_netTimeSync<1355>)->IsInitialized(); - } - - return !(*g_netTimeSync<1311>)->IsInitialized(); -#endif -} - -static InitFunction initFunctionTime([]() -{ - NetLibrary::OnNetLibraryCreate.Connect([](NetLibrary* lib) - { - lib->AddReliableHandler("msgTimeSync", [](const char* data, size_t len) - { - net::Buffer buf(reinterpret_cast(data), len); - -#ifdef GTA_FIVE - if (xbr::IsGameBuildOrGreater<2372>()) - { - (*g_netTimeSync<2372>)->HandleTimeSync(buf); - } - else if (xbr::IsGameBuildOrGreater<2060>()) - { - (*g_netTimeSync<2060>)->HandleTimeSync(buf); - } - else - { - (*g_netTimeSync<1604>)->HandleTimeSync(buf); - } -#elif IS_RDR3 - if (xbr::IsGameBuildOrGreater<1355>()) - { - (*g_netTimeSync<1355>)->HandleTimeSync(buf); - } - else - { - (*g_netTimeSync<1311>)->HandleTimeSync(buf); - } -#endif - }); - }); -}); - -static HookFunction hookFunctionTime([]() -{ - MH_Initialize(); - -#ifdef GTA_FIVE - void* func = (xbr::IsGameBuildOrGreater<2372>()) ? (void*)&netTimeSync__InitializeTimeStub<2372> : - (xbr::IsGameBuildOrGreater<2060>()) ? (void*)&netTimeSync__InitializeTimeStub<2060> : &netTimeSync__InitializeTimeStub<1604>; - - MH_CreateHook(hook::get_pattern("48 8B D9 48 39 79 08 0F 85 ? ? 00 00 41 8B E8", -32), func, (void**)&g_origInitializeTime); -#elif IS_RDR3 - void* func = (xbr::IsGameBuildOrGreater<1355>()) ? (void*)&netTimeSync__InitializeTimeStub<1355> : &netTimeSync__InitializeTimeStub<1311>; - MH_CreateHook((xbr::IsGameBuildOrGreater<1436>()) ? hook::get_pattern("83 C8 FF 4C 89 77 08 83 FD", -87) : hook::get_pattern("48 89 51 08 41 83 F8 02 44 0F 45 C8", -49), func, (void**)&g_origInitializeTime); -#endif - - MH_EnableHook(MH_ALL_HOOKS); - -#ifdef GTA_FIVE - char* loc = hook::get_pattern("48 8B 81 ? 00 00 00 48 83 C0 20 C3"); - loc += 3; - g_CNetPlayerOffset_PlayerInfo = *(int*)loc; -#endif - -#ifdef GTA_FIVE - if (xbr::IsGameBuildOrGreater<2372>()) - { - g_netTimeSync<2372> = hook::get_address**>(hook::get_pattern("48 8B 0D ? ? ? ? 45 33 C9 45 33 C0 41 8D 51 01 E8", 3)); - } - else if (xbr::IsGameBuildOrGreater<2060>()) - { - g_netTimeSync<2060> = hook::get_address**>(hook::get_pattern("48 8B 0D ? ? ? ? 45 33 C9 45 33 C0 41 8D 51 01 E8", 3)); - } - else - { - g_netTimeSync<1604> = hook::get_address**>(hook::get_pattern("EB 16 48 8B 0D ? ? ? ? 45 33 C9 45 33 C0", 5)); - } -#elif IS_RDR3 - auto location = hook::get_pattern("4C 8D 45 50 41 03 C7 44 89 6D 50 89", -4); - - if (xbr::IsGameBuildOrGreater<1355>()) - { - g_netTimeSync<1355> = hook::get_address**>(location); - } - else - { - g_netTimeSync<1311> = hook::get_address**>(location); - } -#endif - - OnMainGameFrame.Connect([]() - { -#if GTA_FIVE - if (xbr::IsGameBuildOrGreater<2372>()) - { - (*g_netTimeSync<2372>)->Update(); - } - else if (xbr::IsGameBuildOrGreater<2060>()) - { - (*g_netTimeSync<2060>)->Update(); - } - else - { - (*g_netTimeSync<1604>)->Update(); - } -#elif IS_RDR3 - if (xbr::IsGameBuildOrGreater<1355>()) - { - (*g_netTimeSync<1355>)->Update(); - } - else - { - (*g_netTimeSync<1311>)->Update(); - } -#endif - }); -}); - template struct WorldGridEntry { @@ -4662,11 +4138,6 @@ static InitFunction initFunction([]() } #endif -#ifdef IS_RDR3 - // RDR3 doesn't restart netTimeSync after disconnecting from a server with enabled onesync - g_initedTimeSync = false; -#endif - g_events.clear(); g_reEventQueue.clear(); diff --git a/code/components/gta-net-five/src/netTimeSync.cpp b/code/components/gta-net-five/src/netTimeSync.cpp new file mode 100644 index 0000000000..0f2cf6892a --- /dev/null +++ b/code/components/gta-net-five/src/netTimeSync.cpp @@ -0,0 +1,333 @@ +#include +#include +#include +#include "CrossBuildRuntime.h" +#include "Hooking.h" +#include "NetLibrary.h" +#include "nutsnbolts.h" +#include "ICoreGameInit.h" +#include "GameInit.h" +#include "netTimeSync.h" + +extern ICoreGameInit* icgi; +extern NetLibrary* g_netLibrary; + +static bool g_initedTimeSync; + +static hook::cdecl_stub _getConnectionManager([]() +{ +#ifdef GTA_FIVE + return hook::get_call(hook::get_pattern("E8 ? ? ? ? C7 44 24 40 60 EA 00 00")); +#elif IS_RDR3 + return hook::get_call(hook::get_pattern("48 8B D0 8B 46 ? 44 8B CD C7", -13)); +#endif +}); + +template +static rage::netTimeSync** g_netTimeSync; + +static bool (*g_origInitializeTime)(void* timeSync, void* connectionMgr, uint32_t flags, void* trustHost, + uint32_t sessionSeed, int* deltaStart, int packetFlags, int initialBackoff, int maxBackoff); + +template +bool netTimeSync__InitializeTimeStub(rage::netTimeSync* timeSync, rage::netConnectionManager* connectionMgr, + uint32_t flags, void* trustHost, uint32_t sessionSeed, int* deltaStart, int packetFlags, int initialBackoff, int maxBackoff) +{ + if (!icgi->OneSyncEnabled) + { + return g_origInitializeTime(timeSync, connectionMgr, flags, trustHost, sessionSeed, deltaStart, packetFlags, initialBackoff, maxBackoff); + } + + timeSync->SetConnectionManager(connectionMgr); + + return true; +} +template +void rage::netTimeSync::Update() +{ + if (!icgi->OneSyncEnabled || !g_netLibrary) + { + return; + } + + if (/*m_connectionMgr /*&& m_flags & 2 && */ !m_disabled) + { + uint32_t curTime = timeGetTime(); + + if (!m_nextSync || int32_t(timeGetTime() - m_nextSync) >= 0) + { + m_requestSequence++; + + net::Buffer outBuffer; + outBuffer.Write(curTime); // request time + outBuffer.Write(m_requestSequence); // request sequence + + g_netLibrary->SendReliableCommand("msgTimeSyncReq", (const char*)outBuffer.GetData().data(), outBuffer.GetCurOffset()); + + m_nextSync = (curTime + m_effectiveTimeBetweenSyncs) | 1; + } + } +} + +template +void rage::netTimeSync::HandleTimeSync(net::Buffer& buffer) +{ + auto reqTime = buffer.Read(); + auto reqSequence = buffer.Read(); + auto resDelta = buffer.Read(); + + if (m_disabled) + { + return; + } + + /*if (!(m_flags & 2)) + { + return; + }*/ + + // out of order? + if (int32_t(reqSequence - m_replySequence) <= 0) + { + return; + } + + auto rtt = timeGetTime() - reqTime; + + // bad timestamp, negative time passed + if (int32_t(rtt) <= 0) + { + return; + } + + int32_t timeDelta = resDelta + (rtt / 2) - timeGetTime(); + + // is this a low RTT, or did we retry often enough? + if (rtt <= 300 || m_retryCount >= 10) + { + if (!m_lastRtt) + { + m_lastRtt = rtt; + } + + // is RTT within variance, low, or retried? + if (rtt <= 100 || (rtt / m_lastRtt) < 2 || m_retryCount >= 10) + { + m_timeDelta = timeDelta; + m_replySequence = reqSequence; + + // progressive backoff once we've established a valid time base + if (m_effectiveTimeBetweenSyncs < m_configMaxBackoff) + { + m_effectiveTimeBetweenSyncs = std::min(m_configMaxBackoff, m_effectiveTimeBetweenSyncs * 2); + } + + m_retryCount = 0; + + // use flag 4 to reset time at least once, even if game session code has done so to a higher value + if (!(m_applyFlags & 4)) + { + m_lastTime = m_timeDelta + timeGetTime(); + } + + m_applyFlags |= 7; + } + else + { + m_nextSync = 0; + m_retryCount++; + } + + // update average RTT + m_lastRtt = (rtt + m_lastRtt) / 2; + } + else + { + m_nextSync = 0; + m_retryCount++; + } +} + +template +bool rage::netTimeSync::IsInitialized() +{ + if (!g_initedTimeSync) + { +#ifdef IS_RDR3 + // we don't want to use cloud time + m_useCloudTime = false; +#endif + + g_origInitializeTime(this, _getConnectionManager(), 1, nullptr, 0, nullptr, 7, 2000, 60000); + + // to make the game not try to get time from us + m_connectionMgr = nullptr; + + g_initedTimeSync = true; + + return false; + } + + return (m_applyFlags & 4) != 0; +} + +template +inline void rage::netTimeSync::SetConnectionManager(netConnectionManager* mgr) +{ + m_connectionMgr = mgr; +} + +bool IsWaitingForTimeSync() +{ +#ifdef GTA_FIVE + if (xbr::IsGameBuildOrGreater<2372>()) + { + return !(*g_netTimeSync<2372>)->IsInitialized(); + } + else if (xbr::IsGameBuildOrGreater<2060>()) + { + return !(*g_netTimeSync<2060>)->IsInitialized(); + } + + return !(*g_netTimeSync<1604>)->IsInitialized(); +#elif IS_RDR3 + if (xbr::IsGameBuildOrGreater<1355>()) + { + return !(*g_netTimeSync<1355>)->IsInitialized(); + } + + return !(*g_netTimeSync<1311>)->IsInitialized(); +#endif +} + +inline void TimeSyncMainGameFrameUpdate() +{ +#if GTA_FIVE + if (xbr::IsGameBuildOrGreater<2372>()) + { + (*g_netTimeSync<2372>)->Update(); + } + else if (xbr::IsGameBuildOrGreater<2060>()) + { + (*g_netTimeSync<2060>)->Update(); + } + else + { + (*g_netTimeSync<1604>)->Update(); + } +#elif IS_RDR3 + if (xbr::IsGameBuildOrGreater<1355>()) + { + (*g_netTimeSync<1355>)->Update(); + } + else + { + (*g_netTimeSync<1311>)->Update(); + } +#endif +} + +inline void HandleTimeSyncUpdatePacket(net::Buffer& buf) +{ +#ifdef GTA_FIVE + if (xbr::IsGameBuildOrGreater<2372>()) + { + (*g_netTimeSync<2372>)->HandleTimeSync(buf); + } + else if (xbr::IsGameBuildOrGreater<2060>()) + { + (*g_netTimeSync<2060>)->HandleTimeSync(buf); + } + else + { + (*g_netTimeSync<1604>)->HandleTimeSync(buf); + } +#elif IS_RDR3 + if (xbr::IsGameBuildOrGreater<1355>()) + { + (*g_netTimeSync<1355>)->HandleTimeSync(buf); + } + else + { + (*g_netTimeSync<1311>)->HandleTimeSync(buf); + } +#endif +} + +static InitFunction initFunction([]() +{ + NetLibrary::OnNetLibraryCreate.Connect([](NetLibrary* lib) + { + lib->AddReliableHandler("msgTimeSync", [](const char* data, size_t len) + { + net::Buffer buf(reinterpret_cast(data), len); + HandleTimeSyncUpdatePacket(buf); + }); + }); +}); + +static HookFunction hookFunction([]() +{ + MH_Initialize(); + + { +#ifdef GTA_FIVE + void* func = xbr::IsGameBuildOrGreater<2372>() ? (void*)&netTimeSync__InitializeTimeStub<2372> : + xbr::IsGameBuildOrGreater<2060>() ? (void*)&netTimeSync__InitializeTimeStub<2060> : + &netTimeSync__InitializeTimeStub<1604>; + + auto location = hook::get_pattern("48 8B D9 48 39 79 08 0F 85 ? ? 00 00 41 8B E8", -32); +#elif IS_RDR3 + void* func = (xbr::IsGameBuildOrGreater<1355>()) ? (void*)&netTimeSync__InitializeTimeStub<1355> : + &netTimeSync__InitializeTimeStub<1311>; + + auto location = xbr::IsGameBuildOrGreater<1436>() ? hook::get_pattern("83 C8 FF 4C 89 77 08 83 FD", -87) : + hook::get_pattern("48 89 51 08 41 83 F8 02 44 0F 45 C8", -49); +#endif + + MH_CreateHook(location, func, (void**)&g_origInitializeTime); + } + + MH_EnableHook(MH_ALL_HOOKS); + + { +#ifdef GTA_FIVE + if (xbr::IsGameBuildOrGreater<2372>()) + { + g_netTimeSync<2372> = hook::get_address**>(hook::get_pattern("48 8B 0D ? ? ? ? 45 33 C9 45 33 C0 41 8D 51 01 E8", 3)); + } + else if (xbr::IsGameBuildOrGreater<2060>()) + { + g_netTimeSync<2060> = hook::get_address**>(hook::get_pattern("48 8B 0D ? ? ? ? 45 33 C9 45 33 C0 41 8D 51 01 E8", 3)); + } + else + { + g_netTimeSync<1604> = hook::get_address**>(hook::get_pattern("EB 16 48 8B 0D ? ? ? ? 45 33 C9 45 33 C0", 5)); + } +#elif IS_RDR3 + auto location = hook::get_pattern("4C 8D 45 50 41 03 C7 44 89 6D 50 89", -4); + + if (xbr::IsGameBuildOrGreater<1355>()) + { + g_netTimeSync<1355> = hook::get_address**>(location); + } + else + { + g_netTimeSync<1311> = hook::get_address**>(location); + } +#endif + } + + OnMainGameFrame.Connect([]() + { + TimeSyncMainGameFrameUpdate(); + }); + +#ifdef IS_RDR3 + OnKillNetworkDone.Connect([]() + { + // RDR3 doesn't restart netTimeSync after disconnecting from a server with enabled OneSync + g_initedTimeSync = false; + }); +#endif +}); diff --git a/code/components/gta-net-rdr3/component.lua b/code/components/gta-net-rdr3/component.lua index 985f2bb81c..a2a8fb044f 100644 --- a/code/components/gta-net-rdr3/component.lua +++ b/code/components/gta-net-rdr3/component.lua @@ -17,6 +17,7 @@ return function() 'components/gta-net-five/src/CloneObjectManager.cpp', 'components/gta-net-five/src/ObjectIdManager.cpp', 'components/gta-net-five/src/rlNetBuffer.cpp', + 'components/gta-net-five/src/netTimeSync.cpp', 'components/gta-net-five/src/MumbleVoice.cpp', } end diff --git a/code/components/gta-net-rdr3/include/netTimeSync.h b/code/components/gta-net-rdr3/include/netTimeSync.h new file mode 100644 index 0000000000..e03f85dd07 --- /dev/null +++ b/code/components/gta-net-rdr3/include/netTimeSync.h @@ -0,0 +1,58 @@ +#pragma once + +struct TimeSyncPadding1311 +{ + char m_pad[4]; +}; + +struct TimeSyncPadding1355 +{ + char m_pad[24]; +}; + +template +using TimeSyncPadding = std::conditional_t; + +namespace rage +{ +class netConnectionManager; + +template +class netTimeSync +{ +public: + virtual ~netTimeSync() = 0; + + void Update(); + void HandleTimeSync(net::Buffer& buffer); + bool IsInitialized(); + void SetConnectionManager(netConnectionManager* mgr); + +private: + netConnectionManager* m_connectionMgr; // +8 (1311) + uint32_t m_unkTrust; // +16 + uint32_t m_sessionKey; // +20 + char m_pad_24[44]; // +24 + TimeSyncPadding<(Build >= 1355)> m_pad_68; + uint32_t m_nextSync; // +72 + uint32_t m_configTimeBetweenSyncs; // +76 + uint32_t m_configMaxBackoff; // +80, usually 60000 + uint32_t m_effectiveTimeBetweenSyncs; // +84 + uint32_t m_lastRtt; // +88 + uint32_t m_retryCount; // +92 + uint32_t m_requestSequence; // +96 + uint32_t m_replySequence; // +100 + uint32_t m_flags; // +104 + uint32_t m_packetFlags; // +108 + int32_t m_timeDelta; // +112 + char m_pad_116[28]; + uint32_t m_lastTime; // +144, used to prevent time from going backwards + uint8_t m_applyFlags; // +148 + uint8_t m_unk5; // +149 + uint8_t m_disabled; // +150 + uint8_t m_useCloudTime; // +151 +}; +} + +static_assert(sizeof(rage::netTimeSync<1311>) == 152); +static_assert(sizeof(rage::netTimeSync<1355>) == 176);