327 lines
11 KiB
C++
327 lines
11 KiB
C++
#include <std_include.hpp>
|
|
#include "loader/component_loader.hpp"
|
|
|
|
#include "game/game.hpp"
|
|
#include "game/dvars.hpp"
|
|
|
|
#include "fastfiles.hpp"
|
|
#include "filesystem.hpp"
|
|
#include "dvars.hpp"
|
|
|
|
#include <utils/hook.hpp>
|
|
#include <utils/string.hpp>
|
|
|
|
namespace patches
|
|
{
|
|
namespace
|
|
{
|
|
utils::hook::detour com_register_common_dvars_hook;
|
|
utils::hook::detour com_game_mode_supports_feature_hook;
|
|
utils::hook::detour cg_set_client_dvar_from_server_hook;
|
|
utils::hook::detour live_get_map_index_hook;
|
|
utils::hook::detour content_do_we_have_content_pack_hook;
|
|
utils::hook::detour init_network_dvars_hook;
|
|
|
|
std::string get_login_username()
|
|
{
|
|
char username[UNLEN + 1];
|
|
DWORD username_len = UNLEN + 1;
|
|
if (!GetUserNameA(username, &username_len))
|
|
{
|
|
return "Unknown Soldier";
|
|
}
|
|
|
|
return std::string{ username, username_len - 1 };
|
|
}
|
|
|
|
void com_register_common_dvars_stub()
|
|
{
|
|
game::dvar_t* name_dvar;
|
|
game::dvar_t* com_maxfps;
|
|
|
|
name_dvar = game::Dvar_RegisterString("name", get_login_username().data(), game::DVAR_FLAG_SAVED, "Player name.");
|
|
|
|
if (game::environment::is_dedi())
|
|
{
|
|
com_maxfps = game::Dvar_RegisterInt("com_maxfps", 85, 0, 100, game::DVAR_FLAG_NONE, "Cap frames per second");
|
|
}
|
|
else
|
|
{
|
|
com_maxfps = game::Dvar_RegisterInt("com_maxfps", 0, 0, 1000, game::DVAR_FLAG_SAVED, "Cap frames per second");
|
|
}
|
|
|
|
*reinterpret_cast<game::dvar_t**>(0x146005758) = com_maxfps;
|
|
dvars::disable::re_register("com_maxfps");
|
|
dvars::disable::de_register("com_maxfps");
|
|
|
|
return com_register_common_dvars_hook.invoke<void>();
|
|
}
|
|
|
|
bool com_game_mode_supports_feature_stub(game::Com_GameMode_Feature feature)
|
|
{
|
|
if (feature == game::FEATURE_GRAVITY)
|
|
{
|
|
return true;
|
|
}
|
|
else if (feature == game::FEATURE_TIMESCALE)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return com_game_mode_supports_feature_hook.invoke<bool>(feature);
|
|
}
|
|
|
|
const char* live_get_local_client_name()
|
|
{
|
|
return game::Dvar_FindVar("name")->current.string;
|
|
}
|
|
|
|
std::vector<std::string> dvar_save_variables;
|
|
void dvar_write_single_variable(const game::dvar_t* dvar, int* user_data)
|
|
{
|
|
if ((dvar->flags & game::DVAR_FLAG_SAVED) != 0)
|
|
{
|
|
const char* val = game::Dvar_DisplayableLatchedValue(dvar);
|
|
auto h = *user_data;
|
|
|
|
std::string dvar_name = dvars::dvar_get_name(dvar);
|
|
if (dvar_name.empty())
|
|
{
|
|
game::FS_Printf(h, "setcl %d \"%s\"\n", dvar->checksum, val);
|
|
}
|
|
else
|
|
{
|
|
dvar_save_variables.push_back(dvar_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void dvar_write_variables_stub(int handle)
|
|
{
|
|
dvar_save_variables.clear();
|
|
|
|
int* user_data = &handle;
|
|
game::Dvar_ForEach(dvar_write_single_variable, user_data);
|
|
|
|
std::sort(dvar_save_variables.begin(), dvar_save_variables.end()); // alphabetize sort
|
|
for (size_t i = 0; i < dvar_save_variables.size(); i++)
|
|
{
|
|
const auto* dvar_name = dvar_save_variables.at(i).data();
|
|
const auto* dvar = game::Dvar_FindVar(dvar_name);
|
|
const char* val = game::Dvar_DisplayableLatchedValue(dvar);
|
|
game::FS_Printf(handle, "seta %s \"%s\"\n", dvar_name, val);
|
|
}
|
|
}
|
|
|
|
void missing_content_error_stub(int, const char*)
|
|
{
|
|
game::Com_Error(game::ERR_DROP, utils::string::va("MISSING FILE\n%s.ff",
|
|
fastfiles::get_current_fastfile().data()));
|
|
}
|
|
|
|
const char* stored_mapname;
|
|
int live_get_map_index_stub(const char* map)
|
|
{
|
|
stored_mapname = map;
|
|
return live_get_map_index_hook.invoke<int>(map);
|
|
}
|
|
|
|
bool content_do_we_have_content_pack_stub(int index)
|
|
{
|
|
if (stored_mapname != nullptr && !fastfiles::exists(stored_mapname))
|
|
{
|
|
stored_mapname = nullptr;
|
|
return false;
|
|
}
|
|
return content_do_we_have_content_pack_hook.invoke<bool>(index);
|
|
}
|
|
|
|
void cg_set_client_dvar_from_server_stub(void* client_num, void* cgame_glob, const char* dvar_checksum, const char* value)
|
|
{
|
|
unsigned int checksum = static_cast<unsigned int>(atoi(dvar_checksum));
|
|
auto* dvar = game::Dvar_FindMalleableVar(checksum);
|
|
|
|
static unsigned int cg_fov_checksum = game::Dvar_GenerateChecksum("cg_fov");
|
|
static unsigned int cg_fovScale_checksum = game::Dvar_GenerateChecksum("cg_fovScale");
|
|
|
|
if (checksum == cg_fov_checksum ||
|
|
checksum == cg_fovScale_checksum)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// register new dvar
|
|
if (!dvar)
|
|
{
|
|
game::Dvar_SetFromStringByChecksum(checksum, value, game::DvarSetSource::DVAR_SOURCE_EXTERNAL);
|
|
}
|
|
// only set if dvar has no flags or has external flag
|
|
else if (dvar->flags == game::DVAR_FLAG_NONE ||
|
|
(dvar->flags & game::DVAR_FLAG_EXTERNAL) != 0)
|
|
{
|
|
game::Dvar_SetFromStringFromSource(dvar, value, game::DvarSetSource::DVAR_SOURCE_EXTERNAL);
|
|
}
|
|
|
|
// original code
|
|
unsigned int index = 0;
|
|
auto result = utils::hook::invoke<__int64>(0x140B7AC60, dvar, &index); // NetConstStrings_SV_GetNetworkDvarIndex
|
|
if (result)
|
|
{
|
|
std::string index_str = std::to_string(index);
|
|
return cg_set_client_dvar_from_server_hook.invoke<void>(client_num, cgame_glob, index_str.data(), value);
|
|
}
|
|
}
|
|
|
|
game::dvar_t* get_client_dvar(const char* name)
|
|
{
|
|
game::dvar_t* dvar = game::Dvar_FindVar(name);
|
|
if (!dvar)
|
|
{
|
|
static game::dvar_t dummy{ 0 };
|
|
dummy.checksum = game::Dvar_GenerateChecksum(name);
|
|
return &dummy;
|
|
}
|
|
return dvar;
|
|
}
|
|
|
|
bool get_client_dvar_checksum(game::dvar_t* dvar, unsigned int* checksum)
|
|
{
|
|
*checksum = dvar->checksum;
|
|
return true;
|
|
}
|
|
|
|
char* db_read_raw_file_stub(const char* filename, char* buf, const int size)
|
|
{
|
|
std::string file_name = filename;
|
|
if (file_name.find(".cfg") == std::string::npos)
|
|
{
|
|
file_name.append(".cfg");
|
|
}
|
|
|
|
std::string buffer{};
|
|
if (filesystem::read_file(file_name, &buffer))
|
|
{
|
|
snprintf(buf, size, "%s\n", buffer.data());
|
|
return buf;
|
|
}
|
|
|
|
return game::DB_ReadRawFile(filename, buf, size);
|
|
}
|
|
|
|
void cbuf_execute_buffer_internal_stub(int local_client_num, int controller_index, char* buffer, [[maybe_unused]]void* callback)
|
|
{
|
|
game::Dvar_OverrideCheatProtection(0);
|
|
game::Cbuf_ExecuteBufferInternal(local_client_num, controller_index, buffer, game::Cmd_ExecuteSingleCommand);
|
|
game::Dvar_OverrideCheatProtection(1);
|
|
}
|
|
|
|
void init_network_dvars_stub(game::dvar_t* dvar)
|
|
{
|
|
//init_network_dvars_hook.invoke<void>(dvar);
|
|
}
|
|
|
|
void disconnect()
|
|
{
|
|
utils::hook::invoke<void>(0x140C58E20); // SV_MainMP_MatchEnd
|
|
}
|
|
}
|
|
|
|
class component final : public component_interface
|
|
{
|
|
public:
|
|
void post_unpack() override
|
|
{
|
|
// register custom dvars
|
|
com_register_common_dvars_hook.create(0x140BADF30, com_register_common_dvars_stub);
|
|
|
|
// patch some features
|
|
com_game_mode_supports_feature_hook.create(game::Com_GameMode_SupportsFeature, com_game_mode_supports_feature_stub);
|
|
|
|
// get client name from dvar
|
|
utils::hook::jump(0x140D32770, live_get_local_client_name);
|
|
|
|
// write better config
|
|
utils::hook::jump(0x140BB2A90, dvar_write_variables_stub);
|
|
|
|
// show missing fastfiles
|
|
utils::hook::call(0x1403BBD4B, missing_content_error_stub);
|
|
|
|
// show missing map
|
|
stored_mapname = nullptr;
|
|
live_get_map_index_hook.create(0x140CE72C0, live_get_map_index_stub);
|
|
content_do_we_have_content_pack_hook.create(0x140CE8550, content_do_we_have_content_pack_stub);
|
|
|
|
// make setclientdvar behave like older games
|
|
cg_set_client_dvar_from_server_hook.create(0x140856D70, cg_set_client_dvar_from_server_stub);
|
|
utils::hook::call(0x140B0A9BB, get_client_dvar_checksum); // setclientdvar
|
|
utils::hook::call(0x140B0ACD7, get_client_dvar_checksum); // setclientdvars
|
|
utils::hook::call(0x140B0A984, get_client_dvar); // setclientdvar
|
|
utils::hook::call(0x140B0AC9F, get_client_dvar); // setclientdvars
|
|
utils::hook::set<uint8_t>(0x140B0A9AC, 0xEB); // setclientdvar
|
|
utils::hook::set<uint8_t>(0x140B0ACC8, 0xEB); // setclientdvars
|
|
|
|
// Allow executing custom cfg files with the "exec" command
|
|
utils::hook::call(0x140B7CEF9, db_read_raw_file_stub);
|
|
// Add cheat override to exec
|
|
utils::hook::call(0x140B7CF11, cbuf_execute_buffer_internal_stub);
|
|
|
|
// don't register every replicated dvar as a network dvar
|
|
init_network_dvars_hook.create(0x140B7A920, init_network_dvars_stub);
|
|
|
|
// some [data validation] anti tamper thing that kills performance
|
|
dvars::override::register_int("dvl", 0, 0, 0, game::DVAR_FLAG_READ);
|
|
|
|
// killswitches
|
|
dvars::override::register_bool("mission_team_contracts_enabled", 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())
|
|
{
|
|
dvars::override::register_bool("killswitch_announcers", false, game::DVAR_FLAG_READ);
|
|
dvars::override::register_int("igs_announcer", 0x1F, 0, 0x7FFFFFFF, game::DVAR_FLAG_READ); // show all announcer packs
|
|
}
|
|
|
|
// disable cod account
|
|
dvars::override::register_bool("enable_cod_account", false, game::DVAR_FLAG_READ);
|
|
|
|
// enable boss battles
|
|
dvars::override::register_bool("online_zombie_boss_battle", true, game::DVAR_FLAG_READ);
|
|
dvars::override::register_bool("online_zombie_boss_zmb", true, game::DVAR_FLAG_READ);
|
|
dvars::override::register_bool("online_zombie_boss_rave", true, game::DVAR_FLAG_READ);
|
|
dvars::override::register_bool("online_zombie_boss_disco", true, game::DVAR_FLAG_READ);
|
|
dvars::override::register_bool("online_zombie_boss_town", true, game::DVAR_FLAG_READ);
|
|
dvars::override::register_bool("online_zombie_boss_final", true, game::DVAR_FLAG_READ);
|
|
dvars::override::register_bool("online_zombie_boss_dc", true, game::DVAR_FLAG_READ);
|
|
|
|
// uncheat protect gamepad-related dvars
|
|
dvars::override::register_float("gpad_button_deadzone", 0.13f, 0, 1, game::DVAR_FLAG_SAVED);
|
|
dvars::override::register_float("gpad_stick_deadzone_min", 0.2f, 0, 1, game::DVAR_FLAG_SAVED);
|
|
dvars::override::register_float("gpad_stick_deadzone_max", 0.01f, 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);
|
|
|
|
// disable host migration
|
|
utils::hook::jump(0x140C5A200, disconnect);
|
|
|
|
// precache is always allowed
|
|
utils::hook::set(0x1406D5280, 0xC301B0); // NetConstStrings_IsPrecacheAllowed
|
|
|
|
// allow localized string to create config strings post init
|
|
utils::hook::nop(0x1405EE287, 2);
|
|
|
|
utils::hook::nop(0x140E6A2FB, 2); // don't wait for occlusion query to succeed (forever loop)
|
|
utils::hook::nop(0x140E6A30C, 2); // ^
|
|
}
|
|
};
|
|
}
|
|
|
|
REGISTER_COMPONENT(patches::component) |