#include #include "../loader/component_loader.hpp" #include #include #include "command.hpp" constexpr auto CMD_MAX_NESTING = 8; namespace command { std::unordered_map> handlers; namespace { void cmd_vstr_f() { const params_sv 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 == nullptr) { 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 execute(dvar->current.string); } else { game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, "%s is not a string-based dvar\n", dvar->name); } } } // namespace void main_handler() { params_sv params = {}; const auto command = utils::string::to_lower(params[0]); if (const auto got = handlers.find(command); got != handlers.end()) { got->second(params); } } params_sv::params_sv() : nesting_(game::sv_cmd_args->nesting) { assert(game::sv_cmd_args->nesting < CMD_MAX_NESTING); } int params_sv::size() const { return game::sv_cmd_args->argc[this->nesting_]; } const char* params_sv::get(const int index) const { if (index >= this->size()) { return ""; } return game::sv_cmd_args->argv[this->nesting_][index]; } std::string params_sv::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; } void add_raw(const char* name, void (*callback)()) { game::Cmd_AddCommandInternal( name, callback, utils::memory::get_allocator()->allocate()); } void add(const char* name, const std::function& callback) { const auto command = utils::string::to_lower(name); if (!handlers.contains(command)) { add_raw(name, main_handler); } handlers[command] = callback; } void execute(std::string command, const bool sync) { command += "\n"; if (sync) { game::Cmd_ExecuteSingleCommand(game::LOCAL_CLIENT_0, 0, command.data()); } else { game::Cbuf_AddText(game::LOCAL_CLIENT_0, command.data()); } } class component final : public component_interface { public: void post_unpack() override { add_commands_generic(); } private: static void add_commands_generic() { add("properQuit", [](const params_sv&) { utils::nt::raise_hard_exception(); }); add("echo", [](const params_sv& params) { for (auto i = 1; i < params.size(); i++) { game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, "%s ", params.get(i)); } game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, "\n"); }); // Override vstr this way auto* cmd = game::Cmd_FindCommand("vstr"); if (cmd != nullptr) { cmd->function = cmd_vstr_f; } } }; } // namespace command REGISTER_COMPONENT(command::component)