From a44912dbd8bbb6fbe87f97ad96e66abc869265d8 Mon Sep 17 00:00:00 2001 From: Arthur <121949966+ShinyWasabi@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:16:48 +0300 Subject: [PATCH] feat(lua): Expose Script Patches & Functions (#3393) --- docs/lua/tables/script.md | 62 +++++++++++++++++++++++++------------ src/lua/bindings/script.cpp | 59 ++++++++++++++++++++++++++++++++--- src/script_function.cpp | 12 +++---- src/script_function.hpp | 8 ++--- 4 files changed, 106 insertions(+), 35 deletions(-) diff --git a/docs/lua/tables/script.md b/docs/lua/tables/script.md index a922cc0c..6a076e78 100644 --- a/docs/lua/tables/script.md +++ b/docs/lua/tables/script.md @@ -2,11 +2,16 @@ Table containing helper functions related to gta scripts. -## Functions (3) +## Functions (5) ### `register_looped(name, func)` Registers a function that will be looped as a gta script. + +- **Parameters:** + - `name` (string): name of your new looped script + - `func` (function): function that will be executed in a forever loop. + **Example Usage:** ```lua script.register_looped("nameOfMyLoopedScript", function (script) @@ -31,18 +36,13 @@ script.register_looped("nameOfMyLoopedScript", function (script) end) ``` -- **Parameters:** - - `name` (string): name of your new looped script - - `func` (function): function that will be executed in a forever loop. - -**Example Usage:** -```lua -script.register_looped(name, func) -``` - ### `run_in_fiber(func)` Executes a function once inside the fiber pool, you can call natives inside it and yield or sleep. + +- **Parameters:** + - `func` (function): function that will be executed once in the fiber pool. + **Example Usage:** ```lua script.run_in_fiber(function (script) @@ -67,23 +67,45 @@ script.run_in_fiber(function (script) end) ``` -- **Parameters:** - - `func` (function): function that will be executed once in the fiber pool. - -**Example Usage:** -```lua -script.run_in_fiber(func) -``` - ### `execute_as_script(script_name, func)` - **Parameters:** - - `script_name` (string): target script thread. - - `func` (function): function that will be executed once in the script thread. + - `script_name` (string): Target script thread. + - `func` (function): Function that will be executed once in the script thread. **Example Usage:** ```lua script.execute_as_script(script_name, func) ``` +### `add_patch(script_name, name, pattern, offset, _patch)` +Adds a patch for the specified script. + +- **Parameters:** + - `script_name` (string): The name of the script. + - `name` (string): The name of the patch. + - `pattern` (string): Pattern to scan for within the script. + - `offset` (integer): The position within the pattern. + - `_patch` (table): The bytes to be written into the script's bytecode. + +**Example Usage:** +```lua +script.add_patch("fm_content_xmas_truck", "Flickering Fix", "56 ? ? 4F ? ? 40 ? 5D ? ? ? 74", 0, {0x2B, 0x00, 0x00}) +``` + +### `call_function(name, script_name, pattern, offset, _args)` + +Calls a function from the specified script. + +- **Parameters:** + - `name` (string): The name of the script function. + - `script_name` (string): The name of the script. + - `pattern` (string): The pattern to scan for within the script. + - `offset` (integer): The position within the pattern. + - `_args` (table): The arguments to pass to the script function. + +**Example Usage:** +```lua +script.call_function("Collect Collectible", "freemode", "2D 05 33 00 00", 0, {17, 0, 1, 1, 0}) +``` \ No newline at end of file diff --git a/src/lua/bindings/script.cpp b/src/lua/bindings/script.cpp index a47108d7..a0a9d0ff 100644 --- a/src/lua/bindings/script.cpp +++ b/src/lua/bindings/script.cpp @@ -4,6 +4,10 @@ #include "lua/lua_manager.hpp" #include "script_mgr.hpp" #include "gta_util.hpp" +#include "script_function.hpp" +#include "lua/bindings/network.hpp" +#include "memory/pattern.hpp" +#include "services/script_patcher/script_patcher_service.hpp" namespace lua::script { @@ -155,19 +159,64 @@ namespace lua::script // Lua API: function // Table: script // Name: execute_as_script - // Param: script_name: string: target script thread. - // Param: func: function: function that will be executed once in the script thread. + // Param: script_name: string: Target script thread. + // Param: func: function: Function that will be executed once in the script thread. static void execute_as_script(const std::string& script_name, sol::protected_function func) { big::gta_util::execute_as_script(rage::joaat(script_name), func); } + // Lua API: function + // Table: script + // Name: add_patch + // Param: script_name: string: The name of the script. + // Param: name: string: The name of the patch. + // Param: pattern: string: The pattern to scan for within the script. + // Param offset: integer: The position within the pattern. + // Param _patch: table: The bytes to be written into the script's bytecode. + // Adds a patch for the specified script. + // **Example Usage:** + //```lua + //script.add_patch("fm_content_xmas_truck", "Flickering Fix", "56 ? ? 4F ? ? 40 ? 5D ? ? ? 74", 0, {0x2B, 0x00, 0x00}) + //``` + static void add_patch(const std::string& script_name, const std::string& name, const std::string& pattern, int offset, sol::table _patch) + { + auto patch = convert_sequence(_patch); + + big::g_script_patcher_service->add_patch({rage::joaat(script_name), name, ::memory::pattern(pattern), offset, patch, nullptr}); // TO-DO: Add toggle feature? + if (auto program = big::gta_util::find_script_program(rage::joaat(script_name))) + big::g_script_patcher_service->on_script_load(program); + } + + // Lua API: function + // Table: script + // Name: call_function + // Param: name: string: The name of the script function. + // Param: script_name: string: The name of the script. + // Param: pattern: string: The pattern to scan for within the script. + // Param offset: integer: The position within the pattern. + // Param _args: table: The arguments to pass to the script function. + // Calls a function from the specified script. + // **Example Usage:** + //```lua + //script.call_function("Collect Collectible", "freemode", "2D 05 33 00 00", 0, {17, 0, 1, 1, 0}) + //``` + static void call_function(const std::string& name, const std::string& script_name, const std::string& pattern, int offset, sol::table _args) + { + auto args = convert_sequence(_args); + + big::script_function script_function(name, rage::joaat(script_name), pattern, offset); + script_function(args); + } + void bind(sol::state& state) { - auto ns = state["script"].get_or_create(); - ns["register_looped"] = register_looped; - ns["run_in_fiber"] = run_in_fiber; + auto ns = state["script"].get_or_create(); + ns["register_looped"] = register_looped; + ns["run_in_fiber"] = run_in_fiber; ns["execute_as_script"] = execute_as_script; + ns["add_patch"] = add_patch; + ns["call_function"] = call_function; auto usertype = state.new_usertype("script_util"); diff --git a/src/script_function.cpp b/src/script_function.cpp index 42a6ef7f..9d6d9ae8 100644 --- a/src/script_function.cpp +++ b/src/script_function.cpp @@ -38,7 +38,7 @@ namespace big } } - void script_function::call(rage::scrThread* thread, rage::scrProgram* program, std::initializer_list args) + void script_function::call(rage::scrThread* thread, rage::scrProgram* program, const std::vector& args) { auto tls_ctx = rage::tlsContext::get(); auto stack = (uint64_t*)thread->m_stack; @@ -49,7 +49,7 @@ namespace big rage::scrThreadContext ctx = thread->m_context; - for (auto& arg : args) + for (const auto& arg : args) stack[ctx.m_stack_pointer++] = arg; stack[ctx.m_stack_pointer++] = 0; @@ -62,7 +62,7 @@ namespace big tls_ctx->m_is_script_thread_active = og_thread != nullptr; } - void script_function::call_latent(rage::scrThread* thread, rage::scrProgram* program, std::initializer_list args, bool& done) + void script_function::call_latent(rage::scrThread* thread, rage::scrProgram* program, const std::vector& args, bool& done) { g_fiber_pool->queue_job([this, thread, program, args, &done] { auto stack = (uint64_t*)thread->m_stack; @@ -71,7 +71,7 @@ namespace big rage::scrThreadContext ctx = thread->m_context; - for (auto& arg : args) + for (const auto& arg : args) stack[ctx.m_stack_pointer++] = arg; stack[ctx.m_stack_pointer++] = 0; @@ -101,7 +101,7 @@ namespace big }); } - void script_function::static_call(std::initializer_list args) + void script_function::static_call(const std::vector& args) { populate_ip(); @@ -119,7 +119,7 @@ namespace big delete[] (uint8_t*)thread; // without the cast it ends up calling the destructor which leads to some pretty funny crashes } - void script_function::operator()(std::initializer_list args) + void script_function::operator()(const std::vector& args) { populate_ip(); diff --git a/src/script_function.hpp b/src/script_function.hpp index 839bf69f..09ecd19d 100644 --- a/src/script_function.hpp +++ b/src/script_function.hpp @@ -16,13 +16,13 @@ namespace big public: script_function(const std::string& name, const rage::joaat_t script, const std::string& pattern, int32_t offset); void populate_ip(); - void call(rage::scrThread* thread, rage::scrProgram* program, std::initializer_list args); - void call_latent(rage::scrThread* thread, rage::scrProgram* program, std::initializer_list args, bool& done); + void call(rage::scrThread* thread, rage::scrProgram* program, const std::vector& args); + void call_latent(rage::scrThread* thread, rage::scrProgram* program, const std::vector& args, bool& done); // for pure functions that do not need access to thread stack - void static_call(std::initializer_list args); + void static_call(const std::vector& args); - void operator()(std::initializer_list args); + void operator()(const std::vector& args); }; namespace scr_functions