diff --git a/src/component/command.cpp b/src/component/command.cpp new file mode 100644 index 0000000..007005c --- /dev/null +++ b/src/component/command.cpp @@ -0,0 +1,104 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/scripting/entity.hpp" +#include "game/scripting/execution.hpp" +#include "command.hpp" + +namespace command +{ + std::unordered_map> handlers; + + 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_(game::cmd_args->nesting) + { + } + + int params::size() const + { + return game::cmd_args->argc[this->nesting_]; + } + + const char* params::get(const int index) const + { + if (index >= this->size()) + { + return ""; + } + + return game::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; + } + + 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.find(command) == handlers.end()) + { + add_raw(name, main_handler); + } + + handlers[command] = callback; + } + + std::vector script_commands; + utils::memory::allocator allocator; + + 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(); + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + + } + }; +} + +REGISTER_COMPONENT(command::component) diff --git a/src/component/command.hpp b/src/component/command.hpp new file mode 100644 index 0000000..54f08c4 --- /dev/null +++ b/src/component/command.hpp @@ -0,0 +1,28 @@ +#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, const std::function& callback); + + void add_script_command(const std::string& name, const std::function& callback); + void clear_script_commands(); +} \ No newline at end of file diff --git a/src/component/gsc.cpp b/src/component/gsc.cpp index 5f77bd1..4837813 100644 --- a/src/component/gsc.cpp +++ b/src/component/gsc.cpp @@ -2,6 +2,7 @@ #include "loader/component_loader.hpp" #include "scheduler.hpp" #include "scripting.hpp" +#include "command.hpp" #include "game/scripting/event.hpp" #include "game/scripting/execution.hpp" @@ -250,6 +251,33 @@ namespace gsc } } + unsigned int make_array() + { + unsigned int index = 0; + const auto variable = game::AllocVariable(&index); + variable->w.type = game::SCRIPT_ARRAY; + variable->u.f.prev = 0; + variable->u.f.next = 0; + + return index; + } + + void add_array_key_value(unsigned int parent_id, const std::string& _key, const scripting::script_value& value) + { + const auto key = game::SL_GetString(_key.data(), 0); + + scripting::push_value(scripting::entity(parent_id)); + scripting::push_value(value); + game::Scr_AddArrayStringIndexed(key); + } + + void add_array_value(unsigned int parent_id, const scripting::script_value& value) + { + scripting::push_value(scripting::entity(parent_id)); + scripting::push_value(value); + game::Scr_AddArray(); + } + class component final : public component_interface { public: @@ -276,6 +304,32 @@ namespace gsc return {}; }); + function::add("addcommand", [](function_args args) -> scripting::script_value + { + const auto name = args[0].as(); + const auto function = args[1].get_raw(); + + if (function.type != game::SCRIPT_FUNCTION) + { + throw std::runtime_error("Invalid type"); + } + + const auto pos = function.u.codePosValue; + command::add_script_command(name, [pos](const command::params& params) + { + const auto array = make_array(); + for (auto i = 0; i < params.size(); i++) + { + add_array_value(array, params[i]); + } + + const auto entity = scripting::entity(array); + scripting::exec_ent_thread(*game::levelEntityId, pos, {entity}); + }); + + return {}; + }); + utils::hook::jump(0x56C8EB, call_builtin_stub); utils::hook::jump(0x56CBDC, call_builtin_method_stub); utils::hook::jump(0x56B726, vm_execute_stub); diff --git a/src/component/gsc.hpp b/src/component/gsc.hpp index d44a32e..4168a34 100644 --- a/src/component/gsc.hpp +++ b/src/component/gsc.hpp @@ -19,4 +19,8 @@ namespace gsc { void add(const std::string& name, const script_method& func); } + + unsigned int make_array(); + void add_array_key_value(unsigned int parent_id, const std::string& _key, const scripting::script_value& value); + void add_array_value(unsigned int parent_id, const scripting::script_value& value); } \ No newline at end of file diff --git a/src/component/json.cpp b/src/component/json.cpp index 938953f..34d526f 100644 --- a/src/component/json.cpp +++ b/src/component/json.cpp @@ -15,33 +15,6 @@ namespace json { namespace { - unsigned int make_array() - { - unsigned int index = 0; - const auto variable = game::AllocVariable(&index); - variable->w.type = game::SCRIPT_ARRAY; - variable->u.f.prev = 0; - variable->u.f.next = 0; - - return index; - } - - void add_array_key_value(unsigned int parent_id, const std::string& _key, const scripting::script_value& value) - { - const auto key = game::SL_GetString(_key.data(), 0); - - scripting::push_value(scripting::entity(parent_id)); - scripting::push_value(value); - game::Scr_AddArrayStringIndexed(key); - } - - void add_array_value(unsigned int parent_id, const scripting::script_value& value) - { - scripting::push_value(scripting::entity(parent_id)); - scripting::push_value(value); - game::Scr_AddArray(); - } - nlohmann::json gsc_to_json(scripting::script_value value); nlohmann::json entity_to_array(unsigned int id) @@ -154,22 +127,22 @@ namespace json return obj.get(); case (nlohmann::detail::value_t::array): { - const auto arr = make_array(); + const auto arr = gsc::make_array(); for (const auto& [key, value] : obj.items()) { - add_array_value(arr, json_to_gsc(value)); + gsc::add_array_value(arr, json_to_gsc(value)); } return scripting::entity(arr); } case (nlohmann::detail::value_t::object): { - const auto arr = make_array(); + const auto arr = gsc::make_array(); for (const auto& [key, value] : obj.items()) { - add_array_key_value(arr, key, json_to_gsc(value)); + gsc::add_array_key_value(arr, key, json_to_gsc(value)); } return scripting::entity(arr); diff --git a/src/component/scripting.cpp b/src/component/scripting.cpp index 27c33ac..7df58ff 100644 --- a/src/component/scripting.cpp +++ b/src/component/scripting.cpp @@ -2,6 +2,7 @@ #include "loader/component_loader.hpp" #include "scheduler.hpp" +#include "command.hpp" #include "game/scripting/event.hpp" #include "game/scripting/execution.hpp" @@ -69,8 +70,8 @@ namespace scripting void g_shutdown_game_stub(const int free_scripts) { + command::clear_script_commands(); replaced_functions.clear(); - g_shutdown_game_hook.invoke(free_scripts); } @@ -133,9 +134,6 @@ namespace scripting g_shutdown_game_hook.create(0x50C100, g_shutdown_game_stub); scr_add_class_field_hook.create(0x567CD0, scr_add_class_field_stub); - //vm_notify_hook.create(0x569720, vm_notify_stub); - - //scr_emit_function_hook.create(0x561400, scr_emit_function_stub); } }; } diff --git a/src/game/scripting/execution.cpp b/src/game/scripting/execution.cpp index 9fb7825..ee1441c 100644 --- a/src/game/scripting/execution.cpp +++ b/src/game/scripting/execution.cpp @@ -140,6 +140,7 @@ namespace scripting { const auto id = entity.get_entity_id(); + stack_isolation _; for (auto i = arguments.rbegin(); i != arguments.rend(); ++i) { push_value(*i); @@ -149,16 +150,9 @@ namespace scripting const auto local_id = game::AllocThread(id); const auto result = game::VM_Execute(local_id, pos, arguments.size()); + game::RemoveRefToObject(result); - const auto value = get_return_value(); - - game::RemoveRefToValue(game::scr_VmPub->top->type, game::scr_VmPub->top->u); - game::scr_VmPub->top->type = (game::scriptType_e)0; - - --game::scr_VmPub->top; - --game::scr_VmPub->inparamcount; - - return value; + return get_return_value(); } script_value call_script_function(const entity& entity, const std::string& filename, diff --git a/src/game/structs.hpp b/src/game/structs.hpp index 03c079b..5a40ecc 100644 --- a/src/game/structs.hpp +++ b/src/game/structs.hpp @@ -17,6 +17,15 @@ namespace game int flags; }; + struct CmdArgs + { + int nesting; + int localClientNum[8]; + int controllerIndex[8]; + int argc[8]; + const char** argv[8]; + }; + struct msg_t { int overflowed; diff --git a/src/game/symbols.hpp b/src/game/symbols.hpp index 693efc4..6d83da1 100644 --- a/src/game/symbols.hpp +++ b/src/game/symbols.hpp @@ -18,8 +18,9 @@ namespace game WEAK symbol ConcatArgs{0x502150}; WEAK symbol Cbuf_AddText{0x545680}; - WEAK symbol Cmd_AddCommandInternal{0x0}; - WEAK symbol Cmd_Argv{0x0}; + WEAK symbol Cmd_AddCommandInternal{0x545DF0}; + WEAK symbol Cmd_RemoveCommand{0x545E20}; + WEAK symbol Cmd_Argv{0x467600}; WEAK symbol Dvar_FindVar{0x5BDCC0}; @@ -60,6 +61,8 @@ namespace game // Variables + WEAK symbol cmd_args{0x1C978D0}; + WEAK symbol g_script_error_level{0x20B21FC}; WEAK symbol g_script_error{0x20B4218}; diff --git a/src/utils/memory.cpp b/src/utils/memory.cpp index 0022989..144754b 100644 --- a/src/utils/memory.cpp +++ b/src/utils/memory.cpp @@ -54,14 +54,14 @@ namespace utils return this->pool_.empty(); } - /*char* memory::allocator::duplicate_string(const std::string& string) + char* memory::allocator::duplicate_string(const std::string& string) { std::lock_guard _(this->mutex_); const auto data = memory::duplicate_string(string); this->pool_.push_back(data); return data; - }*/ + } void* memory::allocate(const size_t length) { @@ -70,12 +70,12 @@ namespace utils return data; } - /*char* memory::duplicate_string(const std::string& string) + char* memory::duplicate_string(const std::string& string) { const auto new_string = allocate_array(string.size() + 1); std::memcpy(new_string, string.data(), string.size()); return new_string; - }*/ + } void memory::free(void* data) { diff --git a/src/utils/memory.hpp b/src/utils/memory.hpp index bcc9d96..3ead34b 100644 --- a/src/utils/memory.hpp +++ b/src/utils/memory.hpp @@ -34,7 +34,7 @@ namespace utils bool empty() const; - //char* duplicate_string(const std::string& string); + char* duplicate_string(const std::string& string); private: std::mutex mutex_; @@ -55,7 +55,7 @@ namespace utils return static_cast(allocate(count * sizeof(T))); } - //static char* duplicate_string(const std::string& string); + static char* duplicate_string(const std::string& string); static void free(void* data); static void free(const void* data);