From b342e8aeeda746aa301ecfc2fbcd0a2229958023 Mon Sep 17 00:00:00 2001 From: tupoy-ya Date: Thu, 15 Jan 2026 02:29:14 +0500 Subject: [PATCH 1/9] feat: Port session browser from V1. --- src/core/hooking/Hooking.cpp | 2 + src/game/backend/CustomMatchmaking.cpp | 126 +++++++++++ src/game/backend/CustomMatchmaking.hpp | 55 +++++ src/game/frontend/submenus/Network.cpp | 2 + .../submenus/Network/SessionBrowser.cpp | 214 ++++++++++++++++++ .../submenus/Network/SessionBrowser.hpp | 8 + src/game/gta/Network.cpp | 2 +- src/game/gta/Network.hpp | 2 +- src/game/hooks/Hooks.hpp | 4 + .../Matchmaking/MatchmakingFindSessions.cpp | 102 +++++++++ src/game/pointers/Pointers.cpp | 10 + src/game/pointers/Pointers.hpp | 4 + .../NetworkGameFilterMatchmakingComponent.hpp | 33 +++ 13 files changed, 562 insertions(+), 2 deletions(-) create mode 100644 src/game/frontend/submenus/Network/SessionBrowser.cpp create mode 100644 src/game/frontend/submenus/Network/SessionBrowser.hpp create mode 100644 src/game/hooks/Matchmaking/MatchmakingFindSessions.cpp create mode 100644 src/types/network/NetworkGameFilterMatchmakingComponent.hpp diff --git a/src/core/hooking/Hooking.cpp b/src/core/hooking/Hooking.cpp index f1967fd5..6f0b5709 100644 --- a/src/core/hooking/Hooking.cpp +++ b/src/core/hooking/Hooking.cpp @@ -47,6 +47,8 @@ namespace YimMenu BaseHook::Add(new DetourHook("MatchmakingSessionDetailSendResponse", Pointers.MatchmakingSessionDetailSendResponse, Hooks::Matchmaking::MatchmakingSessionDetailSendResponse)); BaseHook::Add(new DetourHook("MatchmakingUnadvertise", Pointers.MatchmakingUnadvertise, Hooks::Matchmaking::MatchmakingUnadvertise)); BaseHook::Add(new DetourHook("MatchmakingUpdate", Pointers.MatchmakingUpdate, Hooks::Matchmaking::MatchmakingUpdate)); + BaseHook::Add(new DetourHook("MatchmakingFindSessions", Pointers.MatchmakingFindSessions, Hooks::Matchmaking::MatchmakingFindSessions)); + BaseHook::Add(new DetourHook("MatchmakingFindSessionsResponse", Pointers.MatchmakingFindSessionsResponse, Hooks::Matchmaking::MatchmakingFindSessionsResponse)); BaseHook::Add(new DetourHook("AssistedAimShouldReleaseEntity", Pointers.AssistedAimShouldReleaseEntity, Hooks::Misc::AssistedAimShouldReleaseEntity)); } diff --git a/src/game/backend/CustomMatchmaking.cpp b/src/game/backend/CustomMatchmaking.cpp index f8cd0292..da903f2d 100644 --- a/src/game/backend/CustomMatchmaking.cpp +++ b/src/game/backend/CustomMatchmaking.cpp @@ -2,6 +2,7 @@ #include "core/util/Joaat.hpp" #include "types/network/MatchmakingId.hpp" #include "types/network/rlSessionDetail.hpp" +#include "types/network/NetworkGameFilterMatchmakingComponent.hpp" #include "core/commands/BoolCommand.hpp" #include "core/commands/IntCommand.hpp" #include "core/commands/ListCommand.hpp" @@ -115,6 +116,131 @@ namespace YimMenu } + bool CustomMatchmaking::MatchmakeImpl(std::optional constraint, std::optional enforce_player_limit) + { + for (auto& session : m_found_sessions) + { + session.is_valid = true; + } + + NetworkGameFilterMatchmakingComponent component{}; + strcpy(component.m_filter_name, "Group"); + component.m_filter_type = 1; + component.m_game_mode = 0; + component.m_num_parameters = 0; + component.m_session_type = 25600; + + /* + if (g.session_browser.region_filter_enabled) + { + component.SetParameter("MMATTR_REGION", 4, g.session_browser.region_filter); + } + */ + + if (constraint) + { + component.SetParameter("MMATTR_DISCRIMINATOR", 0, constraint.value()); + } + + component.SetParameter("MMATTR_MM_GROUP_2", 2, 30); + + rage::rlTaskStatus state{}; + static rage::rlSessionInfo result_sessions[MAX_SESSIONS_TO_FIND]; + + m_active = true; + m_num_valid_sessions = 0; + + if (BaseHook::Get>()->Original()(0, 1, &component, MAX_SESSIONS_TO_FIND, result_sessions, &m_num_sessions_found, &state)) + { + while (state.m_Status == 1) + ScriptMgr::Yield(); + + if (state.m_Status == 3) + { + std::unordered_map stok_map = {}; + + LOGF(VERBOSE, "Matchmaking success, found {} sessions.", m_num_sessions_found); + + for (int i = 0; i < m_num_sessions_found; i++) + { + m_found_sessions[i].info = result_sessions[i]; + + if (auto it = stok_map.find(m_found_sessions[i].info.m_SessionToken); it != stok_map.end()) + { + if (/*g.session_browser.filter_multiplexed_sessions*/true) + { + it->second->is_valid = false; + } + + it->second->attributes.multiplex_count++; + m_found_sessions[i].is_valid = false; + continue; + } + + if (enforce_player_limit.has_value() && enforce_player_limit.value() + && m_found_sessions[i].attributes.player_count >= 30) + m_found_sessions[i].is_valid = false; + + /* + if (g.session_browser.language_filter_enabled + && (eGameLanguage)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.pool_filter_enabled + && ((m_found_sessions[i].attributes.discriminator & (1 << 14)) == (1 << 14)) + != (bool)g.session_browser.pool_filter) + m_found_sessions[i].is_valid = false; + + */ + + stok_map.emplace(m_found_sessions[i].info.m_SessionToken, &m_found_sessions[i]); + } + + if (/*g.session_browser.sort_method*/1 != 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 == 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 : 1; + + if (result < 0) + return /*g.session_browser.sort_direction*/1 ? 1 : -1; + + + std::unreachable(); + }); + } + + m_active = false; + return true; + } + } + else + { + m_active = false; + return false; + } + + m_active = false; + return false; + } + bool CustomMatchmaking::OnAdvertiseImpl(int& num_slots, int& available_slots, rage::rlSessionInfo* info, MatchmakingAttributes* attrs, MatchmakingId* id, rage::rlTaskStatus* status) { PatchMatchmakingAttrs(attrs); diff --git a/src/game/backend/CustomMatchmaking.hpp b/src/game/backend/CustomMatchmaking.hpp index 999910f0..b4b9d9d9 100644 --- a/src/game/backend/CustomMatchmaking.hpp +++ b/src/game/backend/CustomMatchmaking.hpp @@ -1,4 +1,5 @@ #pragma once +#include "types/network/rlSessionInfo.hpp" #include "types/network/rlTaskStatus.hpp" #include "types/network/MatchmakingId.hpp" @@ -20,6 +21,9 @@ namespace YimMenu bool OnUnadvertiseImpl(MatchmakingId* id); void OnSendSessionDetailResponseImpl(rage::rlSessionDetailMsg* message); + bool MatchmakeImpl(std::optional constraint = std::nullopt, std::optional enforce_player_limit = std::nullopt); + + CustomMatchmaking(); static CustomMatchmaking& GetInstance() @@ -28,9 +32,40 @@ namespace YimMenu return instance; } + public: + constexpr static int MAX_SESSIONS_TO_FIND = 1000; + + struct session_attributes + { + int discriminator; + int player_count; + int region; + int language; + int multiplex_count = 1; + }; + + struct session + { + rage::rlSessionInfo info; + session_attributes attributes; + bool is_valid; + }; + + private: + int m_num_sessions_found = 0; + int m_num_valid_sessions = 0; + bool m_active = false; + session m_found_sessions[MAX_SESSIONS_TO_FIND]; + std::unordered_map> m_MultiplexedSessions; + public: + static bool Matchmake(std::optional constraint = std::nullopt, std::optional enforce_player_limit = std::nullopt) + { + return GetInstance().MatchmakeImpl(constraint, enforce_player_limit); + } + static bool OnAdvertise(int& num_slots, int& available_slots, rage::rlSessionInfo* info, MatchmakingAttributes* attrs, MatchmakingId* id, rage::rlTaskStatus* status) { return GetInstance().OnAdvertiseImpl(num_slots, available_slots, info, attrs, id, status); @@ -50,5 +85,25 @@ namespace YimMenu { return GetInstance().OnSendSessionDetailResponseImpl(message); } + + static int GetNumFoundSessions() + { + return GetInstance().m_num_sessions_found; + } + + static int GetNumValidSessions() + { + return GetInstance().m_num_valid_sessions; + } + + static session* GetFoundSessions() + { + return GetInstance().m_found_sessions; + } + + static bool IsActive() + { + return GetInstance().m_active; + } }; } \ No newline at end of file diff --git a/src/game/frontend/submenus/Network.cpp b/src/game/frontend/submenus/Network.cpp index 57ff38b0..513643d3 100644 --- a/src/game/frontend/submenus/Network.cpp +++ b/src/game/frontend/submenus/Network.cpp @@ -4,6 +4,7 @@ #include "game/frontend/items/Items.hpp" #include "game/frontend/submenus/Network/SavedPlayers.hpp" #include "game/frontend/submenus/Network/RandomEvents.hpp" +#include "game/frontend/submenus/Network/SessionBrowser.hpp" #include "game/gta/Network.hpp" namespace YimMenu::Submenus @@ -136,6 +137,7 @@ namespace YimMenu::Submenus AddCategory(std::move(session)); AddCategory(std::move(spoofing)); AddCategory(std::move(BuildSavedPlayersMenu())); + AddCategory(std::move(BuildSessionBrowser())); AddCategory(BuildRandomEventsMenu()); } } \ No newline at end of file diff --git a/src/game/frontend/submenus/Network/SessionBrowser.cpp b/src/game/frontend/submenus/Network/SessionBrowser.cpp new file mode 100644 index 00000000..6a33a29e --- /dev/null +++ b/src/game/frontend/submenus/Network/SessionBrowser.cpp @@ -0,0 +1,214 @@ +#include "SavedPlayers.hpp" +#include "core/backend/FiberPool.hpp" +#include "core/frontend/widgets/imgui_colors.h" +#include "core/frontend/Notifications.hpp" +#include "game/backend/CustomMatchmaking.hpp" +#include "game/backend/SavedPlayers.hpp" +#include "game/gta/Network.hpp" +#include "game/pointers/Pointers.hpp" +#include "imgui.h" + +namespace YimMenu::Submenus +{ + static int selected_session_idx = -1; + + void RenderSessionBrowser() + { + static char name_buf[32]; + static char search[64]; + static char session_info[0x100]{}; + ImGui::Text("Total sessions found: %i", CustomMatchmaking::GetNumFoundSessions()); + + if (ImGui::BeginListBox("###sessions", ImVec2(300, -ImGui::GetFrameHeight()))) + { + if (CustomMatchmaking::GetNumFoundSessions()) + { + for (int i = 0; i < CustomMatchmaking::GetNumFoundSessions(); i++) + { + auto& session = CustomMatchmaking::GetFoundSessions()[i]; + + if (!session.is_valid) + continue; + + std::string session_str; + if (session.attributes.multiplex_count > 1) + session_str = std::format("{:X} (x{})", session.info.m_SessionToken, session.attributes.multiplex_count); + else + session_str = std::format("{:X}", session.info.m_SessionToken); + + auto host_rid = session.info.m_HostInfo.m_GamerHandle.m_RockstarId; + /* + auto player = g_player_database_service->get_player_by_rockstar_id(host_rid); + + if ((g.session_browser.exclude_modder_sessions && player && player->block_join) + || (g.session_browser.filter_multiplexed_sessions && session.attributes.multiplex_count > 1)) + continue; + */ + + if (ImGui::Selectable(session_str.c_str(), i == selected_session_idx)) + { + selected_session_idx = i; + //g_pointers->m_gta.m_encode_session_info(&session.info, session_info, 0xA9, nullptr); + } + + if (ImGui::IsItemHovered()) + { + auto tool_tip = std::format("Number of Players: {}\nRegion: {}\nLanguage: {}\nHost Rockstar ID: {}\nDiscriminator: {:X}\nIndex: {}", + session.attributes.player_count, + /*regions[*/session.attributes.region/*].name*/, + /*languages.at((eGameLanguage)*/session.attributes.language/*)*/, + session.info.m_HostInfo.m_GamerHandle.m_RockstarId, // TODO: this is not accurate + session.attributes.discriminator, + i); + ImGui::SetTooltip("%s", tool_tip.c_str()); + } + } + } + else + { + ImGui::TextUnformatted("No sessions"); + } + + ImGui::EndListBox(); + } + + if (selected_session_idx != -1) + { + ImGui::SameLine(); + if (ImGui::BeginChild("###selected_session", ImVec2(300, -ImGui::GetFrameHeight()), false, ImGuiWindowFlags_NoBackground)) + { + auto& session = CustomMatchmaking::GetFoundSessions()[selected_session_idx]; + + ImGui::Text("Num Players: %d", session.attributes.player_count); + ImGui::Text("Discriminator: 0x%X", session.attributes.discriminator); + ImGui::Text("Region: %i", /*regions[*/session.attributes.region/*].name*/); + ImGui::Text("Language: %i", /*languages.at((eGameLanguage)*/session.attributes.language/*).data()*/); + + auto& data = session.info.m_HostInfo; + ImGui::Text("Host Rockstar ID: %llu", data.m_GamerHandle.m_RockstarId); + + if(ImGui::Button("Copy Session Info")) + { + FiberPool::Push([] { + ImGui::SetClipboardText(session_info); + }); + } + + if(ImGui::Button("Join")) + { + FiberPool::Push([session] { + LOGF(VERBOSE, "Trying to join session '{:X}' hosted by: {}, at: {}", session.info.m_SessionToken, session.info.m_HostInfo.m_GamerHandle.m_RockstarId, selected_session_idx); + Network::JoinSessionInfo(&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.at(g.session_browser.language_filter).data())) + { + for (const auto& [id, language] : languages) + { + if (ImGui::Selectable(language.data(), g.session_browser.language_filter == id)) + { + g.session_browser.language_filter = 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::Checkbox("Pool Type", &g.session_browser.pool_filter_enabled); + if (g.session_browser.pool_filter_enabled) + { + ImGui::SameLine(); + ImGui::Combo("###pooltype", &g.session_browser.pool_filter, "Normal\0Bad Sport\0"); + } + + ImGui::Checkbox("Filter Multiplexed Sessions", &g.session_browser.filter_multiplexed_sessions); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Removes advertised sessions"); + + ImGui::Checkbox("Exclude Modder Sessions", &g.session_browser.exclude_modder_sessions); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Excludes hosts that you have blocked in the Player Database"); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Sorting")) + { + ImGui::Combo("Sort By", &g.session_browser.sort_method, "Off\0Player Count\0"); + if (g.session_browser.sort_method != 0) + ImGui::Combo("Direction", &g.session_browser.sort_direction, "Ascending\0Descending\0"); + ImGui::TreePop(); + } + + ImGui::Checkbox("Replace Game Matchmaking", &g.session_browser.replace_game_matchmaking); + ImGui::SameLine(); + components::help_marker("This will replace the default game matchmaking with a custom one that will use the filters and sorting set here"); + **/ + + static uint32_t discriminator = 730776930; + + ImGui::InputScalar("Discriminator", ImGuiDataType_U32, &discriminator, nullptr, nullptr, "%08X"); + + if(ImGui::Button("Refresh")) + { + FiberPool::Push( [] { + selected_session_idx = -1; + + if (!CustomMatchmaking::Matchmake(discriminator)) + Notifications::Show("Matchmaking", "Matchmaking failed", NotificationType::Error); + }); + } + } + + std::shared_ptr BuildSessionBrowser() + { + auto menu = std::make_shared("Session Browser"); + menu->AddItem(std::make_unique(RenderSessionBrowser)); + + return menu; + } +} \ No newline at end of file diff --git a/src/game/frontend/submenus/Network/SessionBrowser.hpp b/src/game/frontend/submenus/Network/SessionBrowser.hpp new file mode 100644 index 00000000..356f0c69 --- /dev/null +++ b/src/game/frontend/submenus/Network/SessionBrowser.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "core/frontend/manager/Category.hpp" +#include "game/frontend/items/Items.hpp" + +namespace YimMenu::Submenus +{ + std::shared_ptr BuildSessionBrowser(); +} \ No newline at end of file diff --git a/src/game/gta/Network.cpp b/src/game/gta/Network.cpp index e5c4de28..9ba1714b 100644 --- a/src/game/gta/Network.cpp +++ b/src/game/gta/Network.cpp @@ -25,7 +25,7 @@ namespace YimMenu::Network *join_type_global.As() = to_launch; } - void JoinSessionInfo(rage::rlSessionInfo* info) + void JoinSessionInfo(const rage::rlSessionInfo* info) { static std::optional session_to_join; static bool ensure_native_hook_initialized = ([] { diff --git a/src/game/gta/Network.hpp b/src/game/gta/Network.hpp index 3b497cd7..bf989419 100644 --- a/src/game/gta/Network.hpp +++ b/src/game/gta/Network.hpp @@ -23,7 +23,7 @@ namespace YimMenu::Network }; void LaunchJoinType(JoinType to_launch); - void JoinSessionInfo(rage::rlSessionInfo* info); + void JoinSessionInfo(const rage::rlSessionInfo* info); void JoinRockstarId(std::uint64_t id); std::optional ResolveRockstarId(std::string_view name); } \ No newline at end of file diff --git a/src/game/hooks/Hooks.hpp b/src/game/hooks/Hooks.hpp index 00a31305..61e0d578 100644 --- a/src/game/hooks/Hooks.hpp +++ b/src/game/hooks/Hooks.hpp @@ -10,6 +10,7 @@ class CBattlEyePlayerModifyContext; namespace rage { + class JSONNode; class netConnectionManager; class netArrayHandler; class netEvent; @@ -26,6 +27,7 @@ namespace rage class MatchmakingAttributes; class MatchmakingId; +class NetworkGameFilterMatchmakingComponent; namespace YimMenu { @@ -60,6 +62,8 @@ namespace YimMenu::Hooks extern bool MatchmakingUpdate(int profile_index, MatchmakingId* id, int num_slots, int available_slots, rage::rlSessionInfo* info, MatchmakingAttributes* data, rage::rlTaskStatus* status); extern bool MatchmakingUnadvertise(int profile_index, MatchmakingId* id, rage::rlTaskStatus* status); extern bool MatchmakingSessionDetailSendResponse(rage::netConnectionManager* mgr, void* request_frame, rage::rlSessionDetailMsg* msg); + extern bool MatchmakingFindSessions(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* m_filter, unsigned int max_sessions, rage::rlSessionInfo* result_sessions, int* result_session_count, rage::rlTaskStatus* state); + extern bool MatchmakingFindSessionsResponse(void* _this, void* unused, rage::JSONNode* node, int* unk); } namespace Info diff --git a/src/game/hooks/Matchmaking/MatchmakingFindSessions.cpp b/src/game/hooks/Matchmaking/MatchmakingFindSessions.cpp new file mode 100644 index 00000000..0b55d171 --- /dev/null +++ b/src/game/hooks/Matchmaking/MatchmakingFindSessions.cpp @@ -0,0 +1,102 @@ +#include "game/hooks/Hooks.hpp" +#include "core/hooking/DetourHook.hpp" +#include "game/backend/CustomMatchmaking.hpp" +#include "types/network/NetworkGameFilterMatchmakingComponent.hpp" + +namespace rage +{ + class JSONNode + { + public: + char* m_key; //0x0000 + char pad_0008[40]; //0x0008 + class rage::JSONNode* m_sibling;//0x0030 + class rage::JSONNode* m_child; //0x0038 + char* m_value; //0x0040 + char pad_0040[8]; //0x0048 + + 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) == 0x50); +} + +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 YimMenu::Hooks +{ + bool Matchmaking::MatchmakingFindSessions(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* m_filter, unsigned int max_sessions, rage::rlSessionInfo* result_sessions, int* result_session_count, rage::rlTaskStatus* state) + { + LOGF(VERBOSE, "MatchmakingFindSessions({}, {}, {}, {}, {}, {}, {})", profile_index, available_slots, (void*)m_filter, max_sessions, (void*)result_sessions, (void*)result_session_count, (void*)state); + + LOG(VERBOSE) << "m_filter_type " << m_filter->m_filter_type; + LOG(VERBOSE) << "m_filter_name " << m_filter->m_filter_name; + LOG(VERBOSE) << "m_game_mode " << m_filter->m_game_mode; + LOG(VERBOSE) << "m_session_type " << m_filter->m_session_type; + LOG(VERBOSE) << "m_enabled_params_bitset " << m_filter->m_enabled_params_bitset; + + for (uint32_t i = 0; i < 8; i++) + { + bool active_param = (m_filter->m_enabled_params_bitset & (1 << i)) != 0; + if (active_param) + { + LOG(VERBOSE) << m_filter->m_param_names[i] << "(" << i << ") = " << m_filter->m_param_values[i]; + } + } + + return BaseHook::Get>()->Original()(profile_index, available_slots, m_filter, max_sessions, result_sessions, result_session_count, state); + } + + bool Matchmaking::MatchmakingFindSessionsResponse(void* _this, void* unused, rage::JSONNode* node, int* unk) + { + bool ret = BaseHook::Get>()->Original()(_this, unused, node, unk); + + if (CustomMatchmaking::IsActive()) + { + int i = 0; + for (auto result = node->get_child_node("Results")->m_child; result; result = result->m_sibling) + { + const auto& attributes = result->get_child_node("Attributes")->m_value; + LOG(VERBOSE) << "Session " << attributes; + const auto& values = split(attributes, ','); + CustomMatchmaking::GetFoundSessions()[i].attributes.discriminator = std::stoi(values[2]); + CustomMatchmaking::GetFoundSessions()[i].attributes.player_count = std::stoi(values[4]); + CustomMatchmaking::GetFoundSessions()[i].attributes.language = std::stoi(values[5]); + CustomMatchmaking::GetFoundSessions()[i].attributes.region = std::stoi(values[6]); + i++; + } + } + + return ret; + } +} diff --git a/src/game/pointers/Pointers.cpp b/src/game/pointers/Pointers.cpp index d97ab9fe..ac2a1f38 100644 --- a/src/game/pointers/Pointers.cpp +++ b/src/game/pointers/Pointers.cpp @@ -447,6 +447,16 @@ namespace YimMenu MatchmakingUnadvertise = addr.Sub(0xC).Rip().As(); }); + static constexpr auto doMatchmakingFindSessions = Pattern<"4C 89 5C 24 20 E8 ? ? ? ? 84 C0 74 ? C7 47">("MatchmakingFindSessions"); + scanner.Add(doMatchmakingFindSessions, [this](PointerCalculator addr) { + MatchmakingFindSessions = addr.Add(6).Rip().As(); + }); + + static constexpr auto matchmakingFindSessionsResponse = Pattern<"4C 89 CE 49 89 CE">("MatchmakingFindSessionsResponse"); + scanner.Add(matchmakingFindSessionsResponse, [this](PointerCalculator addr) { + MatchmakingFindSessionsResponse = addr.Sub(0x1B).As(); + }); + static constexpr auto matchmakingSessionDetailSendResponsePtrn = Pattern<"48 B8 01 00 00 00 0D 00 00 00">("SessionDetailSendResponse"); scanner.Add(matchmakingSessionDetailSendResponsePtrn, [this](PointerCalculator addr) { MatchmakingSessionDetailSendResponse = addr.Add(0x2F).Rip().As(); diff --git a/src/game/pointers/Pointers.hpp b/src/game/pointers/Pointers.hpp index ac5553c6..dc12299f 100644 --- a/src/game/pointers/Pointers.hpp +++ b/src/game/pointers/Pointers.hpp @@ -45,6 +45,7 @@ class CNetworkSession; class CGameDataHash; class CStatsMpCharacterMappingData; class CAnticheatContext; +class NetworkGameFilterMatchmakingComponent; namespace YimMenu { @@ -70,6 +71,7 @@ namespace YimMenu using GetPresenceAttributes = bool (*)(int profile_index, rage::rlScGamerHandle* handles, int num_handles, rage::rlQueryPresenceAttributesContext** contexts, int count, rage::rlScTaskStatus* state); using GetAvatars = bool (*)(rage::rlGetAvatarsContext* context, rage::rlGetAvatarsPlayerList* players); using AssistedAimFindNewTarget = bool (*)(__int64 a1); + using MatchmakingFindSessions = bool (*)(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* m_filter, unsigned int max_sessions, rage::rlSessionInfo* result_sessions, int* result_session_count, rage::rlTaskStatus* state); } struct PointerData @@ -170,6 +172,8 @@ namespace YimMenu PVOID MatchmakingUpdate; PVOID MatchmakingUnadvertise; PVOID MatchmakingSessionDetailSendResponse; + PVOID MatchmakingFindSessions; + PVOID MatchmakingFindSessionsResponse; PVOID GameSkeletonUpdate; }; diff --git a/src/types/network/NetworkGameFilterMatchmakingComponent.hpp b/src/types/network/NetworkGameFilterMatchmakingComponent.hpp new file mode 100644 index 00000000..f6e3847e --- /dev/null +++ b/src/types/network/NetworkGameFilterMatchmakingComponent.hpp @@ -0,0 +1,33 @@ +#pragma once + +class NetworkGameFilterMatchmakingComponent +{ +public: + // do not use for actual network filters, this will break things + inline void SetParameter(const char* name, int index, int value) + { + std::strcpy(m_param_names[index], name); + m_param_mappings[index] = index; + m_param_values[index] = value; + m_enabled_params_bitset |= (1 << index); + + if (m_num_parameters <= (uint32_t)index) + m_num_parameters++; + } + + uint32_t m_filter_type; //0x0000 + char m_filter_name[24]; //0x0004 + uint32_t m_num_parameters; //0x001C + uint16_t m_game_mode; //0x0020 + uint16_t m_session_type; //0x0022 + uint32_t m_param_unk[8]; //0x0024 + char m_param_names[8][24]; //0x0044 + char pad_0104[4]; //0x0104 + uint32_t m_param_mappings[8]; //0x0108 + char pad_0128[352]; //0x0128 + uint32_t m_param_values[8]; //0x0288 + char pad_02A8[96]; //0x02A8 + uint32_t m_enabled_params_bitset; //0x0308 + char pad_030C[8]; //0x030C +}; //Size: 0x0314 +static_assert(sizeof(NetworkGameFilterMatchmakingComponent) == 0x314); From e5162c70d6b5bcd890e5970abfe2461fb5307ffa Mon Sep 17 00:00:00 2001 From: tupoy-ya Date: Thu, 15 Jan 2026 02:31:34 +0500 Subject: [PATCH 2/9] chore: Remove debug logs. --- src/game/backend/CustomMatchmaking.cpp | 2 -- .../Matchmaking/MatchmakingFindSessions.cpp | 21 +------------------ 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/src/game/backend/CustomMatchmaking.cpp b/src/game/backend/CustomMatchmaking.cpp index da903f2d..5c76d389 100644 --- a/src/game/backend/CustomMatchmaking.cpp +++ b/src/game/backend/CustomMatchmaking.cpp @@ -159,8 +159,6 @@ namespace YimMenu { std::unordered_map stok_map = {}; - LOGF(VERBOSE, "Matchmaking success, found {} sessions.", m_num_sessions_found); - for (int i = 0; i < m_num_sessions_found; i++) { m_found_sessions[i].info = result_sessions[i]; diff --git a/src/game/hooks/Matchmaking/MatchmakingFindSessions.cpp b/src/game/hooks/Matchmaking/MatchmakingFindSessions.cpp index 0b55d171..6e2dd9b6 100644 --- a/src/game/hooks/Matchmaking/MatchmakingFindSessions.cpp +++ b/src/game/hooks/Matchmaking/MatchmakingFindSessions.cpp @@ -57,23 +57,6 @@ namespace YimMenu::Hooks { bool Matchmaking::MatchmakingFindSessions(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* m_filter, unsigned int max_sessions, rage::rlSessionInfo* result_sessions, int* result_session_count, rage::rlTaskStatus* state) { - LOGF(VERBOSE, "MatchmakingFindSessions({}, {}, {}, {}, {}, {}, {})", profile_index, available_slots, (void*)m_filter, max_sessions, (void*)result_sessions, (void*)result_session_count, (void*)state); - - LOG(VERBOSE) << "m_filter_type " << m_filter->m_filter_type; - LOG(VERBOSE) << "m_filter_name " << m_filter->m_filter_name; - LOG(VERBOSE) << "m_game_mode " << m_filter->m_game_mode; - LOG(VERBOSE) << "m_session_type " << m_filter->m_session_type; - LOG(VERBOSE) << "m_enabled_params_bitset " << m_filter->m_enabled_params_bitset; - - for (uint32_t i = 0; i < 8; i++) - { - bool active_param = (m_filter->m_enabled_params_bitset & (1 << i)) != 0; - if (active_param) - { - LOG(VERBOSE) << m_filter->m_param_names[i] << "(" << i << ") = " << m_filter->m_param_values[i]; - } - } - return BaseHook::Get>()->Original()(profile_index, available_slots, m_filter, max_sessions, result_sessions, result_session_count, state); } @@ -86,9 +69,7 @@ namespace YimMenu::Hooks int i = 0; for (auto result = node->get_child_node("Results")->m_child; result; result = result->m_sibling) { - const auto& attributes = result->get_child_node("Attributes")->m_value; - LOG(VERBOSE) << "Session " << attributes; - const auto& values = split(attributes, ','); + const auto& values = split(result->get_child_node("Attributes")->m_value, ','); CustomMatchmaking::GetFoundSessions()[i].attributes.discriminator = std::stoi(values[2]); CustomMatchmaking::GetFoundSessions()[i].attributes.player_count = std::stoi(values[4]); CustomMatchmaking::GetFoundSessions()[i].attributes.language = std::stoi(values[5]); From 51aabaf1fcb65cb576428c6c79eb5a26af677ae6 Mon Sep 17 00:00:00 2001 From: tupoy-ya Date: Thu, 15 Jan 2026 02:47:39 +0500 Subject: [PATCH 3/9] fix: Display region and language names. --- src/game/backend/CustomMatchmaking.cpp | 28 ----------------- src/game/backend/CustomMatchmaking.hpp | 31 +++++++++++++++++++ .../submenus/Network/SessionBrowser.cpp | 19 ++++++------ 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/game/backend/CustomMatchmaking.cpp b/src/game/backend/CustomMatchmaking.cpp index 5c76d389..93983142 100644 --- a/src/game/backend/CustomMatchmaking.cpp +++ b/src/game/backend/CustomMatchmaking.cpp @@ -13,34 +13,6 @@ namespace YimMenu::Features { - static std::vector> g_RegionCodes = { - {0, "CIS"}, - {1, "South America"}, - {2, "US East"}, - {3, "Europe"}, - {4, "China"}, - {5, "Australia"}, - {6, "US West"}, - {7, "Japan"}, - {8, "Unknown"}, - }; - - static std::vector> g_LanguageTypes = { - {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 (Simplified)"}, - }; - BoolCommand _SpoofRegionType{ "mmspoofregiontype", "Spoof Region Type", diff --git a/src/game/backend/CustomMatchmaking.hpp b/src/game/backend/CustomMatchmaking.hpp index b4b9d9d9..217d292c 100644 --- a/src/game/backend/CustomMatchmaking.hpp +++ b/src/game/backend/CustomMatchmaking.hpp @@ -14,6 +14,37 @@ class MatchmakingId; namespace YimMenu { + namespace Features + { + static std::vector> g_RegionCodes = { + {0, "CIS"}, + {1, "South America"}, + {2, "US East"}, + {3, "Europe"}, + {4, "China"}, + {5, "Australia"}, + {6, "US West"}, + {7, "Japan"}, + {8, "Unknown"}, + }; + + static std::vector> g_LanguageTypes = { + {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 (Simplified)"}, + }; + } + class CustomMatchmaking { bool OnAdvertiseImpl(int& num_slots, int& available_slots, rage::rlSessionInfo* info, MatchmakingAttributes* attrs, MatchmakingId* id, rage::rlTaskStatus* status); diff --git a/src/game/frontend/submenus/Network/SessionBrowser.cpp b/src/game/frontend/submenus/Network/SessionBrowser.cpp index 6a33a29e..59a029f8 100644 --- a/src/game/frontend/submenus/Network/SessionBrowser.cpp +++ b/src/game/frontend/submenus/Network/SessionBrowser.cpp @@ -37,9 +37,9 @@ namespace YimMenu::Submenus session_str = std::format("{:X}", session.info.m_SessionToken); auto host_rid = session.info.m_HostInfo.m_GamerHandle.m_RockstarId; - /* - auto player = g_player_database_service->get_player_by_rockstar_id(host_rid); + auto player = SavedPlayers::GetPlayerData(host_rid); + /* if ((g.session_browser.exclude_modder_sessions && player && player->block_join) || (g.session_browser.filter_multiplexed_sessions && session.attributes.multiplex_count > 1)) continue; @@ -53,13 +53,12 @@ namespace YimMenu::Submenus if (ImGui::IsItemHovered()) { - auto tool_tip = std::format("Number of Players: {}\nRegion: {}\nLanguage: {}\nHost Rockstar ID: {}\nDiscriminator: {:X}\nIndex: {}", + auto tool_tip = std::format("Number of Players: {}\nRegion: {}\nLanguage: {}\nHost Rockstar ID: {}\nDiscriminator: {:X}", session.attributes.player_count, - /*regions[*/session.attributes.region/*].name*/, - /*languages.at((eGameLanguage)*/session.attributes.language/*)*/, + Features::g_RegionCodes.at(session.attributes.region).second, + Features::g_LanguageTypes.at(session.attributes.language).second, session.info.m_HostInfo.m_GamerHandle.m_RockstarId, // TODO: this is not accurate - session.attributes.discriminator, - i); + session.attributes.discriminator); ImGui::SetTooltip("%s", tool_tip.c_str()); } } @@ -81,8 +80,8 @@ namespace YimMenu::Submenus ImGui::Text("Num Players: %d", session.attributes.player_count); ImGui::Text("Discriminator: 0x%X", session.attributes.discriminator); - ImGui::Text("Region: %i", /*regions[*/session.attributes.region/*].name*/); - ImGui::Text("Language: %i", /*languages.at((eGameLanguage)*/session.attributes.language/*).data()*/); + ImGui::Text("Region: %s", Features::g_RegionCodes.at(session.attributes.region).second); + ImGui::Text("Language: %s", Features::g_LanguageTypes.at(session.attributes.language).second); auto& data = session.info.m_HostInfo; ImGui::Text("Host Rockstar ID: %llu", data.m_GamerHandle.m_RockstarId); @@ -189,7 +188,7 @@ namespace YimMenu::Submenus components::help_marker("This will replace the default game matchmaking with a custom one that will use the filters and sorting set here"); **/ - static uint32_t discriminator = 730776930; + static uint32_t discriminator = 730776930; // 0xA9A8562 for non_cheater pool ImGui::InputScalar("Discriminator", ImGuiDataType_U32, &discriminator, nullptr, nullptr, "%08X"); From 991d612d03e6a5d2a2d79b8fbbc7e759ded5baf8 Mon Sep 17 00:00:00 2001 From: tupoy-ya Date: Thu, 15 Jan 2026 04:08:41 +0500 Subject: [PATCH 4/9] feat: Use player names for session names when possible. --- .../submenus/Network/SessionBrowser.cpp | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/game/frontend/submenus/Network/SessionBrowser.cpp b/src/game/frontend/submenus/Network/SessionBrowser.cpp index 59a029f8..d09a5568 100644 --- a/src/game/frontend/submenus/Network/SessionBrowser.cpp +++ b/src/game/frontend/submenus/Network/SessionBrowser.cpp @@ -12,6 +12,17 @@ namespace YimMenu::Submenus { static int selected_session_idx = -1; + std::string GetSessionName(const CustomMatchmaking::session& session) + { + auto host_rid = session.info.m_HostInfo.m_GamerHandle.m_RockstarId; + + const auto player = SavedPlayers::GetPlayerData(host_rid); + if(player) + return player->m_Name; + + return std::format("{:X}", session.info.m_SessionToken); + } + void RenderSessionBrowser() { static char name_buf[32]; @@ -30,20 +41,14 @@ namespace YimMenu::Submenus if (!session.is_valid) continue; - std::string session_str; - if (session.attributes.multiplex_count > 1) - session_str = std::format("{:X} (x{})", session.info.m_SessionToken, session.attributes.multiplex_count); - else - session_str = std::format("{:X}", session.info.m_SessionToken); - auto host_rid = session.info.m_HostInfo.m_GamerHandle.m_RockstarId; auto player = SavedPlayers::GetPlayerData(host_rid); - /* - if ((g.session_browser.exclude_modder_sessions && player && player->block_join) - || (g.session_browser.filter_multiplexed_sessions && session.attributes.multiplex_count > 1)) - continue; - */ + std::string session_str; + if (session.attributes.multiplex_count > 1) + session_str = std::format("{} (x{})", GetSessionName(session), session.attributes.multiplex_count); + else + session_str = GetSessionName(session); if (ImGui::Selectable(session_str.c_str(), i == selected_session_idx)) { @@ -96,7 +101,6 @@ namespace YimMenu::Submenus if(ImGui::Button("Join")) { FiberPool::Push([session] { - LOGF(VERBOSE, "Trying to join session '{:X}' hosted by: {}, at: {}", session.info.m_SessionToken, session.info.m_HostInfo.m_GamerHandle.m_RockstarId, selected_session_idx); Network::JoinSessionInfo(&session.info); }); } From b7617e6056131cbe99cb1a9492dbc273bfaca5f0 Mon Sep 17 00:00:00 2001 From: tupoy-ya Date: Fri, 16 Jan 2026 01:39:43 +0500 Subject: [PATCH 5/9] refactor: Use YimMenuV2 naming convention. --- src/game/backend/CustomMatchmaking.cpp | 69 ++++++++------- src/game/backend/CustomMatchmaking.hpp | 84 +++++++++---------- .../submenus/Network/SessionBrowser.cpp | 36 ++++---- .../Matchmaking/MatchmakingFindSessions.cpp | 8 +- .../NetworkGameFilterMatchmakingComponent.hpp | 40 ++++----- 5 files changed, 118 insertions(+), 119 deletions(-) diff --git a/src/game/backend/CustomMatchmaking.cpp b/src/game/backend/CustomMatchmaking.cpp index 93983142..39c58670 100644 --- a/src/game/backend/CustomMatchmaking.cpp +++ b/src/game/backend/CustomMatchmaking.cpp @@ -21,7 +21,7 @@ namespace YimMenu::Features "mmregiontype", "Region Type", "The region to spoof the session to", - g_RegionCodes}; + g_RegionCodes}; BoolCommand _SpoofLanguage{ "mmspooflanguage", @@ -85,22 +85,21 @@ namespace YimMenu CustomMatchmaking::CustomMatchmaking() { - } bool CustomMatchmaking::MatchmakeImpl(std::optional constraint, std::optional enforce_player_limit) { - for (auto& session : m_found_sessions) + for (auto& session : m_FoundSessions) { - session.is_valid = true; + session.m_IsValid = true; } NetworkGameFilterMatchmakingComponent component{}; - strcpy(component.m_filter_name, "Group"); - component.m_filter_type = 1; - component.m_game_mode = 0; - component.m_num_parameters = 0; - component.m_session_type = 25600; + strcpy(component.m_FilterName, "Group"); + component.m_FilterType = 1; + component.m_GameMode = 0; + component.m_NumParameters = 0; + component.m_SessionType = 25600; /* if (g.session_browser.region_filter_enabled) @@ -119,37 +118,37 @@ namespace YimMenu rage::rlTaskStatus state{}; static rage::rlSessionInfo result_sessions[MAX_SESSIONS_TO_FIND]; - m_active = true; - m_num_valid_sessions = 0; + m_Active = true; + m_NumValidSessions = 0; - if (BaseHook::Get>()->Original()(0, 1, &component, MAX_SESSIONS_TO_FIND, result_sessions, &m_num_sessions_found, &state)) + if (BaseHook::Get>()->Original()(0, 1, &component, MAX_SESSIONS_TO_FIND, result_sessions, &m_NumSessionsFound, &state)) { while (state.m_Status == 1) ScriptMgr::Yield(); if (state.m_Status == 3) { - std::unordered_map stok_map = {}; + std::unordered_map stok_map = {}; - for (int i = 0; i < m_num_sessions_found; i++) + for (int i = 0; i < m_NumSessionsFound; i++) { - m_found_sessions[i].info = result_sessions[i]; + m_FoundSessions[i].m_Info = result_sessions[i]; - if (auto it = stok_map.find(m_found_sessions[i].info.m_SessionToken); it != stok_map.end()) + if (auto it = stok_map.find(m_FoundSessions[i].m_Info.m_SessionToken); it != stok_map.end()) { - if (/*g.session_browser.filter_multiplexed_sessions*/true) + if (/*g.session_browser.filter_multiplexed_sessions*/ true) { - it->second->is_valid = false; + it->second->m_IsValid = false; } - it->second->attributes.multiplex_count++; - m_found_sessions[i].is_valid = false; + it->second->m_Attributes.m_MultiplexCount++; + m_FoundSessions[i].m_IsValid = false; continue; } if (enforce_player_limit.has_value() && enforce_player_limit.value() - && m_found_sessions[i].attributes.player_count >= 30) - m_found_sessions[i].is_valid = false; + && m_FoundSessions[i].m_Attributes.m_PlayerCount >= 30) + m_FoundSessions[i].m_IsValid = false; /* if (g.session_browser.language_filter_enabled @@ -170,44 +169,44 @@ namespace YimMenu */ - stok_map.emplace(m_found_sessions[i].info.m_SessionToken, &m_found_sessions[i]); + stok_map.emplace(m_FoundSessions[i].m_Info.m_SessionToken, &m_FoundSessions[i]); } - if (/*g.session_browser.sort_method*/1 != 0) + if (/*g.session_browser.sort_method*/ 1 != 0) { - std::qsort(m_found_sessions, m_num_sessions_found, sizeof(session), [](const void* a1, const void* a2) -> int { + std::qsort(m_FoundSessions, m_NumSessionsFound, sizeof(Session), [](const void* a1, const void* a2) -> int { std::strong_ordering result; - if (/*g.session_browser.sort_method*/1 == 1) + if (/*g.session_browser.sort_method*/ 1 == 1) { - result = (((session*)(a1))->attributes.player_count <=> ((session*)(a2))->attributes.player_count); + result = (((Session*)(a1))->m_Attributes.m_PlayerCount <=> ((Session*)(a2))->m_Attributes.m_PlayerCount); } if (result == 0) return 0; if (result > 0) - return /*g.session_browser.sort_direction*/1 ? -1 : 1; + return /*g.session_browser.sort_direction*/ 1 ? -1 : 1; if (result < 0) - return /*g.session_browser.sort_direction*/1 ? 1 : -1; + return /*g.session_browser.sort_direction*/ 1 ? 1 : -1; std::unreachable(); }); } - m_active = false; + m_Active = false; return true; } } else { - m_active = false; + m_Active = false; return false; } - m_active = false; + m_Active = false; return false; } @@ -247,7 +246,7 @@ namespace YimMenu auto id_hash = GetIdHash(id); m_MultiplexedSessions.emplace(id_hash, std::vector{}); - + // create the multiplexed sessions for (int i = 0; i < Features::_MultiplexCount.GetState() - 1; i++) { @@ -294,7 +293,7 @@ namespace YimMenu auto num_slots_copy = num_slots; auto available_slots_copy = available_slots; FiberPool::Push([session, num_slots_copy, available_slots_copy, info, attrs]() { - auto session_copy = session; // the compiler doesn't like it if I use session directly + auto session_copy = session; // the compiler doesn't like it if I use session directly BaseHook::Get>()->Original()(0, &session_copy, num_slots_copy, available_slots_copy, info, attrs, nullptr); // life's too short to check the task result }); } @@ -331,7 +330,7 @@ namespace YimMenu } } - return true; + return true; } void CustomMatchmaking::OnSendSessionDetailResponseImpl(rage::rlSessionDetailMsg* message) diff --git a/src/game/backend/CustomMatchmaking.hpp b/src/game/backend/CustomMatchmaking.hpp index 217d292c..81f8a4a1 100644 --- a/src/game/backend/CustomMatchmaking.hpp +++ b/src/game/backend/CustomMatchmaking.hpp @@ -17,31 +17,31 @@ namespace YimMenu namespace Features { static std::vector> g_RegionCodes = { - {0, "CIS"}, - {1, "South America"}, - {2, "US East"}, - {3, "Europe"}, - {4, "China"}, - {5, "Australia"}, - {6, "US West"}, - {7, "Japan"}, - {8, "Unknown"}, + {0, "CIS"}, + {1, "South America"}, + {2, "US East"}, + {3, "Europe"}, + {4, "China"}, + {5, "Australia"}, + {6, "US West"}, + {7, "Japan"}, + {8, "Unknown"}, }; static std::vector> g_LanguageTypes = { - {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 (Simplified)"}, + {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 (Simplified)"}, }; } @@ -53,7 +53,7 @@ namespace YimMenu void OnSendSessionDetailResponseImpl(rage::rlSessionDetailMsg* message); bool MatchmakeImpl(std::optional constraint = std::nullopt, std::optional enforce_player_limit = std::nullopt); - + CustomMatchmaking(); @@ -66,27 +66,27 @@ namespace YimMenu public: constexpr static int MAX_SESSIONS_TO_FIND = 1000; - struct session_attributes + struct SessionAttributes { - int discriminator; - int player_count; - int region; - int language; - int multiplex_count = 1; + int m_Discriminator; + int m_PlayerCount; + int m_Region; + int m_Language; + int m_MultiplexCount = 1; }; - struct session + struct Session { - rage::rlSessionInfo info; - session_attributes attributes; - bool is_valid; + rage::rlSessionInfo m_Info; + SessionAttributes m_Attributes; + bool m_IsValid; }; private: - int m_num_sessions_found = 0; - int m_num_valid_sessions = 0; - bool m_active = false; - session m_found_sessions[MAX_SESSIONS_TO_FIND]; + int m_NumSessionsFound = 0; + int m_NumValidSessions = 0; + bool m_Active = false; + Session m_FoundSessions[MAX_SESSIONS_TO_FIND]; std::unordered_map> m_MultiplexedSessions; @@ -119,22 +119,22 @@ namespace YimMenu static int GetNumFoundSessions() { - return GetInstance().m_num_sessions_found; + return GetInstance().m_NumSessionsFound; } static int GetNumValidSessions() { - return GetInstance().m_num_valid_sessions; + return GetInstance().m_NumValidSessions; } - static session* GetFoundSessions() + static Session* GetFoundSessions() { - return GetInstance().m_found_sessions; + return GetInstance().m_FoundSessions; } static bool IsActive() { - return GetInstance().m_active; + return GetInstance().m_Active; } }; } \ No newline at end of file diff --git a/src/game/frontend/submenus/Network/SessionBrowser.cpp b/src/game/frontend/submenus/Network/SessionBrowser.cpp index d09a5568..4535381c 100644 --- a/src/game/frontend/submenus/Network/SessionBrowser.cpp +++ b/src/game/frontend/submenus/Network/SessionBrowser.cpp @@ -12,15 +12,15 @@ namespace YimMenu::Submenus { static int selected_session_idx = -1; - std::string GetSessionName(const CustomMatchmaking::session& session) + std::string GetSessionName(const CustomMatchmaking::Session& session) { - auto host_rid = session.info.m_HostInfo.m_GamerHandle.m_RockstarId; + auto host_rid = session.m_Info.m_HostInfo.m_GamerHandle.m_RockstarId; const auto player = SavedPlayers::GetPlayerData(host_rid); if(player) return player->m_Name; - return std::format("{:X}", session.info.m_SessionToken); + return std::format("{:X}", session.m_Info.m_SessionToken); } void RenderSessionBrowser() @@ -38,15 +38,15 @@ namespace YimMenu::Submenus { auto& session = CustomMatchmaking::GetFoundSessions()[i]; - if (!session.is_valid) + if (!session.m_IsValid) continue; - auto host_rid = session.info.m_HostInfo.m_GamerHandle.m_RockstarId; + auto host_rid = session.m_Info.m_HostInfo.m_GamerHandle.m_RockstarId; auto player = SavedPlayers::GetPlayerData(host_rid); std::string session_str; - if (session.attributes.multiplex_count > 1) - session_str = std::format("{} (x{})", GetSessionName(session), session.attributes.multiplex_count); + if (session.m_Attributes.m_MultiplexCount > 1) + session_str = std::format("{} (x{})", GetSessionName(session), session.m_Attributes.m_MultiplexCount); else session_str = GetSessionName(session); @@ -59,11 +59,11 @@ namespace YimMenu::Submenus if (ImGui::IsItemHovered()) { auto tool_tip = std::format("Number of Players: {}\nRegion: {}\nLanguage: {}\nHost Rockstar ID: {}\nDiscriminator: {:X}", - session.attributes.player_count, - Features::g_RegionCodes.at(session.attributes.region).second, - Features::g_LanguageTypes.at(session.attributes.language).second, - session.info.m_HostInfo.m_GamerHandle.m_RockstarId, // TODO: this is not accurate - session.attributes.discriminator); + session.m_Attributes.m_PlayerCount, + Features::g_RegionCodes.at(session.m_Attributes.m_Region).second, + Features::g_LanguageTypes.at(session.m_Attributes.m_Language).second, + session.m_Info.m_HostInfo.m_GamerHandle.m_RockstarId, // TODO: this is not accurate + session.m_Attributes.m_Discriminator); ImGui::SetTooltip("%s", tool_tip.c_str()); } } @@ -83,12 +83,12 @@ namespace YimMenu::Submenus { auto& session = CustomMatchmaking::GetFoundSessions()[selected_session_idx]; - ImGui::Text("Num Players: %d", session.attributes.player_count); - ImGui::Text("Discriminator: 0x%X", session.attributes.discriminator); - ImGui::Text("Region: %s", Features::g_RegionCodes.at(session.attributes.region).second); - ImGui::Text("Language: %s", Features::g_LanguageTypes.at(session.attributes.language).second); + ImGui::Text("Num Players: %d", session.m_Attributes.m_PlayerCount); + ImGui::Text("Discriminator: 0x%X", session.m_Attributes.m_Discriminator); + ImGui::Text("Region: %s", Features::g_RegionCodes.at(session.m_Attributes.m_Region).second); + ImGui::Text("Language: %s", Features::g_LanguageTypes.at(session.m_Attributes.m_Language).second); - auto& data = session.info.m_HostInfo; + auto& data = session.m_Info.m_HostInfo; ImGui::Text("Host Rockstar ID: %llu", data.m_GamerHandle.m_RockstarId); if(ImGui::Button("Copy Session Info")) @@ -101,7 +101,7 @@ namespace YimMenu::Submenus if(ImGui::Button("Join")) { FiberPool::Push([session] { - Network::JoinSessionInfo(&session.info); + Network::JoinSessionInfo(&session.m_Info); }); } } diff --git a/src/game/hooks/Matchmaking/MatchmakingFindSessions.cpp b/src/game/hooks/Matchmaking/MatchmakingFindSessions.cpp index 6e2dd9b6..09f6b00b 100644 --- a/src/game/hooks/Matchmaking/MatchmakingFindSessions.cpp +++ b/src/game/hooks/Matchmaking/MatchmakingFindSessions.cpp @@ -70,10 +70,10 @@ namespace YimMenu::Hooks 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, ','); - CustomMatchmaking::GetFoundSessions()[i].attributes.discriminator = std::stoi(values[2]); - CustomMatchmaking::GetFoundSessions()[i].attributes.player_count = std::stoi(values[4]); - CustomMatchmaking::GetFoundSessions()[i].attributes.language = std::stoi(values[5]); - CustomMatchmaking::GetFoundSessions()[i].attributes.region = std::stoi(values[6]); + CustomMatchmaking::GetFoundSessions()[i].m_Attributes.m_Discriminator = std::stoi(values[2]); + CustomMatchmaking::GetFoundSessions()[i].m_Attributes.m_PlayerCount = std::stoi(values[4]); + CustomMatchmaking::GetFoundSessions()[i].m_Attributes.m_Language = std::stoi(values[5]); + CustomMatchmaking::GetFoundSessions()[i].m_Attributes.m_Region = std::stoi(values[6]); i++; } } diff --git a/src/types/network/NetworkGameFilterMatchmakingComponent.hpp b/src/types/network/NetworkGameFilterMatchmakingComponent.hpp index f6e3847e..4a9165fa 100644 --- a/src/types/network/NetworkGameFilterMatchmakingComponent.hpp +++ b/src/types/network/NetworkGameFilterMatchmakingComponent.hpp @@ -6,28 +6,28 @@ class NetworkGameFilterMatchmakingComponent // do not use for actual network filters, this will break things inline void SetParameter(const char* name, int index, int value) { - std::strcpy(m_param_names[index], name); - m_param_mappings[index] = index; - m_param_values[index] = value; - m_enabled_params_bitset |= (1 << index); + std::strcpy(m_ParamNames[index], name); + m_ParamMappings[index] = index; + m_ParamValues[index] = value; + m_EnabledParamsBitset |= (1 << index); - if (m_num_parameters <= (uint32_t)index) - m_num_parameters++; + if (m_NumParameters <= (uint32_t)index) + m_NumParameters++; } - uint32_t m_filter_type; //0x0000 - char m_filter_name[24]; //0x0004 - uint32_t m_num_parameters; //0x001C - uint16_t m_game_mode; //0x0020 - uint16_t m_session_type; //0x0022 - uint32_t m_param_unk[8]; //0x0024 - char m_param_names[8][24]; //0x0044 - char pad_0104[4]; //0x0104 - uint32_t m_param_mappings[8]; //0x0108 - char pad_0128[352]; //0x0128 - uint32_t m_param_values[8]; //0x0288 - char pad_02A8[96]; //0x02A8 - uint32_t m_enabled_params_bitset; //0x0308 - char pad_030C[8]; //0x030C + uint32_t m_FilterType; //0x0000 + char m_FilterName[24]; //0x0004 + uint32_t m_NumParameters; //0x001C + uint16_t m_GameMode; //0x0020 + uint16_t m_SessionType; //0x0022 + uint32_t m_ParamUnk[8]; //0x0024 + char m_ParamNames[8][24]; //0x0044 + char pad_0104[4]; //0x0104 + uint32_t m_ParamMappings[8]; //0x0108 + char pad_0128[352]; //0x0128 + uint32_t m_ParamValues[8]; //0x0288 + char pad_02A8[96]; //0x02A8 + uint32_t m_EnabledParamsBitset; //0x0308 + char pad_030C[8]; //0x030C }; //Size: 0x0314 static_assert(sizeof(NetworkGameFilterMatchmakingComponent) == 0x314); From 07d3b1d4159364ecf0c281193407b002223ee162 Mon Sep 17 00:00:00 2001 From: tupoy-ya Date: Fri, 16 Jan 2026 17:37:47 +0500 Subject: [PATCH 6/9] feat: Port filters to YimV2 UI. --- src/game/backend/CustomMatchmaking.cpp | 92 +++++++++++----- .../submenus/Network/SessionBrowser.cpp | 101 +++--------------- 2 files changed, 82 insertions(+), 111 deletions(-) diff --git a/src/game/backend/CustomMatchmaking.cpp b/src/game/backend/CustomMatchmaking.cpp index 39c58670..13cf6858 100644 --- a/src/game/backend/CustomMatchmaking.cpp +++ b/src/game/backend/CustomMatchmaking.cpp @@ -13,6 +13,15 @@ namespace YimMenu::Features { + static std::vector> g_SortMethods = { + {0, "Off"}, + {1, "Player Count"}, + }; + static std::vector> g_SortDirections = { + {0, "Ascending"}, + {1, "Descending"}, + }; + BoolCommand _SpoofRegionType{ "mmspoofregiontype", "Spoof Region Type", @@ -56,6 +65,50 @@ namespace YimMenu::Features 2, 7, 5}; + + BoolCommand _LanguageFilterEnabled{ + "mmlanguagefilterenabled", + "Filter By Language", + "Filter sessions by language"}; + ListCommand _LanguageFilter{ + "mmlanguagefilter", + "Language", + "The language to filter", + g_LanguageTypes}; + BoolCommand _FilterMultiplexedSessions{ + "mmfiltermultiplexedsessions", + "Filter Multiplexed Sessions", + "Filter out multiplexed sessions"}; + + BoolCommand _PlayerCountFilterEnabled{ + "mmplayercountfilterenabled", + "Filter By Player Count", + "Filter by player count"}; + IntCommand _PlayerCountFilterMin{ + "mmplayercountfiltermin", + "Player Count Minimum", + "Minimum players filter", + 1, + 32, + 25}; + IntCommand _PlayerCountFilterMax{ + "mmplayercountfiltermax", + "Player Count Maximum", + "Maximum players filter", + 1, + 32, + 25}; + + ListCommand _SortMethod{ + "mmsortmethod", + "Sort By", + "", + g_SortMethods}; + ListCommand _SortDirection{ + "mmsortdirection", + "Sort Direction", + "", + g_SortDirections}; } namespace YimMenu @@ -101,13 +154,6 @@ namespace YimMenu component.m_NumParameters = 0; component.m_SessionType = 25600; - /* - if (g.session_browser.region_filter_enabled) - { - component.SetParameter("MMATTR_REGION", 4, g.session_browser.region_filter); - } - */ - if (constraint) { component.SetParameter("MMATTR_DISCRIMINATOR", 0, constraint.value()); @@ -136,7 +182,7 @@ namespace YimMenu if (auto it = stok_map.find(m_FoundSessions[i].m_Info.m_SessionToken); it != stok_map.end()) { - if (/*g.session_browser.filter_multiplexed_sessions*/ true) + if (Features::_FilterMultiplexedSessions.GetState()) { it->second->m_IsValid = false; } @@ -150,34 +196,26 @@ namespace YimMenu && m_FoundSessions[i].m_Attributes.m_PlayerCount >= 30) m_FoundSessions[i].m_IsValid = false; - /* - if (g.session_browser.language_filter_enabled - && (eGameLanguage)m_found_sessions[i].attributes.language != g.session_browser.language_filter) - m_found_sessions[i].is_valid = false; + if (Features::_LanguageFilterEnabled.GetState() + && m_FoundSessions[i].m_Attributes.m_Language != Features::_LanguageFilter.GetState()) + m_FoundSessions[i].m_IsValid = 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)) + if (Features::_PlayerCountFilterEnabled.GetState() + && (m_FoundSessions[i].m_Attributes.m_PlayerCount < Features::_PlayerCountFilterMin.GetState() + || m_FoundSessions[i].m_Attributes.m_PlayerCount > Features::_PlayerCountFilterMax.GetState())) { - m_found_sessions[i].is_valid = false; + m_FoundSessions[i].m_IsValid = false; } - if (g.session_browser.pool_filter_enabled - && ((m_found_sessions[i].attributes.discriminator & (1 << 14)) == (1 << 14)) - != (bool)g.session_browser.pool_filter) - m_found_sessions[i].is_valid = false; - - */ - stok_map.emplace(m_FoundSessions[i].m_Info.m_SessionToken, &m_FoundSessions[i]); } - if (/*g.session_browser.sort_method*/ 1 != 0) + if (Features::_SortMethod.GetState() != 0) { std::qsort(m_FoundSessions, m_NumSessionsFound, sizeof(Session), [](const void* a1, const void* a2) -> int { std::strong_ordering result; - if (/*g.session_browser.sort_method*/ 1 == 1) + if (Features::_SortMethod.GetState() == 1) { result = (((Session*)(a1))->m_Attributes.m_PlayerCount <=> ((Session*)(a2))->m_Attributes.m_PlayerCount); } @@ -186,10 +224,10 @@ namespace YimMenu return 0; if (result > 0) - return /*g.session_browser.sort_direction*/ 1 ? -1 : 1; + return Features::_SortDirection.GetState() ? -1 : 1; if (result < 0) - return /*g.session_browser.sort_direction*/ 1 ? 1 : -1; + return Features::_SortDirection.GetState() ? 1 : -1; std::unreachable(); diff --git a/src/game/frontend/submenus/Network/SessionBrowser.cpp b/src/game/frontend/submenus/Network/SessionBrowser.cpp index 4535381c..76e077a3 100644 --- a/src/game/frontend/submenus/Network/SessionBrowser.cpp +++ b/src/game/frontend/submenus/Network/SessionBrowser.cpp @@ -4,6 +4,7 @@ #include "core/frontend/Notifications.hpp" #include "game/backend/CustomMatchmaking.hpp" #include "game/backend/SavedPlayers.hpp" +#include "game/frontend/items/Items.hpp" #include "game/gta/Network.hpp" #include "game/pointers/Pointers.hpp" #include "imgui.h" @@ -108,90 +109,6 @@ namespace YimMenu::Submenus 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.at(g.session_browser.language_filter).data())) - { - for (const auto& [id, language] : languages) - { - if (ImGui::Selectable(language.data(), g.session_browser.language_filter == id)) - { - g.session_browser.language_filter = 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::Checkbox("Pool Type", &g.session_browser.pool_filter_enabled); - if (g.session_browser.pool_filter_enabled) - { - ImGui::SameLine(); - ImGui::Combo("###pooltype", &g.session_browser.pool_filter, "Normal\0Bad Sport\0"); - } - - ImGui::Checkbox("Filter Multiplexed Sessions", &g.session_browser.filter_multiplexed_sessions); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Removes advertised sessions"); - - ImGui::Checkbox("Exclude Modder Sessions", &g.session_browser.exclude_modder_sessions); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Excludes hosts that you have blocked in the Player Database"); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Sorting")) - { - ImGui::Combo("Sort By", &g.session_browser.sort_method, "Off\0Player Count\0"); - if (g.session_browser.sort_method != 0) - ImGui::Combo("Direction", &g.session_browser.sort_direction, "Ascending\0Descending\0"); - ImGui::TreePop(); - } - - ImGui::Checkbox("Replace Game Matchmaking", &g.session_browser.replace_game_matchmaking); - ImGui::SameLine(); - components::help_marker("This will replace the default game matchmaking with a custom one that will use the filters and sorting set here"); - **/ - static uint32_t discriminator = 730776930; // 0xA9A8562 for non_cheater pool ImGui::InputScalar("Discriminator", ImGuiDataType_U32, &discriminator, nullptr, nullptr, "%08X"); @@ -212,6 +129,22 @@ namespace YimMenu::Submenus auto menu = std::make_shared("Session Browser"); menu->AddItem(std::make_unique(RenderSessionBrowser)); + auto filters = std::make_shared("Filters"); + + filters->AddItem(std::make_shared("mmlanguagefilterenabled"_J)); + filters->AddItem(std::make_shared("mmlanguagefilter"_J)); + + filters->AddItem(std::make_shared("mmfiltermultiplexedsessions"_J)); + + filters->AddItem(std::make_shared("mmplayercountfilterenabled"_J)); + filters->AddItem(std::make_shared("mmplayercountfiltermin"_J)); + filters->AddItem(std::make_shared("mmplayercountfiltermax"_J)); + + filters->AddItem(std::make_shared("mmsortmethod"_J)); + filters->AddItem(std::make_shared("mmsortdirection"_J)); + + menu->AddItem(filters); + return menu; } } \ No newline at end of file From ed3384d5f5663d3349bd91675b67fc64d3e06224 Mon Sep 17 00:00:00 2001 From: tupoy-ya Date: Fri, 16 Jan 2026 17:38:13 +0500 Subject: [PATCH 7/9] fix(CI): Potentially fix zig CI. Why is it even a thing? --- src/game/backend/CustomMatchmaking.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/backend/CustomMatchmaking.cpp b/src/game/backend/CustomMatchmaking.cpp index 13cf6858..83e83f8e 100644 --- a/src/game/backend/CustomMatchmaking.cpp +++ b/src/game/backend/CustomMatchmaking.cpp @@ -213,7 +213,7 @@ namespace YimMenu if (Features::_SortMethod.GetState() != 0) { std::qsort(m_FoundSessions, m_NumSessionsFound, sizeof(Session), [](const void* a1, const void* a2) -> int { - std::strong_ordering result; + std::strong_ordering result(0); if (Features::_SortMethod.GetState() == 1) { From 546420c33ae04809131a4674f75033d4b8abc4ca Mon Sep 17 00:00:00 2001 From: tupoy-ya Date: Fri, 16 Jan 2026 17:54:47 +0500 Subject: [PATCH 8/9] fix(CI): Another attempt at getting zig(?) mingw CI to work. --- src/game/backend/CustomMatchmaking.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/backend/CustomMatchmaking.cpp b/src/game/backend/CustomMatchmaking.cpp index 83e83f8e..3329e4cf 100644 --- a/src/game/backend/CustomMatchmaking.cpp +++ b/src/game/backend/CustomMatchmaking.cpp @@ -213,7 +213,7 @@ namespace YimMenu if (Features::_SortMethod.GetState() != 0) { std::qsort(m_FoundSessions, m_NumSessionsFound, sizeof(Session), [](const void* a1, const void* a2) -> int { - std::strong_ordering result(0); + std::strong_ordering result = std::strong_ordering::equal; if (Features::_SortMethod.GetState() == 1) { From a74e590631fad7986aff13ace1cfa083d50e6aa9 Mon Sep 17 00:00:00 2001 From: tupoy-ya Date: Fri, 16 Jan 2026 18:30:50 +0500 Subject: [PATCH 9/9] fix: Copy Session Info works now. --- src/game/frontend/submenus/Network/SessionBrowser.cpp | 2 +- src/game/pointers/Pointers.cpp | 5 +++++ src/game/pointers/Pointers.hpp | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/game/frontend/submenus/Network/SessionBrowser.cpp b/src/game/frontend/submenus/Network/SessionBrowser.cpp index 76e077a3..79276955 100644 --- a/src/game/frontend/submenus/Network/SessionBrowser.cpp +++ b/src/game/frontend/submenus/Network/SessionBrowser.cpp @@ -54,7 +54,7 @@ namespace YimMenu::Submenus if (ImGui::Selectable(session_str.c_str(), i == selected_session_idx)) { selected_session_idx = i; - //g_pointers->m_gta.m_encode_session_info(&session.info, session_info, 0xA9, nullptr); + Pointers.EncodeSessionInfo(&session.m_Info, session_info, 0xA9, nullptr); } if (ImGui::IsItemHovered()) diff --git a/src/game/pointers/Pointers.cpp b/src/game/pointers/Pointers.cpp index ac2a1f38..3f76e22f 100644 --- a/src/game/pointers/Pointers.cpp +++ b/src/game/pointers/Pointers.cpp @@ -462,6 +462,11 @@ namespace YimMenu MatchmakingSessionDetailSendResponse = addr.Add(0x2F).Rip().As(); }); + static constexpr auto encodeSessionInfoPtrn = Pattern<"E8 ? ? ? ? 48 85 C0 74 ? 48 89 BC">("EncodeSessionInfo"); + scanner.Add(encodeSessionInfoPtrn, [this](PointerCalculator addr) { + EncodeSessionInfo = addr.Add(1).Rip().As(); + }); + static constexpr auto gameSkeletonUpdatePtrn = Pattern<"56 48 83 EC 20 48 8B 81 40 01 00 00 48 85 C0">("GameSkeletonUpdate"); scanner.Add(gameSkeletonUpdatePtrn, [this](PointerCalculator addr) { GameSkeletonUpdate = addr.As(); diff --git a/src/game/pointers/Pointers.hpp b/src/game/pointers/Pointers.hpp index dc12299f..30c8de1c 100644 --- a/src/game/pointers/Pointers.hpp +++ b/src/game/pointers/Pointers.hpp @@ -72,6 +72,7 @@ namespace YimMenu using GetAvatars = bool (*)(rage::rlGetAvatarsContext* context, rage::rlGetAvatarsPlayerList* players); using AssistedAimFindNewTarget = bool (*)(__int64 a1); using MatchmakingFindSessions = bool (*)(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* m_filter, unsigned int max_sessions, rage::rlSessionInfo* result_sessions, int* result_session_count, rage::rlTaskStatus* state); + using EncodeSessionInfo = bool (*)(rage::rlSessionInfo* info, char* buffer, int buffer_size, int* bytes_written); } struct PointerData @@ -174,6 +175,7 @@ namespace YimMenu PVOID MatchmakingSessionDetailSendResponse; PVOID MatchmakingFindSessions; PVOID MatchmakingFindSessionsResponse; + Functions::EncodeSessionInfo EncodeSessionInfo; PVOID GameSkeletonUpdate; };