From ea73dd9b30e369b6e02aec161b684acccd89f375 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 15:53:46 -0500 Subject: [PATCH 1/7] Bump deps/zlib from `19f8551` to `04f42ce` (#326) Bumps [deps/zlib](https://github.com/madler/zlib) from `19f8551` to `04f42ce`. - [Release notes](https://github.com/madler/zlib/releases) - [Commits](https://github.com/madler/zlib/compare/19f8551627d2a89b910d961fc9c7f626f3af7b21...04f42ceca40f73e2978b50e93806c2a18c1281fc) --- updated-dependencies: - dependency-name: deps/zlib dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- deps/zlib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/zlib b/deps/zlib index 19f85516..04f42cec 160000 --- a/deps/zlib +++ b/deps/zlib @@ -1 +1 @@ -Subproject commit 19f8551627d2a89b910d961fc9c7f626f3af7b21 +Subproject commit 04f42ceca40f73e2978b50e93806c2a18c1281fc From 143ec109410c29944667004230952e0b374f3ef1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 15:53:52 -0500 Subject: [PATCH 2/7] Bump deps/curl from `b82eb72` to `68fa9bf` (#325) Bumps [deps/curl](https://github.com/curl/curl) from `b82eb72` to `68fa9bf`. - [Release notes](https://github.com/curl/curl/releases) - [Commits](https://github.com/curl/curl/compare/b82eb72d8097c93313329a4aecd41317a293f506...68fa9bf3f5d7b4fcbb57619f70cb4aabb79a51f6) --- updated-dependencies: - dependency-name: deps/curl dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- deps/curl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/curl b/deps/curl index b82eb72d..68fa9bf3 160000 --- a/deps/curl +++ b/deps/curl @@ -1 +1 @@ -Subproject commit b82eb72d8097c93313329a4aecd41317a293f506 +Subproject commit 68fa9bf3f5d7b4fcbb57619f70cb4aabb79a51f6 From f20bec9282acbe3d778479311fd0369955c61298 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 15:53:57 -0500 Subject: [PATCH 3/7] Bump deps/libtomcrypt from `e10c62e` to `e8e678e` (#324) Bumps [deps/libtomcrypt](https://github.com/libtom/libtomcrypt) from `e10c62e` to `e8e678e`. - [Release notes](https://github.com/libtom/libtomcrypt/releases) - [Commits](https://github.com/libtom/libtomcrypt/compare/e10c62ec18a8b7689adcc3076e40c5a9ef1de3b6...e8e678e1018615536f5b9cd07654d36449a28ac2) --- updated-dependencies: - dependency-name: deps/libtomcrypt dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- deps/libtomcrypt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libtomcrypt b/deps/libtomcrypt index e10c62ec..e8e678e1 160000 --- a/deps/libtomcrypt +++ b/deps/libtomcrypt @@ -1 +1 @@ -Subproject commit e10c62ec18a8b7689adcc3076e40c5a9ef1de3b6 +Subproject commit e8e678e1018615536f5b9cd07654d36449a28ac2 From e0319102665540dba5ec8a60b2b68e52ead22fc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 15:54:03 -0500 Subject: [PATCH 4/7] Bump deps/GSL from `849f7ce` to `991fa66` (#322) Bumps [deps/GSL](https://github.com/Microsoft/GSL) from `849f7ce` to `991fa66`. - [Release notes](https://github.com/Microsoft/GSL/releases) - [Commits](https://github.com/Microsoft/GSL/compare/849f7ceaf7876286aa663b4f0157b4542090fe90...991fa6682e819590c695f00c6b880548e55fa914) --- updated-dependencies: - dependency-name: deps/GSL dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- deps/GSL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/GSL b/deps/GSL index 849f7cea..991fa668 160000 --- a/deps/GSL +++ b/deps/GSL @@ -1 +1 @@ -Subproject commit 849f7ceaf7876286aa663b4f0157b4542090fe90 +Subproject commit 991fa6682e819590c695f00c6b880548e55fa914 From 1b3a38bf38b9e1a49fb2b1d83f96a523772744ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 15:54:08 -0500 Subject: [PATCH 5/7] Bump deps/protobuf from `7c896ce` to `57786d1` (#317) Bumps [deps/protobuf](https://github.com/protocolbuffers/protobuf) from `7c896ce` to `57786d1`. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Commits](https://github.com/protocolbuffers/protobuf/compare/7c896ce8194619f2f9625bd115086f97b1cf7129...57786d126249b5ed4f42b579047941805e742949) --- updated-dependencies: - dependency-name: deps/protobuf dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- deps/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/protobuf b/deps/protobuf index 7c896ce8..57786d12 160000 --- a/deps/protobuf +++ b/deps/protobuf @@ -1 +1 @@ -Subproject commit 7c896ce8194619f2f9625bd115086f97b1cf7129 +Subproject commit 57786d126249b5ed4f42b579047941805e742949 From 503762954fafe61a50c94008ae57268c9ec7f60e Mon Sep 17 00:00:00 2001 From: FutureRave Date: Thu, 13 Oct 2022 22:38:06 +0100 Subject: [PATCH 6/7] fix logger --- src/client/component/logger.cpp | 78 ++++++++++++++------------------- 1 file changed, 33 insertions(+), 45 deletions(-) diff --git a/src/client/component/logger.cpp b/src/client/component/logger.cpp index 776313e1..9f8d7ee7 100644 --- a/src/client/component/logger.cpp +++ b/src/client/component/logger.cpp @@ -1,12 +1,11 @@ #include #include "loader/component_loader.hpp" +#include "game/game.hpp" +#include "game/dvars.hpp" #include "party.hpp" #include "console.hpp" -#include "game/game.hpp" -#include "game/dvars.hpp" - #include namespace logger @@ -15,48 +14,42 @@ namespace logger { utils::hook::detour com_error_hook; + const game::dvar_t* logger_dev = nullptr; + void print_error(const char* msg, ...) { - char buffer[2048]; - + char buffer[2048]{}; va_list ap; + va_start(ap, msg); - - vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap); - + vsnprintf_s(buffer, _TRUNCATE, msg, ap); va_end(ap); - console::error(buffer); + console::error("%s", buffer); } void print_com_error(int, const char* msg, ...) { - char buffer[2048]; - + char buffer[2048]{}; va_list ap; + va_start(ap, msg); - - vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap); - + vsnprintf_s(buffer, _TRUNCATE, msg, ap); va_end(ap); - console::error(buffer); + console::error("%s", buffer); } void com_error_stub(const int error, const char* msg, ...) { - char buffer[2048]; + char buffer[2048]{}; + va_list ap; - { - va_list ap; - va_start(ap, msg); + va_start(ap, msg); + vsnprintf_s(buffer, _TRUNCATE, msg, ap); + va_end(ap); - vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap); - - va_end(ap); - - console::error("Error: %s\n", buffer); - } + console::error("Error: %s\n", buffer); party::clear_sv_motd(); // clear sv_motd on error if it exists @@ -65,50 +58,43 @@ namespace logger void print_warning(const char* msg, ...) { - char buffer[2048]; - + char buffer[2048]{}; va_list ap; + va_start(ap, msg); - - vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap); - + vsnprintf_s(buffer, _TRUNCATE, msg, ap); va_end(ap); - console::warn(buffer); + console::warn("%s", buffer); } void print(const char* msg, ...) { - char buffer[2048]; - + char buffer[2048]{}; va_list ap; + va_start(ap, msg); - - vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap); - + vsnprintf_s(buffer, _TRUNCATE, msg, ap); va_end(ap); - console::info(buffer); + console::info("%s", buffer); } void print_dev(const char* msg, ...) { - static auto* enabled = dvars::register_bool("logger_dev", false, game::DVAR_FLAG_SAVED, "Print dev stuff"); - if (!enabled->current.enabled) + if (!logger_dev->current.enabled) { return; } - char buffer[2048]; - + char buffer[2048]{}; va_list ap; + va_start(ap, msg); - - vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap); - + vsnprintf_s(buffer, _TRUNCATE, msg, ap); va_end(ap); - console::info(buffer); + console::info("%s", buffer); } } @@ -126,6 +112,8 @@ namespace logger } com_error_hook.create(game::Com_Error, com_error_stub); + + logger_dev = dvars::register_bool("logger_dev", false, game::DVAR_FLAG_SAVED, "Print dev stuff"); } }; } From 2d4eefd597653e2b4b148c1b5cb2eabc63b53584 Mon Sep 17 00:00:00 2001 From: m Date: Fri, 14 Oct 2022 11:39:27 -0500 Subject: [PATCH 7/7] better GSC api (#310) --- src/client/component/command.cpp | 6 +- src/client/component/dedicated.cpp | 4 +- src/client/component/gsc.cpp | 220 ++++++++++++++------- src/client/component/gsc.hpp | 30 ++- src/client/component/io.cpp | 73 +++---- src/client/game/scripting/script_value.cpp | 12 ++ src/client/game/scripting/script_value.hpp | 62 ++++++ 7 files changed, 290 insertions(+), 117 deletions(-) diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index da7cc2ac..7b15f93f 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -609,10 +609,12 @@ namespace command add_commands_generic(); - gsc::function::add("executecommand", []() + gsc::function::add("executecommand", [](const gsc::function_args& args) { - const auto cmd = gsc::get_argument(0).as(); + const auto cmd = args[0].as(); command::execute(cmd, true); + + return scripting::script_value{}; }); } diff --git a/src/client/component/dedicated.cpp b/src/client/component/dedicated.cpp index 7dbca256..e7761255 100644 --- a/src/client/component/dedicated.cpp +++ b/src/client/component/dedicated.cpp @@ -225,9 +225,9 @@ namespace dedicated }), true); // return 0 so the game doesn't override the cfg - gsc::function::add("isusingmatchrulesdata", []() + gsc::function::add("isusingmatchrulesdata", [](const gsc::function_args& args) { - game::Scr_AddInt(0); + return 0; }); // delay console commands until the initialization is done // COULDN'T FOUND diff --git a/src/client/component/gsc.cpp b/src/client/component/gsc.cpp index c8a45f66..da3bad50 100644 --- a/src/client/component/gsc.cpp +++ b/src/client/component/gsc.cpp @@ -26,11 +26,13 @@ namespace gsc { - void* func_table[0x1000]{}; - void* meth_table[0x1000]{}; + builtin_function func_table[0x1000]{}; + builtin_method meth_table[0x1000]{}; namespace { + utils::hook::detour scr_emit_function_hook; + game::dvar_t* developer_script = nullptr; auto compiler = ::gsc::compiler(); @@ -42,8 +44,8 @@ namespace gsc std::unordered_map init_handles; std::unordered_map loaded_scripts; - std::unordered_map functions; - std::unordered_map methods; + std::unordered_map functions; + std::unordered_map methods; std::optional gsc_error; @@ -52,6 +54,8 @@ namespace gsc auto function_id_start = 0x30A; auto method_id_start = 0x8586; + game::scr_entref_t saved_ent_ref; + std::string unknown_function_error{}; unsigned int current_filename{}; @@ -349,11 +353,16 @@ namespace gsc } } - void builtin_call_error(const std::string& error_str) + std::uint16_t get_function_id() { const auto pos = game::scr_function_stack->pos; - const auto function_id = *reinterpret_cast( + return *reinterpret_cast( reinterpret_cast(pos - 2)); + } + + void builtin_call_error(const std::string& error_str) + { + const auto function_id = get_function_id(); if (function_id > 0x1000) { @@ -367,11 +376,12 @@ namespace gsc } } - void* vm_error_stub(void* a1) + void vm_error_stub(void* a1) { if (!developer_script->current.enabled && !force_error_print) { - return utils::hook::invoke(SELECT_VALUE(0x415C90_b, 0x59DDA0_b), a1); + utils::hook::invoke(SELECT_VALUE(0x415C90_b, 0x59DDA0_b), a1); + return; } console::warn("*********** script runtime error *************\n"); @@ -405,7 +415,7 @@ namespace gsc print_callstack(); console::warn("**********************************************\n"); - return utils::hook::invoke(SELECT_VALUE(0x415C90_b, 0x59DDA0_b), a1); + utils::hook::invoke(SELECT_VALUE(0x415C90_b, 0x59DDA0_b), a1); } void get_unknown_function_error(const char* code_pos) @@ -471,31 +481,49 @@ namespace gsc return res; } - void register_gsc_functions_stub(void* a1, void* a2) + void scr_emit_function_stub(unsigned int filename, unsigned int thread_name, char* code_pos) { - utils::hook::invoke(SELECT_VALUE(0x2E0F50_b, 0x1CE010_b), a1, a2); - for (const auto& function : functions) - { - game::Scr_RegisterFunction(function.first, 0, function.second); - } + current_filename = filename; + scr_emit_function_hook.invoke(filename, thread_name, code_pos); } - void register_gsc_methods_stub(void* a1, void* a2) + function_args get_arguments() { - utils::hook::invoke(SELECT_VALUE(0x2E0FB0_b, 0x1CE120_b), a1, a2); - for (const auto& method : methods) + std::vector args; + + for (auto i = 0; static_cast(i) < game::scr_VmPub->outparamcount; ++i) { - game::Scr_RegisterFunction(method.first, 0, method.second); + const auto value = game::scr_VmPub->top[-i]; + args.push_back(value); } + + return args; } - void execute_custom_function(builtin_function function) + void return_value(const scripting::script_value& value) + { + if (game::scr_VmPub->outparamcount) + { + game::Scr_ClearOutParams(); + } + + scripting::push_value(value); + } + + void call_custom_function(const uint16_t id) { auto error = false; try { - function(); + const auto& function = functions[id]; + const auto result = function(get_arguments()); + const auto type = result.get_raw().type; + + if (type) + { + return_value(result); + } } catch (const std::exception& e) { @@ -510,13 +538,20 @@ namespace gsc } } - void execute_custom_method(scripting::script_function method, game::scr_entref_t ent_ref) + void call_custom_method(const uint16_t id) { auto error = false; try { - method(ent_ref); + const auto& method = methods[id]; + const auto result = method(saved_ent_ref, get_arguments()); + const auto type = result.get_raw().type; + + if (type) + { + return_value(result); + } } catch (const std::exception& e) { @@ -533,10 +568,8 @@ namespace gsc void vm_call_builtin_function_stub(builtin_function function) { - auto is_custom_function = false; - { - is_custom_function = functions.find(function) != functions.end(); - } + const auto function_id = get_function_id(); + const auto is_custom_function = functions.find(function_id) != functions.end(); if (!is_custom_function) { @@ -544,15 +577,30 @@ namespace gsc } else { - execute_custom_function(function); + call_custom_function(function_id); } } - utils::hook::detour scr_emit_function_hook; - void scr_emit_function_stub(unsigned int filename, unsigned int thread_name, char* code_pos) + game::scr_entref_t get_entity_id_stub(unsigned int ent_id) { - current_filename = filename; - scr_emit_function_hook.invoke(filename, thread_name, code_pos); + const auto ref = game::Scr_GetEntityIdRef(ent_id); + saved_ent_ref = ref; + return ref; + } + + void vm_call_builtin_method_stub(builtin_method method) + { + const auto function_id = get_function_id(); + const auto is_custom_function = methods.find(function_id) != methods.end(); + + if (!is_custom_function) + { + method(saved_ent_ref); + } + else + { + call_custom_method(function_id); + } } } @@ -597,52 +645,67 @@ namespace gsc } } - scripting::script_value get_argument(int index) - { - if (index >= static_cast(game::scr_VmPub->outparamcount)) - { - return {}; - } - - return game::scr_VmPub->top[-index]; - } - namespace function { - void add(const std::string& name, builtin_function function) + void add(const std::string& name, script_function function) { if (xsk::gsc::h1::resolver::find_function(name)) { const auto id = xsk::gsc::h1::resolver::function_id(name); - functions[function] = id; + functions[id] = function; } else { const auto id = ++function_id_start; xsk::gsc::h1::resolver::add_function(name, static_cast(id)); - functions[function] = id; + functions[id] = function; } } } namespace method { - void add(const std::string& name, builtin_method method) + void add(const std::string& name, script_method method) { if (xsk::gsc::h1::resolver::find_method(name)) { const auto id = xsk::gsc::h1::resolver::method_id(name); - methods[method] = id; + methods[id] = method; } else { const auto id = ++method_id_start; xsk::gsc::h1::resolver::add_method(name, static_cast(id)); - methods[method] = id; + methods[id] = method; } } } + function_args::function_args(std::vector values) + : values_(values) + { + } + + unsigned int function_args::size() const + { + return static_cast(this->values_.size()); + } + + std::vector function_args::get_raw() const + { + return this->values_; + } + + scripting::value_wrap function_args::get(const int index) const + { + if (index >= this->values_.size()) + { + throw std::runtime_error(utils::string::va("parameter %d does not exist", index)); + } + + return { this->values_[index], index }; + } + class component final : public component_interface { public: @@ -693,9 +756,6 @@ namespace gsc utils::hook::call(SELECT_VALUE(0x3BD75A_b, 0x50473A_b), find_variable_stub); scr_emit_function_hook.create(SELECT_VALUE(0x3BD680_b, 0x504660_b), scr_emit_function_stub); - utils::hook::call(SELECT_VALUE(0x3BDC4F_b, 0x504C7F_b), register_gsc_functions_stub); - utils::hook::call(SELECT_VALUE(0x3BDC5B_b, 0x504C8B_b), register_gsc_methods_stub); - utils::hook::set(SELECT_VALUE(0x3BD86C_b, 0x50484C_b), 0x1000); // change builtin func count utils::hook::set(SELECT_VALUE(0x3BD872_b, 0x504852_b) + 4, @@ -712,47 +772,62 @@ namespace gsc utils::hook::inject(SELECT_VALUE(0x3BDC36_b, 0x504C66_b) + 3, &meth_table); utils::hook::set(SELECT_VALUE(0x3BDC3F_b, 0x504C6F_b), sizeof(meth_table)); + /* + trying to do a jump hook to push the ent reference (if it exists) and the builtin function/methods works, but + if longjmp is called because of a runtime error in our custom functions/methods, then the game just kinda dies + or gets incredibly slow but will eventually load. for functions, the workaround is easy but for methods, we still + have to remember the entity that called the method so the workaround is just hooking the Scr_GetEntityIdRef call + */ utils::hook::nop(SELECT_VALUE(0x3CB723_b, 0x512783_b), 8); utils::hook::call(SELECT_VALUE(0x3CB723_b, 0x512783_b), vm_call_builtin_function_stub); - function::add("print", []() + utils::hook::call(SELECT_VALUE(0x3CBA12_b, 0x512A72_b), get_entity_id_stub); + utils::hook::nop(SELECT_VALUE(0x3CBA46_b, 0x512AA6_b), 6); + utils::hook::nop(SELECT_VALUE(0x3CBA4E_b, 0x512AAE_b), 2); + utils::hook::call(SELECT_VALUE(0x3CBA46_b, 0x512AA6_b), vm_call_builtin_method_stub); + + function::add("print", [](const function_args& args) { - const auto num = game::Scr_GetNumParam(); std::string buffer{}; - for (auto i = 0; i < num; i++) + for (auto i = 0u; i < args.size(); ++i) { - const auto str = game::Scr_GetString(i); + const auto str = args[i].as(); buffer.append(str); buffer.append("\t"); } - console::info("%s\n", buffer.data()); + + return scripting::script_value{}; }); - function::add("assert", []() + function::add("assert", [](const function_args& args) { - const auto expr = get_argument(0).as(); + const auto expr = args[0].as(); if (!expr) { throw std::runtime_error("assert fail"); } + + return scripting::script_value{}; }); - function::add("assertex", []() + function::add("assertex", [](const function_args& args) { - const auto expr = get_argument(0).as(); + const auto expr = args[0].as(); if (!expr) { - const auto error = get_argument(1).as(); + const auto error = args[1].as(); throw std::runtime_error(error); } + + return scripting::script_value{}; }); - function::add("replacefunc", []() + function::add("replacefunc", [](const function_args& args) { - const auto what = get_argument(0).get_raw(); - const auto with = get_argument(1).get_raw(); + const auto what = args[0].get_raw(); + const auto with = args[1].get_raw(); if (what.type != game::SCRIPT_FUNCTION) { @@ -765,26 +840,29 @@ namespace gsc } logfile::set_gsc_hook(what.u.codePosValue, with.u.codePosValue); + + return scripting::script_value{}; }); - function::add("toupper", []() + function::add("toupper", [](const function_args& args) { - const auto string = get_argument(0).as(); - game::Scr_AddString(utils::string::to_upper(string).data()); + const auto string = args[0].as(); + return utils::string::to_upper(string); }); - function::add("logprint", []() + function::add("logprint", [](const function_args& args) { std::string buffer{}; - const auto params = game::Scr_GetNumParam(); - for (auto i = 0; i < params; i++) + for (auto i = 0u; i < args.size(); ++i) { - const auto string = game::Scr_GetString(i); + const auto string = args[i].as(); buffer.append(string); } game::G_LogPrintf("%s", buffer.data()); + + return scripting::script_value{}; }); scripting::on_shutdown([](int free_scripts) diff --git a/src/client/component/gsc.hpp b/src/client/component/gsc.hpp index 964e6a1f..a8c4211e 100644 --- a/src/client/component/gsc.hpp +++ b/src/client/component/gsc.hpp @@ -4,26 +4,44 @@ namespace gsc { + class function_args + { + public: + function_args(std::vector); + + unsigned int size() const; + std::vector get_raw() const; + scripting::value_wrap get(const int index) const; + + scripting::value_wrap operator[](const int index) const + { + return this->get(index); + } + private: + std::vector values_; + }; + using builtin_function = void(*)(); using builtin_method = void(*)(game::scr_entref_t); - extern void* func_table[0x1000]; - extern void* meth_table[0x1000]; + using script_function = std::function; + using script_method = std::function; + + extern builtin_function func_table[0x1000]; + extern builtin_method meth_table[0x1000]; game::ScriptFile* find_script(game::XAssetType /*type*/, const char* name, int /*allow_create_default*/); void load_main_handles(); void load_init_handles(); - scripting::script_value get_argument(int index); - namespace function { - void add(const std::string& name, builtin_function function); + void add(const std::string& name, script_function function); } namespace method { - void add(const std::string& name, builtin_method function); + void add(const std::string& name, script_method function); } } diff --git a/src/client/component/io.cpp b/src/client/component/io.cpp index 8d13d752..ffedbe0f 100644 --- a/src/client/component/io.cpp +++ b/src/client/component/io.cpp @@ -23,7 +23,7 @@ namespace io { if (path.generic_string().find("..") != std::string::npos) { - throw std::runtime_error("io: directory traversal is not allowed"); + throw std::runtime_error("directory traversal is not allowed"); } } @@ -38,7 +38,7 @@ namespace io return (fs_game_path / path).generic_string(); } - throw std::runtime_error("io: fs_game is not properly defined"); + throw std::runtime_error("fs_game is not properly defined"); } } @@ -47,60 +47,59 @@ namespace io public: void post_unpack() override { - gsc::function::add("fileexists", []() + gsc::function::add("fileexists", [](const gsc::function_args& args) { - const auto path = convert_path(gsc::get_argument(0).as()); - game::Scr_AddBool(utils::io::file_exists(path)); + const auto path = convert_path(args[0].as()); + return utils::io::file_exists(path); }); - gsc::function::add("writefile", []() + gsc::function::add("writefile", [](const gsc::function_args& args) { - const auto path = convert_path(gsc::get_argument(0).as()); - const auto data = gsc::get_argument(1).as(); - const auto params = game::Scr_GetNumParam(); + const auto path = convert_path(args[0].as()); + const auto data = args[1].as(); auto append = false; - if (params > 2) + if (args.size() > 2u) { - append = gsc::get_argument(2).as(); + append = args[2].as(); } - game::Scr_AddBool(utils::io::write_file(path, data, append)); + return utils::io::write_file(path, data, append); }); - gsc::function::add("readfile", []() + gsc::function::add("readfile", [](const gsc::function_args& args) { - const auto path = convert_path(gsc::get_argument(0).as()); - game::Scr_AddString(utils::io::read_file(path).data()); + const auto path = convert_path(args[0].as()); + return utils::io::read_file(path); }); - gsc::function::add("filesize", []() + gsc::function::add("filesize", [](const gsc::function_args& args) { - const auto path = convert_path(gsc::get_argument(0).as()); - game::Scr_AddInt(static_cast(utils::io::file_size(path))); + const auto path = convert_path(args[0].as()); + return static_cast(utils::io::file_size(path)); }); - gsc::function::add("createdirectory", []() + gsc::function::add("createdirectory", [](const gsc::function_args& args) { - const auto path = convert_path(gsc::get_argument(0).as()); - game::Scr_AddBool(utils::io::create_directory(path)); + const auto path = convert_path(args[0].as()); + return utils::io::create_directory(path); }); - gsc::function::add("directoryexists", []() + gsc::function::add("directoryexists", [](const gsc::function_args& args) { - const auto path = convert_path(gsc::get_argument(0).as()); - game::Scr_AddBool(utils::io::directory_exists(path)); + const auto path = convert_path(args[0].as()); + return utils::io::directory_exists(path); }); - gsc::function::add("directoryisempty", []() + gsc::function::add("directoryisempty", [](const gsc::function_args& args) { - const auto path = convert_path(gsc::get_argument(0).as()); - game::Scr_AddBool(utils::io::directory_is_empty(path)); + const auto path = convert_path(args[0].as()); + return utils::io::directory_is_empty(path); }); - gsc::function::add("listfiles", []() + gsc::function::add("listfiles", [](const gsc::function_args& args) { - const auto path = convert_path(gsc::get_argument(0).as()); + const auto path = convert_path(args[0].as()); const auto files = utils::io::list_files(path); scripting::array array{}; @@ -109,20 +108,22 @@ namespace io array.push(file); } - scripting::push_value(array); + return array; }); - gsc::function::add("copyfolder", []() + gsc::function::add("copyfolder", [](const gsc::function_args& args) { - const auto source = convert_path(gsc::get_argument(0).as()); - const auto target = convert_path(gsc::get_argument(1).as()); + const auto source = convert_path(args[0].as()); + const auto target = convert_path(args[1].as()); utils::io::copy_folder(source, target); + + return scripting::script_value{}; }); - gsc::function::add("removefile", []() + gsc::function::add("removefile", [](const gsc::function_args& args) { - const auto path = convert_path(gsc::get_argument(0).as()); - game::Scr_AddBool(utils::io::remove_file(path)); + const auto path = convert_path(args[0].as()); + return utils::io::remove_file(path); }); } }; diff --git a/src/client/game/scripting/script_value.cpp b/src/client/game/scripting/script_value.cpp index 371693c0..535c4e88 100644 --- a/src/client/game/scripting/script_value.cpp +++ b/src/client/game/scripting/script_value.cpp @@ -15,6 +15,12 @@ namespace scripting { } + script_value::script_value(const value_wrap& value) + : value_(value.get_raw()) + { + } + + script_value::script_value(const int value) { game::VariableValue variable{}; @@ -292,6 +298,12 @@ namespace scripting return this->value_.get(); } + value_wrap::value_wrap(const scripting::script_value& value, int argument_index) + : value_(value) + , argument_index_(argument_index) + { + } + std::string script_value::to_string() const { if (this->is()) diff --git a/src/client/game/scripting/script_value.hpp b/src/client/game/scripting/script_value.hpp index d20a01c2..b22cdead 100644 --- a/src/client/game/scripting/script_value.hpp +++ b/src/client/game/scripting/script_value.hpp @@ -9,12 +9,14 @@ namespace scripting { class entity; class array; + class value_wrap; class script_value { public: script_value() = default; script_value(const game::VariableValue& value); + script_value(const value_wrap& value); script_value(int value); script_value(unsigned int value); @@ -53,6 +55,20 @@ namespace scripting return get(); } + template + T* as_ptr() + { + + const auto value = this->as(); + + if (!value) + { + throw std::runtime_error("is null"); + } + + return reinterpret_cast(value); + } + const game::VariableValue& get_raw() const; variable_value value_{}; @@ -62,4 +78,50 @@ namespace scripting T get() const; }; + + class value_wrap + { + public: + value_wrap(const scripting::script_value& value, int argument_index); + + template + T as() const + { + try + { + return this->value_.as(); + } + catch (const std::exception& e) + { + throw std::runtime_error(utils::string::va("parameter %d %s", this->argument_index_, e.what())); + } + } + + template + T* as_ptr() + { + try + { + return this->value_.as_ptr(); + } + catch (const std::exception& e) + { + throw std::runtime_error(utils::string::va("parameter %d %s", this->argument_index_, e.what())); + } + } + + template + T is() const + { + return this->value_.is(); + } + + const game::VariableValue& get_raw() const + { + return this->value_.get_raw(); + } + + int argument_index_{}; + scripting::script_value value_; + }; }