Network Time (#724)
* fix(NativeHooks): Fix softlock when loading Cayo Perico * fear(netTime): Try to modify time remotely * fix(timeSync): Make it work more consistently * feat(netTime): net time sync for all!
This commit is contained in:
parent
e442e284db
commit
c2e9e61c01
@ -3,7 +3,7 @@ include(FetchContent)
|
|||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
gtav_classes
|
gtav_classes
|
||||||
GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git
|
GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git
|
||||||
GIT_TAG fa5e1d119c025f739075920447eedb7942f5e1fa
|
GIT_TAG 5ab30af647d7e64404d76ea6168e7f78f5e365af
|
||||||
GIT_PROGRESS TRUE
|
GIT_PROGRESS TRUE
|
||||||
CONFIGURE_COMMAND ""
|
CONFIGURE_COMMAND ""
|
||||||
BUILD_COMMAND ""
|
BUILD_COMMAND ""
|
||||||
|
@ -11,6 +11,7 @@ namespace big
|
|||||||
g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "2D 00 07 00 00 5D ? ? ? 56 ? ? 71", 5, { 0x2E, 0x00, 0x00 }, &g.tunables.no_idle_kick });
|
g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "2D 00 07 00 00 5D ? ? ? 56 ? ? 71", 5, { 0x2E, 0x00, 0x00 }, &g.tunables.no_idle_kick });
|
||||||
g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "5D ? ? ? 76 57 ? ? 5D ? ? ? 76", 0, { 0x2E, 0x00, 0x00 }, nullptr }); // end session kick protection
|
g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "5D ? ? ? 76 57 ? ? 5D ? ? ? 76", 0, { 0x2E, 0x00, 0x00 }, nullptr }); // end session kick protection
|
||||||
g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "2D 01 09 00 00 5D ? ? ? 56 ? ? 2E", 5, { 0x2E, 0x01, 0x00 }, nullptr }); // disable death when undermap/spectating
|
g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "2D 01 09 00 00 5D ? ? ? 56 ? ? 2E", 5, { 0x2E, 0x01, 0x00 }, nullptr }); // disable death when undermap/spectating
|
||||||
|
g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "71 2E ? ? 55 ? ? 61 ? ? ? 47 ? ? 63", 0, { 0x72 }, nullptr }); // load island even if stranded animal IPL choice is not set
|
||||||
g_script_patcher_service->add_patch({ RAGE_JOAAT("shop_controller"), "2D 01 04 00 00 2C ? ? ? 56 ? ? 71", 5, { 0x71, 0x2E, 0x01, 0x01 }, nullptr }); // despawn bypass
|
g_script_patcher_service->add_patch({ RAGE_JOAAT("shop_controller"), "2D 01 04 00 00 2C ? ? ? 56 ? ? 71", 5, { 0x71, 0x2E, 0x01, 0x01 }, nullptr }); // despawn bypass
|
||||||
g_script_patcher_service->add_patch({ RAGE_JOAAT("shop_controller"), "38 00 5D ? ? ? 38 00 5D ? ? ? 38 00 41", 0, std::vector<uint8_t>(12, 0x0), nullptr}); // godmode/invisibility detection bypass
|
g_script_patcher_service->add_patch({ RAGE_JOAAT("shop_controller"), "38 00 5D ? ? ? 38 00 5D ? ? ? 38 00 41", 0, std::vector<uint8_t>(12, 0x0), nullptr}); // godmode/invisibility detection bypass
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ namespace rage
|
|||||||
class snSession;
|
class snSession;
|
||||||
class snPlayer;
|
class snPlayer;
|
||||||
class CDynamicEntity;
|
class CDynamicEntity;
|
||||||
|
class netTimeSyncMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace datafile_commands
|
namespace datafile_commands
|
||||||
@ -115,4 +116,6 @@ namespace big::functions
|
|||||||
using load_cloud_file = void(*)(sCloudFile** out_cloud_file, char* buffer, int size, const char* reason);
|
using load_cloud_file = void(*)(sCloudFile** out_cloud_file, char* buffer, int size, const char* reason);
|
||||||
using set_as_active_cloud_file = void(*)(datafile_commands::SveFileObject* object, sCloudFile** file);
|
using set_as_active_cloud_file = void(*)(datafile_commands::SveFileObject* object, sCloudFile** file);
|
||||||
using save_json_data = char*(*)(datafile_commands::SveFileObject* object, int* out_length, const char* reason);
|
using save_json_data = char*(*)(datafile_commands::SveFileObject* object, int* out_length, const char* reason);
|
||||||
|
|
||||||
|
using sync_network_time = bool(*)(rage::netConnectionManager* mgr, rage::netConnectionPeer* peer, int connection_id, rage::netTimeSyncMsg* msg, int flags);
|
||||||
}
|
}
|
||||||
|
@ -397,7 +397,8 @@ namespace rage
|
|||||||
virtual EventType get_event_type() = 0;
|
virtual EventType get_event_type() = 0;
|
||||||
virtual uint32_t _0x18() = 0;
|
virtual uint32_t _0x18() = 0;
|
||||||
|
|
||||||
char pad_0008[56]; //0x0008
|
uint32_t m_timestamp; //0x0008
|
||||||
|
char pad_0008[52]; //0x000C
|
||||||
uint32_t m_msg_id; //0x0040
|
uint32_t m_msg_id; //0x0040
|
||||||
uint32_t m_connection_identifier; //0x0044
|
uint32_t m_connection_identifier; //0x0044
|
||||||
InFrame* m_this; //0x0048
|
InFrame* m_this; //0x0048
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "util/spam.hpp"
|
#include "util/spam.hpp"
|
||||||
#include "util/kick.hpp"
|
#include "util/kick.hpp"
|
||||||
#include <network/Network.hpp>
|
#include <network/Network.hpp>
|
||||||
|
#include <network/netTime.hpp>
|
||||||
|
|
||||||
namespace big
|
namespace big
|
||||||
{
|
{
|
||||||
@ -218,6 +219,26 @@ namespace big
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case rage::eNetMessage::MsgNetTimeSync:
|
||||||
|
{
|
||||||
|
if (player)
|
||||||
|
{
|
||||||
|
int action = buffer.Read<int>(2);
|
||||||
|
uint32_t counter = buffer.Read<uint32_t>(32);
|
||||||
|
uint32_t token = buffer.Read<uint32_t>(32);
|
||||||
|
uint32_t timestamp = buffer.Read<uint32_t>(32);
|
||||||
|
uint32_t time_diff = (*g_pointers->m_network_time)->m_time_offset + frame->m_timestamp;
|
||||||
|
|
||||||
|
if (action == 0)
|
||||||
|
{
|
||||||
|
player->player_time_value = timestamp;
|
||||||
|
player->player_time_value_received_time = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
|
||||||
|
if (!player->time_difference || time_diff > player->time_difference.value())
|
||||||
|
player->time_difference = time_diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "script_function.hpp"
|
||||||
|
|
||||||
namespace big
|
namespace big
|
||||||
{
|
{
|
||||||
@ -54,7 +55,7 @@ namespace big
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
*scr_globals::gsbd.as<int*>() = 4;
|
scr_functions::set_freemode_session_active({});
|
||||||
src->set_return_value<BOOL>(TRUE);
|
src->set_return_value<BOOL>(TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -732,6 +732,18 @@ namespace big
|
|||||||
m_save_json_data = ptr.as<functions::save_json_data>();
|
m_save_json_data = ptr.as<functions::save_json_data>();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Network Time
|
||||||
|
main_batch.add("NT", "48 8B 0D ? ? ? ? E8 ? ? ? ? 33 DB 84 C0 74 41", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_network_time = ptr.add(3).rip().as<rage::netTime**>();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sync Network Time
|
||||||
|
main_batch.add("SNT", "E8 ? ? ? ? 8B 43 5C", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_sync_network_time = ptr.add(1).rip().as<functions::sync_network_time>();
|
||||||
|
});
|
||||||
|
|
||||||
// Queue Dependency
|
// Queue Dependency
|
||||||
main_batch.add("QD", "48 89 5C 24 ? 57 48 83 EC ? 0F B6 99", [this](memory::handle ptr)
|
main_batch.add("QD", "48 89 5C 24 ? 57 48 83 EC ? 0F B6 99", [this](memory::handle ptr)
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,7 @@ namespace rage
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
class atSingleton;
|
class atSingleton;
|
||||||
class RageSecurity;
|
class RageSecurity;
|
||||||
|
class netTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace big
|
namespace big
|
||||||
@ -217,6 +218,9 @@ namespace big
|
|||||||
functions::load_cloud_file m_load_cloud_file;
|
functions::load_cloud_file m_load_cloud_file;
|
||||||
functions::set_as_active_cloud_file m_set_as_active_cloud_file;
|
functions::set_as_active_cloud_file m_set_as_active_cloud_file;
|
||||||
functions::save_json_data m_save_json_data;
|
functions::save_json_data m_save_json_data;
|
||||||
|
|
||||||
|
rage::netTime** m_network_time;
|
||||||
|
functions::sync_network_time m_sync_network_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline pointers* g_pointers{};
|
inline pointers* g_pointers{};
|
||||||
|
@ -27,6 +27,7 @@ namespace big
|
|||||||
namespace scr_functions
|
namespace scr_functions
|
||||||
{
|
{
|
||||||
static inline script_function join_ceo("JC", RAGE_JOAAT("freemode"), "2D 04 1D 00 00 5D", 0);
|
static inline script_function join_ceo("JC", RAGE_JOAAT("freemode"), "2D 04 1D 00 00 5D", 0);
|
||||||
|
static inline script_function set_freemode_session_active("SFSA", RAGE_JOAAT("freemode"), "2D 00 02 00 00 75 5D ? ? ? 50", 0);
|
||||||
static inline script_function dance_loop("DL", RAGE_JOAAT("am_mp_nightclub"), "2D 00 14 00 00 4F ? ? 47 ? ? 5D ? ? ? 56", 0);
|
static inline script_function dance_loop("DL", RAGE_JOAAT("am_mp_nightclub"), "2D 00 14 00 00 4F ? ? 47 ? ? 5D ? ? ? 56", 0);
|
||||||
static inline script_function init_nightclub_script("INS", RAGE_JOAAT("am_mp_nightclub"), "2D 00 11 00 00 4F", 0);
|
static inline script_function init_nightclub_script("INS", RAGE_JOAAT("am_mp_nightclub"), "2D 00 11 00 00 4F", 0);
|
||||||
|
|
||||||
|
@ -60,6 +60,11 @@ namespace big
|
|||||||
int block_join_reason = 0;
|
int block_join_reason = 0;
|
||||||
bool is_spammer = false;
|
bool is_spammer = false;
|
||||||
|
|
||||||
|
std::optional<std::uint32_t> player_time_value;
|
||||||
|
std::optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>> player_time_value_received_time;
|
||||||
|
std::optional<std::uint32_t> time_difference;
|
||||||
|
std::uint32_t num_time_syncs_sent = 9999;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool equals(const CNetGamePlayer* net_game_player) const;
|
bool equals(const CNetGamePlayer* net_game_player) const;
|
||||||
|
|
||||||
|
@ -22,7 +22,9 @@ namespace
|
|||||||
".gg",
|
".gg",
|
||||||
"--->",
|
"--->",
|
||||||
"shopgta5",
|
"shopgta5",
|
||||||
"doit#"
|
"doit#",
|
||||||
|
"krutka#",
|
||||||
|
"<b>"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,11 @@
|
|||||||
#include "util/scripts.hpp"
|
#include "util/scripts.hpp"
|
||||||
#include "services/gta_data/gta_data_service.hpp"
|
#include "services/gta_data/gta_data_service.hpp"
|
||||||
#include "util/system.hpp"
|
#include "util/system.hpp"
|
||||||
|
#include <network/Network.hpp>
|
||||||
|
#include <network/netTime.hpp>
|
||||||
|
|
||||||
|
#include <timeapi.h>
|
||||||
|
#pragma comment(lib, "winmm.lib")
|
||||||
|
|
||||||
namespace big::toxic
|
namespace big::toxic
|
||||||
{
|
{
|
||||||
@ -344,4 +349,101 @@ namespace big::toxic
|
|||||||
{
|
{
|
||||||
WEAPON::REMOVE_ALL_PED_WEAPONS(PLAYER::GET_PLAYER_PED_SCRIPT_INDEX(target->id()), FALSE);
|
WEAPON::REMOVE_ALL_PED_WEAPONS(PLAYER::GET_PLAYER_PED_SCRIPT_INDEX(target->id()), FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool set_time(player_ptr target, uint32_t millis)
|
||||||
|
{
|
||||||
|
if (!g_player_service->get_self()->is_host())
|
||||||
|
{
|
||||||
|
g_notification_service->push_error("Modify Time", "Modifying time requires session host");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target->player_time_value.has_value())
|
||||||
|
{
|
||||||
|
g_notification_service->push_error("Modify Time", "We do not have the player's timestamp yet");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
target->num_time_syncs_sent++;
|
||||||
|
|
||||||
|
rage::netTimeSyncMsg msg{};
|
||||||
|
msg.action = 1;
|
||||||
|
msg.counter = target->num_time_syncs_sent;
|
||||||
|
msg.token = (*g_pointers->m_network_time)->m_time_token;
|
||||||
|
msg.timestamp = target->player_time_value.value() + (uint32_t)(std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()) - target->player_time_value_received_time.value()).count();
|
||||||
|
msg.increment = millis;
|
||||||
|
|
||||||
|
auto peer = g_pointers->m_get_connection_peer(gta_util::get_network()->m_game_session_ptr->m_net_connection_mgr, (int)target->get_session_player()->m_player_data.m_peer_id_2);
|
||||||
|
|
||||||
|
for (int j = 0; j < 100; j++)
|
||||||
|
{
|
||||||
|
g_pointers->m_sync_network_time(gta_util::get_network()->m_game_session_ptr->m_net_connection_mgr,
|
||||||
|
peer, (*g_pointers->m_network_time)->m_connection_identifier, &msg, 0x1000000); // repeatedly spamming the event will eventually cause certain bounds checks to disable for some reason
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void warp_time_forward(player_ptr target, uint32_t millis)
|
||||||
|
{
|
||||||
|
if (!target->player_time_value.has_value())
|
||||||
|
{
|
||||||
|
g_notification_service->push_error("Warp Time", "We do not have the player's timestamp yet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_time(target, target->time_difference.value() + millis + (uint32_t)(std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()) - target->player_time_value_received_time.value()).count()))
|
||||||
|
target->time_difference.value() += millis;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void set_time_all(uint32_t millis)
|
||||||
|
{
|
||||||
|
if (!g_player_service->get_self()->is_host())
|
||||||
|
{
|
||||||
|
g_notification_service->push_error("Modify Time", "Modifying time requires session host");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t largest_counter = 9999;
|
||||||
|
g_player_service->iterate([&largest_counter](const player_entry& plyr)
|
||||||
|
{
|
||||||
|
if (plyr.second->num_time_syncs_sent > largest_counter)
|
||||||
|
largest_counter = plyr.second->num_time_syncs_sent;
|
||||||
|
});
|
||||||
|
|
||||||
|
(*g_pointers->m_network_time)->m_time_offset = millis - timeGetTime();
|
||||||
|
|
||||||
|
rage::netTimeSyncMsg msg{};
|
||||||
|
g_player_service->iterate([&largest_counter, &msg, millis](const player_entry& plyr)
|
||||||
|
{
|
||||||
|
if (!plyr.second->player_time_value.has_value())
|
||||||
|
{
|
||||||
|
LOG(WARNING) << "Skipping " << plyr.second->get_name() << " in time warp";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
largest_counter++;
|
||||||
|
|
||||||
|
msg.action = 1;
|
||||||
|
msg.counter = largest_counter;
|
||||||
|
msg.token = (*g_pointers->m_network_time)->m_time_token;
|
||||||
|
msg.timestamp = plyr.second->player_time_value.value() + (uint32_t)(std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()) - plyr.second->player_time_value_received_time.value()).count();
|
||||||
|
msg.increment = millis;
|
||||||
|
|
||||||
|
auto peer = g_pointers->m_get_connection_peer(gta_util::get_network()->m_game_session_ptr->m_net_connection_mgr, (int)plyr.second->get_session_player()->m_player_data.m_peer_id_2);
|
||||||
|
|
||||||
|
for (int j = 0; j < 25; j++)
|
||||||
|
{
|
||||||
|
g_pointers->m_sync_network_time(gta_util::get_network()->m_game_session_ptr->m_net_connection_mgr,
|
||||||
|
peer, (*g_pointers->m_network_time)->m_connection_identifier, &msg, 0x1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
plyr.second->num_time_syncs_sent = largest_counter + 32;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void warp_time_forward_all(uint32_t millis)
|
||||||
|
{
|
||||||
|
set_time_all((*g_pointers->m_network_time)->m_time + millis);
|
||||||
|
}
|
||||||
}
|
}
|
@ -319,6 +319,22 @@ namespace big
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Checkbox("Force Thunder", &g.session.force_thunder);
|
ImGui::Checkbox("Force Thunder", &g.session.force_thunder);
|
||||||
|
|
||||||
|
components::small_text("Warp Time (requires session host)");
|
||||||
|
|
||||||
|
components::button("+1 Minute", [] { toxic::warp_time_forward_all(60 * 1000); });
|
||||||
|
ImGui::SameLine();
|
||||||
|
components::button("+5 Minutes", [] { toxic::warp_time_forward_all(5 * 60 * 1000); });
|
||||||
|
ImGui::SameLine();
|
||||||
|
components::button("+48 Minutes", [] { toxic::warp_time_forward_all(48 * 60 * 1000); });
|
||||||
|
ImGui::SameLine();
|
||||||
|
components::button("+96 Minutes", [] { toxic::warp_time_forward_all(96 * 60 * 1000); });
|
||||||
|
ImGui::SameLine();
|
||||||
|
components::button("+200 Minutes", [] { toxic::warp_time_forward_all(200 * 60 * 1000); });
|
||||||
|
ImGui::SameLine();
|
||||||
|
components::button("Stop Time", [] { toxic::set_time_all(INT_MAX - 3000); });
|
||||||
|
if (ImGui::IsItemHovered())
|
||||||
|
ImGui::SetTooltip("This cannot be reversed. Use with caution");
|
||||||
|
|
||||||
components::sub_title("Script Host Features");
|
components::sub_title("Script Host Features");
|
||||||
ImGui::Checkbox("Disable CEO Money", &g.session.block_ceo_money);
|
ImGui::Checkbox("Disable CEO Money", &g.session.block_ceo_money);
|
||||||
if (ImGui::IsItemHovered())
|
if (ImGui::IsItemHovered())
|
||||||
|
@ -109,6 +109,22 @@ namespace big
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
components::button("Remove All Weapons", [] { toxic::remove_all_weapons(g_player_service->get_selected()); });
|
components::button("Remove All Weapons", [] { toxic::remove_all_weapons(g_player_service->get_selected()); });
|
||||||
|
|
||||||
|
components::small_text("Warp Time (requires session host)");
|
||||||
|
|
||||||
|
components::button("+1 Minute", [] { toxic::warp_time_forward(g_player_service->get_selected(), 60 * 1000); });
|
||||||
|
ImGui::SameLine();
|
||||||
|
components::button("+5 Minutes", [] { toxic::warp_time_forward(g_player_service->get_selected(), 5 * 60 * 1000); });
|
||||||
|
ImGui::SameLine();
|
||||||
|
components::button("+48 Minutes", [] { toxic::warp_time_forward(g_player_service->get_selected(), 48 * 60 * 1000); });
|
||||||
|
ImGui::SameLine();
|
||||||
|
components::button("+96 Minutes", [] { toxic::warp_time_forward(g_player_service->get_selected(), 96 * 60 * 1000); });
|
||||||
|
ImGui::SameLine();
|
||||||
|
components::button("+200 Minutes", [] { toxic::warp_time_forward(g_player_service->get_selected(), 200 * 60 * 1000); });
|
||||||
|
ImGui::SameLine();
|
||||||
|
components::button("Stop Time", [] { toxic::set_time(g_player_service->get_selected(), INT_MAX - 3000); });
|
||||||
|
if (ImGui::IsItemHovered())
|
||||||
|
ImGui::SetTooltip("This cannot be reversed. Use with caution");
|
||||||
|
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user