Add dvar_cheats component + some fixes
This commit is contained in:
parent
bc68d8d404
commit
f56c477a95
@ -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)
|
||||
{
|
||||
|
201
src/client/component/dvar_cheats.cpp
Normal file
201
src/client/component/dvar_cheats.cpp
Normal 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)
|
@ -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
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user