mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-10 03:43:15 +08:00
feat(network): join by username
This commit is contained in:
@@ -270,6 +270,19 @@ namespace YimMenu
|
||||
return res;
|
||||
}
|
||||
|
||||
void SavedPlayers::AddPlayerDataImpl(std::uint64_t id, std::string_view username)
|
||||
{
|
||||
if (auto data = GetPlayerDataImpl(id))
|
||||
return;
|
||||
|
||||
SavedPlayerData data;
|
||||
data.m_Name = username;
|
||||
|
||||
m_SavedPlayers.emplace(id, data);
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
void SavedPlayers::UpdateRockstarIdImpl(std::uint64_t id, std::uint64_t new_id)
|
||||
{
|
||||
if (auto player = GetPlayerDataImpl(id))
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace YimMenu
|
||||
void FetchPlayerInfoImpl(bool tracked_only = false);
|
||||
SavedPlayerData* GetPlayerDataImpl(std::uint64_t id);
|
||||
SavedPlayerData* GetPlayerDataImpl(Player player, bool create = true);
|
||||
void AddPlayerDataImpl(std::uint64_t id, std::string_view username);
|
||||
void UpdateRockstarIdImpl(std::uint64_t id, std::uint64_t new_id);
|
||||
void RemovePlayerDataImpl(std::uint64_t id);
|
||||
void LoadImpl();
|
||||
@@ -56,6 +57,11 @@ namespace YimMenu
|
||||
return GetInstance().GetPlayerDataImpl(player, create);
|
||||
}
|
||||
|
||||
static void AddPlayerData(std::uint64_t id, std::string_view username)
|
||||
{
|
||||
GetInstance().AddPlayerDataImpl(id, username);
|
||||
}
|
||||
|
||||
// TODO: this cannot be safely called outside the saved players frontend menu. Invalidates all SavedPlayerData pointers due to potential map reallocation
|
||||
static void UpdateRockstarId(std::uint64_t old_id, std::uint64_t new_id)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Network.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
#include "core/frontend/Notifications.hpp"
|
||||
#include "game/frontend/items/Items.hpp"
|
||||
#include "game/frontend/submenus/Network/SavedPlayers.hpp"
|
||||
#include "game/gta/Network.hpp"
|
||||
@@ -25,6 +26,24 @@ namespace YimMenu::Submenus
|
||||
joinGroup->AddItem(joinSession);
|
||||
joinGroup->AddItem(std::make_shared<ImGuiItem>([] {
|
||||
static std::uint64_t rockstar_id{};
|
||||
static char name_buf[24]{};
|
||||
|
||||
ImGui::SetNextItemWidth(180.0f);
|
||||
ImGui::InputText("Username", name_buf, sizeof(name_buf));
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Join##username"))
|
||||
FiberPool::Push([] {
|
||||
auto rid = YimMenu::Network::ResolveRockstarId(name_buf);
|
||||
if (rid)
|
||||
{
|
||||
YimMenu::Network::JoinRockstarId(*rid);
|
||||
}
|
||||
else
|
||||
{
|
||||
Notifications::Show("Joiner", "Failed to get RID from username", NotificationType::Error);
|
||||
}
|
||||
});
|
||||
|
||||
ImGui::SetNextItemWidth(150.0f);
|
||||
ImGui::InputScalar("Rockstar Id", ImGuiDataType_U64, &rockstar_id);
|
||||
ImGui::SameLine();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "SavedPlayers.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
#include "core/frontend/widgets/imgui_colors.h"
|
||||
#include "core/frontend/Notifications.hpp"
|
||||
#include "game/backend/SavedPlayers.hpp"
|
||||
#include "game/gta/Network.hpp"
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
@@ -72,7 +73,7 @@ namespace YimMenu::Submenus
|
||||
ImGui::SetNextItemWidth(200.f);
|
||||
ImGui::InputTextWithHint("Search", "Search", g_NameToSearch, sizeof(g_NameToSearch));
|
||||
|
||||
if (ImGui::BeginListBox("###player-list", {180, static_cast<float>(*Pointers.ScreenResY - 700 - 38 * 4)}))
|
||||
if (ImGui::BeginListBox("###player-list", {180, -100 /* static_cast<float>(*Pointers.ScreenResY - 700 - 38 * 4) */}))
|
||||
{
|
||||
auto& players = SavedPlayers::GetSavedPlayers();
|
||||
|
||||
@@ -108,7 +109,7 @@ namespace YimMenu::Submenus
|
||||
if (!g_SelectedPlayer || g_SelectedRid == 0)
|
||||
return;
|
||||
|
||||
if (ImGui::BeginChild("##player-editor", {500, static_cast<float>(*Pointers.ScreenResY - 688 - 38 * 4)}, 0, ImGuiWindowFlags_NoBackground))
|
||||
if (ImGui::BeginChild("##player-editor", {500, -100 /* static_cast<float>(*Pointers.ScreenResY - 688 - 38 * 4) */}, 0, ImGuiWindowFlags_NoBackground))
|
||||
{
|
||||
ImGui::SetNextItemWidth(180.f);
|
||||
if (ImGui::InputText("Name", g_SelectedPlayerName, sizeof(g_SelectedPlayerName)))
|
||||
@@ -178,10 +179,32 @@ namespace YimMenu::Submenus
|
||||
RenderPlayerEditor();
|
||||
}
|
||||
|
||||
static void RenderAddNewPlayer()
|
||||
{
|
||||
static char name_buf[24]{};
|
||||
|
||||
ImGui::SetNextItemWidth(180.0f);
|
||||
ImGui::InputText("Username", name_buf, sizeof(name_buf));
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Add"))
|
||||
FiberPool::Push([] {
|
||||
auto rid = YimMenu::Network::ResolveRockstarId(name_buf);
|
||||
if (rid)
|
||||
{
|
||||
SavedPlayers::AddPlayerData(*rid, name_buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
Notifications::Show("Saved Players", "Failed to get RID from username", NotificationType::Error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<Category> BuildSavedPlayersMenu()
|
||||
{
|
||||
auto menu = std::make_shared<Category>("Saved Players");
|
||||
auto players = std::make_shared<Group>("Players");
|
||||
auto new_player = std::make_shared<Group>("New");
|
||||
auto tracking = std::make_shared<Group>("Tracking");
|
||||
auto notifications = std::make_shared<Group>("Notifications");
|
||||
|
||||
@@ -189,6 +212,10 @@ namespace YimMenu::Submenus
|
||||
RenderSavedPlayers();
|
||||
}));
|
||||
|
||||
new_player->AddItem(std::make_shared<ImGuiItem>([] {
|
||||
RenderAddNewPlayer();
|
||||
}));
|
||||
|
||||
notifications->AddItem(std::make_shared<BoolCommandItem>("playerdbnotifywhenjoinable"_J));
|
||||
notifications->AddItem(std::make_shared<BoolCommandItem>("playerdbnotifywhenunjoinable"_J));
|
||||
notifications->AddItem(std::make_shared<BoolCommandItem>("playerdbnotifywhenonline"_J));
|
||||
@@ -206,6 +233,7 @@ namespace YimMenu::Submenus
|
||||
tracking->AddItem(std::make_shared<ConditionalItem>("playerdbnotify"_J, std::move(notifications)));
|
||||
|
||||
menu->AddItem(players);
|
||||
menu->AddItem(new_player);
|
||||
menu->AddItem(tracking);
|
||||
|
||||
return menu;
|
||||
|
||||
@@ -6,9 +6,13 @@
|
||||
#include "game/gta/ScriptGlobal.hpp"
|
||||
#include "game/gta/ScriptFunction.hpp"
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
#include "types/rage/atArray.hpp"
|
||||
#include "types/network/rlSessionInfo.hpp"
|
||||
#include "types/network/rlSessionByGamerHandleTaskResult.hpp"
|
||||
#include "types/network/rlTaskStatus.hpp"
|
||||
#include "types/socialclub/rlQueryAccountsResult.hpp"
|
||||
#include "types/socialclub/rlGetAvatarsContext.hpp"
|
||||
#include "types/socialclub/rlScTaskStatus.hpp"
|
||||
|
||||
namespace YimMenu::Network
|
||||
{
|
||||
@@ -37,6 +41,7 @@ namespace YimMenu::Network
|
||||
{
|
||||
ctx->SetReturnValue(TRUE);
|
||||
}
|
||||
session_to_join = std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -84,4 +89,38 @@ namespace YimMenu::Network
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::uint64_t> ResolveRockstarId(std::string_view name)
|
||||
{
|
||||
if (!Pointers.GetAvatars)
|
||||
return std::nullopt;
|
||||
|
||||
// these are probably too large to keep on the stack
|
||||
auto results = std::make_unique<rage::rlGetAvatarsResults>();
|
||||
auto players = std::make_unique<rage::rlGetAvatarsPlayerList>();
|
||||
|
||||
rage::rlGetAvatarsContext context;
|
||||
|
||||
strncpy(players->m_PlayerNames[0], name.data(), sizeof(players->m_PlayerNames[0]));
|
||||
players->m_NumEntries = 1;
|
||||
|
||||
context.m_Type = rage::rlGetAvatarsContext::Type::BY_NICKNAME;
|
||||
context.m_AvatarSize = rage::rlGetAvatarsContext::AvatarSize::SMALL; // we don't need the avatars, but you gotta give it something
|
||||
context.m_Result = results.get();
|
||||
|
||||
if (Pointers.GetAvatars(&context, players.get()))
|
||||
{
|
||||
while (context.m_Status.m_Status == 1)
|
||||
{
|
||||
ScriptMgr::Yield();
|
||||
}
|
||||
|
||||
if (context.m_Status.m_Status == 3 && results->m_NumResults == 1)
|
||||
{
|
||||
return results->m_Results[0].m_RockstarId;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
@@ -25,4 +25,5 @@ namespace YimMenu::Network
|
||||
void LaunchJoinType(JoinType to_launch);
|
||||
void JoinSessionInfo(rage::rlSessionInfo* info);
|
||||
void JoinRockstarId(std::uint64_t id);
|
||||
std::optional<std::uint64_t> ResolveRockstarId(std::string_view name);
|
||||
}
|
||||
@@ -352,6 +352,11 @@ namespace YimMenu
|
||||
BytePatches::Add(ptr.As<void*>(), std::vector<std::uint8_t>{0xB0, 0x01})->Apply();
|
||||
});
|
||||
|
||||
constexpr auto getAvatarsPtrn = Pattern<"89 4E 7C 48 8B CE E8 ? ? ? ? 84 C0">("GetAvatars");
|
||||
scanner.Add(getAvatarsPtrn, [this](PointerCalculator ptr) {
|
||||
GetAvatars = ptr.Add(6).Add(1).Rip().As<Functions::GetAvatars>();
|
||||
});
|
||||
|
||||
if (!scanner.Scan())
|
||||
{
|
||||
LOG(WARNING) << "Some socialclub patterns could not be found";
|
||||
|
||||
@@ -27,6 +27,9 @@ namespace rage
|
||||
class rlSessionByGamerTaskResult;
|
||||
class rlQueryPresenceAttributesContext;
|
||||
class rlScGamerHandle;
|
||||
class rlQueryAccountsResult;
|
||||
class rlGetAvatarsContext;
|
||||
class rlGetAvatarsPlayerList;
|
||||
}
|
||||
class CPedFactory;
|
||||
class CNetGamePlayer;
|
||||
@@ -60,6 +63,7 @@ namespace YimMenu
|
||||
using JoinSessionByInfo = bool (*)(CNetworkSession* network, rage::rlSessionInfo* info, int unk, int flags, rage::rlGamerHandle* handles, int num_handles);
|
||||
using GetSessionByGamerHandle = bool (*)(int profile_index, rage::rlGamerHandle* handles, int num_handles, rage::rlSessionByGamerTaskResult* results, int num_results, bool* success, rage::rlTaskStatus* state);
|
||||
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);
|
||||
}
|
||||
|
||||
struct PointerData
|
||||
@@ -129,6 +133,7 @@ namespace YimMenu
|
||||
Functions::JoinSessionByInfo JoinSessionByInfo;
|
||||
Functions::GetSessionByGamerHandle GetSessionByGamerHandle;
|
||||
Functions::GetPresenceAttributes GetPresenceAttributes;
|
||||
Functions::GetAvatars GetAvatars;
|
||||
};
|
||||
|
||||
struct Pointers : PointerData
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace rage
|
||||
atArray() :
|
||||
m_Data(nullptr),
|
||||
m_Size(0),
|
||||
m_Count(0)
|
||||
m_Capacity(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -37,9 +37,9 @@ namespace rage
|
||||
return m_Size;
|
||||
}
|
||||
|
||||
std::uint16_t count() const
|
||||
std::uint16_t capacity() const
|
||||
{
|
||||
return m_Count;
|
||||
return m_Capacity;
|
||||
}
|
||||
|
||||
T& operator[](std::uint16_t index) const
|
||||
@@ -59,10 +59,10 @@ namespace rage
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
public:
|
||||
T* m_Data;
|
||||
std::uint16_t m_Size;
|
||||
std::uint16_t m_Count;
|
||||
std::uint16_t m_Capacity; // includes items allocated due to reservations and alignment
|
||||
};
|
||||
static_assert(sizeof(rage::atArray<std::uint32_t>) == 0x10, "rage::atArray is not properly sized");
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include "rlScTaskStatus.hpp"
|
||||
|
||||
namespace rage
|
||||
{
|
||||
struct rlGetAvatarsPlayerList
|
||||
{
|
||||
union
|
||||
{
|
||||
char m_PlayerNames[51][250];
|
||||
char m_PlayerHandles[51][250];
|
||||
};
|
||||
int m_NumEntries;
|
||||
};
|
||||
static_assert(sizeof(rlGetAvatarsPlayerList) == 0x31D4);
|
||||
|
||||
struct rlGetAvatarsResult
|
||||
{
|
||||
std::uint64_t m_RockstarId; // 0x00
|
||||
char m_Nickname[51]; // 0x08
|
||||
char m_AvatarUrl[127]; // 0x3B
|
||||
};
|
||||
static_assert(sizeof(rlGetAvatarsResult) == 0xC0);
|
||||
|
||||
struct rlGetAvatarsResults
|
||||
{
|
||||
rlGetAvatarsResult m_Results[250];
|
||||
int m_NumResults;
|
||||
};
|
||||
static_assert(sizeof(rlGetAvatarsResults) == 0xBB88);
|
||||
|
||||
// this is probably something else that LLVM decided to inline into the GetAvatars func
|
||||
class rlGetAvatarsContext
|
||||
{
|
||||
public:
|
||||
enum class Type : std::uint8_t
|
||||
{
|
||||
BY_NICKNAME,
|
||||
BY_ROCKSTAR_ID
|
||||
};
|
||||
|
||||
enum class AvatarSize
|
||||
{
|
||||
SMALL = 1,
|
||||
MEDIUM = 2,
|
||||
LARGE = 4
|
||||
};
|
||||
|
||||
virtual ~rlGetAvatarsContext() = default;
|
||||
|
||||
char m_Pad[80]; // 0x08
|
||||
void* m_Result; // 0x58
|
||||
char m_Pad2[4]; // 0x60
|
||||
Type m_Type; // 0x64
|
||||
AvatarSize m_AvatarSize; // 0x68
|
||||
rage::rlScTaskStatus m_Status{}; // 0x70
|
||||
};
|
||||
static_assert(sizeof(rlGetAvatarsContext) == 0x80);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
namespace rage
|
||||
{
|
||||
class rlQueryAccountsResult
|
||||
{
|
||||
public:
|
||||
enum class Relationship : std::uint32_t
|
||||
{
|
||||
NONE = 1,
|
||||
BLOCKED_BY_ME = 2,
|
||||
BLOCKED_BY_THEM = 4,
|
||||
BLOCKED_BY_BOTH = 6,
|
||||
INVITED_BY_ME = 8,
|
||||
INVITED_BY_THEM = 16,
|
||||
FRIEND = 32
|
||||
};
|
||||
|
||||
std::uint64_t m_RockstarId; // 0x00 (gets parsed as a 32 bit value? wtf?)
|
||||
char m_Nickname[51]; // 0x08
|
||||
char m_AvatarUrl[2048]; // 0x3B
|
||||
bool m_IsVisible; // 0x83B
|
||||
Relationship m_Relationship; // 0x83C
|
||||
bool m_IsClanmate; // 0x840
|
||||
};
|
||||
static_assert(sizeof(rlQueryAccountsResult) == 0x848);
|
||||
}
|
||||
Reference in New Issue
Block a user