diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index 262dd5a..c905933 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -8,10 +8,15 @@ constexpr auto CMD_MAX_NESTING = 8; namespace command { -std::unordered_map> handlers; +std::unordered_map handlers; + +game::CmdArgs* get_cmd_args() { + return static_cast( + game::Sys_GetValue(game::THREAD_VALUE_CMD)); +} void main_handler() { - params_sv params = {}; + params_sv params; const auto command = utils::string::to_lower(params[0]); @@ -20,6 +25,31 @@ void main_handler() { } } +params::params() : nesting_(get_cmd_args()->nesting) { + assert(this->nesting_ < game::CMD_MAX_NESTING); +} + +int params::size() const { return get_cmd_args()->argc[this->nesting_]; } + +const char* params::get(const int index) const { + if (index >= this->size()) { + return ""; + } + + return get_cmd_args()->argv[this->nesting_][index]; +} + +std::string params::join(const int index) const { + std::string result; + + for (auto i = index; i < this->size(); i++) { + if (i > index) + result.append(" "); + result.append(this->get(i)); + } + return result; +} + params_sv::params_sv() : nesting_(game::sv_cmd_args->nesting) { assert(game::sv_cmd_args->nesting < CMD_MAX_NESTING); } @@ -54,8 +84,7 @@ void add_raw(const char* name, void (*callback)()) { utils::memory::get_allocator()->allocate()); } -void add_sv(const char* name, - const std::function& callback) { +void add_sv(const char* name, const sv_command_param_function& callback) { const auto command = utils::string::to_lower(name); if (!handlers.contains(command)) { diff --git a/src/client/component/command.hpp b/src/client/component/command.hpp index 6d96d79..906533a 100644 --- a/src/client/component/command.hpp +++ b/src/client/component/command.hpp @@ -2,6 +2,20 @@ namespace command { +class params { +public: + params(); + + [[nodiscard]] int size() const; + [[nodiscard]] const char* get(int index) const; + [[nodiscard]] std::string join(int index) const; + + const char* operator[](const int index) const { return this->get(index); } + +private: + int nesting_; +}; + class params_sv { public: params_sv(); @@ -16,9 +30,11 @@ private: int nesting_; }; +using command_param_function = std::function; +using sv_command_param_function = std::function; + void add_raw(const char* name, void (*callback)()); -void add_sv(const char* name, - const std::function& callback); +void add_sv(const char* name, const sv_command_param_function& callback); void execute(std::string command, bool sync = false); } // namespace command diff --git a/src/client/component/command_additions.cpp b/src/client/component/command_additions.cpp new file mode 100644 index 0000000..fcf437b --- /dev/null +++ b/src/client/component/command_additions.cpp @@ -0,0 +1,50 @@ +#include +#include "loader/component_loader.hpp" + +#include + +#include "command.hpp" + +namespace command_additions { +namespace { +void cmd_vstr_f() { + const command::params params; + + if (params.size() < 2) { + game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, + "vstr : execute a variable command\n"); + return; + } + + const auto* dvar_name = params.get(1); + const auto* dvar = game::Dvar_FindVar(dvar_name); + + if (!dvar) { + game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, "%s doesn't exist\n", + dvar_name); + return; + } + + if (dvar->type == game::DVAR_TYPE_STRING || + dvar->type == game::DVAR_TYPE_ENUM) { + // Adds \n automatically + command::execute(dvar->current.string); + } else { + game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, + "%s is not a string-based dvar\n", dvar->name); + } +} +} // namespace + +class component final : public component_interface { +public: + void post_unpack() override { + // Because this plugin is loaded late we must override the structure + auto* cmd_vstr_f_var = reinterpret_cast( + game::select(0x355E69C, 0x243FB1C)); + cmd_vstr_f_var->function = cmd_vstr_f; + } +}; +} // namespace command_additions + +REGISTER_COMPONENT(command_additions::component) diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 746fe85..4ba1d20 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -158,6 +158,15 @@ struct CmdArgs { static_assert(sizeof(CmdArgs) == 10476); +enum { + THREAD_VALUE_PROF_STACK = 0x0, + THREAD_VALUE_VA = 0x1, + THREAD_VALUE_COM_ERROR = 0x2, + THREAD_VALUE_TRACE = 0x3, + THREAD_VALUE_CMD = 0x4, + THREAD_VALUE_COUNT = 0x5, +}; + typedef enum { NA_BOT = 0x0, NA_BAD = 0x1, diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 73da759..501881f 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -18,6 +18,8 @@ WEAK symbol Cbuf_InsertText{0x695E10, WEAK symbol Cmd_ExecuteSingleCommand{0x50B470, 0x829AD0}; +WEAK symbol Sys_GetValue{0x529EB0, 0x67D4F0}; + WEAK symbol SV_SendServerCommand{0x588B10, 0x6106E0}; WEAK symbol SV_GameSendServerCommand{