[ClanTag] Add back component + extras (#357)

* [ClanTags] Add back component
This commit is contained in:
Edo 2022-07-16 23:24:26 +02:00 committed by GitHub
parent e961f70fda
commit 491d4d3ac3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 943 additions and 213 deletions

View File

@ -61,7 +61,7 @@ namespace Components
Loader::Register(new Network());
Loader::Register(new Session());
Loader::Register(new Theatre());
//Loader::Register(new ClanTags());
Loader::Register(new ClanTags());
Loader::Register(new Download());
Loader::Register(new Playlist());
Loader::Register(new RawFiles());

View File

@ -366,7 +366,7 @@ namespace Components
// In case a loaded mod didn't call "BotStop" before the VM shutdown
Events::OnVMShutdown([]
{
for (std::size_t i = 0; i < std::extent_v<decltype(g_botai)>; i++)
for (std::size_t i = 0; i < std::extent_v<decltype(g_botai)>; ++i)
{
g_botai[i].active = false;
}

View File

@ -3,14 +3,14 @@
namespace Components
{
std::string CardTitles::CustomTitles[18];
Dvar::Var CardTitles::CustomTitleDvar;
Dvar::Var CardTitles::CustomTitle;
CClient* CardTitles::GetClientByIndex(std::uint32_t index)
{
return &reinterpret_cast<CClient*>(0x8E77B0)[index];
}
std::int32_t CardTitles::GetPlayerCardClientInfo(std::int32_t lookupResult, playercarddata_s* data)
std::int32_t CardTitles::GetPlayerCardClientInfo(std::int32_t lookupResult, Game::PlayerCardData* data)
{
std::int32_t returnResult = lookupResult;
@ -22,12 +22,12 @@ namespace Components
}
else
{
for (auto clientNum = 0; clientNum < 18; clientNum++)
for (std::size_t clientNum = 0; clientNum < Game::MAX_CLIENTS; ++clientNum)
{
CClient* c = GetClientByIndex(clientNum);
if (c != nullptr)
{
if (!strcmp(data->name, c->Name))
if (!std::strcmp(data->name, c->Name))
{
// Since a 4 byte integer is overkill for a row num: We can use it to store the customprefix + clientNum and use a 2 byte integer for the row number
returnResult += 0xFF000000;
@ -62,7 +62,6 @@ namespace Components
mov [ebx + 4], eax
pop ebx
push 62EB2Ch
retn
}
}
@ -83,10 +82,10 @@ namespace Components
{
if (prefix == 0xFE)
{
if (!CardTitles::CustomTitleDvar.get<std::string>().empty())
if (!CardTitles::CustomTitle.get<std::string>().empty())
{
// 0xFF in front of the title to skip localization. Or else it will wait for a couple of seconds for the asset of type localize
const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitleDvar.get<const char*>());
const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitle.get<const char*>());
// prepare return value
operand->internals.stringVal.string = title;
@ -157,20 +156,20 @@ namespace Components
{
std::string list;
for (int i = 0; i < 18; i++)
for (std::size_t i = 0; i < Game::MAX_CLIENTS; i++)
{
char playerTitle[18];
if (Game::svs_clients[i].state >= 3)
if (Game::svs_clients[i].state >= Game::CS_CONNECTED)
{
strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].connectInfoString, "customTitle"), 17);
strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].userinfo, "customTitle"), _TRUNCATE);
}
else
{
memset(playerTitle, 0, 18);
playerTitle[0] = '\0';
}
list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).c_str(), playerTitle));
list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).data(), playerTitle));
}
const auto* command = Utils::String::VA("%c customTitles \"%s\"", 21, list.data());
@ -179,7 +178,7 @@ namespace Components
void CardTitles::ParseCustomTitles(const char* msg)
{
for (int i = 0; i < 18; ++i)
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
{
const char* playerTitle = Game::Info_ValueForKey(msg, std::to_string(i).c_str());
@ -192,7 +191,7 @@ namespace Components
{
Scheduler::Once([]
{
CardTitles::CustomTitleDvar = Dvar::Register<const char*>("customtitle", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, "Custom card title");
CardTitles::CustomTitle = Dvar::Register<const char*>("customTitle", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, "Custom card title");
}, Scheduler::Pipeline::MAIN);
ServerCommands::OnCommand(21, [](Command::Params* params)
@ -215,8 +214,5 @@ namespace Components
// Table lookup stuff
Utils::Hook(0x62DCC1, CardTitles::TableLookupByRowHookStub).install()->quick();
Utils::Hook::Nop(0x62DCC6, 1);
// This is placed here in case the anticheat has been disabled!
// This checks specifically for launching the process suspended to inject a dll
}
}

View File

@ -12,18 +12,6 @@ namespace Components
std::int32_t tableColumn;
};
struct playercarddata_s
{
std::uint32_t padding;
std::uint32_t playercardNumber;
std::uint32_t unknown;
std::uint32_t unknown2;
std::uint32_t level; //Level is counted from 0 -> Value 69 is Level 70
std::uint32_t prestige;
std::uint32_t padding2;
char name[40];
};
struct CClient
{
std::uint32_t IsValid; // 0x0000
@ -54,21 +42,21 @@ namespace Components
class CardTitles : public Component
{
public:
static Dvar::Var CustomTitleDvar;
AssertOffset(Game::PlayerCardData, Game::PlayerCardData::name, 0x1C);
static Dvar::Var CustomTitle;
static std::string CustomTitles[18];
static void SendCustomTitlesToClients();
static void ParseCustomTitles(const char * msg);
static void ParseCustomTitles(const char* msg);
CardTitles();
private:
static CClient * GetClientByIndex(std::uint32_t index);
static std::int32_t GetPlayerCardClientInfo(std::int32_t lookupResult, playercarddata_s * data);
static CClient* GetClientByIndex(std::uint32_t index);
static std::int32_t GetPlayerCardClientInfo(std::int32_t lookupResult, Game::PlayerCardData* data);
static void GetPlayerCardClientInfoStub();
static const char* TableLookupByRowHook(Game::Operand * operand, tablelookuprequest_s * request);
static const char* TableLookupByRowHook(Game::Operand* operand, tablelookuprequest_s* request);
static void TableLookupByRowHookStub();
};
}

View File

@ -0,0 +1,295 @@
#include <STDInclude.hpp>
namespace Components
{
Game::dvar_t* ClanTags::ClanName;
// bgs_t and clientState_s do not have this
char ClanTags::ClientState[Game::MAX_CLIENTS][5];
const char* ClanTags::GetClanTagWithName(int clientNum, const char* playerName)
{
assert(static_cast<std::size_t>(clientNum) < Game::MAX_CLIENTS);
if (ClientState[clientNum][0] == '\0')
{
return playerName;
}
return Utils::String::VA("[%s]%s", ClientState[clientNum], playerName);
}
void ClanTags::SendClanTagsToClients()
{
std::string list;
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
{
list.append(std::format("\\{}\\{}", std::to_string(i), ClientState[i]));
}
const auto* command = Utils::String::VA("%c clanNames \"%s\"", 22, list.data());
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command);
}
void ClanTags::ParseClanTags(const char* infoString)
{
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
{
const auto* clanTag = Game::Info_ValueForKey(infoString, std::to_string(i).data());
if (clanTag[0] == '\0')
{
ClientState[i][0] = '\0';
}
else
{
strncpy_s(ClientState[i], clanTag, _TRUNCATE);
}
}
}
int ClanTags::CL_FilterChar(unsigned char input)
{
if (input == '^')
{
return ' ';
}
if (input < ' ')
{
return -1;
}
if (input == 188 || input == 189)
{
return -1;
}
return input;
}
void ClanTags::CL_SanitizeClanName()
{
char saneNameBuf[5];
std::memset(saneNameBuf, 0, sizeof(saneNameBuf));
auto* saneName = saneNameBuf;
const auto* currentName = Game::Dvar_GetString("clanName");
if (currentName)
{
auto nameLen = std::strlen(currentName);
for (std::size_t i = 0; i < nameLen; ++i)
{
auto curChar = CL_FilterChar(static_cast<unsigned char>(currentName[i]));
if (curChar > 0)
{
*saneName++ = curChar & 0xFF;
}
}
Game::Dvar_SetStringByName("clanName", saneNameBuf);
}
}
char* ClanTags::GamerProfile_GetClanName(int controllerIndex)
{
assert(static_cast<std::size_t>(controllerIndex) < Game::MAX_LOCAL_CLIENTS);
CL_SanitizeClanName();
strncpy_s(Game::gamerSettings[0].exeConfig.clanPrefix, Game::Dvar_GetString("clanName"), _TRUNCATE);
return Game::gamerSettings[controllerIndex].exeConfig.clanPrefix;
}
void ClanTags::Dvar_InfoString_Stub(char* s, const char* key, const char* value)
{
Utils::Hook::Call<void(char*, const char*, const char*)>(0x4AE560)(s, key, value); // Info_SetValueForKey
// Set 'clanAbbrev' in the info string
Utils::Hook::Call<void(char*, const char*, const char*)>(0x4AE560)(s, "clanAbbrev", GamerProfile_GetClanName(0)); // Info_SetValueForKey
}
void ClanTags::ClientUserinfoChanged(const char* s, int clientNum)
{
assert(static_cast<std::size_t>(clientNum) < Game::MAX_CLIENTS);
auto* clanAbbrev = Game::Info_ValueForKey(s, "clanAbbrev");
if (clanAbbrev[0] == '\0')
{
ClientState[clientNum][0] = '\0';
}
else
{
strncpy_s(ClientState[clientNum], clanAbbrev, _TRUNCATE);
}
SendClanTagsToClients();
}
void __declspec(naked) ClanTags::ClientUserinfoChanged_Stub()
{
__asm
{
pushad
push [esp + 0x20 + 0x824] // clientNum
push ecx // s
call ClientUserinfoChanged
add esp, 0x8
popad
push 0x445334 // Return address
push 0x47C820 // Info_ValueForKey
// Jump to Info_ValueForKey & add return address
retn
}
}
void __declspec(naked) ClanTags::DrawPlayerNameOnScoreboard()
{
__asm
{
push eax
pushad
push edi
push [ebp]
call GetClanTagWithName
add esp, 0x8
mov [esp + 0x20], eax
popad
pop edi
push 0x591247 // Return address
push 0x5909E0 // DrawListString
retn
}
}
// s1 is always an empty string
int ClanTags::PartyClient_Frame_Stub(const char* s0, [[maybe_unused]] const char* s1)
{
return Utils::Hook::Call<int(const char*, const char*)>(0x4B0100)(s0, GamerProfile_GetClanName(0)); // I_strcmp
}
// clanAbbrev is always an empty string
void ClanTags::Party_UpdateClanName_Stub(Game::PartyData* party, [[maybe_unused]] const char* clanAbbrev)
{
Utils::Hook::Call<void(Game::PartyData*, const char*)>(0x4B3B10)(party, GamerProfile_GetClanName(0)); // Party_UpdateClanName
}
void ClanTags::PlayerCards_SetCachedPlayerData(Game::PlayerCardData* data, const int clientNum)
{
strncpy_s(data->clanAbbrev, ClientState[clientNum], _TRUNCATE);
}
void __declspec(naked) ClanTags::PlayerCards_SetCachedPlayerData_Stub()
{
static DWORD func = 0x4D6F80; // I_strncpyz
__asm
{
call func
add esp, 0xC
mov byte ptr [esi + 0x3C], 0x0
// Copy the clanName
push [esp + 0xC] // clientNum
push esi // g_PlayerCardCache
call PlayerCards_SetCachedPlayerData
add esp, 0x8
// Exit function
pop esi
ret
}
}
Game::PlayerCardData* ClanTags::PlayerCards_GetLiveProfileDataForClient_Stub(const unsigned int clientIndex)
{
auto* result = Utils::Hook::Call<Game::PlayerCardData*(unsigned int)>(0x46C0F0)(clientIndex);
strncpy_s(result->clanAbbrev, ClientState[clientIndex], _TRUNCATE);
return result;
}
Game::PlayerCardData* ClanTags::PlayerCards_GetLiveProfileDataForController_Stub(const unsigned int controllerIndex)
{
auto* result = Utils::Hook::Call<Game::PlayerCardData*(unsigned int)>(0x463B90)(controllerIndex);
strncpy_s(result->clanAbbrev, GamerProfile_GetClanName(static_cast<int>(controllerIndex)), _TRUNCATE); // controllerIndex should always be 0
return result;
}
Game::PlayerCardData* ClanTags::PlayerCards_GetPartyMemberData(const int localClientNum, const Game::PlayerCardClientLookupType lookupType, const unsigned int memberIndex)
{
auto* result = Utils::Hook::Call<Game::PlayerCardData*(int, Game::PlayerCardClientLookupType, unsigned int)>(0x4A4A90)(localClientNum, lookupType, memberIndex);
strncpy_s(result->clanAbbrev, ClientState[memberIndex], _TRUNCATE); // controllerIndex should always be 0
return result;
}
ClanTags::ClanTags()
{
Scheduler::Once([]
{
ClanName = Game::Dvar_RegisterString("clanName", "", Game::DVAR_ARCHIVE,
"Your clan abbreviation");
}, Scheduler::Pipeline::MAIN);
std::memset(&ClientState, 0, sizeof(char[Game::MAX_CLIENTS][5]));
ServerCommands::OnCommand(22, [](Command::Params* params)
{
if (std::strcmp(params->get(1), "clanNames") == 0)
{
if (params->size() == 3)
{
ParseClanTags(params->get(2));
return true;
}
}
return false;
});
Utils::Hook(0x430B00, Dvar_InfoString_Stub, HOOK_CALL).install()->quick();
Utils::Hook(0x44532F, ClientUserinfoChanged_Stub, HOOK_JUMP).install()->quick();
// clanName before playerName
Utils::Hook(0x591242, DrawPlayerNameOnScoreboard, HOOK_JUMP).install()->quick();
Utils::Hook(0x49765B, PartyClient_Frame_Stub, HOOK_CALL).install()->quick();
Utils::Hook(0x49767E, Party_UpdateClanName_Stub, HOOK_CALL).install()->quick();
// clanName in the PlayerCard (GetPlayerCardClientData)
Utils::Hook(0x458DF4, PlayerCards_SetCachedPlayerData_Stub, HOOK_JUMP).install()->quick();
Utils::Hook(0x62EAB6, PlayerCards_GetLiveProfileDataForClient_Stub, HOOK_CALL).install()->quick();
Utils::Hook(0x62EAC3, PlayerCards_GetLiveProfileDataForController_Stub, HOOK_CALL).install()->quick();
Utils::Hook(0x62EAE8, PlayerCards_GetPartyMemberData, HOOK_CALL).install()->quick();
// clanName in CG_Obituary
Utils::Hook(0x586DD6, PlayerName::GetClientName, HOOK_CALL).install()->quick();
Utils::Hook(0x586E2A, PlayerName::GetClientName, HOOK_CALL).install()->quick();
Command::Add("statGet", [](Command::Params* params)
{
if (params->size() < 2)
{
Logger::PrintError(Game::CON_CHANNEL_SERVER, "statget usage: statget <index>\n");
return;
}
const auto index = std::atoi(params->get(1));
const auto stat = Game::LiveStorage_GetStat(0, index);
Logger::Print(Game::CON_CHANNEL_SYSTEM, "Stat {}: {}\n", index, stat);
});
}
}

View File

@ -0,0 +1,48 @@
#pragma once
namespace Components
{
class ClanTags : public Component
{
public:
ClanTags();
static const char* GetClanTagWithName(int clientNum, const char* playerName);
static void SendClanTagsToClients();
static void CL_SanitizeClanName();
private:
static Game::dvar_t* ClanName;
static const char* dvarNameList[];
static char ClientState[Game::MAX_CLIENTS][5];
static void ParseClanTags(const char* infoString);
static int CL_FilterChar(unsigned char input);
static char* GamerProfile_GetClanName(int controllerIndex);
static void Dvar_InfoString_Stub(char* s, const char* key, const char* value);
static void SetCachedPlayerData(int clientNum);
static void ClientUserinfoChanged(const char* s, int clientNum);
static void ClientUserinfoChanged_Stub();
static void DrawPlayerNameOnScoreboard();
static int PartyClient_Frame_Stub(const char* s0, const char* s1);
static void Party_UpdateClanName_Stub(Game::PartyData* party, const char* clanAbbrev);
static void PlayerCards_SetCachedPlayerData(Game::PlayerCardData* data, int clientNum);
static void PlayerCards_SetCachedPlayerData_Stub();
static Game::PlayerCardData* PlayerCards_GetLiveProfileDataForClient_Stub(unsigned int clientIndex);
static Game::PlayerCardData* PlayerCards_GetLiveProfileDataForController_Stub(unsigned int controllerIndex);
static Game::PlayerCardData* PlayerCards_GetPartyMemberData(int localClientNum, Game::PlayerCardClientLookupType lookupType, unsigned int memberIndex);
};
}

View File

@ -1,100 +0,0 @@
#include <STDInclude.hpp>
namespace Components
{
std::string ClanTags::Tags[18];
void ClanTags::ParseClantags(const char* infoString)
{
for (int i = 0; i < 18; i++)
{
const char* clantag = Game::Info_ValueForKey(infoString, std::to_string(i).data());
if (clantag) ClanTags::Tags[i] = clantag;
else ClanTags::Tags[i].clear();
}
}
void ClanTags::SendClantagsToClients()
{
std::string list;
for (int i = 0; i < 18; ++i)
{
char clantag[5] = { 0 };
if (Game::svs_clients[i].state >= 3)
{
strncpy_s(clantag, Game::Info_ValueForKey(Game::svs_clients[i].connectInfoString, "clantag"), 4);
}
list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).data(), clantag));
}
std::string command = Utils::String::VA("%c clantags \"%s\"", 22, list.data());
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command.data());
}
const char* ClanTags::GetUserClantag(std::uint32_t /*clientnum*/, const char* playername)
{
#if 0
if (ClanTags::Tags[clientnum].empty()) return playername;
return Utils::String::VA("[%s] %s", ClanTags::Tags[clientnum].data(), playername);
#else
return playername;
#endif
}
__declspec(naked) void ClanTags::DrawPlayerNameOnScoreboard()
{
__asm
{
push eax
pushad
push edi
push [ebp]
call ClanTags::GetUserClantag
add esp, 8
mov [esp + 20h], eax
popad
pop edi
push 591247h // Return address
push 5909E0h // Draw string func
retn
}
}
ClanTags::ClanTags()
{
// Create clantag dvar
Scheduler::Once([]
{
Dvar::Register<const char*>("clantag", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE,
"If set, your clantag will be shown on the scoreboard.");
}, Scheduler::Pipeline::MAIN);
// Servercommand hook
ServerCommands::OnCommand(22, [](Command::Params* params)
{
if (params->get(1) == "clantags"s && !Dedicated::IsEnabled())
{
if (params->size() == 3)
{
ClanTags::ParseClantags(params->get(2));
return true;
}
}
return false;
});
// Draw clantag before playername
Utils::Hook(0x591242, ClanTags::DrawPlayerNameOnScoreboard).install()->quick();
}
}

View File

@ -1,20 +0,0 @@
#pragma once
namespace Components
{
class ClanTags : public Component
{
public:
static void ParseClantags(const char * infoString);
static void SendClantagsToClients();
static const char* GetUserClantag(std::uint32_t clientnum, const char * playername);
ClanTags();
private:
static std::string Tags[18];
static void DrawPlayerNameOnScoreboard();
};
}

View File

@ -334,17 +334,17 @@ namespace Components
ClientCommand::Add("kill", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
{
assert(ent->client != nullptr);
assert(ent->client->connected != Game::clientConnected_t::CON_DISCONNECTED);
assert(ent->client->sess.connected != Game::CON_DISCONNECTED);
if (ent->client->sessionState != Game::sessionState_t::SESS_STATE_PLAYING || !ClientCommand::CheatsOk(ent))
if (ent->client->sess.sessionState != Game::SESS_STATE_PLAYING || !ClientCommand::CheatsOk(ent))
return;
Scheduler::Once([ent]
{
ent->flags &= ~(Game::entityFlag::FL_GODMODE | Game::entityFlag::FL_DEMI_GODMODE);
ent->flags &= ~(Game::FL_GODMODE | Game::FL_DEMI_GODMODE);
ent->health = 0;
ent->client->ps.stats[0] = 0;
Game::player_die(ent, ent, ent, 100000, 12, 0, nullptr, Game::hitLocation_t::HITLOC_NONE, 0);
Game::player_die(ent, ent, ent, 100000, 12, 0, nullptr, Game::HITLOC_NONE, 0);
}, Scheduler::Pipeline::SERVER);
});
}

View File

@ -68,7 +68,7 @@ namespace Components
{
maxclientCount = Dvar::Var("party_maxplayers").get<int>();
//maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame);
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData_s*>(0x1081C00));
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData*>(0x1081C00));
}
wclear(Console::InfoWindow);

View File

@ -87,7 +87,7 @@ namespace Components
{
list.append(Utils::String::VA(" %llX", Game::svs_clients[i].steamID));
Utils::InfoString info(Game::svs_clients[i].connectInfoString);
Utils::InfoString info(Game::svs_clients[i].userinfo);
list.append(Utils::String::VA(" %llX", strtoull(info.get("realsteamId").data(), nullptr, 16)));
}
else
@ -294,7 +294,6 @@ namespace Components
Scheduler::Loop([]
{
CardTitles::SendCustomTitlesToClients();
//Clantags::SendClantagsToClients();
}, Scheduler::Pipeline::SERVER, 10s);
// Heartbeats

View File

@ -760,7 +760,7 @@ namespace Components
else
{
// Score and ping are irrelevant
const char* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast<Game::PartyData_t*>(0x1081C00), i);
const char* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast<Game::PartyData*>(0x1081C00), i);
if (!namePtr || !namePtr[0]) continue;
playerInfo["name"] = namePtr;

View File

@ -5,6 +5,7 @@ namespace Components
Utils::Signal<Events::ClientCallback> Events::ClientDisconnectSignal;
Utils::Signal<Events::Callback> Events::SteamDisconnectSignal;
Utils::Signal<Events::Callback> Events::ShutdownSystemSignal;
Utils::Signal<Events::Callback> Events::ClientInitSignal;
Utils::Signal<Events::Callback> Events::ServerInitSignal;
void Events::OnClientDisconnect(const Utils::Slot<ClientCallback>& callback)
@ -22,6 +23,11 @@ namespace Components
ShutdownSystemSignal.connect(callback);
}
void Events::OnClientInit(const Utils::Slot<Callback>& callback)
{
ClientInitSignal.connect(callback);
}
void Events::OnSVInit(const Utils::Slot<Callback>& callback)
{
ServerInitSignal.connect(callback);
@ -52,6 +58,14 @@ namespace Components
Utils::Hook::Call<void(unsigned char)>(0x421EE0)(sys); // Scr_ShutdownSystem
}
void Events::CL_InitOnceForAllClients_HK()
{
ClientInitSignal();
ClientInitSignal.clear();
Utils::Hook::Call<void()>(0x404CA0)(); // CL_InitOnceForAllClients
}
void Events::SV_Init_Hk()
{
ServerInitSignal();
@ -69,6 +83,8 @@ namespace Components
Utils::Hook(0x47548B, Scr_ShutdownSystem_Hk, HOOK_CALL).install()->quick(); // G_LoadGame
Utils::Hook(0x4D06BA, Scr_ShutdownSystem_Hk, HOOK_CALL).install()->quick(); // G_ShutdownGame
Utils::Hook(0x60BE5B, CL_InitOnceForAllClients_HK, HOOK_CALL).install()->quick(); // Com_Init_Try_Block_Function
Utils::Hook(0x4D3665, SV_Init_Hk, HOOK_CALL).install()->quick(); // SV_Init
}
}

View File

@ -18,6 +18,8 @@ namespace Components
static void OnVMShutdown(const Utils::Slot<Callback>& callback);
static void OnClientInit(const Utils::Slot<Callback>& callback);
// Client & Server (triggered once)
static void OnSVInit(const Utils::Slot<Callback>& callback);
@ -25,11 +27,13 @@ namespace Components
static Utils::Signal<ClientCallback> ClientDisconnectSignal;
static Utils::Signal<Callback> SteamDisconnectSignal;
static Utils::Signal<Callback> ShutdownSystemSignal;
static Utils::Signal<Callback> ClientInitSignal;
static Utils::Signal<Callback> ServerInitSignal;
static void ClientDisconnect_Hk(int clientNum);
static void SteamDisconnect_Hk();
static void Scr_ShutdownSystem_Hk(unsigned char sys);
static void CL_InitOnceForAllClients_HK();
static void SV_Init_Hk();
};
}

View File

@ -859,6 +859,7 @@ namespace Components
Menus::Add("ui_mp/iw4x_credits.menu");
Menus::Add("ui_mp/resetclass.menu");
Menus::Add("ui_mp/popup_customtitle.menu");
Menus::Add("ui_mp/popup_customclan.menu");
}
Menus::~Menus()

View File

@ -332,7 +332,7 @@ namespace Components
else
{
maxclientCount = Dvar::Var("party_maxplayers").get<int>();
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData_s*>(0x1081C00));
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData*>(0x1081C00));
}
Utils::InfoString info;

View File

@ -44,14 +44,14 @@ namespace Components
}
}
char* PlayerName::GetClientName(int localClientNum, int index, char* buf, size_t size)
int PlayerName::GetClientName(int localClientNum, int index, char* buf, int size)
{
Game::CL_GetClientName(localClientNum, index, buf, size);
const auto result = Game::CL_GetClientName(localClientNum, index, buf, size);
// Append clantag to username & remove the colors
strncpy_s(buf, size, TextRenderer::StripColors(ClanTags::GetUserClantag(index, buf)).data(), size);
// Prepend clanName to username & remove the colors
strncpy_s(buf, size, TextRenderer::StripColors(ClanTags::GetClanTagWithName(index, buf)).data(), size);
return buf;
return result;
}
char* PlayerName::CleanStrStub(char* string)

View File

@ -9,6 +9,8 @@ namespace Components
static void UserInfoCopy(char* buffer, const char* name, size_t size);
static int GetClientName(int localClientNum, int index, char* buf, int size);
private:
static Dvar::Var sv_allowColoredNames;
// Message used when kicking players
@ -16,7 +18,6 @@ namespace Components
static char* CleanStrStub(char* string);
static void ClientCleanName();
static char* GetClientName(int localClientNum, int index, char* buf, size_t size);
static bool CopyClientNameCheck(char* dest, const char* source, int size);
static void SV_UserinfoChangedStub();

View File

@ -64,5 +64,6 @@ namespace Components
{
// Server command receive hook
Utils::Hook(0x59449F, ServerCommands::CG_DeployServerCommand_Stub).install()->quick();
Utils::Hook::Nop(0x5944A4, 6);
}
}

View File

@ -217,7 +217,7 @@ namespace Components
else
{
// Score and ping are irrelevant
const auto* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast<Game::PartyData_t*>(0x1081C00), i);
const auto* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast<Game::PartyData*>(0x1081C00), i);
if (!namePtr || !namePtr[0]) continue;
name = namePtr;

View File

@ -63,10 +63,34 @@ namespace Components
{
const auto* ent = Game::GetPlayerEntity(entref);
Logger::Debug("Resetting name of {} ", ent->s.number);
Logger::Debug("Resetting name of {}", ent->s.number);
UserInfoOverrides[ent->s.number].erase("name");
Game::ClientUserinfoChanged(ent->s.number);
});
Script::AddMethod("SetClanTag", [](Game::scr_entref_t entref) // gsc: self setClanTag(<string>)
{
const auto* ent = Game::GetPlayerEntity(entref);
const auto* clanName = Game::Scr_GetString(0);
if (clanName == nullptr)
{
Game::Scr_ParamError(0, "^1SetClanTag: Illegal parameter!\n");
}
Logger::Debug("Setting clanName of {} to {}", ent->s.number, clanName);
UserInfoOverrides[ent->s.number]["clanAbbrev"] = clanName;
Game::ClientUserinfoChanged(ent->s.number);
});
Script::AddMethod("ResetClanTag", [](Game::scr_entref_t entref) // gsc: self ResetClanTag()
{
const auto* ent = Game::GetPlayerEntity(entref);
Logger::Debug("Resetting clanName of {}", ent->s.number);
UserInfoOverrides[ent->s.number].erase("clanAbbrev");
Game::ClientUserinfoChanged(ent->s.number);
});
}
UserInfo::UserInfo()

View File

@ -66,6 +66,8 @@ namespace Game
CL_SelectStringTableEntryInDvar_f_t CL_SelectStringTableEntryInDvar_f = CL_SelectStringTableEntryInDvar_f_t(0x4A4560);
CL_DrawStretchPic_t CL_DrawStretchPic = CL_DrawStretchPic_t(0x412490);
CL_ConsoleFixPosition_t CL_ConsoleFixPosition = CL_ConsoleFixPosition_t(0x44A430);
CL_GetLocalClientActiveCount_t CL_GetLocalClientActiveCount = CL_GetLocalClientActiveCount_t(0x5BAD90);
CL_ControllerIndexFromClientNum_t CL_ControllerIndexFromClientNum = CL_ControllerIndexFromClientNum_t(0x449E30);
Cmd_AddCommand_t Cmd_AddCommand = Cmd_AddCommand_t(0x470090);
Cmd_AddServerCommand_t Cmd_AddServerCommand = Cmd_AddServerCommand_t(0x4DCE00);
@ -128,6 +130,8 @@ namespace Game
Dvar_RegisterVec3Color_t Dvar_RegisterVec3Color = Dvar_RegisterVec3Color_t(0x4918B0);
Dvar_GetUnpackedColorByName_t Dvar_GetUnpackedColorByName = Dvar_GetUnpackedColorByName_t(0x406530);
Dvar_GetString_t Dvar_GetString = Dvar_GetString_t(0x4EC6B0);
Dvar_GetVariantString_t Dvar_GetVariantString = Dvar_GetVariantString_t(0x4C47E0);
Dvar_FindVar_t Dvar_FindVar = Dvar_FindVar_t(0x4D5390);
Dvar_InfoString_Big_t Dvar_InfoString_Big = Dvar_InfoString_Big_t(0x4D98A0);
Dvar_SetCommand_t Dvar_SetCommand = Dvar_SetCommand_t(0x4EE430);
@ -253,6 +257,8 @@ namespace Game
Live_GetPrestige_t Live_GetPrestige = Live_GetPrestige_t(0x430F90);
Live_GetXp_t Live_GetXp = Live_GetXp_t(0x404C60);
LiveStorage_GetStat_t LiveStorage_GetStat = LiveStorage_GetStat_t(0x471F60);
Scr_AddSourceBuffer_t Scr_AddSourceBuffer = Scr_AddSourceBuffer_t(0x61ABC0);
PC_ReadToken_t PC_ReadToken = PC_ReadToken_t(0x4ACCD0);
@ -498,8 +504,8 @@ namespace Game
int* g_streamPosIndex = reinterpret_cast<int*>(0x16E5578);
bool* g_lobbyCreateInProgress = reinterpret_cast<bool*>(0x66C9BC2);
party_t** partyIngame = reinterpret_cast<party_t**>(0x1081C00);
PartyData_s** partyData = reinterpret_cast<PartyData_s**>(0x107E500);
PartyData* g_lobbyData = reinterpret_cast<PartyData*>(0x1081C00);
PartyData* g_partyData = reinterpret_cast<PartyData*>(0x107E500);
int* numIP = reinterpret_cast<int*>(0x64A1E68);
netIP_t* localIP = reinterpret_cast<netIP_t*>(0x64A1E28);
@ -552,7 +558,7 @@ namespace Game
scrVmPub_t* scrVmPub = reinterpret_cast<scrVmPub_t*>(0x2040CF0);
scrVarPub_t* scrVarPub = reinterpret_cast<scrVarPub_t*>(0x201A408);
clientstate_t* clcState = reinterpret_cast<clientstate_t*>(0xB2C540);
clientState_t* clcState = reinterpret_cast<clientState_t*>(0xB2C540);
GfxScene* scene = reinterpret_cast<GfxScene*>(0x6944914);
@ -616,6 +622,11 @@ namespace Game
clientConnection_t* clientConnections = reinterpret_cast<clientConnection_t*>(0xA1E878);
unsigned int* playerCardUIStringIndex = reinterpret_cast<unsigned int*>(0x62CD7A8);
char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38] = reinterpret_cast<char(*)[PLAYER_CARD_UI_STRING_COUNT][38]>(0x62CB4F8);
GamerSettingState* gamerSettings = reinterpret_cast<GamerSettingState*>(0x107D3E8);
void Sys_LockRead(FastCriticalSection* critSect)
{
InterlockedIncrement(&critSect->readCount);
@ -836,8 +847,8 @@ namespace Game
{
for (auto i = 0; i < *svs_clientCount; ++i)
{
if (svs_clients[i].state != clientstate_t::CS_FREE
&& svs_clients[i].netchan.remoteAddress.type == netadrtype_t::NA_BOT)
if (svs_clients[i].state != CS_FREE
&& svs_clients[i].netchan.remoteAddress.type == NA_BOT)
{
SV_GameDropClient(i, "GAME_GET_TO_COVER");
}
@ -1746,6 +1757,5 @@ namespace Game
ret
}
}
#pragma optimize("", on)
}

View File

@ -97,7 +97,7 @@ namespace Game
typedef void(__cdecl * CG_SetupWeaponDef_t)(int localClientNum, unsigned int weapIndex);
extern CG_SetupWeaponDef_t CG_SetupWeaponDef;
typedef char*(__cdecl * CL_GetClientName_t)(int localClientNum, int index, char *buf, size_t size);
typedef int(__cdecl * CL_GetClientName_t)(int localClientNum, int index, char *buf, int size);
extern CL_GetClientName_t CL_GetClientName;
typedef int(__cdecl * CL_IsCgameInitialized_t)();
@ -139,6 +139,12 @@ namespace Game
typedef void(__cdecl * CL_ConsoleFixPosition_t)();
extern CL_ConsoleFixPosition_t CL_ConsoleFixPosition;
typedef int(__cdecl * CL_GetLocalClientActiveCount_t)();
extern CL_GetLocalClientActiveCount_t CL_GetLocalClientActiveCount;
typedef int(__cdecl * CL_ControllerIndexFromClientNum_t)(int localActiveClientNum);
extern CL_ControllerIndexFromClientNum_t CL_ControllerIndexFromClientNum;
typedef void(__cdecl * Cmd_AddCommand_t)(const char* cmdName, void(*function), cmd_function_t* allocedCmd, bool isKey);
extern Cmd_AddCommand_t Cmd_AddCommand;
@ -325,6 +331,12 @@ namespace Game
typedef void(__cdecl * Dvar_GetUnpackedColorByName_t)(const char* dvarName, float* expandedColor);
extern Dvar_GetUnpackedColorByName_t Dvar_GetUnpackedColorByName;
typedef char*(__cdecl* Dvar_GetString_t)(const char* dvarName);
extern Dvar_GetString_t Dvar_GetString;
typedef char*(__cdecl * Dvar_GetVariantString_t)(const char* dvarName);
extern Dvar_GetVariantString_t Dvar_GetVariantString;
typedef dvar_t*(__cdecl * Dvar_FindVar_t)(const char* dvarName);
extern Dvar_FindVar_t Dvar_FindVar;
@ -456,7 +468,7 @@ namespace Game
typedef void(__cdecl * Image_Release_t)(GfxImage* image);
extern Image_Release_t Image_Release;
typedef char*(__cdecl * Info_ValueForKey_t)(const char* infoString, const char* key);
typedef char*(__cdecl * Info_ValueForKey_t)(const char* s, const char* key);
extern Info_ValueForKey_t Info_ValueForKey;
typedef void(__cdecl * Key_SetCatcher_t)(int localClientNum, int catcher);
@ -651,6 +663,9 @@ namespace Game
typedef int(__cdecl * Live_GetXp_t)(int controllerIndex);
extern Live_GetXp_t Live_GetXp;
typedef int(__cdecl * LiveStorage_GetStat_t)(int controllerIndex, int index);
extern LiveStorage_GetStat_t LiveStorage_GetStat;
typedef char*(__cdecl * Scr_AddSourceBuffer_t)(const char* filename, const char* extFilename, const char* codePos, bool archive);
extern Scr_AddSourceBuffer_t Scr_AddSourceBuffer;
@ -663,16 +678,16 @@ namespace Game
typedef void(__cdecl * PC_SourceError_t)(int, const char*, ...);
extern PC_SourceError_t PC_SourceError;
typedef int(__cdecl * Party_GetMaxPlayers_t)(party_s* party);
typedef int(__cdecl * Party_GetMaxPlayers_t)(PartyData* party);
extern Party_GetMaxPlayers_t Party_GetMaxPlayers;
typedef int(__cdecl * PartyHost_CountMembers_t)(PartyData_s* party);
typedef int(__cdecl * PartyHost_CountMembers_t)(PartyData* party);
extern PartyHost_CountMembers_t PartyHost_CountMembers;
typedef netadr_t *(__cdecl * PartyHost_GetMemberAddressBySlot_t)(int unk, void *party, const int slot);
extern PartyHost_GetMemberAddressBySlot_t PartyHost_GetMemberAddressBySlot;
typedef const char *(__cdecl * PartyHost_GetMemberName_t)(PartyData_s* party, const int clientNum);
typedef const char *(__cdecl * PartyHost_GetMemberName_t)(PartyData* party, const int clientNum);
extern PartyHost_GetMemberName_t PartyHost_GetMemberName;
typedef void(__cdecl * Playlist_ParsePlaylists_t)(const char* data);
@ -1116,6 +1131,10 @@ namespace Game
typedef void*(__cdecl * Z_VirtualAlloc_t)(int size);
extern Z_VirtualAlloc_t Z_VirtualAlloc;
constexpr std::size_t STATIC_MAX_LOCAL_CLIENTS = 1;
constexpr std::size_t MAX_LOCAL_CLIENTS = 1;
constexpr std::size_t MAX_CLIENTS = 18;
extern XAssetHeader* DB_XAssetPool;
extern unsigned int* g_poolSize;
@ -1150,8 +1169,8 @@ namespace Game
extern int* g_streamPosIndex;
extern bool* g_lobbyCreateInProgress;
extern party_t** partyIngame;
extern PartyData_s** partyData;
extern PartyData* g_lobbyData;
extern PartyData* g_partyData;
extern int* numIP;
extern netIP_t* localIP;
@ -1205,7 +1224,7 @@ namespace Game
extern scrVmPub_t* scrVmPub;
extern scrVarPub_t* scrVarPub;
extern clientstate_t* clcState;
extern clientState_t* clcState;
extern GfxScene* scene;
@ -1273,6 +1292,12 @@ namespace Game
extern clientConnection_t* clientConnections;
constexpr std::size_t PLAYER_CARD_UI_STRING_COUNT = 18;
extern unsigned int* playerCardUIStringIndex;
extern char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38];
extern GamerSettingState* gamerSettings;
void Sys_LockRead(FastCriticalSection* critSect);
void Sys_UnlockRead(FastCriticalSection* critSect);

View File

@ -232,7 +232,7 @@ namespace Game
CS_CONNECTED = 0x3,
CS_CLIENTLOADING = 0x4,
CS_ACTIVE = 0x5,
} clientstate_t;
} clientState_t;
enum serverState_t
{
@ -1611,6 +1611,32 @@ namespace Game
unsigned int playerCardNameplate;
};
enum PlayerCardClientLookupType
{
PLAYERCARD_LOOKUP_SCRIPTSLOT = 0x0,
PLAYERCARD_LOOKUP_LIVEPROFILE_CLIENT = 0x1,
PLAYERCARD_LOOKUP_LIVEPROFILE_CONTROLLER = 0x2,
PLAYERCARD_LOOKUP_LOBBY = 0x3,
PLAYERCARD_LOOKUP_MYTEAM = 0x4,
PLAYERCARD_LOOKUP_ENEMYTEAM = 0x5,
PLAYERCARD_LOOKUP_COUNT = 0x6,
};
struct PlayerCardData
{
unsigned int lastUpdateTime;
unsigned int titleIndex;
unsigned int iconIndex;
unsigned int nameplateIndex;
int rank;
int prestige;
team_t team;
char name[32];
char clanAbbrev[5];
};
static_assert(sizeof(PlayerCardData) == 0x44);
enum usercmdButtonBits
{
CMD_BUTTON_ATTACK = 0x1,
@ -5082,18 +5108,6 @@ namespace Game
char uiName[32];
};
typedef struct party_s
{
unsigned char pad1[544];
int privateSlots;
int publicSlots;
} party_t;
typedef struct PartyData_s
{
DWORD unk;
} PartyData_t;
struct fileInIwd_s
{
unsigned int pos;
@ -6046,15 +6060,66 @@ namespace Game
VISIONSETCOUNT
} visionSetMode_t;
enum hintType_t
{
HINT_NONE = 0x0,
HINT_NOICON = 0x1,
HINT_ACTIVATE = 0x2,
HINT_HEALTH = 0x3,
HINT_FRIENDLY = 0x4,
FIRST_WEAPON_HINT = 0x5,
LAST_WEAPON_HINT = 0x57C,
HINT_NUM_HINTS = 0x57D,
};
struct playerTeamState_t
{
int location;
};
struct clientSession_t
{
sessionState_t sessionState;
int forceSpectatorClient;
int killCamEntity;
int killCamLookAtEntity;
int status_icon;
int archiveTime;
int score;
int deaths;
int kills;
int assists;
unsigned __int16 scriptPersId;
clientConnected_t connected;
usercmd_s cmd;
usercmd_s oldcmd;
int localClient;
int predictItemPickup;
char newnetname[16];
int maxHealth;
int enterTime;
playerTeamState_t teamState;
int voteCount;
int teamVoteCount;
float moveSpeedScaleMultiplier;
int viewmodelIndex;
int noSpectate;
int teamInfo;
clientState_s cs;
int psOffsetTime;
int hasRadar;
int isRadarBlocked;
int radarMode;
int weaponHudIconOverrides[6];
unsigned int unusableEntFlags[64];
float spectateDefaultPos[3];
float spectateDefaultAngles[3];
};
typedef struct gclient_s
{
playerState_s ps;
sessionState_t sessionState; // 12572
unsigned char __pad0[40];
clientConnected_t connected; // 12616
unsigned char __pad1[144];
team_t team; // 12764
unsigned char __pad2[436];
clientSession_t sess;
int flags; // 13204
int spectatorClient;
int lastCmdTime;
@ -6065,7 +6130,13 @@ namespace Game
unsigned char __pad3[324]; // 13232
int visionDuration[5];
char visionName[5][64];
unsigned char __pad4[36];
int lastStand;
int lastStandTime;
int hudElemLastAssignedSoundID;
float lockedTargetOffset[3];
unsigned __int16 attachShieldTagName;
hintType_t hintForcedType;
int hintForcedString;
} gclient_t;
static_assert(sizeof(gclient_t) == 13932);
@ -6224,14 +6295,14 @@ namespace Game
typedef struct client_s
{
clientstate_t state; // 0
char __pad0[4]; // 4
clientState_t state; // 0
int sendAsActive; // 4
int deltaMessage; // 8
char __pad1[12]; // 12
netchan_t netchan; // 24
char __pad2[20]; // 1604
const char* delayDropReason; // 1624
char connectInfoString[1024]; // 1628
char userinfo[1024]; // 1628
char __pad3[132096]; // 2652
int reliableSequence; // 134748
int reliableAcknowledge; // 134752
@ -6244,7 +6315,7 @@ namespace Game
char lastClientCommandString[1024]; // 134816
gentity_t* gentity; // 135840
char name[16]; // 135844
char __pad4[4]; // 135860
int nextReliableTime; // 135860
int lastPacketTime; // 135864
int lastConnectTime; // 135868
int snapNum; // 135872
@ -7875,6 +7946,8 @@ namespace Game
unsigned int playerCardNameplate;
};
static_assert(sizeof(clientInfo_t) == 0x52C);
struct cgs_t
{
int viewX;
@ -8181,6 +8254,375 @@ namespace Game
static_assert(sizeof(DeferredQueue) == 0x5908);
struct GamerSettingCommonConfig
{
float viewSensitivity;
float snd_volume;
float blacklevel;
float gpadButtonLStickDeflect;
float gpadButtonRStickDeflect;
float safearea_adjusted_horizontal;
float safearea_adjusted_vertical;
int playTimeSP;
int playTimeMP;
int playTimeSO;
int percentCompleteSP;
int percentCompleteMP;
int percentCompleteSO;
float gamma;
bool hasEverPlayed_MainMenu;
bool hasEverPlayed_SP;
bool hasEverPlayed_SO;
bool hasEverPlayed_MP;
bool invertPitch;
bool autoAim;
bool delicateFlower;
char gpadButtonsConfig[32];
char gpadSticksConfig[32];
};
struct KeyPairStringData
{
short index;
short maxSize;
};
union KeyPairDataUnion
{
char byteVal;
bool boolVal;
short shortVal;
int intVal;
float floatVal;
KeyPairStringData stringData;
};
struct GamerSettingKeyPair
{
char type;
char unused[3];
KeyPairDataUnion u;
};
struct GamerSettingExeConfig
{
int playlist;
bool mapPrefs[16];
char clanPrefix[5];
};
struct GamerSettingState
{
bool isProfileLoggedIn;
bool errorOnRead;
GamerSettingCommonConfig commonConfig;
GamerSettingKeyPair commonKeyPairs[50];
char commonKeyPairsStringPool[512];
GamerSettingExeConfig exeConfig;
GamerSettingKeyPair exeKeyPairs[50];
char exeKeyPairsStringPool[512];
};
static_assert(sizeof(GamerSettingState) == 0x7C0);
struct SessionStaticData
{
char* sessionName;
bool registerUsersWithVoice;
};
enum IWNetServerSessionStatus
{
SESSION_ONCLIENTONLY = 0x0,
SESSION_BEINGCREATED = 0x1,
SESSION_CREATED = 0x2,
SESSION_BEINGDELETED = 0x3,
};
struct IWNetServerInfoAboutPlayer
{
bool active;
__int64 uid;
char skill;
char teamIndex;
int mapPackFlags;
};
struct IWNetSessionStatus
{
IWNetServerSessionStatus status;
int sessionId;
int lastHeartbeatSent;
bool needsUpdate;
bool updatingPlayers;
int newPlayerCount;
IWNetServerInfoAboutPlayer pendingServerInfoForPlayers[18];
};
struct XSESSION_INFO
{
XNKID sessionID;
XNADDR hostAddress;
XNKEY keyExchangeKey;
};
struct ClientInfo
{
bool registered;
bool voiceRegistered;
unsigned __int64 xuid;
int natType;
netadr_t addr;
int voiceConnectivityBits;
int lastConnectivityTestTime;
bool muted;
bool privateSlot;
};
struct NomineeInfo
{
int upload;
int NAT;
bool onLSP;
int connectivity;
int cpuSpeed;
int avgPing;
};
struct RegisteredUser
{
bool active;
unsigned __int64 xuid;
};
struct SessionDynamicData
{
bool sessionHandle;
IWNetSessionStatus iwnetServerSessionStatus;
XSESSION_INFO sessionInfo;
bool keysGenerated;
bool sessionStartCalled;
unsigned __int64 sessionNonce;
int privateSlots;
int publicSlots;
int flags;
bool qosListenEnabled;
ClientInfo users[18];
int voiceConnectivityBits;
int sessionCreateController;
int sessionDeleteTime;
RegisteredUser internalRegisteredUsers[18];
};
struct SessionData
{
SessionStaticData staticData;
SessionDynamicData dyn;
};
struct BestHostData
{
int nominee;
NomineeInfo info;
int lastHeardFrom;
int lastSentTo;
};
struct BandwidthTestPerClientData
{
int bytesReceived;
};
struct BandwidthTestData
{
int testIndex;
int testClientNum;
int startTimeArbitrator;
int announceTime;
int winnerClientNum;
BandwidthTestPerClientData clientData[18];
char testClientName[32];
bool inProgress;
int startTime;
int roundsComplete;
bool receiving;
int receiveIndex;
int receiveStartTime;
int receiveBytes;
int resultsSendTime;
};
struct MigrateData
{
bool migrateActive;
bool weAreArbitrating;
int arbitratorClientNum;
int indexBits;
int startTime;
int timeoutDuration;
int lastBroadcastTime;
bool decidedOurNominee;
BestHostData bestHost;
int expectedNewHost;
BandwidthTestData bandwidthTestData;
};
struct QoSData
{
float percent;
};
struct PartyInfo
{
bool active;
XSESSION_INFO info;
int occupiedPublicSlots;
int occupiedPrivateSlots;
int numPublicSlots;
int numPrivateSlots;
int pingBias;
int ping;
int upload;
int desirability;
};
struct PartyMember
{
char status;
bool headsetPresent;
char gamertag[32];
char clanAbbrev[5];
int qport;
char challenge[6];
int lastPacketTime;
int lastHeartbeatTime;
int lastPartyStateAck;
XNADDR xnaddr;
int availableMapPackFlags;
int ackedMembers;
XNKID privatePartyId;
int subpartyIndex;
int trueSkill;
int rank;
int prestige;
int team;
unsigned __int16 score;
int deaths;
bool vetoedMap;
unsigned int playerCardIcon;
unsigned int playerCardTitle;
unsigned int playerCardNameplate;
int voiceConnectivityBits;
bool invited;
int natType;
unsigned __int64 player;
bool migrateHeardFrom;
int migratePingTime;
int migratePing;
bool migrateNominated;
NomineeInfo migrateNomineeInfo;
};
struct SubpartyInfo
{
int members[18];
int count;
int skill;
int score;
int team;
};
struct PartyHostDetails
{
int partyListSlot;
netadr_t addr;
XSESSION_INFO sessionInfo;
int lastPacketTime;
int lastPacketSentTime;
int numPrivateSlots;
int numPublicSlots;
int hostNum;
bool accepted;
char challenge[6];
};
struct PartyHostData
{
int partyStateChangeTime;
int partyStateLastSendTime;
int expectedPlayers;
int vetoPassTime;
bool vetoPossible;
bool preloadingMap;
bool firstLobby;
bool migrateAfterRound;
bool stopAfterRound;
int partyCreationTime;
};
struct PartyData
{
SessionData* session;
SessionData* presenceSession;
SessionData* searchSession;
MigrateData migrateData;
QoSData qosData;
PartyInfo* partyList;
int partyListSize;
PartyMember partyMembers[18];
SubpartyInfo subparties[18];
int subpartyCount;
PartyHostDetails currentHost;
PartyHostDetails potentialHost;
PartyHostData hostData;
unsigned __int64 lobbySteamID;
int areWeHost;
int joiningAnotherParty;
int searchingForGames;
int inParty;
int party_systemActive;
bool veto;
int vetoTime;
int headsetPresent;
int headsetTime;
int clanAbbrevTime;
int rankTime;
int playerCardTime;
int uploadSentTime;
int voiceBitsTime;
int idTime;
int availableMapPackFlagsTime;
int searchStartTime;
int searchEndTime;
int joinAttemptForUI;
int lastMergeTime;
int mergeAttemptStartTime;
int originalPartiesInList;
int partyId;
int nextSessionSearchTime;
int mapPackFlags;
int lastPartyStateTime;
int gameStartTime;
int interEndTime;
int inactiveKeepaliveTime;
int hostTimeouts;
char lobbyFlags;
PartyData* partyToNotify;
bool registeredWithArbitration;
bool rejoining;
int partyStatePacketCount;
int partyStateLastMemberIndex;
int unfinishedPartServerTimes[2];
msg_t partyStatePartMsgs[2];
char partyStatePartMsgBufs[2][1400];
char lastEntries[8];
int currentEntry;
char axisWins;
char alliesWins;
};
static_assert(sizeof(PartyData) == 0x23D8);
#pragma endregion
#ifndef IDA