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(
|
||||
gtav_classes
|
||||
GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git
|
||||
GIT_TAG fa5e1d119c025f739075920447eedb7942f5e1fa
|
||||
GIT_TAG 5ab30af647d7e64404d76ea6168e7f78f5e365af
|
||||
GIT_PROGRESS TRUE
|
||||
CONFIGURE_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"), "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<uint8_t>(12, 0x0), nullptr}); // godmode/invisibility detection bypass
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "util/spam.hpp"
|
||||
#include "util/kick.hpp"
|
||||
#include <network/Network.hpp>
|
||||
#include <network/netTime.hpp>
|
||||
|
||||
namespace big
|
||||
{
|
||||
@ -218,6 +219,26 @@ namespace big
|
||||
|
||||
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
|
||||
#include "script_function.hpp"
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -732,6 +732,18 @@ namespace big
|
||||
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
|
||||
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>
|
||||
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{};
|
||||
|
@ -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);
|
||||
|
||||
|
@ -60,6 +60,11 @@ namespace big
|
||||
int block_join_reason = 0;
|
||||
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:
|
||||
bool equals(const CNetGamePlayer* net_game_player) const;
|
||||
|
||||
|
@ -22,7 +22,9 @@ namespace
|
||||
".gg",
|
||||
"--->",
|
||||
"shopgta5",
|
||||
"doit#"
|
||||
"doit#",
|
||||
"krutka#",
|
||||
"<b>"
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,11 @@
|
||||
#include "util/scripts.hpp"
|
||||
#include "services/gta_data/gta_data_service.hpp"
|
||||
#include "util/system.hpp"
|
||||
#include <network/Network.hpp>
|
||||
#include <network/netTime.hpp>
|
||||
|
||||
#include <timeapi.h>
|
||||
#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::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::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())
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user