From 1dda47256247ec3cbfdaf4bc7b2ee66976c8e22d Mon Sep 17 00:00:00 2001 From: maybegreat48 <96936658+maybegreat48@users.noreply.github.com> Date: Mon, 19 Dec 2022 17:39:06 +0000 Subject: [PATCH] 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! --- scripts/gtav-classes.cmake | 2 +- src/backend/script_patches.hpp | 1 + src/function_types.hpp | 3 + src/gta/net_game_event.hpp | 3 +- src/hooks/protections/receive_net_message.cpp | 21 ++++ src/native_hooks/freemode.hpp | 3 +- src/pointers.cpp | 12 +++ src/pointers.hpp | 4 + src/script_function.hpp | 1 + src/services/players/player.hpp | 5 + src/util/spam.hpp | 4 +- src/util/toxic.hpp | 102 ++++++++++++++++++ src/views/network/view_session.cpp | 16 +++ src/views/players/player/player_toxic.cpp | 16 +++ 14 files changed, 189 insertions(+), 4 deletions(-) diff --git a/scripts/gtav-classes.cmake b/scripts/gtav-classes.cmake index bb672ef6..f43ca942 100644 --- a/scripts/gtav-classes.cmake +++ b/scripts/gtav-classes.cmake @@ -3,7 +3,7 @@ include(FetchContent) FetchContent_Declare( gtav_classes GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git - GIT_TAG fa5e1d119c025f739075920447eedb7942f5e1fa + GIT_TAG 5ab30af647d7e64404d76ea6168e7f78f5e365af GIT_PROGRESS TRUE CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/src/backend/script_patches.hpp b/src/backend/script_patches.hpp index ed984f13..6d8e52a7 100644 --- a/src/backend/script_patches.hpp +++ b/src/backend/script_patches.hpp @@ -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"), "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"), "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"), "38 00 5D ? ? ? 38 00 5D ? ? ? 38 00 41", 0, std::vector(12, 0x0), nullptr}); // godmode/invisibility detection bypass diff --git a/src/function_types.hpp b/src/function_types.hpp index 5feeb9f3..0def2562 100644 --- a/src/function_types.hpp +++ b/src/function_types.hpp @@ -14,6 +14,7 @@ namespace rage class snSession; class snPlayer; class CDynamicEntity; + class netTimeSyncMsg; } 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 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 sync_network_time = bool(*)(rage::netConnectionManager* mgr, rage::netConnectionPeer* peer, int connection_id, rage::netTimeSyncMsg* msg, int flags); } diff --git a/src/gta/net_game_event.hpp b/src/gta/net_game_event.hpp index 4f12b787..0beb6777 100644 --- a/src/gta/net_game_event.hpp +++ b/src/gta/net_game_event.hpp @@ -397,7 +397,8 @@ namespace rage virtual EventType get_event_type() = 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_connection_identifier; //0x0044 InFrame* m_this; //0x0048 diff --git a/src/hooks/protections/receive_net_message.cpp b/src/hooks/protections/receive_net_message.cpp index 2ea8f4f6..3ab5e447 100644 --- a/src/hooks/protections/receive_net_message.cpp +++ b/src/hooks/protections/receive_net_message.cpp @@ -6,6 +6,7 @@ #include "util/spam.hpp" #include "util/kick.hpp" #include +#include namespace big { @@ -218,6 +219,26 @@ namespace big break; } + case rage::eNetMessage::MsgNetTimeSync: + { + if (player) + { + int action = buffer.Read(2); + uint32_t counter = buffer.Read(32); + uint32_t token = buffer.Read(32); + uint32_t timestamp = buffer.Read(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::system_clock::now()); + if (!player->time_difference || time_diff > player->time_difference.value()) + player->time_difference = time_diff; + } + } + break; + } } } } diff --git a/src/native_hooks/freemode.hpp b/src/native_hooks/freemode.hpp index 374f83b6..f395121a 100644 --- a/src/native_hooks/freemode.hpp +++ b/src/native_hooks/freemode.hpp @@ -1,4 +1,5 @@ #pragma once +#include "script_function.hpp" namespace big { @@ -54,7 +55,7 @@ namespace big }); } - *scr_globals::gsbd.as() = 4; + scr_functions::set_freemode_session_active({}); src->set_return_value(TRUE); } } diff --git a/src/pointers.cpp b/src/pointers.cpp index ff8208ed..6e881245 100644 --- a/src/pointers.cpp +++ b/src/pointers.cpp @@ -732,6 +732,18 @@ namespace big m_save_json_data = ptr.as(); }); + // 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(); + }); + + // Sync Network Time + main_batch.add("SNT", "E8 ? ? ? ? 8B 43 5C", [this](memory::handle ptr) + { + m_sync_network_time = ptr.add(1).rip().as(); + }); + // Queue Dependency main_batch.add("QD", "48 89 5C 24 ? 57 48 83 EC ? 0F B6 99", [this](memory::handle ptr) { diff --git a/src/pointers.hpp b/src/pointers.hpp index 38f27cd8..7afe680f 100644 --- a/src/pointers.hpp +++ b/src/pointers.hpp @@ -18,6 +18,7 @@ namespace rage template class atSingleton; class RageSecurity; + class netTime; } namespace big @@ -217,6 +218,9 @@ namespace big functions::load_cloud_file m_load_cloud_file; functions::set_as_active_cloud_file m_set_as_active_cloud_file; 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{}; diff --git a/src/script_function.hpp b/src/script_function.hpp index ec115da3..f0711ec7 100644 --- a/src/script_function.hpp +++ b/src/script_function.hpp @@ -27,6 +27,7 @@ namespace big namespace scr_functions { 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 init_nightclub_script("INS", RAGE_JOAAT("am_mp_nightclub"), "2D 00 11 00 00 4F", 0); diff --git a/src/services/players/player.hpp b/src/services/players/player.hpp index 12ba3105..9f54732c 100644 --- a/src/services/players/player.hpp +++ b/src/services/players/player.hpp @@ -60,6 +60,11 @@ namespace big int block_join_reason = 0; bool is_spammer = false; + std::optional player_time_value; + std::optional> player_time_value_received_time; + std::optional time_difference; + std::uint32_t num_time_syncs_sent = 9999; + protected: bool equals(const CNetGamePlayer* net_game_player) const; diff --git a/src/util/spam.hpp b/src/util/spam.hpp index 16a48931..727ae3eb 100644 --- a/src/util/spam.hpp +++ b/src/util/spam.hpp @@ -22,7 +22,9 @@ namespace ".gg", "--->", "shopgta5", - "doit#" + "doit#", + "krutka#", + "" }; } diff --git a/src/util/toxic.hpp b/src/util/toxic.hpp index a56d1ca9..af0130da 100644 --- a/src/util/toxic.hpp +++ b/src/util/toxic.hpp @@ -8,6 +8,11 @@ #include "util/scripts.hpp" #include "services/gta_data/gta_data_service.hpp" #include "util/system.hpp" +#include +#include + +#include +#pragma comment(lib, "winmm.lib") namespace big::toxic { @@ -344,4 +349,101 @@ namespace big::toxic { 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::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::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::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); + } } \ No newline at end of file diff --git a/src/views/network/view_session.cpp b/src/views/network/view_session.cpp index a62cc0e7..d93b3910 100644 --- a/src/views/network/view_session.cpp +++ b/src/views/network/view_session.cpp @@ -319,6 +319,22 @@ namespace big ImGui::SameLine(); 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"); ImGui::Checkbox("Disable CEO Money", &g.session.block_ceo_money); if (ImGui::IsItemHovered()) diff --git a/src/views/players/player/player_toxic.cpp b/src/views/players/player/player_toxic.cpp index 9fc99234..e8a70a19 100644 --- a/src/views/players/player/player_toxic.cpp +++ b/src/views/players/player/player_toxic.cpp @@ -109,6 +109,22 @@ namespace big ImGui::SameLine(); 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(); } }