From c1806941993e3a9fe3f3a3671fc2974c296ab927 Mon Sep 17 00:00:00 2001 From: maybegreat48 <96936658+maybegreat48@users.noreply.github.com> Date: Fri, 24 May 2024 21:17:54 +0000 Subject: [PATCH] Session multiplexer and more (#3167) --- cmake/gtav-classes.cmake | 2 +- .../looped/session/block_ceo_creation.cpp | 46 +++++ .../looped/session/increase_player_limit.cpp | 32 +++ src/backend/script_patches.hpp | 69 ++----- src/core/settings.hpp | 8 +- src/gta_pointers.hpp | 9 +- src/hooking/hooking.cpp | 8 +- src/hooking/hooking.hpp | 9 +- src/hooks/matchmaking/advertise_session.cpp | 16 ++ .../create_session_detail_response.cpp | 13 ++ .../process_matchmaking_find_response.cpp | 0 .../start_matchmaking_find_sessions.cpp | 0 src/hooks/matchmaking/unadvertise_session.cpp | 21 ++ .../update_session_advertisement.cpp | 21 ++ .../protections/script_event_handler.cpp | 3 +- .../send_session_matchmaking_attributes.cpp | 25 --- src/pointers.cpp | 56 ++++- .../matchmaking/matchmaking_service.cpp | 191 ++++++++++++++++++ .../matchmaking/matchmaking_service.hpp | 19 ++ src/views/network/view_network.cpp | 2 + src/views/network/view_spoofing.cpp | 10 + 21 files changed, 460 insertions(+), 100 deletions(-) create mode 100644 src/backend/looped/session/block_ceo_creation.cpp create mode 100644 src/backend/looped/session/increase_player_limit.cpp create mode 100644 src/hooks/matchmaking/advertise_session.cpp create mode 100644 src/hooks/matchmaking/create_session_detail_response.cpp rename src/hooks/{misc => matchmaking}/process_matchmaking_find_response.cpp (100%) rename src/hooks/{misc => matchmaking}/start_matchmaking_find_sessions.cpp (100%) create mode 100644 src/hooks/matchmaking/unadvertise_session.cpp create mode 100644 src/hooks/matchmaking/update_session_advertisement.cpp delete mode 100644 src/hooks/spoofing/send_session_matchmaking_attributes.cpp diff --git a/cmake/gtav-classes.cmake b/cmake/gtav-classes.cmake index 3079c503..fc73953e 100644 --- a/cmake/gtav-classes.cmake +++ b/cmake/gtav-classes.cmake @@ -3,7 +3,7 @@ include(FetchContent) FetchContent_Declare( gtav_classes GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git - GIT_TAG 46c1c63396cf40b206b9f5dd612f293809eefcd1 + GIT_TAG c741a3031de7981d12c97f55569f930a82556d63 GIT_PROGRESS TRUE CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/src/backend/looped/session/block_ceo_creation.cpp b/src/backend/looped/session/block_ceo_creation.cpp new file mode 100644 index 00000000..12497ebe --- /dev/null +++ b/src/backend/looped/session/block_ceo_creation.cpp @@ -0,0 +1,46 @@ +#include "backend/looped_command.hpp" +#include "natives.hpp" +#include "services/players/player_service.hpp" +#include "services/script_patcher/script_patcher_service.hpp" +#include "pointers.hpp" +#include "core/scr_globals.hpp" +#include "gta_util.hpp" +#include "gta/script_handler.hpp" + +namespace big +{ + class block_ceo_creation : looped_command + { + using looped_command::looped_command; + + virtual void on_enable() override + { + g_script_patcher_service->update(); + } + + virtual void on_tick() override + { + if (!STREAMING::IS_PLAYER_SWITCH_IN_PROGRESS()) [[likely]] + { + if (*g_pointers->m_gta.m_is_session_started && SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH("maintransition"_J) == 0) + { + if (auto freemode = gta_util::find_script_thread("freemode"_J); freemode && freemode->m_net_component + && ((CGameScriptHandlerNetComponent*)freemode->m_net_component)->is_local_player_host()) + { + for (int i = 0; i < 10; i++) + { + *scr_globals::gsbd_fm_events.at(11).at(132).at(i, 1).as() = self::id; + } + } + } + } + } + + virtual void on_disable() override + { + g_script_patcher_service->update(); + } + }; + + block_ceo_creation g_block_ceo_creation("blockceos", "BLOCK_CEO_CREATION", "BLOCK_CEO_CREATION_DESC", g.session.block_ceo_creation); +} diff --git a/src/backend/looped/session/increase_player_limit.cpp b/src/backend/looped/session/increase_player_limit.cpp new file mode 100644 index 00000000..f464d740 --- /dev/null +++ b/src/backend/looped/session/increase_player_limit.cpp @@ -0,0 +1,32 @@ +#include "backend/looped_command.hpp" +#include "pointers.hpp" +#include "natives.hpp" + +namespace big +{ + class increase_player_limit : looped_command + { + using looped_command::looped_command; + memory::byte_patch* m_join_request_patch; + + virtual void on_enable() override + { + if (!m_join_request_patch) + m_join_request_patch = memory::byte_patch::make(g_pointers->m_gta.m_session_request_patch, std::to_array({0x90, 0x90, 0x90, 0x90, 0x90, 0x90})).get(); + m_join_request_patch->apply(); + } + + virtual void on_tick() override + { + if (*g_pointers->m_gta.m_is_session_started && !NETWORK::NETWORK_IS_ACTIVITY_SESSION()) + NETWORK::NETWORK_SESSION_CHANGE_SLOTS(32, 0); + } + + virtual void on_disable() override + { + m_join_request_patch->restore(); + } + }; + + increase_player_limit g_increase_player_limit("32players", "INCREASE_PLAYER_LIMIT", "INCREASE_PLAYER_LIMIT_DESC", g.spoofing.increase_player_limit); +} diff --git a/src/backend/script_patches.hpp b/src/backend/script_patches.hpp index a8a189cd..20174722 100644 --- a/src/backend/script_patches.hpp +++ b/src/backend/script_patches.hpp @@ -8,69 +8,28 @@ namespace big { void register_script_patches() { - g_script_patcher_service->add_patch( - {"freemode"_J, "freemode1", "2D 01 08 00 ? 38 00 5D ? ? ? 2A 06", 5, {0x71, 0x2E, 0x01, 0x01}, &g.session.decloak_players}); + g_script_patcher_service->add_patch({"freemode"_J, "freemode1", "2D 01 08 00 ? 38 00 5D ? ? ? 2A 06", 5, {0x71, 0x2E, 0x01, 0x01}, &g.session.decloak_players}); g_script_patcher_service->add_patch({"freemode"_J, "script host kick", "2D 01 04 00 ? 2C ? ? ? 5D ? ? ? 71 57 ? ? 2C", 5, {0x2E, 0x01, 0x00}, nullptr}); // script host kick g_script_patcher_service->add_patch({"freemode"_J, "end session kick protection", "5D ? ? ? 76 57 ? ? 5D ? ? ? 76", 0, {0x2E, 0x00, 0x00}, nullptr}); // end session kick protection g_script_patcher_service->add_patch({"freemode"_J, "disable death when undermap/spectating", "2D 01 09 00 00 5D ? ? ? 56 ? ? 3A", 5, {0x2E, 0x01, 0x00}, nullptr}); // disable death when undermap/spectating g_script_patcher_service->add_patch({"freemode"_J, "load island even if stranded animal IPL choice is not set", "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({"freemode"_J, "disable population load balancing", "2D 00 07 00 00 7B", 5, {0x2E, 0x00, 0x00}, nullptr}); // disable population load balancing - g_script_patcher_service->add_patch( - {"freemode"_J, "freemode7", "2D 02 08 00 00 38 01 56", 5, {0x2E, 0x02, 0x00}, &g.session.block_muggers}); - g_script_patcher_service->add_patch( - {"freemode"_J, "freemode8", "2D 00 CF 00 00", 5, {0x2E, 0x00, 0x00}, &g.session.block_ceo_raids}); - g_script_patcher_service->add_patch( - {"freemode"_J, "prevent normal blip update", "06 56 ? ? 38 02 2C ? ? ? 71 71", 0, {0x2B, 0x55}, &g.spoofing.spoof_blip}); // prevent normal blip update - g_script_patcher_service->add_patch({"freemode"_J, - "prevent normal blip update 2", - "2C ? ? ? 55 ? ? 71 2C ? ? ? 61", - 7, - std::vector(16, 0x0), - &g.spoofing.spoof_blip}); // prevent normal blip update 2 + g_script_patcher_service->add_patch({"freemode"_J, "freemode7", "2D 02 08 00 00 38 01 56", 5, {0x2E, 0x02, 0x00}, &g.session.block_muggers}); + g_script_patcher_service->add_patch({"freemode"_J, "freemode8", "2D 00 CF 00 00", 5, {0x2E, 0x00, 0x00}, &g.session.block_ceo_raids}); + g_script_patcher_service->add_patch({"freemode"_J, "prevent normal blip update", "06 56 ? ? 38 02 2C ? ? ? 71 71", 0, {0x2B, 0x55}, &g.spoofing.spoof_blip}); // prevent normal blip update + g_script_patcher_service->add_patch({"freemode"_J, "prevent normal blip update 2", "2C ? ? ? 55 ? ? 71 2C ? ? ? 61", 7, std::vector(16, 0x0), &g.spoofing.spoof_blip}); // prevent normal blip update 2 + g_script_patcher_service->add_patch({"freemode"_J, "stop relinquishing invalid CEO slots", "2D 01 05 00 00 38 00 2C ? ? ? 39 03 38 03 2C ? ? ? 56", 5, {0x2E, 0x01, 0x00}, &g.session.block_ceo_creation}); // stop relinquishing invalid CEO slots g_script_patcher_service->add_patch({"shop_controller"_J, "despawn bypass", "2D 01 04 00 00 2C ? ? ? 56 ? ? 71", 5, {0x71, 0x2E, 0x01, 0x01}, nullptr}); // despawn bypass g_script_patcher_service->add_patch({"shop_controller"_J, "godmode/invisibility detection bypass", "2D 01 03 00 00 5D ? ? ? 06 56 ? ? 2E ? ? 2C", 5, {0x2E, 0x01, 0x00}, nullptr}); // godmode/invisibility detection bypass - g_script_patcher_service->add_patch({"am_mp_nightclub"_J, - "am_mp_nightclub1", - "2D 01 03 00 00 2C ? ? ? 56 ? ? 72 2E ? ? 38 00", - 5, - {0x72, 0x2E, 0x01, 0x01}, - &g.self.dance_mode}); - g_script_patcher_service->add_patch({"am_mp_nightclub"_J, - "am_mp_nightclub2", - "20 56 ? ? 4F ? ? 46 ? ? 41 ? 71", - 0, - {0x2B, 0x55}, - &g.self.dance_mode}); - g_script_patcher_service->add_patch({"freemode"_J, - "freemode9", - "5D ? ? ? 56 ? ? 72 39 05 38 04 2C ? ? ? 58", - 0, - {0x2B, 0x2B, 0x2B, 0x00, 0x55}, - &g.self.invisibility}); - g_script_patcher_service->add_patch({"freemode"_J, - "freemode10", - "2D 01 03 00 00 38 00 71 72 5D ? ? ? 06 56 ? ? 71 2E ? ? 2C ? ? ? 71", - 5, - {0x72, 0x2E, 0x01, 0x01}, - &g.session.unhide_players_from_player_list}); - - g_script_patcher_service->add_patch({"carmod_shop"_J, - "disable camera", - "2D 01 0A 00 00 4F ? ? 40 ? 41 ? 39 03", - 5, - {0x2E, 0x01, 0x00}, - &g.vehicle.ls_customs}); // disable camera - g_script_patcher_service->add_patch( - {"carmod_shop"_J, "carmod_shop1", "2D 02 10 00 00 2C", 5, {0x71, 0x2E, 0x02, 0x01}, &g.vehicle.ls_customs}); - g_script_patcher_service->add_patch( - {"carmod_shop"_J, "carmod_shop2", "2D 00 B8 00 00", 5, {0x2E, 0x00, 0x00}, &g.vehicle.ls_customs}); + g_script_patcher_service->add_patch({"am_mp_nightclub"_J, "am_mp_nightclub1", "2D 01 03 00 00 2C ? ? ? 56 ? ? 72 2E ? ? 38 00", 5, {0x72, 0x2E, 0x01, 0x01}, &g.self.dance_mode}); + g_script_patcher_service->add_patch({"am_mp_nightclub"_J, "am_mp_nightclub2", "20 56 ? ? 4F ? ? 46 ? ? 41 ? 71", 0, {0x2B, 0x55}, &g.self.dance_mode}); + g_script_patcher_service->add_patch({"freemode"_J, "freemode9", "5D ? ? ? 56 ? ? 72 39 05 38 04 2C ? ? ? 58", 0, {0x2B, 0x2B, 0x2B, 0x00, 0x55}, &g.self.invisibility}); + g_script_patcher_service->add_patch({"freemode"_J, "freemode10", "2D 01 03 00 00 38 00 71 72 5D ? ? ? 06 56 ? ? 71 2E ? ? 2C ? ? ? 71", 5, {0x72, 0x2E, 0x01, 0x01}, &g.session.unhide_players_from_player_list}); + g_script_patcher_service->add_patch({"carmod_shop"_J, "disable camera", "2D 01 0A 00 00 4F ? ? 40 ? 41 ? 39 03", 5, {0x2E, 0x01, 0x00}, &g.vehicle.ls_customs}); // disable camera + g_script_patcher_service->add_patch({"carmod_shop"_J, "carmod_shop1", "2D 02 10 00 00 2C", 5, {0x71, 0x2E, 0x02, 0x01}, &g.vehicle.ls_customs}); + g_script_patcher_service->add_patch({"carmod_shop"_J, "carmod_shop2", "2D 00 B8 00 00", 5, {0x2E, 0x00, 0x00}, &g.vehicle.ls_customs}); g_script_patcher_service->add_patch({"carmod_shop"_J, "allow all vehicles", "2D 03 16 00 00 5D", 5, {0x72, 0x2E, 0x03, 0x01}, nullptr}); // allow all vehicles - g_script_patcher_service->add_patch({"carmod_shop"_J, - "allow all vehicles 2", - "2D 03 07 00 00 71 38 02", - 5, - {0x72, 0x2E, 0x03, 0x01}, - nullptr}); // allow all vehicles 2 + g_script_patcher_service->add_patch({"carmod_shop"_J, "allow all vehicles 2", "2D 03 07 00 00 71 38 02", 5, {0x72, 0x2E, 0x03, 0x01}, nullptr}); // allow all vehicles 2 for (auto& entry : *g_pointers->m_gta.m_script_program_table) { diff --git a/src/core/settings.hpp b/src/core/settings.hpp index b10a62fc..60426c09 100644 --- a/src/core/settings.hpp +++ b/src/core/settings.hpp @@ -436,6 +436,7 @@ namespace big bool block_jobs = false; bool block_muggers = false; bool block_ceo_raids = false; + bool block_ceo_creation = false; int send_to_apartment_idx = 1; int send_to_warehouse_idx = 1; @@ -468,7 +469,7 @@ namespace big NLOHMANN_DEFINE_TYPE_INTRUSIVE(chat_translator, enabled, print_result, draw_result, bypass_same_language, target_language, endpoint); } chat_translator{}; - NLOHMANN_DEFINE_TYPE_INTRUSIVE(session, log_chat_messages, log_text_messages, decloak_players, force_session_host, force_script_host, player_magnet_enabled, player_magnet_count, is_team, join_in_sctv_slots, kick_chat_spammers, kick_host_when_forcing_host, explosion_karma, damage_karma, disable_traffic, disable_peds, force_thunder, block_ceo_money, randomize_ceo_colors, block_jobs, block_muggers, block_ceo_raids, send_to_apartment_idx, send_to_warehouse_idx, chat_commands, chat_command_default_access_level, show_cheating_message, anonymous_bounty, lock_session, fast_join, unhide_players_from_player_list, allow_friends_into_locked_session, trust_friends, use_spam_timer, spam_timer, spam_length, chat_translator) + NLOHMANN_DEFINE_TYPE_INTRUSIVE(session, log_chat_messages, log_text_messages, decloak_players, force_session_host, force_script_host, player_magnet_enabled, player_magnet_count, is_team, join_in_sctv_slots, kick_chat_spammers, kick_host_when_forcing_host, explosion_karma, damage_karma, disable_traffic, disable_peds, force_thunder, block_ceo_money, randomize_ceo_colors, block_jobs, block_muggers, block_ceo_raids, block_ceo_creation, send_to_apartment_idx, send_to_warehouse_idx, chat_commands, chat_command_default_access_level, show_cheating_message, anonymous_bounty, lock_session, fast_join, unhide_players_from_player_list, allow_friends_into_locked_session, trust_friends, use_spam_timer, spam_timer, spam_length, chat_translator) } session{}; struct settings @@ -709,10 +710,13 @@ namespace big bool spoof_session_player_count = false; int session_player_count = 25; int spoof_session_bad_sport_status = 0; + bool multiplex_session = false; + int multiplex_count = 2; + bool increase_player_limit = false; bool voice_chat_audio = false; - NLOHMANN_DEFINE_TYPE_INTRUSIVE(spoofing, hide_from_player_list, spoof_blip, blip_type, spoof_rank, rank, spoof_job_points, job_points, spoof_kd_ratio, kd_ratio, spoof_bad_sport, badsport_type, spoof_player_model, player_model, spoof_cheater, spoof_hide_god, spoof_hide_veh_god, spoof_hide_spectate, spoof_crew_data, crew_tag, rockstar_crew, square_crew_tag, spoof_session_region_type, session_region_type, spoof_session_language, session_language, spoof_session_player_count, session_player_count, spoof_session_bad_sport_status, voice_chat_audio) + NLOHMANN_DEFINE_TYPE_INTRUSIVE(spoofing, hide_from_player_list, spoof_blip, blip_type, spoof_rank, rank, spoof_job_points, job_points, spoof_kd_ratio, kd_ratio, spoof_bad_sport, badsport_type, spoof_player_model, player_model, spoof_cheater, spoof_hide_god, spoof_hide_veh_god, spoof_hide_spectate, spoof_crew_data, crew_tag, rockstar_crew, square_crew_tag, spoof_session_region_type, session_region_type, spoof_session_language, session_language, spoof_session_player_count, session_player_count, spoof_session_bad_sport_status, multiplex_session, multiplex_count, increase_player_limit, voice_chat_audio) } spoofing{}; struct vehicle diff --git a/src/gta_pointers.hpp b/src/gta_pointers.hpp index 88219f93..43ab495a 100644 --- a/src/gta_pointers.hpp +++ b/src/gta_pointers.hpp @@ -232,8 +232,6 @@ namespace big PVOID m_http_start_request; - PVOID m_send_session_matchmaking_attributes; - PVOID m_serialize_take_off_ped_variation_task; PVOID m_serialize_parachute_task; @@ -374,6 +372,13 @@ namespace big PVOID m_can_send_node_to_player; PVOID m_write_node; functions::get_sector_data m_get_sector_data; + + PVOID m_advertise_session; + PVOID m_update_session_advertisement; + PVOID m_unadvertise_session; + PVOID m_send_session_detail_msg; + + PVOID m_session_request_patch; }; #pragma pack(pop) static_assert(sizeof(gta_pointers) % 8 == 0, "Pointers are not properly aligned"); diff --git a/src/hooking/hooking.cpp b/src/hooking/hooking.cpp index 652790bd..2f278ec3 100644 --- a/src/hooking/hooking.cpp +++ b/src/hooking/hooking.cpp @@ -84,8 +84,6 @@ namespace big detour_hook_helper::add("BNA", g_pointers->m_gta.m_broadcast_net_array); - detour_hook_helper::add("SSMA", g_pointers->m_gta.m_send_session_matchmaking_attributes); - detour_hook_helper::add("STOPVT", g_pointers->m_gta.m_serialize_take_off_ped_variation_task); detour_hook_helper::add("SPT", g_pointers->m_gta.m_serialize_parachute_task); @@ -137,6 +135,12 @@ namespace big detour_hook_helper::add("FI", g_pointers->m_gta.m_format_int); detour_hook_helper::add("SLC", g_pointers->m_gta.m_searchlight_crash); + + detour_hook_helper::add("AS", g_pointers->m_gta.m_advertise_session); + detour_hook_helper::add("USA", g_pointers->m_gta.m_update_session_advertisement); + detour_hook_helper::add("US", g_pointers->m_gta.m_unadvertise_session); + detour_hook_helper::add("SSDM", g_pointers->m_gta.m_send_session_detail_msg); + detour_hook_helper::add("WND", g_pointers->m_gta.m_write_node_data); detour_hook_helper::add("CSNTP", g_pointers->m_gta.m_can_send_node_to_player); diff --git a/src/hooking/hooking.hpp b/src/hooking/hooking.hpp index b5b47d93..093f1577 100644 --- a/src/hooking/hooking.hpp +++ b/src/hooking/hooking.hpp @@ -39,6 +39,7 @@ class GtaThread; class CNetworkPlayerMgr; class CNetworkObjectMgr; class CPhysicalScriptGameStateDataNode; +class MatchmakingId; enum class eAckCode : uint32_t; @@ -58,6 +59,7 @@ namespace rage class json_serializer; class netGameEvent; class netSyncDataNode; + class rlSessionDetailMsg; } namespace big @@ -125,8 +127,6 @@ namespace big static unsigned int broadcast_net_array(rage::netArrayHandlerBase* _this, CNetGamePlayer* target, rage::datBitBuffer* bit_buffer, uint16_t counter, uint32_t* elem_start, bool silent); - static bool send_session_matchmaking_attributes(void* a1, rage::rlSessionInfo* info, uint64_t session_id, bool use_session_id, MatchmakingAttributes* attributes); - static void serialize_take_off_ped_variation_task(ClonedTakeOffPedVariationInfo* info, rage::CSyncDataBase* serializer); static void serialize_parachute_task(__int64 info, rage::CSyncDataBase* serializer); @@ -195,6 +195,11 @@ namespace big static bool can_send_node_to_player(void* node, rage::netObject* object, std::uint8_t player, int sync_type, int a5, int a6); static bool write_node(rage::netSyncDataNode* node, int sync_type, int a3, rage::netObject* object, rage::datBitBuffer* buffer, int a6, void* log, std::uint8_t player, int* a9, int* a10); static void searchlight_crash(void* a1, CPed* ped); + + static bool advertise_session(int profile_index, int num_slots, int available_slots, MatchmakingAttributes* data, std::uint64_t session_id, rage::rlSessionInfo* info, MatchmakingId* out_id, rage::rlTaskStatus* status); + static bool update_session_advertisement(int profile_index, MatchmakingId* id, int num_slots, int available_slots, rage::rlSessionInfo* info, MatchmakingAttributes* data, rage::rlTaskStatus* status); + static bool unadvertise_session(int profile_index, MatchmakingId* id, rage::rlTaskStatus* status); + static void send_session_detail_msg(rage::netConnectionManager* mgr, rage::netConnection::InFrame* request_frame, rage::rlSessionDetailMsg* msg); }; class minhook_keepalive diff --git a/src/hooks/matchmaking/advertise_session.cpp b/src/hooks/matchmaking/advertise_session.cpp new file mode 100644 index 00000000..d1e97a48 --- /dev/null +++ b/src/hooks/matchmaking/advertise_session.cpp @@ -0,0 +1,16 @@ +#include "hooking/hooking.hpp" +#include "services/matchmaking/matchmaking_service.hpp" + +#include + +namespace big +{ + bool hooks::advertise_session(int profile_index, int num_slots, int available_slots, MatchmakingAttributes* data, std::uint64_t session_id, rage::rlSessionInfo* info, MatchmakingId* out_id, rage::rlTaskStatus* status) + { + if (g_matchmaking_service) [[likely]] + if (g_matchmaking_service->handle_advertise(num_slots, available_slots, info, data, out_id, status)) + return true; + + return g_hooking->get_original()(profile_index, num_slots, available_slots, data, session_id, info, out_id, status); + } +} \ No newline at end of file diff --git a/src/hooks/matchmaking/create_session_detail_response.cpp b/src/hooks/matchmaking/create_session_detail_response.cpp new file mode 100644 index 00000000..9940261f --- /dev/null +++ b/src/hooks/matchmaking/create_session_detail_response.cpp @@ -0,0 +1,13 @@ +#include "hooking/hooking.hpp" +#include "services/matchmaking/matchmaking_service.hpp" + +namespace big +{ + void hooks::send_session_detail_msg(rage::netConnectionManager* mgr, rage::netConnection::InFrame* request_frame, rage::rlSessionDetailMsg* msg) + { + if (g_matchmaking_service) [[likely]] + g_matchmaking_service->handle_session_detail_send_response(msg); + + g_hooking->get_original()(mgr, request_frame, msg); + } +} \ No newline at end of file diff --git a/src/hooks/misc/process_matchmaking_find_response.cpp b/src/hooks/matchmaking/process_matchmaking_find_response.cpp similarity index 100% rename from src/hooks/misc/process_matchmaking_find_response.cpp rename to src/hooks/matchmaking/process_matchmaking_find_response.cpp diff --git a/src/hooks/misc/start_matchmaking_find_sessions.cpp b/src/hooks/matchmaking/start_matchmaking_find_sessions.cpp similarity index 100% rename from src/hooks/misc/start_matchmaking_find_sessions.cpp rename to src/hooks/matchmaking/start_matchmaking_find_sessions.cpp diff --git a/src/hooks/matchmaking/unadvertise_session.cpp b/src/hooks/matchmaking/unadvertise_session.cpp new file mode 100644 index 00000000..aeeaa6e0 --- /dev/null +++ b/src/hooks/matchmaking/unadvertise_session.cpp @@ -0,0 +1,21 @@ +#include "hooking/hooking.hpp" +#include "services/matchmaking/matchmaking_service.hpp" + +#include + +namespace big +{ + bool hooks::unadvertise_session(int profile_index, MatchmakingId* id, rage::rlTaskStatus* status) + { + if (g_matchmaking_service) [[likely]] + { + if (g_matchmaking_service->handle_unadvertise(id)) + { + status->status = 2; // nope + return true; + } + } + + return g_hooking->get_original()(profile_index, id, status); + } +} \ No newline at end of file diff --git a/src/hooks/matchmaking/update_session_advertisement.cpp b/src/hooks/matchmaking/update_session_advertisement.cpp new file mode 100644 index 00000000..5ba18583 --- /dev/null +++ b/src/hooks/matchmaking/update_session_advertisement.cpp @@ -0,0 +1,21 @@ +#include "hooking/hooking.hpp" +#include "services/matchmaking/matchmaking_service.hpp" + +#include + +namespace big +{ + bool hooks::update_session_advertisement(int profile_index, MatchmakingId* id, int num_slots, int available_slots, rage::rlSessionInfo* info, MatchmakingAttributes* data, rage::rlTaskStatus* status) + { + if (g_matchmaking_service) [[likely]] + g_matchmaking_service->handle_update(num_slots, available_slots, info, data, id); + + if (g.spoofing.increase_player_limit) + { + num_slots = 30; + available_slots = std::max(1, available_slots); + } + + return g_hooking->get_original()(profile_index, id, num_slots, available_slots, info, data, status); + } +} \ No newline at end of file diff --git a/src/hooks/protections/script_event_handler.cpp b/src/hooks/protections/script_event_handler.cpp index c2ca2fbb..34f532ac 100644 --- a/src/hooks/protections/script_event_handler.cpp +++ b/src/hooks/protections/script_event_handler.cpp @@ -125,8 +125,9 @@ namespace big break; case eRemoteEvent::Crash3: { - if (isnan(*(float*)&args[4]) || isnan(*(float*)&args[5])) + if (isnan(*(float*)&args[3]) || isnan(*(float*)&args[4]) || isnan(*(float*)&args[5])) { + session::add_infraction(plyr, Infraction::TRIED_CRASH_PLAYER); g.reactions.crash.process(plyr); return true; } diff --git a/src/hooks/spoofing/send_session_matchmaking_attributes.cpp b/src/hooks/spoofing/send_session_matchmaking_attributes.cpp deleted file mode 100644 index 5832624f..00000000 --- a/src/hooks/spoofing/send_session_matchmaking_attributes.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "hooking/hooking.hpp" -#include "network/Network.hpp" - -namespace big -{ - bool hooks::send_session_matchmaking_attributes(void* a1, rage::rlSessionInfo* info, uint64_t session_id, bool use_session_id, MatchmakingAttributes* attributes) - { - if (g.spoofing.spoof_session_region_type) - attributes->m_param_values[4] = g.spoofing.session_region_type; - - if (g.spoofing.spoof_session_language) - attributes->m_param_values[3] = g.spoofing.session_language; - - if (g.spoofing.spoof_session_player_count) - attributes->m_param_values[7] = g.spoofing.session_player_count; - - if (g.spoofing.spoof_session_bad_sport_status == 1) - attributes->m_param_values[0] |= (1 << 14); // Bad Sport - - if (g.spoofing.spoof_session_bad_sport_status == 2) - attributes->m_param_values[0] &= ~(1 << 14); // Good Sport - - return g_hooking->get_original()(a1, info, session_id, use_session_id, attributes); - } -} \ No newline at end of file diff --git a/src/pointers.cpp b/src/pointers.cpp index 2af560b0..cc323057 100644 --- a/src/pointers.cpp +++ b/src/pointers.cpp @@ -27,7 +27,7 @@ namespace big "48 83 EC 28 83 3D ? ? ? ? ? 75 10", [](memory::handle ptr) { - g_pointers->m_gta.m_region_code = ptr.add(16).rip().add(1).as(); + g_pointers->m_gta.m_region_code = ptr.add(16).rip().as(); } }, // Ocean Quads @@ -806,15 +806,6 @@ namespace big g_pointers->m_gta.m_broadcast_net_array = ptr.as(); } }, - // Send Session Matchmaking Attributes - { - "SSMA", - "48 8B C4 48 89 58 08 48 89 68 10 48 89 70 18 48 89 78 20 41 56 48 81 EC D0 00 00 00 49 8B", - [](memory::handle ptr) - { - g_pointers->m_gta.m_send_session_matchmaking_attributes = ptr.as(); - } - }, // Serialize Take Off Ped Variation Task { "STOPVT", @@ -1776,6 +1767,51 @@ namespace big { g_pointers->m_gta.m_get_sector_data = ptr.as(); } + }, + // Advertise Session + { + "AS", + "F6 D8 1B C9 83 C1 05 EB 43", + [](memory::handle ptr) + { + g_pointers->m_gta.m_advertise_session = ptr.sub(4).rip().as(); + } + }, + // Update Session Advertisement + { + "USA", + "84 C0 74 0A 44 89 43 30", + [](memory::handle ptr) + { + g_pointers->m_gta.m_update_session_advertisement = ptr.sub(0xA).rip().as(); + } + }, + // Unadvertise Session + { + "US", + "EB 21 B9 01 00 00 00 87 4B 28", + [](memory::handle ptr) + { + g_pointers->m_gta.m_unadvertise_session = ptr.sub(4).rip().as(); + } + }, + // Send Session Detail Msg + { + "SSDM", + "4C 8D 85 F0 01 00 00 49 8B D5", // unstable + [](memory::handle ptr) + { + g_pointers->m_gta.m_send_session_detail_msg = ptr.add(0xE).rip().as(); + } + }, + // Session Request Patch + { + "SRP", + "48 8B 9D 60 01 00 00 E9 FF 00 00 00", + [](memory::handle ptr) + { + g_pointers->m_gta.m_session_request_patch = ptr.add(0x13).as(); + } } >(); // don't leave a trailing comma at the end diff --git a/src/services/matchmaking/matchmaking_service.cpp b/src/services/matchmaking/matchmaking_service.cpp index a707ea28..ea78d65c 100644 --- a/src/services/matchmaking/matchmaking_service.cpp +++ b/src/services/matchmaking/matchmaking_service.cpp @@ -3,6 +3,7 @@ #include "hooking/hooking.hpp" #include "pointers.hpp" #include "script.hpp" +#include "fiber_pool.hpp" #include @@ -18,6 +19,30 @@ namespace big g_matchmaking_service = nullptr; } + void matchmaking_service::patch_matchmaking_attributes(MatchmakingAttributes* attributes) + { + if (g.spoofing.spoof_session_region_type) + attributes->m_param_values[4] = g.spoofing.session_region_type; + + if (g.spoofing.spoof_session_language) + attributes->m_param_values[3] = g.spoofing.session_language; + + if (g.spoofing.spoof_session_player_count && g.spoofing.increase_player_limit) + attributes->m_param_values[2] = std::min(29, g.spoofing.session_player_count); + else if (g.spoofing.spoof_session_player_count) + attributes->m_param_values[2] = g.spoofing.session_player_count; + else if (g.spoofing.increase_player_limit) + attributes->m_param_values[2] = std::min(29u, attributes->m_param_values[2]); + + // TODO: the logic is incorrect + + if (g.spoofing.spoof_session_bad_sport_status == 1) + attributes->m_param_values[0] |= (1 << 14); // Bad Sport + + if (g.spoofing.spoof_session_bad_sport_status == 2) + attributes->m_param_values[0] &= ~(1 << 14); // Good Sport + } + bool matchmaking_service::matchmake(std::optional constraint) { for (auto& session : m_found_sessions) @@ -113,4 +138,170 @@ namespace big m_active = false; return false; } + + bool matchmaking_service::handle_advertise(int num_slots, int available_slots, rage::rlSessionInfo* info, MatchmakingAttributes* attributes, MatchmakingId* out_id, rage::rlTaskStatus* status) + { + patch_matchmaking_attributes(attributes); + + if (!g.spoofing.multiplex_session) + return false; + + if (status->status) + return true; + + status->status = 1; // set in progress + + // create the first advertisement + g_fiber_pool->queue_job([this, num_slots, available_slots, info, attributes, out_id, status] { + rage::rlTaskStatus our_status{}; + if (!g_hooking->get_original()(0, num_slots, available_slots, attributes, -1, info, out_id, &our_status)) + { + LOG(WARNING) << __FUNCTION__ ": advertise_session returned false for first advertisement"; + status->status = 2; + return; + } + + while (our_status.status == 1) + script::get_current()->yield(); + + if (our_status.status == 2) + { + LOG(WARNING) << __FUNCTION__ ": advertise_session failed for first advertisement"; + status->status = 2; + return; + } + + MatchmakingId primary_id = *out_id; // create a copy if the original memory gets deallocated + std::uint32_t id_hash = rage::joaat(primary_id.m_data1); + + // m_data1 is generated from m_data2 and m_data3 + m_multiplexed_sessions.emplace(id_hash, std::vector{ }); + + // create multiplex advertisements + for (int i = 0; i < (g.spoofing.multiplex_count - 1); i++) + { + g_fiber_pool->queue_job([this, num_slots, available_slots, info, attributes, id_hash, i] { + rage::rlTaskStatus status; + MatchmakingId multiplexed_id; + if (!g_hooking->get_original()(0, num_slots, available_slots, attributes, -1, info, &multiplexed_id, &status)) + { + LOG(WARNING) << __FUNCTION__ ": advertise_session returned false for multiplex task " << i; + return; + } + + while (status.status == 1) + script::get_current()->yield(); + + if (status.status == 2) + { + LOG(WARNING) << __FUNCTION__ ": advertise_session failed for multiplex task " << i; + return; + } + + if (auto it = m_multiplexed_sessions.find(id_hash); it != m_multiplexed_sessions.end()) + { + it->second.push_back(multiplexed_id); + } + else + { + LOG(WARNING) << __FUNCTION__ ": created a multiplexed session advertisement, but the primary advertisement no longer exists"; + } + }); + } + status->status = 3; // return success for original caller + }); + + return true; + } + + void matchmaking_service::handle_update(int num_slots, int available_slots, rage::rlSessionInfo* info, MatchmakingAttributes* attributes, MatchmakingId* id) + { + patch_matchmaking_attributes(attributes); + + // this can be fire and forget, but it's probably a good idea to be notified if something goes wrong + if (auto it = m_multiplexed_sessions.find(rage::joaat(id->m_data1)); it != m_multiplexed_sessions.end()) + { + if (!g.spoofing.multiplex_session) + { + // option disabled mid-session + return; + } + + int i = 0; + for (auto& multiplex_session : it->second) + { + g_fiber_pool->queue_job([&multiplex_session, num_slots, available_slots, info, attributes, i] { + rage::rlTaskStatus status; + if (!g_hooking->get_original()(0, &multiplex_session, num_slots, available_slots, info, attributes, &status)) + { + LOG(WARNING) << __FUNCTION__ ": update_session_advertisement returned false for multiplex task " << i; + return; + } + + while (status.status == 1) + script::get_current()->yield(); + + if (status.status == 2) + { + LOG(WARNING) << __FUNCTION__ ": update_session_advertisement failed for multiplex task " << i; + return; + } + + }); + i++; + } + } + } + + bool matchmaking_service::handle_unadvertise(MatchmakingId* id) + { + if (auto it = m_multiplexed_sessions.find(rage::joaat(id->m_data1)); it != m_multiplexed_sessions.end()) + { + for (auto& multiplex_session : it->second) + { + g_hooking->get_original()(0, &multiplex_session, nullptr); + } + + m_multiplexed_sessions.erase(it); + return false; + } + else + { + if (g.spoofing.multiplex_session) + { + for (auto& [_, children] : m_multiplexed_sessions) + { + for (auto& session : children) + { + if (session.m_data2 == id->m_data2 && session.m_data3 == id->m_data3) + { + return true; // prevent auto cleanup + } + } + } + } + } + + return false; + } + + void matchmaking_service::handle_session_detail_send_response(rage::rlSessionDetailMsg* msg) + { + if (msg->m_status == 5) + { + if (g.spoofing.increase_player_limit) + { + msg->m_detail.m_player_count = std::min(29, + g.spoofing.spoof_session_player_count ? g.spoofing.session_player_count : (int)msg->m_detail.m_player_count); + msg->m_detail.m_spectator_count = 0; + msg->m_detail.m_session_config.m_public_slots = 30; + msg->m_detail.m_session_config.m_private_slots = 2; + } + patch_matchmaking_attributes(&msg->m_detail.m_session_config.m_matchmaking_attributes); + } + else + { + LOG(WARNING) << __FUNCTION__ ": sending fail code " << msg->m_status; + } + } } \ No newline at end of file diff --git a/src/services/matchmaking/matchmaking_service.hpp b/src/services/matchmaking/matchmaking_service.hpp index 414c0cd8..67aff5dd 100644 --- a/src/services/matchmaking/matchmaking_service.hpp +++ b/src/services/matchmaking/matchmaking_service.hpp @@ -1,5 +1,16 @@ #pragma once +#include +#include + +namespace rage +{ + class rlSessionInfo; + class rlSessionDetailMsg; +} + +class MatchmakingAttributes; + namespace big { class matchmaking_service @@ -26,12 +37,20 @@ namespace big int m_num_sessions_found = 0; bool m_active = false; session m_found_sessions[MAX_SESSIONS_TO_FIND]; + std::unordered_map> m_multiplexed_sessions; + + void patch_matchmaking_attributes(MatchmakingAttributes* attributes); public: matchmaking_service(); ~matchmaking_service(); bool matchmake(std::optional constraint = std::nullopt); + bool handle_advertise(int num_slots, int available_slots, rage::rlSessionInfo* info, MatchmakingAttributes* attributes, MatchmakingId* out_id, rage::rlTaskStatus* status); + void handle_update(int num_slots, int available_slots, rage::rlSessionInfo* info, MatchmakingAttributes* attributes, MatchmakingId* id); + bool handle_unadvertise(MatchmakingId* id); + void handle_session_detail_send_response(rage::rlSessionDetailMsg* msg); + inline int get_num_found_sessions() { return m_num_sessions_found; diff --git a/src/views/network/view_network.cpp b/src/views/network/view_network.cpp index c4f078b9..65b77e61 100644 --- a/src/views/network/view_network.cpp +++ b/src/views/network/view_network.cpp @@ -603,6 +603,8 @@ namespace big components::script_patch_checkbox("BLOCK_MUGGERS"_T, &g.session.block_muggers, "BLOCK_MUGGERS_DESC"_T.data()); components::script_patch_checkbox("BLOCK_CEO_RAIDS"_T, &g.session.block_ceo_raids, "BLOCK_CEO_RAIDS_DESC"_T); + ImGui::SameLine(); + components::command_checkbox<"blockceos">(); ImGui::EndGroup(); } diff --git a/src/views/network/view_spoofing.cpp b/src/views/network/view_spoofing.cpp index 6453106a..c4d45638 100644 --- a/src/views/network/view_spoofing.cpp +++ b/src/views/network/view_spoofing.cpp @@ -156,5 +156,15 @@ namespace big ImGui::RadioButton("VIEW_SPOOFING_SPORT_GOOD"_T.data(), &g.spoofing.spoof_session_bad_sport_status, 1); ImGui::SameLine(); ImGui::RadioButton("VIEW_SPOOFING_SPORT_BAD"_T.data(), &g.spoofing.spoof_session_bad_sport_status, 2); + + ImGui::Checkbox("MULTIPLEX_SESSION"_T.data(), &g.spoofing.multiplex_session); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("MULTIPLEX_SESSION_DESC"_T.data()); + if (g.spoofing.multiplex_session) + { + ImGui::SameLine(); + ImGui::SliderInt("###multiplex_cnt", &g.spoofing.multiplex_count, 2, 5); + } + components::command_checkbox<"32players">(); } }