dedi fixes and more

This commit is contained in:
quaK 2024-08-12 01:52:13 +03:00
parent 71843c3821
commit df9040c6af
9 changed files with 166 additions and 264 deletions

View File

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

View File

@ -50,26 +50,22 @@ namespace dedicated
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)
{
game::Cbuf_ExecuteBufferInternal(0, 0, command, game::Cmd_ExecuteSingleCommand);
}
else
{
get_startup_command_queue().emplace_back(command);
}
}
auto& com_num_console_lines = *game::com_num_console_lines;
auto* com_console_lines = game::com_console_lines.get();
void execute_startup_command_queue()
{
const auto queue = get_startup_command_queue();
get_startup_command_queue().clear();
for (const auto& command : queue)
for (auto i = 0; i < com_num_console_lines; i++)
{
game::Cbuf_ExecuteBufferInternal(0, 0, command.data(), game::Cmd_ExecuteSingleCommand);
auto cmd = com_console_lines[i];
// if command is map or map_rotate, its already been called
if (cmd == "map"s || cmd == "map_rotate"s)
{
continue;
}
game::Cbuf_ExecuteBufferInternal(0, 0, cmd, game::Cmd_ExecuteSingleCommand);
}
}
@ -268,9 +264,6 @@ namespace dedicated
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>(0x140B21137 + 2, 0x480); // g_hardcore flags
utils::hook::jump(0x140C12400, sv_get_game_type_stub);
@ -397,7 +390,7 @@ namespace dedicated
// remove disconnect command
game::Cmd_RemoveCommand("disconnect");
execute_startup_command_queue();
execute_startup_commands();
// Send heartbeat to dpmaster
scheduler::once(send_heartbeat, scheduler::pipeline::server);

View File

@ -296,16 +296,52 @@ namespace party
bool preloaded_map = false;
void perform_game_initialization()
void xstartlobby(const int max_clients, const bool private_match)
{
command::execute("onlinegame 1", true);
command::execute("xblive_privatematch 1", true);
//command::execute("xstartprivateparty", true);
command::execute("xstartprivatematch", true);
// Retrieve game variables
const int privateMatch = private_match; //game::Dvar_FindVar("xblive_privatematch")->current.integer;
const int maxPlayers = max_clients; //game::Dvar_FindVar("party_maxplayers")->current.integer;
const int privateClients = 0; //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)
{
xstartlobby(max_clients, 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);
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();
if (mode != game::GAME_MODE_MP && mode != game::GAME_MODE_CP)
@ -317,20 +353,15 @@ namespace party
{
scheduler::once([=]()
{
connect_to_party(target, mapname, gametype, sv_maxclients);
connect_to_party(target, mapname, gametype, sv_maxclients, private_match);
}, scheduler::pipeline::main, 1s);
return;
}
const auto ui_maxclients = game::Dvar_FindVar("ui_maxclients");
const auto party_maxplayers = game::Dvar_FindVar("party_maxplayers");
game::Dvar_SetInt(ui_maxclients, sv_maxclients);
game::Dvar_SetInt(party_maxplayers, sv_maxclients);
perform_game_initialization(sv_maxclients, private_match);
command::execute(utils::string::va("ui_mapname %s", mapname.data()), true);
command::execute(utils::string::va("ui_gametype %s", gametype.data()), true);
perform_game_initialization();
game::Dvar_SetFromStringByName("ui_mapname", mapname.data(), game::DVAR_SOURCE_INTERNAL);
game::Dvar_SetFromStringByName("ui_gametype", gametype.data(), game::DVAR_SOURCE_INTERNAL);
// setup agent count
utils::hook::invoke<void>(0x140C19B00, gametype.data());
@ -344,7 +375,7 @@ namespace party
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* clients = game::Dvar_FindVar("ui_maxclients");
auto* private_clients = game::Dvar_FindVar("ui_privateClients");
@ -398,8 +429,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,
bool map_is_preloaded, bool migrate)
{
profile_infos::xuid::clear_xuids();
hash_cache.clear();
if (game::environment::is_dedi())
@ -426,6 +455,8 @@ namespace party
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)
@ -439,6 +470,30 @@ namespace party
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)
@ -506,7 +561,18 @@ namespace party
command::execute(utils::string::va("seta ui_hardcore %d", hardcore->current.enabled), true);
}
perform_game_initialization();
if (!utils::hook::invoke<bool>(0x1409CDCF0) || 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());
@ -573,7 +639,6 @@ namespace party
{
command::execute("luiOpenPopup AcceptingInvite", true);
profile_infos::xuid::clear_xuids();
profile_infos::clear_profile_infos();
server_connection_state.host = target;
@ -648,8 +713,15 @@ namespace party
// enable custom kick reason in GScr_KickPlayer
utils::hook::set<uint8_t>(0x140B5377E, 0xEB);
// disable this, maybe causes no issues, but fixes Session unregister on map change/restart
utils::hook::set<uint8_t>(0x140851B50, 0xC3); // CG_ServerCmdMP_ParsePlayerInfos
cl_prepare_for_map_restart_hook.create(0x1409B3FE0, cl_prepare_for_map_restart_stub);
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)
{
@ -667,21 +739,24 @@ namespace party
start_map(args.get(1), false);
});
command::add("devmap", [](const command::params& args)
if (!game::environment::is_dedi())
{
if (args.size() != 2)
command::add("devmap", [](const command::params& args)
{
return;
}
if (args.size() != 2)
{
return;
}
if (game::Com_GameMode_GetActiveGameMode() == game::GAME_MODE_SP)
{
command::execute(utils::string::va("spmap %s", args.get(1)));
return;
}
if (game::Com_GameMode_GetActiveGameMode() == game::GAME_MODE_SP)
{
command::execute(utils::string::va("spmap %s", args.get(1)));
return;
}
start_map(args.get(1), true);
});
start_map(args.get(1), true);
});
}
command::add("map_restart", []()
{
@ -855,6 +930,7 @@ namespace party
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("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_discordImageUrl", get_dvar_string("sv_discordImageUrl"));
info.set("sv_discordImageText", get_dvar_string("sv_discordImageText"));
@ -866,7 +942,7 @@ namespace party
{
for (const auto& file : mod_files)
{
const auto hash = get_file_hash(utils::string::va("%s/mod%s",
const auto hash = get_file_hash(utils::string::va("%s/mod%s",
fs_game.data(), file.extension.data()));
info.set(file.name, hash);
}
@ -973,7 +1049,9 @@ namespace party
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);
}
void disconnect()
{
utils::hook::invoke<void>(0x140C58E20); // SV_MainMP_MatchEnd
}
}
class component final : public component_interface
@ -268,8 +273,15 @@ namespace patches
// killswitches
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_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
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_hysteresis", 0.1f, 0, 1, game::DVAR_FLAG_SAVED);
// block changing name in-game
utils::hook::set<uint8_t>(0x140C4DF90, 0xC3);
// disable host migration
utils::hook::set<uint8_t>(0x140C5A200, 0xC3);
utils::hook::jump(0x140C5A200, disconnect);
// precache is always allowed
utils::hook::set(0x1406D5280, 0xC301B0); // NetConstStrings_IsPrecacheAllowed

View File

@ -37,38 +37,12 @@ namespace profile_infos
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)
{
game::XUID xuid{ user_id };
game::PlayercardCache_AddToDownload(0, xuid);
*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)
@ -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)
{
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))
{
assert(i == 0);
auto self = load_profile_info();
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;
}
}
@ -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
{
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;
void session_unregister_remote_player_stub(game::SessionData* session, const int 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:
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
//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
//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)
{
@ -410,29 +246,6 @@ namespace profile_infos
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 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
{
std::string m_memberplayer_card{};

View File

@ -10,6 +10,14 @@
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
{
public:
@ -28,6 +36,12 @@ namespace ranked
{
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"
#define PROTOCOL 1
#define PROTOCOL 2
namespace game
{

View File

@ -378,6 +378,9 @@ namespace game
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<WeaponDef*> bg_weaponDefs{ 0x145210120 };