Add session browser (#633)

This commit is contained in:
maybegreat48 2022-11-24 21:49:05 +00:00 committed by GitHub
parent d0b523873d
commit 88aa1f8e56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 809 additions and 208 deletions

View File

@ -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 ""

View File

@ -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)" }
};
}

View File

@ -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" },

View File

@ -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 }
}
},
};
}

View File

@ -3,6 +3,7 @@
#include <datanodes/vehicle/CVehicleGadgetDataNode.hpp>
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);

View File

@ -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

View File

@ -79,6 +79,13 @@ namespace big
detour_hook_helper::add<hooks::add_player_to_session>("APTS", g_pointers->m_add_player_to_session);
detour_hook_helper::add<hooks::send_chat_net_message>("SCNM", g_pointers->m_send_chat_net_message);
detour_hook_helper::add<hooks::process_matchmaking_find_response>("PMFR", g_pointers->m_process_matchmaking_find_response);
detour_hook_helper::add<hooks::serialize_player_data_msg>("SJPD", g_pointers->m_serialize_player_data_msg);
detour_hook_helper::add<hooks::serialize_join_request_message>("SJRM", g_pointers->m_serialize_join_request_message);
detour_hook_helper::add<hooks::start_matchmaking_find_sessions>("SMFS", g_pointers->m_start_matchmaking_find_sessions);
g_hooking = this;
}

View File

@ -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

View File

@ -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<std::string> split(const std::string& s, char seperator)
{
std::vector<std::string> 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<hooks::process_matchmaking_find_response>()(_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;
}
}

View File

@ -0,0 +1,16 @@
#include "hooking.hpp"
#include "gta_util.hpp"
#include <network/Network.hpp>
#include <network/RemoteGamerInfoMsg.hpp>
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<hooks::serialize_join_request_message>()(info, data, size, bits_serialized);
}
}

View File

@ -0,0 +1,19 @@
#include "hooking.hpp"
#include "gta_util.hpp"
#include <network/Network.hpp>
#include <network/CNetGamePlayerDataMsg.hpp>
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<hooks::serialize_player_data_msg>()(msg, buffer);
msg->m_matchmaking_group = old_group;
return ret;
}
}

View File

@ -0,0 +1,52 @@
#include "hooking.hpp"
#include "services/matchmaking/matchmaking_service.hpp"
#include <network/Network.hpp>
#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<hooks::start_matchmaking_find_sessions>()(profile_index, available_slots, filter, max_sessions, results, num_sessions_found, status);
}
}
}

View File

@ -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<hooks::script_vm>()(start_stack, scr_globals, program, ctx);
program->m_code_blocks = orig_bytecode;
return ret;
script_vm_guard guard(program);
return g_hooking->get_original<hooks::script_vm>()(start_stack, scr_globals, program, ctx);
}
}

View File

@ -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<gui_service>();
auto script_patcher_service_instance = std::make_unique<script_patcher_service>();
auto player_database_service_instance = std::make_unique<player_database_service>();
auto matchmaking_service_instance = std::make_unique<matchmaking_service>();
LOG(INFO) << "Registered service instances...";
g_script_mgr.add_script(std::make_unique<script>(&gui::script_func, "GUI", false));
@ -135,6 +137,8 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
thread_pool_instance.reset();
LOG(INFO) << "Thread pool uninitialized.";
matchmaking_service_instance.reset();
LOG(INFO) << "Matchmaking Service reset.";
player_database_service_instance.reset();
LOG(INFO) << "Player Database Service reset.";
script_patcher_service_instance.reset();

View File

@ -54,13 +54,14 @@ namespace memory
byte_patch(TAddr address, std::span<T, N> span)
: m_address((void*)address)
{
m_size = span.size_bytes();
m_size = span.size();
m_original_bytes = std::make_unique<byte[]>(m_size);
memcpy(m_original_bytes.get(), m_address, m_size);
m_value = std::make_unique<byte[]>(m_size);
memcpy(m_value.get(), span.data(), m_size);
for (int i = 0; i < m_size; i++)
m_value[i] = span[i];
}
protected:

View File

@ -6,12 +6,98 @@
#include "shop_controller.hpp"
#include "network_session_host.hpp"
#include "am_launcher.hpp"
#include "crossmap.hpp"
#include <script/scrProgram.hpp>
#include <script/scrProgramTable.hpp>
namespace big
{
static bool map_native(rage::scrNativeHash* hash)
{
for (auto const& mapping : g_crossmap)
{
if (mapping.first == *hash)
{
*hash = mapping.second;
return true;
}
}
return false;
}
native_hook::native_hook(rage::scrProgram* program, const std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler>& native_replacements)
{
hook_instance(program, native_replacements);
}
native_hook::~native_hook()
{
if (m_handler_hook)
{
m_handler_hook->disable();
m_handler_hook.reset();
}
if (m_vmt_hook)
{
m_vmt_hook->disable();
m_vmt_hook.reset();
}
}
void native_hook::hook_instance(rage::scrProgram* program, const std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler>& native_replacements)
{
m_program = program;
m_vmt_hook = std::make_unique<vmt_hook>(m_program, 3);
m_vmt_hook->hook(0, &scrprogram_dtor);
m_vmt_hook->enable();
m_handler_hook = std::make_unique<vmt_hook>(&m_program->m_native_entrypoints, m_program->m_native_count);
m_handler_hook->enable();
std::unordered_map<rage::scrNativeHandler, rage::scrNativeHandler> handler_replacements;
for (auto& [replacement_hash, replacement_handler] : native_replacements)
{
auto native = replacement_hash;
map_native(&native);
auto og_handler = g_pointers->m_get_native_handler(g_pointers->m_native_registration_table, native);
if (!og_handler)
continue;
handler_replacements[og_handler] = replacement_handler;
}
for (int i = 0; i < m_program->m_native_count; i++)
{
if (auto it = handler_replacements.find((rage::scrNativeHandler)program->m_native_entrypoints[i]); it != handler_replacements.end())
{
m_handler_hook->hook(i, it->second);
}
}
}
void native_hook::scrprogram_dtor(rage::scrProgram* this_, char free_memory)
{
if (auto it = g_native_hooks->m_native_hooks.find(this_); it != g_native_hooks->m_native_hooks.end())
{
auto og_func = it->second->m_vmt_hook->get_original<decltype(&native_hook::scrprogram_dtor)>(0);
it->second->m_vmt_hook->disable();
it->second->m_vmt_hook.reset();
it->second->m_handler_hook->disable();
it->second->m_handler_hook.reset();
g_native_hooks->m_native_hooks.erase(it);
og_func(this_, free_memory);
}
else
{
LOG(FATAL) << "Cannot find hook for program";
}
}
constexpr auto ALL_SCRIPT_HASH = RAGE_JOAAT("ALL_SCRIPTS");
native_hooks::native_hooks()
@ -38,7 +124,7 @@ namespace big
native_hooks::~native_hooks()
{
m_script_hooks.clear();
m_native_hooks.clear();
g_native_hooks = nullptr;
}
@ -75,15 +161,10 @@ namespace big
if (!native_replacements.empty())
{
m_script_hooks.emplace(
program,
std::make_unique<script_hook>(program, native_replacements)
m_native_hooks.emplace(
program,
std::make_unique<native_hook>(program, native_replacements)
);
}
}
void native_hooks::unhook_program(rage::scrProgram* program)
{
m_script_hooks.erase(program);
}
}

View File

@ -1,16 +1,33 @@
#pragma once
#include "gta/joaat.hpp"
#include "gta/script_thread.hpp"
#include "script_hook.hpp"
#include "vmt_hook.hpp"
namespace big
{
class native_hook final
{
public:
explicit native_hook(rage::scrProgram* program, const std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler>& native_replacements);
~native_hook();
private:
void hook_instance(rage::scrProgram* program, const std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler>& native_replacements);
static void scrprogram_dtor(rage::scrProgram* this_, char free_memory);
rage::scrProgram* m_program;
std::unique_ptr<vmt_hook> m_vmt_hook;
std::unique_ptr<vmt_hook> m_handler_hook;
};
class native_hooks final
{
friend class native_hook;
using native_detour = std::pair<rage::scrNativeHash, rage::scrNativeHandler>;
std::unordered_map<rage::joaat_t, std::vector<native_detour>> m_native_registrations;
std::unordered_map<rage::scrProgram*, std::unique_ptr<script_hook>> m_script_hooks;
std::unordered_map<rage::scrProgram*, std::unique_ptr<native_hook>> m_native_hooks;
public:
native_hooks();
@ -38,8 +55,6 @@ namespace big
void add_native_detour(rage::joaat_t script_hash, rage::scrNativeHash hash, rage::scrNativeHandler detour);
void hook_program(rage::scrProgram* program);
void unhook_program(rage::scrProgram* program);
};
inline native_hooks* g_native_hooks{};

View File

@ -11,7 +11,7 @@ namespace big
{
if (g->session.join_queued)
{
g_pointers->m_join_session_by_info(*g_pointers->m_network, &g->session.info, 0, 0, nullptr, 0);
g_pointers->m_join_session_by_info(*g_pointers->m_network, &g->session.info, 1, 1 | 2 | 4, nullptr, 0);
g->session.join_queued = false;
src->set_return_value<BOOL>(TRUE);
}

View File

@ -440,12 +440,18 @@ namespace big
m_format_metric_for_sending = ptr.as<PVOID>();
});
// Get Session By Gamer Handle
// Start Get Session By Gamer Handle
main_batch.add("SGSBGH", "E8 ? ? ? ? 84 C0 0F 84 ? ? ? ? 8B 05 ? ? ? ? 48 8D 4C 24", [this](memory::handle ptr)
{
m_start_get_session_by_gamer_handle = ptr.add(1).rip().as<functions::start_get_session_by_gamer_handle>();
});
// Start Matchmaking Find Sessions
main_batch.add("SGSBGH", "E8 ? ? ? ? 84 C0 0F 84 F6 FE FF FF", [this](memory::handle ptr)
{
m_start_matchmaking_find_sessions = ptr.add(1).rip().as<functions::start_matchmaking_find_sessions>();
});
// Join Session By Info
main_batch.add("JSBI", "E8 ? ? ? ? 0F B6 CB 84 C0 41 0F 44 CD", [this](memory::handle ptr)
{
@ -554,6 +560,30 @@ namespace big
m_send_chat_net_message = ptr.add(1).rip().as<PVOID>();
});
// Process Matchmaking Find Response
main_batch.add("PMFR", "48 89 5C 24 08 48 89 74 24 10 57 48 81 EC 90 00 00 00 41", [this](memory::handle ptr)
{
m_process_matchmaking_find_response = ptr.as<PVOID>();
});
// Serialize Player Data Message
main_batch.add("SPDM", "48 89 5C 24 08 48 89 74 24 10 48 89 7C 24 18 41 56 48 83 EC 20 BF 01 00 00 00", [this](memory::handle ptr)
{
m_serialize_player_data_msg = ptr.as<PVOID>();
});
// Serialize Join Request Message
main_batch.add("SPDM", "E8 ? ? ? ? 84 C0 0F 84 99 00 00 00 49 8D 8F 78 0D 00 00", [this](memory::handle ptr)
{
m_serialize_join_request_message = ptr.add(1).rip().as<PVOID>();
});
// Is Matchmaking Session Valid
main_batch.add("IMSV", "E8 ? ? ? ? 48 81 C7 B8 03 00 00 88 03", [this](memory::handle ptr)
{
memory::byte_patch::make(ptr.add(1).rip().as<void*>(), std::to_array({ 0xB0, 0x01, 0xC3 }))->apply(); // has no observable side effects
});
auto mem_region = memory::module("GTA5.exe");
main_batch.run(mem_region);

View File

@ -120,6 +120,7 @@ namespace big
Network** m_network;
functions::start_get_session_by_gamer_handle m_start_get_session_by_gamer_handle;
functions::start_matchmaking_find_sessions m_start_matchmaking_find_sessions;
functions::join_session_by_info m_join_session_by_info;
memory::byte_patch* m_bypass_max_count_of_active_sticky_bombs;
@ -169,6 +170,12 @@ namespace big
PVOID m_add_player_to_session;
PVOID m_send_chat_net_message;
PVOID m_process_matchmaking_find_response;
PVOID m_serialize_player_data_msg;
// PVOID m_apply_join_request_data;
PVOID m_serialize_join_request_message;
};
inline pointers* g_pointers{};

View File

@ -1,119 +0,0 @@
#include "common.hpp"
#include "crossmap.hpp"
#include "gta/script_program.hpp"
#include "logger.hpp"
#include "pointers.hpp"
#include "script_hook.hpp"
#include "native_hooks/native_hooks.hpp"
namespace big
{
inline std::unordered_map<rage::scrProgram*, script_hook*> script_hook::s_map;
static bool map_native(rage::scrNativeHash* hash)
{
for (auto const& mapping : g_crossmap)
{
if (mapping.first == *hash)
{
*hash = mapping.second;
return true;
}
}
return false;
}
script_hook::script_hook(rage::joaat_t script_hash, std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler> native_replacements) :
m_script_hash(script_hash),
m_native_replacements(std::move(native_replacements))
{
ensure();
}
script_hook::script_hook(rage::scrProgram* program, std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler> native_replacements) :
m_native_replacements(std::move(native_replacements))
{
hook_instance(program);
}
script_hook::~script_hook()
{
if (m_program)
{
for (auto [hash, handler_ptr] : m_native_handler_ptrs)
{
auto og_handler = g_pointers->m_get_native_handler(g_pointers->m_native_registration_table, hash);
*handler_ptr = og_handler;
}
}
if (m_vmt_hook)
{
m_vmt_hook->disable();
if (m_program)
s_map.erase(m_program);
}
}
void script_hook::ensure()
{
if (m_vmt_hook)
return;
if (auto program = g_pointers->m_script_program_table->find_script(m_script_hash))
{
if (program->is_valid())
{
hook_instance(program);
LOG_IF(G3LOG_DEBUG, g->debug.logs.script_hook_logs) << "Hooked " << program->m_name << " script (" << HEX_TO_UPPER(static_cast<void*>(program)) << ")";
}
}
}
void script_hook::hook_instance(rage::scrProgram* program)
{
m_program = program;
s_map.emplace(m_program, this);
m_vmt_hook = std::make_unique<vmt_hook>(m_program, 3);
m_vmt_hook->hook(0, &scrprogram_dtor);
for (auto [replacement_hash, replacement_handler] : m_native_replacements)
{
auto hash = replacement_hash;
map_native(&hash);
auto og_handler = g_pointers->m_get_native_handler(g_pointers->m_native_registration_table, hash);
if (!og_handler)
continue;
auto handler_ptr = m_program->get_address_of_native_entrypoint(og_handler);
if (!handler_ptr)
continue;
m_native_handler_ptrs.emplace(hash, reinterpret_cast<rage::scrNativeHandler*>(handler_ptr));
*handler_ptr = replacement_handler;
}
}
void script_hook::scrprogram_dtor(rage::scrProgram* this_, bool free_memory)
{
if (auto it = s_map.find(this_); it != s_map.end())
{
auto hook = it->second;
hook->m_program = nullptr;
s_map.erase(it);
auto og_func = hook->m_vmt_hook->get_original<decltype(&scrprogram_dtor)>(0);
hook->m_vmt_hook->disable();
hook->m_vmt_hook.reset();
g_native_hooks->unhook_program(this_);
og_func(this_, free_memory);
}
}
}

View File

@ -1,30 +0,0 @@
#pragma once
#include "common.hpp"
#include "gta/fwddec.hpp"
#include "gta/joaat.hpp"
#include "gta/natives.hpp"
#include "vmt_hook.hpp"
namespace big
{
class script_hook
{
public:
explicit script_hook(rage::joaat_t script_hash, std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler> native_replacements);
explicit script_hook(rage::scrProgram* program, std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler> native_replacements);
~script_hook();
void ensure();
private:
void hook_instance(rage::scrProgram *program);
static void scrprogram_dtor(rage::scrProgram *this_, bool free_memory);
static std::unordered_map<rage::scrProgram*, script_hook*> s_map;
rage::joaat_t m_script_hash;
rage::scrProgram* m_program;
std::unique_ptr<vmt_hook> m_vmt_hook;
std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler> m_native_replacements;
std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler*> m_native_handler_ptrs;
};
}

View File

@ -31,6 +31,7 @@ namespace big
SESSION,
SPOOFING,
PLAYER_DATABASE,
SESSION_BROWSER,
SETTINGS,
CONTEXT_MENU_SETTINGS,
@ -80,6 +81,7 @@ namespace big
{ tabs::SPOOFING, { "Spoofing", view::spoofing }},
{ tabs::SESSION, { "Session", view::session }},
{ tabs::PLAYER_DATABASE, { "Player Database", view::player_database }},
{ tabs::SESSION_BROWSER, { "Session Browser", view::session_browser }},
}}},
{tabs::SETTINGS, { "Settings", view::settings, {
{ tabs::CONTEXT_MENU_SETTINGS, { "Context Menu", view::context_menu_settings}},

View File

@ -0,0 +1,108 @@
#include "matchmaking_service.hpp"
#include "script.hpp"
#include "pointers.hpp"
#include "hooking.hpp"
#include <network/Network.hpp>
namespace big
{
matchmaking_service::matchmaking_service()
{
g_matchmaking_service = this;
}
matchmaking_service::~matchmaking_service()
{
g_matchmaking_service = nullptr;
}
bool matchmaking_service::matchmake(std::optional<int> constraint)
{
for (auto& session : m_found_sessions)
{
session.is_valid = true;
}
NetworkGameFilterMatchmakingComponent component{};
strcpy(component.m_filter_name, "Group");
component.m_game_mode = 0;
component.m_num_parameters = 0;
if (g->session_browser.region_filter_enabled)
{
component.SetParameter("MMATTR_REGION", 0, g->session_browser.region_filter);
}
if (constraint)
{
component.SetParameter("MMATTR_DISCRIMINATOR", 1, constraint.value());
}
int state = 0;
static rage::rlSessionInfo result_sessions[MAX_SESSIONS_TO_FIND];
m_active = true;
if (g_hooking->get_original<hooks::start_matchmaking_find_sessions>()(0, 1, &component, MAX_SESSIONS_TO_FIND, result_sessions, &m_num_sessions_found, &state))
{
while (state == 1)
script::get_current()->yield();
if (state == 3)
{
for (int i = 0; i < m_num_sessions_found; i++)
{
m_found_sessions[i].info = result_sessions[i];
if (constraint && m_found_sessions[i].attributes.player_count >= 30)
m_found_sessions[i].is_valid = false;
if (g->session_browser.language_filter_enabled && m_found_sessions[i].attributes.language != g->session_browser.language_filter)
m_found_sessions[i].is_valid = false;
if (g->session_browser.player_count_filter_enabled && (m_found_sessions[i].attributes.player_count < g->session_browser.player_count_filter_minimum ||
m_found_sessions[i].attributes.player_count > g->session_browser.player_count_filter_maximum))
{
m_found_sessions[i].is_valid = false;
}
}
if (g->session_browser.sort_method != 0)
{
std::qsort(m_found_sessions, m_num_sessions_found, sizeof(session), [](const void* a1, const void* a2) -> int
{
std::strong_ordering result;
if (g->session_browser.sort_method == 1)
{
result = (((session*)(a1))->attributes.player_count <=> ((session*)(a2))->attributes.player_count);
}
if (result == 0)
return 0;
if (result > 0)
return g->session_browser.sort_direction ? -1 : 1;
if (result < 0)
return g->session_browser.sort_direction ? 1 : -1;
std::unreachable();
});
}
m_active = false;
return true;
}
}
else
{
m_active = false;
return false;
}
m_active = false;
return false;
}
}

View File

@ -0,0 +1,50 @@
#pragma once
namespace big
{
class matchmaking_service
{
public:
constexpr static int MAX_SESSIONS_TO_FIND = 1400;
struct session_attributes
{
int discriminator;
int player_count;
int region;
int language;
};
struct session
{
rage::rlSessionInfo info;
session_attributes attributes;
bool is_valid;
};
private:
int m_num_sessions_found = 0;
bool m_active = false;
session m_found_sessions[MAX_SESSIONS_TO_FIND];
public:
matchmaking_service();
~matchmaking_service();
bool matchmake(std::optional<int> constraint = std::nullopt);
inline int get_num_found_sessions()
{
return m_num_sessions_found;
}
inline session* get_found_sessions()
{
return m_found_sessions;
}
inline bool is_active()
{
return m_active;
}
};
inline matchmaking_service* g_matchmaking_service;
}

View File

@ -55,7 +55,20 @@ namespace big::session
session::set_fm_event_index(9);
session::set_fm_event_index(10);
session::set_fm_event_index(11);
}
}
inline void join_session(const rage::rlSessionInfo& info)
{
g->session.join_queued = true;
g->session.info = info;
session::join_type({ eSessionType::NEW_PUBLIC });
if (SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(RAGE_JOAAT("maintransition")) == 0)
{
g->session.join_queued = false;
g_notification_service->push_error("RID Joiner", "Unable to launch maintransition");
}
return;
}
inline void join_by_rockstar_id(uint64_t rid)
{
@ -77,14 +90,7 @@ namespace big::session
if (state == 3 && success)
{
g->session.join_queued = true;
g->session.info = result.m_session_info;
session::join_type({ eSessionType::NEW_PUBLIC });
if (SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(RAGE_JOAAT("maintransition")) == 0)
{
g->session.join_queued = false;
g_notification_service->push_error("RID Joiner", "Unable to launch maintransition");
}
join_session(result.m_session_info);
return;
}
}

View File

@ -11,6 +11,6 @@ namespace big::water
inline void reset_ped_oxygen_time(CPed* ped)
{
auto ped_addr = (uint64_t)ped;
*(float*)(*(uint64_t*)(ped_addr + 0x10C0) + 0x278) = 0;
*(float*)(*(uint64_t*)(ped_addr + 0x10A0) + 0x278) = 0;
}
}

View File

@ -2,6 +2,9 @@
#include "natives.hpp"
#include "util/system.hpp"
#include "view_debug.hpp"
#include "network/Network.hpp"
#include "script.hpp"
#include "gta/joaat.hpp"
namespace big
{
@ -18,13 +21,6 @@ namespace big
{
NETWORK::NETWORK_BAIL(16, 0, 0);
});
if (g_local_player && g_local_player->m_player_info)
{
ImGui::InputScalar("Rockstar ID", ImGuiDataType_S64, &g_local_player->m_player_info->m_net_player_data.m_gamer_handle_2.m_rockstar_id, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly);
}
ImGui::EndTabItem();
}
}
}

View File

@ -43,6 +43,10 @@ namespace big
ImGui::EndListBox();
}
ImGui::Checkbox("Join in SCTV slots", &g->session.join_in_sctv_slots);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Allows you to join full and solo sessions but can be detected by other modders");
components::sub_title("Player Magnet");
ImGui::Checkbox("Enabled", &g->session.player_magnet_enabled);
if (g->session.player_magnet_enabled)

View File

@ -0,0 +1,160 @@
#include "views/view.hpp"
#include "fiber_pool.hpp"
#include "pointers.hpp"
#include "script.hpp"
#include <network/Network.hpp>
#include "util/session.hpp"
#include "core/data/region_codes.hpp"
#include "core/data/language_codes.hpp"
#include "services/matchmaking/matchmaking_service.hpp"
namespace big
{
static int selected_session_idx = -1;
void view::session_browser()
{
static char name_buf[32];
static char search[64];
ImGui::SetNextItemWidth(300.f);
if (ImGui::ListBoxHeader("###sessions", { 300, static_cast<float>(*g_pointers->m_resolution_y - 400 - 38 * 4) }))
{
if (g_matchmaking_service->get_num_found_sessions())
{
for (int i = 0; i < g_matchmaking_service->get_num_found_sessions(); i++)
{
if (!g_matchmaking_service->get_found_sessions()[i].is_valid)
continue;
if (components::selectable(std::to_string(g_matchmaking_service->get_found_sessions()[i].info.m_session_token), i == selected_session_idx))
{
selected_session_idx = i;
}
}
}
else
{
ImGui::Text("No sessions");
}
ImGui::ListBoxFooter();
}
if (selected_session_idx != -1)
{
ImGui::SameLine();
if (ImGui::BeginChild("###selected_session", { 300, static_cast<float>(*g_pointers->m_resolution_y - 388 - 38 * 4) }, false, ImGuiWindowFlags_NoBackground))
{
auto& session = g_matchmaking_service->get_found_sessions()[selected_session_idx];
ImGui::Text("Num Players: %d", session.attributes.player_count);
ImGui::Text("Discriminator: 0x%X", session.attributes.discriminator);
ImGui::Text("Region: %s", regions[session.attributes.region].name);
ImGui::Text("Language: %s", languages[session.attributes.language].name);
auto& data = session.info.m_net_player_data;
ImGui::Text("Host Rockstar ID: %d", data.m_gamer_handle.m_rockstar_id);
components::button("Join", [session]
{
if (SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(RAGE_JOAAT("maintransition")) != 0 ||
STREAMING::IS_PLAYER_SWITCH_IN_PROGRESS())
{
g_notification_service->push_error("RID Joiner", "Player switch in progress, wait a bit.");
return;
}
bool is_session_free_aim = session.attributes.discriminator & (1 << 17);
bool is_local_free_aim = PAD::GET_LOCAL_PLAYER_GAMEPAD_AIM_STATE() > 1;
if (is_session_free_aim != is_local_free_aim)
PLAYER::SET_PLAYER_TARGETING_MODE(is_session_free_aim ? 3 : 1);
session::join_session(session.info);
});
}
ImGui::EndChild();
}
if (ImGui::TreeNode("Filters"))
{
ImGui::Checkbox("Region", &g->session_browser.region_filter_enabled);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("It is highly recommended to keep this filter enabled");
if (g->session_browser.region_filter_enabled)
{
ImGui::SameLine();
if (ImGui::BeginCombo("###region_select", regions[g->session_browser.region_filter].name))
{
for (const auto& region : regions)
{
if (ImGui::Selectable(region.name, g->session_browser.region_filter == region.id))
{
g->session_browser.region_filter = region.id;
}
}
ImGui::EndCombo();
}
}
ImGui::Checkbox("Language", &g->session_browser.language_filter_enabled);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Setting a correct region filter for the language will help tremendously");
if (g->session_browser.language_filter_enabled)
{
ImGui::SameLine();
if (ImGui::BeginCombo("###language_select", languages[g->session_browser.language_filter].name))
{
for (const auto& language : languages)
{
if (ImGui::Selectable(language.name, g->session_browser.language_filter == language.id))
{
g->session_browser.language_filter = language.id;
};
}
ImGui::EndCombo();
}
}
ImGui::Checkbox("Players", &g->session_browser.player_count_filter_enabled);
if (g->session_browser.player_count_filter_enabled)
{
ImGui::InputInt("Minimum", &g->session_browser.player_count_filter_minimum);
ImGui::InputInt("Maximum", &g->session_browser.player_count_filter_maximum);
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Sorting"))
{
ImGui::Combo("Sort By", &g->session_browser.sort_method, "Off\0Player Count");
if (g->session_browser.sort_method != 0)
ImGui::Combo("Direction", &g->session_browser.sort_direction, "Ascending\0Descending");
ImGui::TreePop();
}
if (ImGui::Checkbox("Replace Game Matchmaking", &g->session_browser.replace_game_matchmaking));
if (ImGui::IsItemHovered())
ImGui::SetTooltip("This will replace the default game matchmaking with a custom one that will use the filters and sorting set here");
components::button("Refresh", []
{
selected_session_idx = -1;
if (!g_matchmaking_service->matchmake())
g_notification_service->push_error("Matchmaking", "Matchmaking failed");
});
}
}

View File

@ -112,7 +112,7 @@ namespace big
ImGui::SameLine();
if (ImGui::Button("Copy")) ImGui::SetClipboardText(std::to_string(net_player_data->m_gamer_handle_2.m_rockstar_id).data());
if (ImGui::Button("Copy##rid")) ImGui::SetClipboardText(std::to_string(net_player_data->m_gamer_handle_2.m_rockstar_id).data());
ImGui::Text(
"IP Address: %d.%d.%d.%d:%d",
@ -125,7 +125,7 @@ namespace big
ImGui::SameLine();
if (ImGui::Button("Copy")) ImGui::SetClipboardText(std::format("{}.{}.{}.{}:{}", net_player_data->m_external_ip.m_field1,
if (ImGui::Button("Copy##ip")) ImGui::SetClipboardText(std::format("{}.{}.{}.{}:{}", net_player_data->m_external_ip.m_field1,
net_player_data->m_external_ip.m_field2,
net_player_data->m_external_ip.m_field3,
net_player_data->m_external_ip.m_field4,

View File

@ -29,6 +29,7 @@ namespace big
static void self();
static void session();
static void player_database();
static void session_browser();
static void settings();
static void vehicle();
static void lsc();

View File

@ -5,26 +5,31 @@ namespace big
{
vmt_hook::vmt_hook(void* obj, std::size_t num_funcs) :
m_object(static_cast<void***>(obj)),
m_num_funcs(num_funcs + 1),
m_num_funcs(num_funcs),
m_original_table(*m_object),
m_new_table(std::make_unique<void*[]>(m_num_funcs))
{
std::copy_n(m_original_table - 1, m_num_funcs, m_new_table.get());
std::copy_n(m_original_table, m_num_funcs, m_new_table.get());
}
vmt_hook::~vmt_hook()
{
disable();
}
void vmt_hook::hook(std::size_t index, void* func)
{
m_new_table[index + 1] = func;
m_new_table[index] = func;
}
void vmt_hook::unhook(std::size_t index)
{
m_new_table[index + 1] = m_original_table[index];
m_new_table[index] = m_original_table[index];
}
void vmt_hook::enable()
{
*m_object = m_new_table.get() + 1;
*m_object = m_new_table.get();
}
void vmt_hook::disable()

View File

@ -7,6 +7,7 @@ namespace big
{
public:
explicit vmt_hook(void* obj, std::size_t num_funcs);
~vmt_hook();
vmt_hook(vmt_hook&& that) = delete;
vmt_hook& operator=(vmt_hook&& that) = delete;