From 88aa1f8e5681158af67c5c0d93306da93e0e26c8 Mon Sep 17 00:00:00 2001 From: maybegreat48 <96936658+maybegreat48@users.noreply.github.com> Date: Thu, 24 Nov 2022 21:49:05 +0000 Subject: [PATCH] Add session browser (#633) --- scripts/gtav-classes.cmake | 2 +- src/core/data/language_codes.hpp | 27 +++ src/core/data/region_codes.hpp | 6 +- src/core/globals.hpp | 60 ++++++- src/function_types.hpp | 6 +- src/gta/script_thread.hpp | 2 +- src/hooking.cpp | 7 + src/hooking.hpp | 10 +- .../process_matchmaking_find_response.cpp | 76 +++++++++ .../misc/serialize_join_request_message.cpp | 16 ++ src/hooks/misc/serialize_player_data_msg.cpp | 19 +++ .../misc/start_matchmaking_find_sessions.cpp | 52 ++++++ src/hooks/script/script_vm.cpp | 32 +++- src/main.cpp | 4 + src/memory/byte_patch.hpp | 5 +- src/native_hooks/native_hooks.cpp | 99 ++++++++++- src/native_hooks/native_hooks.hpp | 23 ++- src/native_hooks/network_session_host.hpp | 2 +- src/pointers.cpp | 32 +++- src/pointers.hpp | 7 + src/script_hook.cpp | 119 ------------- src/script_hook.hpp | 30 ---- src/services/gui/gui_service.hpp | 2 + .../matchmaking/matchmaking_service.cpp | 108 ++++++++++++ .../matchmaking/matchmaking_service.hpp | 50 ++++++ src/util/session.hpp | 24 ++- src/util/water.hpp | 2 +- src/views/debug/view_debug_misc.cpp | 10 +- src/views/network/view_session.cpp | 4 + src/views/network/view_session_browser.cpp | 160 ++++++++++++++++++ src/views/players/view_player.cpp | 4 +- src/views/view.hpp | 1 + src/vmt_hook.cpp | 15 +- src/vmt_hook.hpp | 1 + 34 files changed, 809 insertions(+), 208 deletions(-) create mode 100644 src/core/data/language_codes.hpp create mode 100644 src/hooks/misc/process_matchmaking_find_response.cpp create mode 100644 src/hooks/misc/serialize_join_request_message.cpp create mode 100644 src/hooks/misc/serialize_player_data_msg.cpp create mode 100644 src/hooks/misc/start_matchmaking_find_sessions.cpp delete mode 100644 src/script_hook.cpp delete mode 100644 src/script_hook.hpp create mode 100644 src/services/matchmaking/matchmaking_service.cpp create mode 100644 src/services/matchmaking/matchmaking_service.hpp create mode 100644 src/views/network/view_session_browser.cpp diff --git a/scripts/gtav-classes.cmake b/scripts/gtav-classes.cmake index 0f2811d7..efa22ec4 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 16f85bea73691623d1a43c4a912f94f93e509597 + GIT_TAG 6d8c0e6edef4ddef3f8b55f7cbf572743ba18ff3 GIT_PROGRESS TRUE CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/src/core/data/language_codes.hpp b/src/core/data/language_codes.hpp new file mode 100644 index 00000000..7d87712b --- /dev/null +++ b/src/core/data/language_codes.hpp @@ -0,0 +1,27 @@ +#pragma once + +namespace big +{ + struct LanguageType + { + uint32_t id; + const char name[32]; + }; + + const LanguageType languages[] = + { + { 0, "English" }, + { 1, "French" }, + { 2, "German" }, + { 3, "Italian" }, + { 4, "Spanish (Spain)" }, + { 5, "Portuguese (Brazil)" }, + { 6, "Polish" }, + { 7, "Russian" }, + { 8, "Korean" }, + { 9, "Chinese (Traditional)" }, + { 10, "Japanese" }, + { 11, "Spanish (Mexico)" }, + { 12, "Chinese (Simpified)" } + }; +} \ No newline at end of file diff --git a/src/core/data/region_codes.hpp b/src/core/data/region_codes.hpp index 0d01d2a8..0e0d91f8 100644 --- a/src/core/data/region_codes.hpp +++ b/src/core/data/region_codes.hpp @@ -2,12 +2,14 @@ namespace big { - struct RegionType { + struct RegionType + { uint32_t id; const char name[22]; }; - const RegionType regions[] = { + const RegionType regions[] = + { { 0, "CIS" }, { 1, "Africa" }, { 2, "East" }, diff --git a/src/core/globals.hpp b/src/core/globals.hpp index 4d4327f9..6b3c0c2a 100644 --- a/src/core/globals.hpp +++ b/src/core/globals.hpp @@ -9,6 +9,11 @@ class CNetGamePlayer; +namespace rage +{ + class scrProgram; +} + namespace big { class menu_settings; @@ -204,6 +209,7 @@ namespace big bool name_spoof_enabled = false; bool advertise_menu = false; std::string spoofed_name = ""; + bool join_in_sctv_slots = false; // not to be saved bool never_wanted_all = false; @@ -410,6 +416,24 @@ namespace big bool preview_ped = false; }; + struct session_browser + { + bool region_filter_enabled = true; + int region_filter = 0; + + bool language_filter_enabled = false; + int language_filter = 0; + + bool player_count_filter_enabled = false; + int player_count_filter_minimum = 0; + int player_count_filter_maximum = 32; + + int sort_method = 0; + int sort_direction = 0; + + bool replace_game_matchmaking = false; + }; + public: int friend_count = 0; int player_count = 0; @@ -437,6 +461,7 @@ namespace big window window{}; context_menu context_menu{}; esp esp{}; + session_browser session_browser{}; menu_settings(file save_file) : m_save_file(std::move(save_file)) @@ -629,6 +654,7 @@ namespace big this->session.name_spoof_enabled = j["session"]["name_spoof_enabled"]; this->session.advertise_menu = j["session"]["advertise_menu"]; this->session.spoofed_name = j["session"]["spoofed_name"]; + this->session.join_in_sctv_slots = j["session"]["join_in_sctv_slots"]; this->settings.dev_dlc = j["settings"]["dev_dlc"]; this->settings.hotkeys.menu_toggle = j["settings"]["hotkeys"]["menu_toggle"]; @@ -757,6 +783,21 @@ namespace big this->esp.tracer_draw_position[i] = j["esp"]["tracer_draw_position"].at(i); for (int i = 0; i < 2; i++) this->esp.distance_threshold[i] = j["esp"]["distance_threshold"].at(i); + + this->session_browser.region_filter_enabled = j["session_browser"]["region_filter_enabled"]; + this->session_browser.region_filter = j["session_browser"]["region_filter"]; + + this->session_browser.language_filter_enabled = j["session_browser"]["language_filter_enabled"]; + this->session_browser.language_filter = j["session_browser"]["language_filter"]; + + this->session_browser.player_count_filter_enabled = j["session_browser"]["player_count_filter_enabled"]; + this->session_browser.player_count_filter_minimum = j["session_browser"]["player_count_filter_minimum"]; + this->session_browser.player_count_filter_maximum = j["session_browser"]["player_count_filter_maximum"]; + + this->session_browser.sort_method = j["session_browser"]["sort_method"]; + this->session_browser.sort_direction = j["session_browser"]["sort_direction"]; + + this->session_browser.replace_game_matchmaking = j["session_browser"]["replace_game_matchmaking"]; } nlohmann::json to_json() @@ -952,7 +993,8 @@ namespace big { "is_team", this->session.is_team }, { "name_spoof_enabled", this->session.name_spoof_enabled }, { "advertise_menu", this->session.advertise_menu }, - { "spoofed_name", this->session.spoofed_name } + { "spoofed_name", this->session.spoofed_name }, + { "join_in_sctv_slots", this->session.join_in_sctv_slots } } }, { @@ -1141,7 +1183,21 @@ namespace big this->esp.distance_threshold[1] }) } } - } + }, + { + "session_browser", { + { "region_filter_enabled", this->session_browser.region_filter_enabled }, + { "region_filter", this->session_browser.region_filter }, + { "language_filter_enabled", this->session_browser.language_filter_enabled }, + { "language_filter", this->session_browser.language_filter }, + { "player_count_filter_enabled", this->session_browser.player_count_filter_enabled }, + { "player_count_filter_minimum", this->session_browser.player_count_filter_minimum }, + { "player_count_filter_maximum", this->session_browser.player_count_filter_maximum }, + { "sort_method", this->session_browser.sort_method }, + { "sort_direction", this->session_browser.sort_direction }, + { "replace_game_matchmaking", this->session_browser.replace_game_matchmaking } + } + }, }; } diff --git a/src/function_types.hpp b/src/function_types.hpp index 81044a09..8e707aae 100644 --- a/src/function_types.hpp +++ b/src/function_types.hpp @@ -3,6 +3,7 @@ #include class CMsgJoinResponse; +class NetworkGameFilterMatchmakingComponent; namespace rage { @@ -71,13 +72,14 @@ namespace big::functions using fipackfile_mount = bool(*)(rage::fiPackfile* this_, const char* mount_point); using fipackfile_unmount = bool(*)(const char* mount_point); - using start_get_session_by_gamer_handle = bool(*)(int metric_manager, rage::rlGamerHandle* handles, int count, rage::rlSessionByGamerTaskResult* result, int unk, bool* success, int* state); + using start_get_session_by_gamer_handle = bool(*)(int profile_index, rage::rlGamerHandle* handles, int count, rage::rlSessionByGamerTaskResult* result, int unk, bool* success, int* state); + using start_matchmaking_find_sessions = bool(*)(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* m_filter, unsigned int max_sessions, rage::rlSessionInfo* result_sessions, int* result_session_count, int* state); using join_session_by_info = bool(*)(Network* network, rage::rlSessionInfo* info, int unk, int flags, rage::rlGamerHandle* handles, int handlecount); using generate_uuid = bool(*)(std::uint64_t* uuid); using get_vehicle_gadget_array_size = int(*)(eVehicleGadgetType type); - + using write_join_response_data = bool(*)(CMsgJoinResponse* response, void* data, int size, uint32_t* size_used); using queue_packet = bool(*)(rage::netConnectionManager* mgr, int msg_id, void* data, int size, int flags, void* unk); diff --git a/src/gta/script_thread.hpp b/src/gta/script_thread.hpp index 09d5dde4..861c07a5 100644 --- a/src/gta/script_thread.hpp +++ b/src/gta/script_thread.hpp @@ -54,7 +54,7 @@ namespace rage uint32_t m_arg_loc; // 0xC0 char m_padding2[0x4]; // 0xC4 const char* m_exit_message; // 0xC8 - char m_pad[0x4]; + std::uint32_t m_name_hash; // 0xCC char m_name[0x40]; // 0xD4 scriptHandler* m_handler; // 0x114 scriptHandlerNetComponent* m_net_component; // 0x11C diff --git a/src/hooking.cpp b/src/hooking.cpp index 53c18aeb..9a7c4b08 100644 --- a/src/hooking.cpp +++ b/src/hooking.cpp @@ -79,6 +79,13 @@ namespace big detour_hook_helper::add("APTS", g_pointers->m_add_player_to_session); detour_hook_helper::add("SCNM", g_pointers->m_send_chat_net_message); + detour_hook_helper::add("PMFR", g_pointers->m_process_matchmaking_find_response); + detour_hook_helper::add("SJPD", g_pointers->m_serialize_player_data_msg); + + detour_hook_helper::add("SJRM", g_pointers->m_serialize_join_request_message); + + detour_hook_helper::add("SMFS", g_pointers->m_start_matchmaking_find_sessions); + g_hooking = this; } diff --git a/src/hooking.hpp b/src/hooking.hpp index 6e6e86af..c9a36327 100644 --- a/src/hooking.hpp +++ b/src/hooking.hpp @@ -4,7 +4,6 @@ #include "gta/fwddec.hpp" #include "gta/net_game_event.hpp" #include "gta/script_thread.hpp" -#include "script_hook.hpp" #include "vmt_hook.hpp" #include "MinHook.h" #include "gta/enums.hpp" @@ -18,11 +17,13 @@ class CJoinRequestContext; class SessionSortEntry; class RemoteGamerInfoMsg; class CMsgTextMessage; +class CNetGamePlayerDataMsg; namespace rage { class rlMetric; class snSession; + class JSONNode; } namespace big @@ -96,6 +97,13 @@ namespace big static bool add_player_to_session(rage::netConnectionManager* mgr, int receiver_msg_id, int* out_command_hndl, RemoteGamerInfoMsg* msg, int flags, void* unk); static bool send_chat_net_message(rage::netConnectionManager* mgr, int receiver_msg_id, CMsgTextMessage* msg, int flags, void* unk); + + static bool process_matchmaking_find_response(void* _this, void* unused, rage::JSONNode* node, int* unk); + + static bool serialize_player_data_msg(CNetGamePlayerDataMsg* msg, rage::datBitBuffer* buffer); + static bool serialize_join_request_message(RemoteGamerInfoMsg* info, void* data, int size, int* bits_serialized); + + static bool start_matchmaking_find_sessions(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* filter, unsigned int max_sessions, rage::rlSessionInfo* results, int* num_sessions_found, int* status); }; class minhook_keepalive diff --git a/src/hooks/misc/process_matchmaking_find_response.cpp b/src/hooks/misc/process_matchmaking_find_response.cpp new file mode 100644 index 00000000..83b15a37 --- /dev/null +++ b/src/hooks/misc/process_matchmaking_find_response.cpp @@ -0,0 +1,76 @@ +#include "hooking.hpp" +#include "services/matchmaking/matchmaking_service.hpp" + +namespace rage +{ + class JSONNode + { + public: + char* m_key; //0x0000 + char pad_0008[32]; //0x0008 + class rage::JSONNode* m_sibling; //0x0028 + class rage::JSONNode* m_child; //0x0030 + char* m_value; //0x0038 + char pad_0040[8]; //0x0040 + + inline JSONNode* get_child_node(const char* name) + { + for (auto node = m_child; node; node = node->m_sibling) + { + if (strcmp(name, node->m_key) == 0) + return node; + } + + return nullptr; + } + }; //Size: 0x0048 + static_assert(sizeof(rage::JSONNode) == 0x48); +} + +namespace +{ + // https://stackoverflow.com/a/5167641 + static std::vector split(const std::string& s, char seperator) + { + std::vector output; + + std::string::size_type prev_pos = 0, pos = 0; + + while ((pos = s.find(seperator, pos)) != std::string::npos) + { + std::string substring(s.substr(prev_pos, pos - prev_pos)); + + output.push_back(substring); + + prev_pos = ++pos; + } + + output.push_back(s.substr(prev_pos, pos - prev_pos)); // Last word + + return output; + } +} + +namespace big +{ + bool hooks::process_matchmaking_find_response(void* _this, void* unused, rage::JSONNode* node, int* unk) + { + bool ret = g_hooking->get_original()(_this, unused, node, unk); + + if (g_matchmaking_service->is_active()) + { + int i = 0; + for (auto result = node->get_child_node("Results")->m_child; result; result = result->m_sibling) + { + const auto& values = split(result->get_child_node("Attributes")->m_value, ','); + g_matchmaking_service->get_found_sessions()[i].attributes.discriminator = std::stoi(values[2]); + g_matchmaking_service->get_found_sessions()[i].attributes.player_count = std::stoi(values[4]); + g_matchmaking_service->get_found_sessions()[i].attributes.language = std::stoi(values[5]); + g_matchmaking_service->get_found_sessions()[i].attributes.region = std::stoi(values[6]); + i++; + } + } + + return ret; + } +} \ No newline at end of file diff --git a/src/hooks/misc/serialize_join_request_message.cpp b/src/hooks/misc/serialize_join_request_message.cpp new file mode 100644 index 00000000..54ed9d77 --- /dev/null +++ b/src/hooks/misc/serialize_join_request_message.cpp @@ -0,0 +1,16 @@ +#include "hooking.hpp" +#include "gta_util.hpp" +#include +#include + +namespace big +{ + bool hooks::serialize_join_request_message(RemoteGamerInfoMsg* info, void* data, int size, int* bits_serialized) + { + if (info->m_unk == 0) + info->m_unk = 1; + + info->m_required_player_count = 0; + return g_hooking->get_original()(info, data, size, bits_serialized); + } +} \ No newline at end of file diff --git a/src/hooks/misc/serialize_player_data_msg.cpp b/src/hooks/misc/serialize_player_data_msg.cpp new file mode 100644 index 00000000..c82d8730 --- /dev/null +++ b/src/hooks/misc/serialize_player_data_msg.cpp @@ -0,0 +1,19 @@ +#include "hooking.hpp" +#include "gta_util.hpp" +#include +#include + +namespace big +{ + bool hooks::serialize_player_data_msg(CNetGamePlayerDataMsg* msg, rage::datBitBuffer* buffer) + { + int old_group = msg->m_matchmaking_group; + + if (g->session.join_in_sctv_slots) + msg->m_matchmaking_group = 4; + + bool ret = g_hooking->get_original()(msg, buffer); + msg->m_matchmaking_group = old_group; + return ret; + } +} \ No newline at end of file diff --git a/src/hooks/misc/start_matchmaking_find_sessions.cpp b/src/hooks/misc/start_matchmaking_find_sessions.cpp new file mode 100644 index 00000000..b4ebeb36 --- /dev/null +++ b/src/hooks/misc/start_matchmaking_find_sessions.cpp @@ -0,0 +1,52 @@ +#include "hooking.hpp" +#include "services/matchmaking/matchmaking_service.hpp" +#include +#include "fiber_pool.hpp" + +namespace big +{ + bool hooks::start_matchmaking_find_sessions(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* filter, unsigned int max_sessions, rage::rlSessionInfo* results, int* num_sessions_found, int* status) + { + int discriminator = filter->m_param_values[0]; // this is guaranteed to work + + if (g->session_browser.replace_game_matchmaking && filter->m_filter_type == 1) + { + *status = 1; + g_fiber_pool->queue_job([max_sessions, results, num_sessions_found, status, discriminator] + { + bool result = false; + + if (g->session.join_in_sctv_slots) + result = g_matchmaking_service->matchmake(); + else + result = g_matchmaking_service->matchmake(discriminator); + + if (result) + { + for (int i = 0; i < g_matchmaking_service->get_num_found_sessions(); i++) + { + if (g_matchmaking_service->get_found_sessions()[i].is_valid) + { + results[*num_sessions_found] = g_matchmaking_service->get_found_sessions()[i].info; + (*num_sessions_found)++; + + if (max_sessions <= *num_sessions_found) + break; + } + } + + *status = 3; + } + else + { + *status = 2; + } + }); + return true; + } + else + { + return g_hooking->get_original()(profile_index, available_slots, filter, max_sessions, results, num_sessions_found, status); + } + } +} \ No newline at end of file diff --git a/src/hooks/script/script_vm.cpp b/src/hooks/script/script_vm.cpp index 50fbc06e..abd149e1 100644 --- a/src/hooks/script/script_vm.cpp +++ b/src/hooks/script/script_vm.cpp @@ -5,16 +5,30 @@ namespace big { + class script_vm_guard + { + rage::scrProgram* m_program; + uint8_t** m_orig_bytecode; + + public: + script_vm_guard(rage::scrProgram* program) + : m_program(program) + { + m_orig_bytecode = program->m_code_blocks; + + if (auto bytecode = g_script_patcher_service->get_script_bytecode(program->m_name_hash)) + program->m_code_blocks = bytecode; + } + + ~script_vm_guard() + { + m_program->m_code_blocks = m_orig_bytecode; + } + }; + rage::eThreadState hooks::script_vm(uint64_t* start_stack, uint64_t** scr_globals, rage::scrProgram* program, rage::scrThreadContext* ctx) { - uint8_t** orig_bytecode = program->m_code_blocks; - - if (auto bytecode = g_script_patcher_service->get_script_bytecode(program->m_name_hash); bytecode && g_running) - program->m_code_blocks = bytecode; - - auto ret = g_hooking->get_original()(start_stack, scr_globals, program, ctx); - - program->m_code_blocks = orig_bytecode; - return ret; + script_vm_guard guard(program); + return g_hooking->get_original()(start_stack, scr_globals, program, ctx); } } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index f4ce99f0..b7113788 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,6 +24,7 @@ #include "services/vehicle/handling_service.hpp" #include "services/script_patcher/script_patcher_service.hpp" #include "services/player_database/player_database_service.hpp" +#include "services/matchmaking/matchmaking_service.hpp" BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID) { @@ -88,6 +89,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID) auto gui_service_instance = std::make_unique(); auto script_patcher_service_instance = std::make_unique(); auto player_database_service_instance = std::make_unique(); + auto matchmaking_service_instance = std::make_unique(); LOG(INFO) << "Registered service instances..."; g_script_mgr.add_script(std::make_unique