diff --git a/src/component/command.cpp b/src/component/command.cpp new file mode 100644 index 0000000..30b5bde --- /dev/null +++ b/src/component/command.cpp @@ -0,0 +1,156 @@ +#include "stdinc.hpp" +#include "loader/component_loader.hpp" + +#include "game/game.hpp" + +#include "command.hpp" +#include "gsc.hpp" +#include "scripting.hpp" + +#include +#include + +namespace command +{ + std::unordered_map> handlers; + + std::vector script_commands; + utils::memory::allocator allocator; + + game::CmdArgs* get_cmd_args() + { + return reinterpret_cast(game::Sys_GetValue(4)); + } + + void main_handler() + { + params params = {}; + + const auto command = utils::string::to_lower(params[0]); + + if (handlers.find(command) != handlers.end()) + { + handlers[command](params); + } + } + + params::params() + : nesting_(get_cmd_args()->nesting) + { + } + + int params::size() const + { + const auto cmd_args = get_cmd_args(); + return cmd_args->argc[cmd_args->nesting]; + } + + const char* params::get(int index) const + { + if (index >= this->size()) + { + return ""; + } + + const auto cmd_args = get_cmd_args(); + return cmd_args->argv[this->nesting_][index]; + } + + std::string params::join(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, std::function callback) + { + const auto command = utils::string::to_lower(name); + + if (handlers.find(command) == handlers.end()) + { + add_raw(name, main_handler); + } + + handlers[command] = callback; + } + + void add_script_command(const std::string& name, const std::function& callback) + { + script_commands.push_back(name); + const auto name_ = allocator.duplicate_string(name); + add(name_, callback); + } + + void clear_script_commands() + { + for (const auto& name : script_commands) + { + handlers.erase(name); + game::Cmd_RemoveCommand(name.data()); + } + + allocator.clear(); + script_commands.clear(); + } + + void execute(std::string command, const bool sync) + { + command += "\n"; + + if (sync) + { + game::Cmd_ExecuteSingleCommand(0, 0, command.data()); + } + else + { + game::Cbuf_AddText(0, command.data()); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + scripting::on_shutdown(clear_script_commands); + + gsc::function::add("executecommand", [](const std::string& command) + { + execute(command, false); + }); + + gsc::function::add("addcommand", [](const std::string& name, const scripting::function& function) + { + command::add_script_command(name, [function](const command::params& params) + { + scripting::array array; + + for (auto i = 0; i < params.size(); i++) + { + array.push(params[i]); + } + + function({array}); + }); + }); + } + }; +} + +REGISTER_COMPONENT(command::component) diff --git a/src/component/command.hpp b/src/component/command.hpp new file mode 100644 index 0000000..62e8323 --- /dev/null +++ b/src/component/command.hpp @@ -0,0 +1,30 @@ +#pragma once + +namespace command +{ + class params + { + public: + params(); + + int size() const; + const char* get(int index) const; + std::string join(int index) const; + + const char* operator[](const int index) const + { + return this->get(index); + } + + private: + int nesting_; + }; + + void add_raw(const char* name, void (*callback)()); + void add(const char* name, std::function callback); + + void add_script_command(const std::string& name, const std::function& callback); + void clear_script_commands(); + + void execute(std::string command, const bool sync = false); +} \ No newline at end of file diff --git a/src/dllmain.cpp b/src/dllmain.cpp index f628648..4c4c801 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -9,6 +9,7 @@ BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call, LPVOID /*re { if (ul_reason_for_call == DLL_PROCESS_ATTACH) { + utils::hook::jump(reinterpret_cast(&printf), game::Com_Printf); component_loader::post_unpack(); } diff --git a/src/game/scripting/script_value.cpp b/src/game/scripting/script_value.cpp index 18a8138..39e7d70 100644 --- a/src/game/scripting/script_value.cpp +++ b/src/game/scripting/script_value.cpp @@ -385,8 +385,8 @@ namespace scripting } function_argument::function_argument(const arguments& args, const script_value& value, const int index, const bool exists) - : values_(args) - , value_(value) + : script_value(value) + , values_(args) , index_(index) , exists_(exists) { diff --git a/src/game/scripting/script_value.hpp b/src/game/scripting/script_value.hpp index de7ed1a..b9bfc7b 100644 --- a/src/game/scripting/script_value.hpp +++ b/src/game/scripting/script_value.hpp @@ -232,7 +232,7 @@ public: \ class function_argument; using variadic_args = std::vector; - class function_argument + class function_argument : public script_value { public: function_argument(const arguments& args, const script_value& value, const int index, const bool exists); @@ -247,7 +247,7 @@ public: \ try { - return this->value_.as(); + return script_value::as(); } catch (const std::exception& e) { @@ -267,21 +267,6 @@ public: \ return args; } - std::string to_string() const - { - return this->value_.to_string(); - } - - std::string type_name() const - { - return this->value_.type_name(); - } - - script_value get_raw() const - { - return this->value_; - } - operator variadic_args() const { variadic_args args{}; @@ -296,9 +281,9 @@ public: \ operator C>() const { const auto container_type = get_c_typename>>(); - if (!this->value_.is()) + if (!script_value::as()) { - const auto type = get_typename(this->value_.get_raw()); + const auto type = get_typename(this->get_raw()); throw std::runtime_error(utils::string::va("has type '%s' but should be '%s'", type.data(), @@ -307,7 +292,7 @@ public: \ } C> container{}; - const auto array = this->value_.as(); + const auto array = script_value::as(); for (auto i = 0; i < array.size(); i++) { try @@ -332,7 +317,6 @@ public: \ private: arguments values_{}; - script_value value_{}; int index_{}; bool exists_{}; }; diff --git a/src/game/symbols.hpp b/src/game/symbols.hpp index 4a6fa66..638e4b9 100644 --- a/src/game/symbols.hpp +++ b/src/game/symbols.hpp @@ -9,14 +9,16 @@ namespace game WEAK symbol BG_StringHashValue{0x0, 0x0}; WEAK symbol Cbuf_InsertText{0x0, 0x0}; - WEAK symbol Cbuf_AddText{0x0, 0x0}; - WEAK symbol Cmd_ExecuteSingleCommand{0x0, 0x0}; - WEAK symbol Cmd_AddCommandInternal{0x0, 0x0}; + WEAK symbol Cbuf_AddText{0x49B930, 0x56EF70}; + WEAK symbol Cmd_ExecuteSingleCommand{0x619D00, 0x50B470}; + WEAK symbol Cmd_AddCommandInternal{0x661400, 0x6AD580}; WEAK symbol Cmd_Argv{0x0, 0x0}; - WEAK symbol Cmd_RemoveCommand{0x0, 0x0}; + WEAK symbol Cmd_RemoveCommand{0x5F1A90, 0x527EA0}; WEAK symbol ClientUserInfoChanged{0x0, 0x0}; + WEAK symbol Com_Printf{0x566BC0, 0x64C260}; + WEAK symbol Dvar_FindVar{0x0, 0x0}; WEAK symbol Dvar_GetInt{0x0, 0x0}; WEAK symbol Scr_ClearOutParams{0x654D10, 0x588680}; WEAK symbol AllocObject{0x0, 0x0}; - WEAK symbol AllocThread{0x0, 0x0}; - WEAK symbol RemoveRefToObject{0x0, 0x0}; + WEAK symbol AllocThread{0x69E140, 0x43CA60}; + WEAK symbol RemoveRefToObject{0x5517B0, 0x698FA0}; WEAK symbol RemoveRefToVector{0x0, 0x0}; WEAK symbol AddRefToValue_{0x53FD50, 0x6706B0}; @@ -95,13 +97,13 @@ namespace game WEAK symbol GetPlayerEntity{0x0, 0x0}; - WEAK symbol VM_Execute{0x0, 0x0}; + WEAK symbol VM_Execute{0x8ACE60, 0x8EADE0}; WEAK symbol SV_GameDropClient{0x0, 0x0}; WEAK symbol SV_IsTestClient{0x0, 0x0}; WEAK symbol SV_GameSendServerCommand{0x0, 0x0}; - WEAK symbol Sys_GetValue{0x0, 0x0}; + WEAK symbol Sys_GetValue{0x67D4F0, 0x529EB0}; WEAK symbol Sys_Milliseconds{0x0, 0x0}; WEAK symbol longjmp{0x96B980, 0x9D05C4}; @@ -123,7 +125,7 @@ namespace game WEAK symbol g_classMap{0x0, 0x0}; WEAK symbol g_entities{0x0, 0x0}; - WEAK symbol levelEntityId{0x0, 0x0}; + WEAK symbol levelEntityId{0x32C86A0, 0x3DCB2A0}; WEAK symbol svs_clients{0x0, 0x0}; }