Add dvar_cheats component + some fixes

This commit is contained in:
Federico Cecchetto 2022-03-13 03:07:17 +01:00
parent e65c6c7327
commit c84c9398f6
7 changed files with 11791 additions and 2354 deletions

View File

@ -33,7 +33,7 @@ namespace command
}
}
void client_command(const int client_num, void* a2)
void client_command(const int client_num)
{
params_sv params = {};
@ -43,7 +43,7 @@ namespace command
handlers_sv[command](client_num, params);
}
client_command_hook.invoke<void>(client_num, a2);
client_command_hook.invoke<void>(client_num);
}
// Shamelessly stolen from Quake3
@ -59,8 +59,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*>(0x142623FB4); //H1(1.4)
auto* com_console_lines = reinterpret_cast<char**>(0x142623FC0); //H1(1.4)
auto& com_num_console_lines = *reinterpret_cast<int*>(0x142623FB4);
auto* com_console_lines = reinterpret_cast<char**>(0x142623FC0);
auto inq = false;
com_console_lines[0] = command_line;
@ -247,9 +247,9 @@ namespace command
void add(const char* name, const std::function<void()>& callback)
{
add(name, [callback](const params&)
{
callback();
});
{
callback();
});
}
void add_sv(const char* name, std::function<void(int, const params_sv&)> callback)
@ -288,7 +288,7 @@ namespace command
}
else
{
utils::hook::call(0x1400D728F, &parse_commandline_stub); // MWR TEST
utils::hook::call(0x1400D728F, parse_commandline_stub);
utils::hook::jump(0x14041D750, dvar_command_stub);
add_commands_mp();
@ -301,35 +301,11 @@ namespace command
static void add_commands_generic()
{
add("quit", game::Quit);
//add("quit_hard", utils::nt::raise_hard_exception); /* this command delivers you to a windows blue screen, its quit hard from windows xD */
add("crash", []()
{
*reinterpret_cast<int*>(1) = 0;
});
/*add("consoleList", [](const params& params)
{
const std::string input = params.get(1);
std::vector<std::string> matches;
game_console::find_matches(input, matches, false);
for (auto& match : matches)
{
auto* dvar = game::Dvar_FindVar(match.c_str());
if (!dvar)
{
console::info("[CMD]\t %s\n", match.c_str());
}
else
{
console::info("[DVAR]\t%s \"%s\"\n", match.c_str(), game::Dvar_ValueToString(dvar, dvar->current, 0));
}
}
console::info("Total %i matches\n", matches.size());
});*/
add("commandDump", [](const params& argument)
{
console::info("================================ COMMAND DUMP =====================================\n");
@ -402,7 +378,7 @@ namespace command
console::info("%s\n", asset_name);
}, true);
}
});
});*/
add("vstr", [](const params& params)
{
@ -412,12 +388,12 @@ namespace command
return;
}
const auto* dvarName = params.get(1);
const auto* dvar = game::Dvar_FindVar(dvarName);
const auto name = params.get(1);
const auto dvar = game::Dvar_FindVar(name);
if (dvar == nullptr)
{
console::info("%s doesn't exist\n", dvarName);
console::info("%s doesn't exist\n", name);
return;
}
@ -429,7 +405,7 @@ namespace command
}
execute(dvar->current.string);
});*/
});
}
static void add_commands_sp()
@ -444,8 +420,8 @@ namespace command
game::sp::g_entities[0].flags ^= 1;
game::CG_GameMessage(0, utils::string::va("godmode %s",
game::sp::g_entities[0].flags & 1
? "^2on"
: "^1off"));
? "^2on"
: "^1off"));
});
add("demigod", []()
@ -458,8 +434,8 @@ namespace command
game::sp::g_entities[0].flags ^= 2;
game::CG_GameMessage(0, utils::string::va("demigod mode %s",
game::sp::g_entities[0].flags & 2
? "^2on"
: "^1off"));
? "^2on"
: "^1off"));
});
add("notarget", []()
@ -472,8 +448,8 @@ namespace command
game::sp::g_entities[0].flags ^= 4;
game::CG_GameMessage(0, utils::string::va("notarget %s",
game::sp::g_entities[0].flags & 4
? "^2on"
: "^1off"));
? "^2on"
: "^1off"));
});
add("noclip", []()
@ -486,8 +462,8 @@ namespace command
game::sp::g_entities[0].client->flags ^= 1;
game::CG_GameMessage(0, utils::string::va("noclip %s",
game::sp::g_entities[0].client->flags & 1
? "^2on"
: "^1off"));
? "^2on"
: "^1off"));
});
add("ufo", []()
@ -500,16 +476,16 @@ namespace command
game::sp::g_entities[0].client->flags ^= 2;
game::CG_GameMessage(0, utils::string::va("ufo %s",
game::sp::g_entities[0].client->flags & 2
? "^2on"
: "^1off"));
? "^2on"
: "^1off"));
});
}
static void add_commands_mp()
{
//client_command_hook.create(0x1402E98F0, &client_command);
client_command_hook.create(0x140336000, &client_command);
/*add_sv("god", [](const int client_num, const params_sv&)
add_sv("god", [](const int client_num, const params_sv&)
{
if (!game::Dvar_FindVar("sv_cheats")->current.enabled)
{
@ -522,8 +498,8 @@ namespace command
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
utils::string::va("f \"godmode %s\"",
game::mp::g_entities[client_num].flags & 1
? "^2on"
: "^1off"));
? "^2on"
: "^1off"));
});
add_sv("demigod", [](const int client_num, const params_sv&)
@ -594,7 +570,7 @@ namespace command
: "^1off"));
});
add_sv("give", [](const int client_num, const params_sv& params)
/*add_sv("give", [](const int client_num, const params_sv& params)
{
if (!game::Dvar_FindVar("sv_cheats")->current.enabled)
{

View File

@ -0,0 +1,201 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "console.hpp"
#include "scheduler.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace dvar_cheats
{
void apply_sv_cheats(const game::dvar_t* dvar, const game::DvarSetSource source, game::dvar_value* value)
{
static const auto sv_cheats_hash = game::generateHashValue("sv_cheats");
if (dvar && dvar->hash == sv_cheats_hash)
{
// if dedi, do not allow internal to change value so servers can allow cheats if they want to
if (game::environment::is_dedi() && source == game::DvarSetSource::DVAR_SOURCE_INTERNAL)
{
value->enabled = dvar->current.enabled;
}
// if sv_cheats was enabled and it changes to disabled, we need to reset all cheat dvars
else if (dvar->current.enabled && !value->enabled)
{
for (auto i = 0; i < *game::dvarCount; ++i)
{
const auto var = &game::dvarPool[i];
if (var && (var->flags & game::DvarFlags::DVAR_FLAG_CHEAT))
{
game::Dvar_Reset(var, game::DvarSetSource::DVAR_SOURCE_INTERNAL);
}
}
}
}
}
bool dvar_flag_checks(const game::dvar_t* dvar, const game::DvarSetSource source)
{
const auto info = dvars::get_dvar_info_from_hash(dvar->hash);
const auto name = info.has_value()
? info.value().name.data()
: utils::string::va("0x%lX", dvar->hash);
if ((dvar->flags & game::DvarFlags::DVAR_FLAG_WRITE))
{
console::error("%s is write protected\n", name);
return false;
}
if ((dvar->flags & game::DvarFlags::DVAR_FLAG_READ))
{
console::error("%s is read only\n", name);
return false;
}
// only check cheat/replicated values when the source is external
if (source == game::DvarSetSource::DVAR_SOURCE_EXTERNAL)
{
const auto cl_ingame = game::Dvar_FindVar("cl_ingame");
const auto sv_running = game::Dvar_FindVar("sv_running");
if ((dvar->flags & game::DvarFlags::DVAR_FLAG_REPLICATED) && (cl_ingame && cl_ingame->current.enabled) && (
sv_running && !sv_running->current.enabled))
{
console::error("%s can only be changed by the server\n", name);
return false;
}
const auto sv_cheats = game::Dvar_FindVar("sv_cheats");
if ((dvar->flags & game::DvarFlags::DVAR_FLAG_CHEAT) && (sv_cheats && !sv_cheats->current.enabled))
{
console::error("%s is cheat protected\n", name);
return false;
}
}
// pass all the flag checks, allow dvar to be changed
return true;
}
const auto dvar_flag_checks_stub = utils::hook::assemble([](utils::hook::assembler& a)
{
const auto can_set_value = a.newLabel();
const auto zero_source = a.newLabel();
a.pushad64();
a.mov(r8, rdi);
a.mov(edx, esi);
a.mov(rcx, rbx);
a.call_aligned(apply_sv_cheats); //check if we are setting sv_cheats
a.popad64();
a.cmp(esi, 0);
a.jz(zero_source); //if the SetSource is 0 (INTERNAL) ignore flag checks
a.pushad64();
a.mov(edx, esi); //source
a.mov(rcx, rbx); //dvar
a.call_aligned(dvar_flag_checks); //protect read/write/cheat/replicated dvars
a.cmp(al, 1);
a.jz(can_set_value);
// if we get here, we are non-zero source and CANNOT set values
a.popad64(); // if I do this before the jz it won't work. for some reason the popad64 is affecting the ZR flag
a.jmp(0x1404FDCAB);
// if we get here, we are non-zero source and CAN set values
a.bind(can_set_value);
a.popad64(); // if I do this before the jz it won't work. for some reason the popad64 is affecting the ZR flag
a.cmp(esi, 1);
a.jmp(0x1404FDA22);
// if we get here, we are zero source and ignore flags
a.bind(zero_source);
a.jmp(0x1404FDA62);
});
void cg_set_client_dvar_from_server(const int local_client_num, void* cg, const char* dvar_id, const char* value)
{
const auto* dvar = game::Dvar_FindVar(dvar_id);
if (dvar)
{
// If we send as string, it can't be set with source SERVERCMD because the game only allows that source on real server cmd dvars.
// Just use external instead as if it was being set by the console
game::Dvar_SetFromStringByNameFromSource(dvar_id, value, game::DvarSetSource::DVAR_SOURCE_EXTERNAL);
}
else
{
// Not a dvar name, assume it is an id and the game will handle normally
game::CG_SetClientDvarFromServer(local_client_num, cg, dvar_id, value);
}
}
void set_client_dvar_by_string(const int entity_num, const char* value)
{
const auto* dvar = game::Scr_GetString(0); // grab the original dvar again since it's never stored on stack
const auto* command = utils::string::va("q %s \"%s\"", dvar, value);
game::SV_GameSendServerCommand(entity_num, game::SV_CMD_RELIABLE, command);
}
const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a)
{
const auto set_by_string = a.newLabel();
a.pushad64();
// check if we didn't find a network dvar index
a.mov(ecx, dword_ptr(rsp, 0x8C8));
a.cmp(ecx, 0);
a.je(set_by_string);
// we found an index, handle normally
a.popad64();
a.mov(r8d, ptr(rsp, 0x848));
a.lea(r9, ptr(rsp, 0x30));
a.jmp(0x1402E2E57);
// no index, let's send the dvar as a string
a.bind(set_by_string);
a.movzx(ecx, word_ptr(rsp, 0x8C0)); //entity_num
a.lea(rdx, ptr(rsp, 0xB0)); //value
a.call_aligned(set_client_dvar_by_string);
a.popad64();
a.jmp(0x1402E2E7D);
});
class component final : public component_interface
{
public:
void post_unpack() override
{
if (game::environment::is_sp())
{
return;
}
utils::hook::nop(0x1404FDA0D, 4); // let our stub handle zero-source sets
utils::hook::jump(0x1404FDA14, dvar_flag_checks_stub, true); // check extra dvar flags when setting values
// utils::hook::nop(0x14032AACC, 5); // remove error in PlayerCmd_SetClientDvar if setting a non-network dvar
// utils::hook::set<uint8_t>(0x14032AA9B, 0xEB);
// don't check flags on the dvars, send any existing dvar instead
// utils::hook::jump(0x14032AB14, player_cmd_set_client_dvar, true); // send non-network dvars as string
// utils::hook::call(0x1401BB782, cg_set_client_dvar_from_server);
// check for dvars being sent as string before parsing ids
scheduler::once([]()
{
dvars::register_bool("sv_cheats", false, game::DvarFlags::DVAR_FLAG_REPLICATED,
"Allow cheat commands and dvars on this server");
}, scheduler::pipeline::main);
}
};
}
REGISTER_COMPONENT(dvar_cheats::component)

View File

@ -302,7 +302,11 @@ namespace game_console
color_white, 0);
const auto offset_y = height + 3.f;
draw_hint_box(1, dvars::con_inputHintBoxColor->current.vector, 0, offset_y);
const auto line_count_ = dvar->type == game::dvar_type::enumeration
? dvar->domain.enumeration.stringCount + 1
: 1;
draw_hint_box(line_count_, dvars::con_inputHintBoxColor->current.vector, 0, offset_y);
draw_hint_text(0, dvars::dvar_get_domain(dvar->type, dvar->domain).data(),
dvars::con_inputCmdMatchColor->current.vector, 0, offset_y);
}

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@ namespace dvars
{
std::string name;
std::string description;
int hash;
};
extern game::dvar_t* aimassist_enabled;
@ -36,6 +37,7 @@ namespace dvars
std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain);
std::string dvar_get_domain(const game::dvar_type type, const game::dvar_limits& domain);
std::string dvar_get_description(const std::string& name);
std::optional<dvar_info> get_dvar_info_from_hash(const int hash);
game::dvar_t* register_int(const std::string& name, int value, int min, int max,
game::DvarFlags flags, const std::string& description);

View File

@ -940,8 +940,11 @@ namespace game
dvar_value latched;
dvar_value reset;
dvar_limits domain;
char __pad0[0xC];
};
static_assert(sizeof(dvar_t) == 96);
enum connstate_t
{
CA_DISCONNECTED = 0x0,
@ -1350,10 +1353,10 @@ namespace game
struct gclient_s
{
char __pad0[20708];
char name[32]; // 20708
char __pad1[668];
int flags; // 21408
char __pad0[18720];
char name[32]; // 18720
char __pad1[752];
int flags; // 19504
}; // size = ?
struct EntityState

View File

@ -40,6 +40,8 @@ namespace game
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x1401389A0, 0x140220CC0};
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessageBold{0x140138750, 0x140220620};
WEAK symbol<void(int localClientNum, /*mp::cg_s**/void* cg,
const char* dvar, const char* value)> CG_SetClientDvarFromServer{0, 0x140236120};
WEAK symbol<bool()> CL_IsCgameInitialized{0x14017EE30, 0x140245650};
@ -47,6 +49,10 @@ namespace game
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x1403C5D50, 0x1404FBB00};
WEAK symbol<void(char* buffer, int index)> Dvar_GetCombinedString{0x140354DF0, 0x14041D830};
WEAK symbol<const char*(dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x1403C8560, 0x1404FE660};
WEAK symbol<void(dvar_t* dvar, DvarSetSource source)> Dvar_Reset{0, 0x1404FCC40};
WEAK symbol<void(const char*, const char*,
DvarSetSource)> Dvar_SetFromStringByNameFromSource{0, 0x1404FD490};
WEAK symbol<dvar_t*(int hash, const char* name, bool value,
unsigned int flags)> Dvar_RegisterBool{0x1403C47E0, 0x1404FA540};
WEAK symbol<dvar_t*(int hash, const char* name, int value, int min, int max,
@ -202,7 +208,7 @@ namespace game
WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{0x14F05CE50, 0x14FD21180};
WEAK symbol<int> dvarCount{0x14C217D10, 0x14D064CF4};
WEAK symbol<dvar_t*> dvarPool{0x14C217D20, 0x14D064D00};
WEAK symbol<dvar_t> dvarPool{0x14C217D20, 0x14D064D00};
WEAK symbol<void*> DB_XAssetPool{0x140DE8C80, 0x140FEB5D0};