This commit is contained in:
Ahrimdon 2024-08-13 14:41:03 -04:00
commit c943027c4b
10 changed files with 207 additions and 265 deletions

View File

@ -1,3 +1,39 @@
local f0_local0 = function ( f1_arg0 )
if Engine.HasCompletedAnyLevel( f1_arg0 ) then
return true
end
local f1_local0 = {}
for f1_local1 = 0, Engine.TableGetRowCount( CSV.levels.file ) - 1, 1 do
local f1_local4 = CSV.ReadRow( CSV.levels, f1_local1 )
local f1_local5 = f1_local4.name
if not Engine.GetDvarBool( "lui_checkIfLevelInFileSystem" ) or Engine.IsLevelInFileSystem and Engine.IsLevelInFileSystem( f1_local5 ) then
f1_local0[#f1_local0 + 1] = {
buttonLabel = f1_local4.string,
levelName = f1_local5,
objectiveText = f1_local4.desc,
levelNumber = f1_local4.ref,
completedLevelIndex = f1_local4.completedRef,
image = f1_local4.image
}
end
end
local f1_local1 = LUI.DataSourceFromPlayerData.new( CoD.ProgressionBlob.Gold, CoD.PlayMode.SP )
local f1_local2 = f1_local1.spData
for f1_local3 = 1, #f1_local0, 1 do
local f1_local7 = f1_local0[f1_local3].levelName
if f1_local7 ~= "europa" then
local f1_local8 = ""
if f1_local2.missionStateData[f1_local7] ~= nil then
f1_local8 = f1_local2.missionStateData[f1_local7]:GetValue( f1_arg0 )
end
if f1_local8 ~= nil and (f1_local8 == "complete" or f1_local8 == "incomplete") then
return true
end
end
end
return false
end
local f0_local1 = function(arg0) local f0_local1 = function(arg0)
arg0.ResumeButton:SetButtonDisabled(not Engine.CanResumeGame(arg0._controllerIndex)) arg0.ResumeButton:SetButtonDisabled(not Engine.CanResumeGame(arg0._controllerIndex))
if not CONDITIONS.IsTrialLicense(arg0) then if not CONDITIONS.IsTrialLicense(arg0) then

View File

@ -79,8 +79,8 @@ namespace command
static std::string comand_line_buffer = GetCommandLineA(); static std::string comand_line_buffer = GetCommandLineA();
auto* command_line = comand_line_buffer.data(); auto* command_line = comand_line_buffer.data();
auto& com_num_console_lines = *reinterpret_cast<int*>(0x146006DB0); auto& com_num_console_lines = *game::com_num_console_lines;
auto* com_console_lines = reinterpret_cast<char**>(0x146006DC0); auto* com_console_lines = game::com_console_lines.get();
auto inq = false; auto inq = false;
com_console_lines[0] = command_line; com_console_lines[0] = command_line;
@ -111,8 +111,8 @@ namespace command
void parse_startup_variables() void parse_startup_variables()
{ {
auto& com_num_console_lines = *reinterpret_cast<int*>(0x146006DB0); auto& com_num_console_lines = *game::com_num_console_lines;
auto* com_console_lines = reinterpret_cast<char**>(0x146006DC0); auto* com_console_lines = game::com_console_lines.get();
for (int i = 0; i < com_num_console_lines; i++) for (int i = 0; i < com_num_console_lines; i++)
{ {

View File

@ -50,26 +50,22 @@ namespace dedicated
return startup_command_queue; return startup_command_queue;
} }
void execute_startup_command(int client, int /*controllerIndex*/, const char* command) void execute_startup_commands()
{ {
if (game::Live_SyncOnlineDataFlags(0) == 0) auto& com_num_console_lines = *game::com_num_console_lines;
auto* com_console_lines = game::com_console_lines.get();
for (auto i = 0; i < com_num_console_lines; i++)
{ {
game::Cbuf_ExecuteBufferInternal(0, 0, command, game::Cmd_ExecuteSingleCommand); auto cmd = com_console_lines[i];
}
else // if command is map or map_rotate, its already been called
if (cmd == "map"s || cmd == "map_rotate"s)
{ {
get_startup_command_queue().emplace_back(command); continue;
}
} }
void execute_startup_command_queue() game::Cbuf_ExecuteBufferInternal(0, 0, cmd, game::Cmd_ExecuteSingleCommand);
{
const auto queue = get_startup_command_queue();
get_startup_command_queue().clear();
for (const auto& command : queue)
{
game::Cbuf_ExecuteBufferInternal(0, 0, command.data(), game::Cmd_ExecuteSingleCommand);
} }
} }
@ -268,9 +264,6 @@ namespace dedicated
utils::hook::jump(0x140341B60, init_dedicated_server, true); utils::hook::jump(0x140341B60, init_dedicated_server, true);
// delay startup commands until the initialization is done
utils::hook::call(0x140B8D20F, execute_startup_command);
utils::hook::set<uint32_t>(0x140B21107 + 2, 0x482); // g_gametype flags utils::hook::set<uint32_t>(0x140B21107 + 2, 0x482); // g_gametype flags
utils::hook::set<uint32_t>(0x140B21137 + 2, 0x480); // g_hardcore flags utils::hook::set<uint32_t>(0x140B21137 + 2, 0x480); // g_hardcore flags
utils::hook::jump(0x140C12400, sv_get_game_type_stub); utils::hook::jump(0x140C12400, sv_get_game_type_stub);
@ -397,7 +390,7 @@ namespace dedicated
// remove disconnect command // remove disconnect command
game::Cmd_RemoveCommand("disconnect"); game::Cmd_RemoveCommand("disconnect");
execute_startup_command_queue(); execute_startup_commands();
// Send heartbeat to dpmaster // Send heartbeat to dpmaster
scheduler::once(send_heartbeat, scheduler::pipeline::server); scheduler::once(send_heartbeat, scheduler::pipeline::server);

View File

@ -296,16 +296,55 @@ namespace party
bool preloaded_map = false; bool preloaded_map = false;
void perform_game_initialization() void xstartlobby()
{ {
command::execute("onlinegame 1", true); // Retrieve game variables
command::execute("xblive_privatematch 1", true); const int privateMatch = game::Dvar_FindVar("xblive_privatematch")->current.integer;
//command::execute("xstartprivateparty", true); const int maxPlayers = game::Dvar_FindVar("party_maxplayers")->current.integer;
command::execute("xstartprivatematch", true); const int privateClients = game::Dvar_FindVar("ui_privateClients")->current.integer;
const int availableSpots = maxPlayers - privateClients;
// Get party data
auto partyData = game::Lobby_GetPartyData();
// Stop current party and reset settings
utils::hook::invoke<void>(0x1409D07B0, partyData); // Party_StopParty
utils::hook::invoke<void>(0x1409CAB20, partyData, 0, 1); // Party_Awake
utils::hook::invoke<void>(0x1409D0050); // Party_ResetTweakDvars
utils::hook::invoke<void>(0x1409CB460, partyData); // Voice_DisableLocalMics
// Set lobby presence
utils::hook::invoke<void>(0x140D330B0, 0); // Live_SetLobbyPresence
// Run playlist rule if not in a private match
if (!privateMatch/* && !game::environment::is_dedi()*/)
{
utils::hook::invoke<void>(0x140CCD840, 0, 0); // Playlist_RunRule
}
// Set up lobby flags
const int flags = utils::hook::invoke<int>(0x140D9B200, 0); // PartyHost_GetCreateFlags
// Initialize lobby
utils::hook::invoke<void>(0x1409D9940, partyData, 0, 0, flags, privateClients, availableSpots);
}
void perform_game_initialization(const int max_clients, const bool private_match)
{
const auto ui_maxclients = game::Dvar_FindVar("ui_maxclients");
const auto party_maxplayers = game::Dvar_FindVar("party_maxplayers");
game::Dvar_SetInt(ui_maxclients, max_clients);
game::Dvar_SetInt(party_maxplayers, max_clients);
const auto xblive_privatematch = game::Dvar_FindVar("xblive_privatematch");
game::Dvar_SetBool(xblive_privatematch, private_match);
xstartlobby();
command::execute("uploadstats", true); command::execute("uploadstats", true);
} }
void connect_to_party(const game::netadr_s& target, const std::string& mapname, const std::string& gametype, int sv_maxclients) void connect_to_party(const game::netadr_s& target, const std::string& mapname, const std::string& gametype, int sv_maxclients, const bool private_match)
{ {
const auto mode = game::Com_GameMode_GetActiveGameMode(); const auto mode = game::Com_GameMode_GetActiveGameMode();
if (mode != game::GAME_MODE_MP && mode != game::GAME_MODE_CP) if (mode != game::GAME_MODE_MP && mode != game::GAME_MODE_CP)
@ -317,20 +356,15 @@ namespace party
{ {
scheduler::once([=]() scheduler::once([=]()
{ {
connect_to_party(target, mapname, gametype, sv_maxclients); connect_to_party(target, mapname, gametype, sv_maxclients, private_match);
}, scheduler::pipeline::main, 1s); }, scheduler::pipeline::main, 1s);
return; return;
} }
const auto ui_maxclients = game::Dvar_FindVar("ui_maxclients"); perform_game_initialization(sv_maxclients, private_match);
const auto party_maxplayers = game::Dvar_FindVar("party_maxplayers");
game::Dvar_SetInt(ui_maxclients, sv_maxclients);
game::Dvar_SetInt(party_maxplayers, sv_maxclients);
command::execute(utils::string::va("ui_mapname %s", mapname.data()), true); game::Dvar_SetFromStringByName("ui_mapname", mapname.data(), game::DVAR_SOURCE_INTERNAL);
command::execute(utils::string::va("ui_gametype %s", gametype.data()), true); game::Dvar_SetFromStringByName("ui_gametype", gametype.data(), game::DVAR_SOURCE_INTERNAL);
perform_game_initialization();
// setup agent count // setup agent count
utils::hook::invoke<void>(0x140C19B00, gametype.data()); utils::hook::invoke<void>(0x140C19B00, gametype.data());
@ -344,7 +378,7 @@ namespace party
void start_map_for_party(std::string map_name) void start_map_for_party(std::string map_name)
{ {
[[maybe_unused]]auto* mapname = game::Dvar_FindVar("ui_mapname"); [[maybe_unused]] auto* mapname = game::Dvar_FindVar("ui_mapname");
auto* gametype = game::Dvar_FindVar("ui_gametype"); auto* gametype = game::Dvar_FindVar("ui_gametype");
auto* clients = game::Dvar_FindVar("ui_maxclients"); auto* clients = game::Dvar_FindVar("ui_maxclients");
auto* private_clients = game::Dvar_FindVar("ui_privateClients"); auto* private_clients = game::Dvar_FindVar("ui_privateClients");
@ -398,8 +432,6 @@ namespace party
void sv_start_map_for_party_stub(const char* map, const char* game_type, int client_count, int agent_count, bool hardcore, void sv_start_map_for_party_stub(const char* map, const char* game_type, int client_count, int agent_count, bool hardcore,
bool map_is_preloaded, bool migrate) bool map_is_preloaded, bool migrate)
{ {
profile_infos::xuid::clear_xuids();
hash_cache.clear(); hash_cache.clear();
if (game::environment::is_dedi()) if (game::environment::is_dedi())
@ -426,6 +458,8 @@ namespace party
memset(&*reinterpret_cast<__int64*>(0x144E3A490 + 8), 0, 0x78680ui64 - 8); memset(&*reinterpret_cast<__int64*>(0x144E3A490 + 8), 0, 0x78680ui64 - 8);
} }
} }
utils::hook::invoke<void>(0x140C55DF0); // SV_InitMP_SetXUIDConfigStrings
} }
void reset_mem_stuff_stub(utils::hook::assembler& a) void reset_mem_stuff_stub(utils::hook::assembler& a)
@ -439,6 +473,30 @@ namespace party
a.jmp(0x140C563E2); a.jmp(0x140C563E2);
} }
void send_user_info()
{
auto userinfostring = utils::hook::invoke<const char*>(0x1409B2850, 0); // CL_MainMP_GetUserInfoString
auto cmd = utils::string::va("userinfo \"%s\"", userinfostring);
utils::hook::invoke<void>(0x140341430, 0, cmd); // CL_Main_AddReliableCommand
}
utils::hook::detour cl_prepare_for_map_restart_hook;
void cl_prepare_for_map_restart_stub(const char* mapname, const char* gametype)
{
cl_prepare_for_map_restart_hook.invoke<void>(mapname, gametype);
}
utils::hook::detour cl_initialize_gamestate_hook;
void cl_initialize_gamestate_stub(int local_client_num)
{
if (!game::Com_IsAnyLocalServerRunning())
{
send_user_info();
}
cl_initialize_gamestate_hook.invoke<void>(local_client_num);
}
} }
void start_map(const std::string& mapname, bool dev) void start_map(const std::string& mapname, bool dev)
@ -495,7 +553,7 @@ namespace party
command::execute(utils::string::va("seta ui_mapname %s", mapname.data()), true); command::execute(utils::string::va("seta ui_mapname %s", mapname.data()), true);
auto* gametype = game::Dvar_FindVar("g_gametype"); auto* gametype = game::Dvar_FindVar("g_gametype");
if (gametype && gametype->current.string) if (gametype && gametype->current.string && gametype->current.string != "frontend"s)
{ {
command::execute(utils::string::va("seta ui_gametype %s", gametype->current.string), true); command::execute(utils::string::va("seta ui_gametype %s", gametype->current.string), true);
} }
@ -506,7 +564,18 @@ namespace party
command::execute(utils::string::va("seta ui_hardcore %d", hardcore->current.enabled), true); command::execute(utils::string::va("seta ui_hardcore %d", hardcore->current.enabled), true);
} }
perform_game_initialization(); if (!utils::hook::invoke<bool>(0x1409CDCF0, game::Lobby_GetPartyData()) || game::Com_FrontEnd_IsInFrontEnd())
{
if (game::environment::is_dedi())
{
perform_game_initialization(game::Dvar_FindVar("party_maxplayers")->current.integer, false);
}
else
{
perform_game_initialization(game::Dvar_FindVar("party_maxplayers")->current.integer,
game::Dvar_FindVar("xblive_privatematch")->current.integer);
}
}
console::info("Starting map: %s\n", mapname.data()); console::info("Starting map: %s\n", mapname.data());
@ -573,7 +642,6 @@ namespace party
{ {
command::execute("luiOpenPopup AcceptingInvite", true); command::execute("luiOpenPopup AcceptingInvite", true);
profile_infos::xuid::clear_xuids();
profile_infos::clear_profile_infos(); profile_infos::clear_profile_infos();
server_connection_state.host = target; server_connection_state.host = target;
@ -648,8 +716,15 @@ namespace party
// enable custom kick reason in GScr_KickPlayer // enable custom kick reason in GScr_KickPlayer
utils::hook::set<uint8_t>(0x140B5377E, 0xEB); utils::hook::set<uint8_t>(0x140B5377E, 0xEB);
// disable this, maybe causes no issues, but fixes Session unregister on map change/restart cl_prepare_for_map_restart_hook.create(0x1409B3FE0, cl_prepare_for_map_restart_stub);
utils::hook::set<uint8_t>(0x140851B50, 0xC3); // CG_ServerCmdMP_ParsePlayerInfos cl_initialize_gamestate_hook.create(0x1409B2FA0, cl_initialize_gamestate_stub);
command::add("senduserinfo", []()
{
auto userinfostring = utils::hook::invoke<const char*>(0x1409B2850, 0); // CL_MainMP_GetUserInfoString
auto cmd = utils::string::va("userinfo \"%s\"", userinfostring);
utils::hook::invoke<void>(0x140341430, 0, cmd); // CL_Main_AddReliableCommand
});
command::add("map", [](const command::params& args) command::add("map", [](const command::params& args)
{ {
@ -667,6 +742,8 @@ namespace party
start_map(args.get(1), false); start_map(args.get(1), false);
}); });
if (!game::environment::is_dedi())
{
command::add("devmap", [](const command::params& args) command::add("devmap", [](const command::params& args)
{ {
if (args.size() != 2) if (args.size() != 2)
@ -682,6 +759,7 @@ namespace party
start_map(args.get(1), true); start_map(args.get(1), true);
}); });
}
command::add("map_restart", []() command::add("map_restart", []()
{ {
@ -855,6 +933,7 @@ namespace party
info.set("playmode", utils::string::va("%i", game::Com_GameMode_GetActiveGameMode())); info.set("playmode", utils::string::va("%i", game::Com_GameMode_GetActiveGameMode()));
info.set("sv_running", utils::string::va("%i", get_dvar_bool("sv_running") && !game::Com_FrontEndScene_IsActive())); info.set("sv_running", utils::string::va("%i", get_dvar_bool("sv_running") && !game::Com_FrontEndScene_IsActive()));
info.set("dedicated", utils::string::va("%i", get_dvar_bool("dedicated"))); info.set("dedicated", utils::string::va("%i", get_dvar_bool("dedicated")));
info.set("privatematch", utils::string::va("%i", get_dvar_bool("xblive_privatematch")));
info.set("sv_wwwBaseUrl", get_dvar_string("sv_wwwBaseUrl")); info.set("sv_wwwBaseUrl", get_dvar_string("sv_wwwBaseUrl"));
info.set("sv_discordImageUrl", get_dvar_string("sv_discordImageUrl")); info.set("sv_discordImageUrl", get_dvar_string("sv_discordImageUrl"));
info.set("sv_discordImageText", get_dvar_string("sv_discordImageText")); info.set("sv_discordImageText", get_dvar_string("sv_discordImageText"));
@ -973,7 +1052,9 @@ namespace party
server_discord_info.emplace(discord_info); server_discord_info.emplace(discord_info);
} }
connect_to_party(target, mapname, gametype, sv_maxclients); const auto privatematch = std::atoi(info.get("privatematch").data());
connect_to_party(target, mapname, gametype, sv_maxclients, privatematch);
}); });
} }
}; };

View File

@ -219,6 +219,11 @@ namespace patches
{ {
//init_network_dvars_hook.invoke<void>(dvar); //init_network_dvars_hook.invoke<void>(dvar);
} }
void disconnect()
{
utils::hook::invoke<void>(0x140C58E20); // SV_MainMP_MatchEnd
}
} }
class component final : public component_interface class component final : public component_interface
@ -268,8 +273,15 @@ namespace patches
// killswitches // killswitches
dvars::override::register_bool("mission_team_contracts_enabled", true, game::DVAR_FLAG_READ); dvars::override::register_bool("mission_team_contracts_enabled", true, game::DVAR_FLAG_READ);
//dvars::override::register_bool("killswitch_store", true, game::DVAR_FLAG_READ); dvars::override::register_bool("killswitch_store", false, game::DVAR_FLAG_READ);
dvars::override::register_bool("killswitch_quartermaster", false, game::DVAR_FLAG_READ);
dvars::override::register_bool("killswitch_cod_points", false, game::DVAR_FLAG_READ);
dvars::override::register_bool("killswitch_custom_emblems", false, game::DVAR_FLAG_READ);
dvars::override::register_bool("killswitch_matchID", true, game::DVAR_FLAG_READ); dvars::override::register_bool("killswitch_matchID", true, game::DVAR_FLAG_READ);
dvars::override::register_bool("killswitch_mp_leaderboards", true, game::DVAR_FLAG_READ);
dvars::override::register_bool("killswitch_cp_leaderboards", true, game::DVAR_FLAG_READ);
dvars::override::register_bool("killswitch_streak_variants", false, game::DVAR_FLAG_READ);
dvars::override::register_bool("killswitch_blood_anvil", false, game::DVAR_FLAG_READ);
// announcer packs // announcer packs
if (!game::environment::is_dedi()) if (!game::environment::is_dedi())
@ -297,11 +309,8 @@ namespace patches
dvars::override::register_float("gpad_stick_pressed", 0.4f, 0, 1, game::DVAR_FLAG_SAVED); dvars::override::register_float("gpad_stick_pressed", 0.4f, 0, 1, game::DVAR_FLAG_SAVED);
dvars::override::register_float("gpad_stick_pressed_hysteresis", 0.1f, 0, 1, game::DVAR_FLAG_SAVED); dvars::override::register_float("gpad_stick_pressed_hysteresis", 0.1f, 0, 1, game::DVAR_FLAG_SAVED);
// block changing name in-game
utils::hook::set<uint8_t>(0x140C4DF90, 0xC3);
// disable host migration // disable host migration
utils::hook::set<uint8_t>(0x140C5A200, 0xC3); utils::hook::jump(0x140C5A200, disconnect);
// precache is always allowed // precache is always allowed
utils::hook::set(0x1406D5280, 0xC301B0); // NetConstStrings_IsPrecacheAllowed utils::hook::set(0x1406D5280, 0xC301B0); // NetConstStrings_IsPrecacheAllowed

View File

@ -37,38 +37,12 @@ namespace profile_infos
return info; return info;
} }
std::unordered_set<std::uint64_t> get_connected_client_xuids()
{
if (!game::Com_IsAnyLocalServerRunning()) // is_host()
{
return {};
}
std::unordered_set<std::uint64_t> xuids{};
const auto* svs_clients = *game::svs_clients;
for (unsigned int i = 0; i < *game::svs_numclients; ++i)
{
if (svs_clients[i].header.state >= 1)
{
xuids.emplace(xuid::get_client_xuid(i));
}
}
return xuids;
}
void set_playercardcache_to_download(const std::uint64_t user_id) void set_playercardcache_to_download(const std::uint64_t user_id)
{ {
game::XUID xuid{ user_id }; game::XUID xuid{ user_id };
game::PlayercardCache_AddToDownload(0, xuid); game::PlayercardCache_AddToDownload(0, xuid);
*game::g_DWPlayercardCacheDownloadTaskStage = game::PLAYERCARD_CACHE_TASK_STAGE_WAITING; *game::g_DWPlayercardCacheDownloadTaskStage = game::PLAYERCARD_CACHE_TASK_STAGE_WAITING;
} }
void set_client_xuid_to_session(game::SessionData* session, const std::uint32_t client_index)
{
session->dyn.users[client_index].xuid = xuid::get_client_xuid(client_index);
}
} }
profile_info::profile_info(utils::byte_buffer& buffer) profile_info::profile_info(utils::byte_buffer& buffer)
@ -107,16 +81,6 @@ namespace profile_infos
}); });
} }
void remove_profile_info_by_client_index(const std::uint32_t client_index)
{
const auto user_id = xuid::get_client_xuid(client_index);
if (!user_id)
{
return;
}
remove_profile_info(user_id);
}
void send_profile_info(const game::netadr_s& address, const std::string& data) void send_profile_info(const game::netadr_s& address, const std::string& data)
{ {
game::fragment_handler::fragment_data(data.data(), data.size(), [&address](const utils::byte_buffer& buffer) game::fragment_handler::fragment_data(data.data(), data.size(), [&address](const utils::byte_buffer& buffer)
@ -207,12 +171,10 @@ namespace profile_infos
{ {
if (svs_clients[i].header.state >= 1 && !game::SV_ClientIsBot(i) && game::Session_IsHost(game::SV_MainMP_GetServerLobby(), i)) if (svs_clients[i].header.state >= 1 && !game::SV_ClientIsBot(i) && game::Session_IsHost(game::SV_MainMP_GetServerLobby(), i))
{ {
assert(i == 0);
auto self = load_profile_info(); auto self = load_profile_info();
if (self.has_value()) if (self.has_value())
{ {
send_profile_info(addr, xuid::get_client_xuid(i), self.value()); send_profile_info(addr, steam::SteamUser()->GetSteamID().bits, self.value());
break; break;
} }
} }
@ -245,135 +207,12 @@ namespace profile_infos
}); });
} }
namespace xuid
{
client_xuid_array client_xuids{};
void add_client_xuid(const std::uint32_t& client_index, const std::uint64_t& xuid)
{
if (client_xuids[client_index] && client_xuids[client_index] != xuid)
{
remove_profile_info(client_xuids[client_index]); // remove profile if it exists
}
client_xuids[client_index] = xuid;
set_client_xuid_to_session(game::SV_MainMP_GetServerLobby(), client_index);
}
std::uint64_t get_client_xuid(const std::uint32_t& client_index)
{
if (client_xuids[client_index])
{
// returns xuid for player. this must be on both the client & server
// client recieves data for this via playerXuid packet
return client_xuids[client_index];
}
return static_cast<std::uint64_t>(0);
}
void remove_client_xuid(const std::uint32_t& client_index)
{
client_xuids[client_index] = 0;
}
void clear_xuids()
{
for (auto& xuid : client_xuids)
{
xuid = 0;
}
}
client_xuid_array get_xuids()
{
return client_xuids;
}
void send_xuid(const game::netadr_s& addr, const std::uint64_t xuid, const std::uint32_t client_index)
{
utils::byte_buffer buffer{};
buffer.write(client_index);
buffer.write(xuid);
const std::string data = buffer.move_buffer();
game::fragment_handler::fragment_data(data.data(), data.size(), [&](const utils::byte_buffer& buffer)
{
network::send(addr, "playerXuid", buffer.get_buffer());
});
}
void send_xuid_to_all_clients(const std::uint64_t xuid, const std::uint32_t& client_index)
{
const auto* svs_clients = *game::svs_clients;
for (unsigned int i = 0; i < *game::svs_numclients; ++i)
{
if (svs_clients[i].header.state >= 1 && !game::SV_ClientIsBot(i) && !game::Session_IsHost(game::SV_MainMP_GetServerLobby(), i))
{
send_xuid(svs_clients[i].remoteAddress, xuid, client_index);
}
}
}
void send_all_xuids(const game::netadr_s& addr)
{
int i = 0;
for (const auto xuid : xuid::get_xuids())
{
if (xuid == 0)
{
i++;
continue;
}
send_xuid(addr, xuid, i++);
}
if (!game::environment::is_dedi())
{
// send self xuid here too
send_xuid(addr, steam::SteamUser()->GetSteamID().bits, 0);
}
}
}
namespace namespace
{ {
utils::hook::detour client_connect_hook;
const char* client_connect_stub(int client_num, unsigned __int16 script_pers_id)
{
auto result = client_connect_hook.invoke<const char*>(client_num, script_pers_id);
if (game::SV_ClientIsBot(client_num))
{
return result;
}
const auto client = game::svs_clients[client_num];
std::uint64_t xuid{};
game::StringToXUID(client->playerGuid, &xuid);
xuid::add_client_xuid(client_num, xuid); // add to self
// don't send if client is self
if (client_num == 0 && !game::environment::is_dedi() && game::Com_IsAnyLocalServerRunning())
{
return result;
}
xuid::send_xuid_to_all_clients(xuid, client_num); // add to all connected
xuid::send_all_xuids(client->remoteAddress);
return result;
}
utils::hook::detour session_unregister_remote_player_hook; utils::hook::detour session_unregister_remote_player_hook;
void session_unregister_remote_player_stub(game::SessionData* session, const int slot) void session_unregister_remote_player_stub(game::SessionData* session, const int slot)
{ {
session_unregister_remote_player_hook.invoke<void>(session, slot); session_unregister_remote_player_hook.invoke<void>(session, slot);
set_client_xuid_to_session(session, slot);
} }
} }
@ -382,12 +221,9 @@ namespace profile_infos
public: public:
void post_unpack() override void post_unpack() override
{ {
client_connect_hook.create(0x140AFFF10, client_connect_stub); session_unregister_remote_player_hook.create(0x140C73970, session_unregister_remote_player_stub);
// comment out this, since i think i fixed this indirectly with a patch from party.cpp //dvars::override::register_int("playercard_cache_validity_life", 5000, 0, 3600000, 0x0); // 5sec
//session_unregister_remote_player_hook.create(0x140C73970, session_unregister_remote_player_stub);
dvars::override::register_int("playercard_cache_validity_life", 5000, 0, 3600000, 0x0); // 5sec
network::on("profileInfo", [](const game::netadr_s& client_addr, const std::string_view& data) network::on("profileInfo", [](const game::netadr_s& client_addr, const std::string_view& data)
{ {
@ -410,29 +246,6 @@ namespace profile_infos
set_playercardcache_to_download(user_id); set_playercardcache_to_download(user_id);
} }
}); });
network::on("playerXuid", [](const game::netadr_s& server_addr, const std::string_view& data)
{
utils::byte_buffer buffer(data);
std::string final_packet{};
if (!game::fragment_handler::handle(server_addr, buffer, final_packet))
{
return;
}
buffer = utils::byte_buffer(final_packet);
const auto client_index = buffer.read<std::uint32_t>();
const auto xuid = buffer.read<std::uint64_t>();
if (!game::Com_IsAnyLocalServerRunning() && server_addr.addr != party::get_server_connection_state().host.addr)
{
console::debug("playerXuid call from an unknown address\n");
return;
}
xuid::add_client_xuid(client_index, xuid);
});
} }
}; };
} }

View File

@ -5,14 +5,6 @@
namespace profile_infos namespace profile_infos
{ {
namespace xuid
{
using client_xuid_array = std::array<std::uint64_t, 18>;
std::uint64_t get_client_xuid(const std::uint32_t& client_index);
void clear_xuids();
client_xuid_array get_xuids();
}
struct profile_info struct profile_info
{ {
std::string m_memberplayer_card{}; std::string m_memberplayer_card{};

View File

@ -10,6 +10,14 @@
namespace ranked namespace ranked
{ {
namespace
{
bool bots_enabled()
{
return !game::Com_FrontEndScene_IsActive() && game::Com_GameMode_GetActiveGameMode() == game::GAME_MODE_MP;
}
}
class component final : public component_interface class component final : public component_interface
{ {
public: public:
@ -28,6 +36,12 @@ namespace ranked
{ {
dvars::override::register_bool("xblive_privatematch", true, game::DVAR_FLAG_REPLICATED); dvars::override::register_bool("xblive_privatematch", true, game::DVAR_FLAG_REPLICATED);
} }
// Always run bots, even if xblive_privatematch is 0
utils::hook::jump(0x1406E6940, bots_enabled); // BG_BotSystemEnabled
utils::hook::jump(0x1406E6510, bots_enabled); // BG_AISystemEnabled
utils::hook::jump(0x1406E68F0, bots_enabled); // BG_BotFastFileEnabled
utils::hook::jump(0x140C546F0, bots_enabled); // BG_BotsUsingTeamDifficulty
} }
}; };
} }

View File

@ -2,7 +2,7 @@
#include "structs.hpp" #include "structs.hpp"
#define PROTOCOL 1 #define PROTOCOL 2
namespace game namespace game
{ {

View File

@ -125,6 +125,7 @@ namespace game
WEAK symbol<const char* (dvar_t* dvar, DvarValue value)> Dvar_ValueToString{ 0x140CEED00 }; WEAK symbol<const char* (dvar_t* dvar, DvarValue value)> Dvar_ValueToString{ 0x140CEED00 };
WEAK symbol<void(dvar_t* dvar, DvarSetSource source)> Dvar_Reset{ 0x140CEC490 }; WEAK symbol<void(dvar_t* dvar, DvarSetSource source)> Dvar_Reset{ 0x140CEC490 };
WEAK symbol<unsigned int(const char* name)> Dvar_GenerateChecksum{ 0x140CEA520 }; WEAK symbol<unsigned int(const char* name)> Dvar_GenerateChecksum{ 0x140CEA520 };
WEAK symbol<void(dvar_t* dvar, bool value)> Dvar_SetBool{ 0x140CEC7D0 };
WEAK symbol<void(dvar_t* dvar, int value)> Dvar_SetInt{ 0x140CED3D0 }; WEAK symbol<void(dvar_t* dvar, int value)> Dvar_SetInt{ 0x140CED3D0 };
WEAK symbol<void(dvar_t* dvar, float value)> Dvar_SetFloat{ 0x140CECD90 }; WEAK symbol<void(dvar_t* dvar, float value)> Dvar_SetFloat{ 0x140CECD90 };
WEAK symbol<void(bool cheatOverride)> Dvar_OverrideCheatProtection{ 0x140CEB250 }; WEAK symbol<void(bool cheatOverride)> Dvar_OverrideCheatProtection{ 0x140CEB250 };
@ -378,6 +379,9 @@ namespace game
WEAK symbol<int> com_frameTime{ 0x1460053C0 }; WEAK symbol<int> com_frameTime{ 0x1460053C0 };
WEAK symbol<int> com_num_console_lines{ 0x146006DB0 };
WEAK symbol<char*> com_console_lines{ 0x146006DC0 };
WEAK symbol<int> s_frontEndScene_state{ 0x144BFF608 }; WEAK symbol<int> s_frontEndScene_state{ 0x144BFF608 };
WEAK symbol<WeaponDef*> bg_weaponDefs{ 0x145210120 }; WEAK symbol<WeaponDef*> bg_weaponDefs{ 0x145210120 };