Add session browser (#633)
This commit is contained in:
parent
d0b523873d
commit
88aa1f8e56
@ -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 ""
|
||||
|
27
src/core/data/language_codes.hpp
Normal file
27
src/core/data/language_codes.hpp
Normal 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)" }
|
||||
};
|
||||
}
|
@ -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" },
|
||||
|
@ -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 }
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
76
src/hooks/misc/process_matchmaking_find_response.cpp
Normal file
76
src/hooks/misc/process_matchmaking_find_response.cpp
Normal 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;
|
||||
}
|
||||
}
|
16
src/hooks/misc/serialize_join_request_message.cpp
Normal file
16
src/hooks/misc/serialize_join_request_message.cpp
Normal 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);
|
||||
}
|
||||
}
|
19
src/hooks/misc/serialize_player_data_msg.cpp
Normal file
19
src/hooks/misc/serialize_player_data_msg.cpp
Normal 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;
|
||||
}
|
||||
}
|
52
src/hooks/misc/start_matchmaking_find_sessions.cpp
Normal file
52
src/hooks/misc/start_matchmaking_find_sessions.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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{};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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{};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
};
|
||||
}
|
@ -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}},
|
||||
|
108
src/services/matchmaking/matchmaking_service.cpp
Normal file
108
src/services/matchmaking/matchmaking_service.cpp
Normal 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;
|
||||
}
|
||||
}
|
50
src/services/matchmaking/matchmaking_service.hpp
Normal file
50
src/services/matchmaking/matchmaking_service.hpp
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
160
src/views/network/view_session_browser.cpp
Normal file
160
src/views/network/view_session_browser.cpp
Normal 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");
|
||||
});
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user