Add Return Value Support for Script Functions (#3479)
* Add Return Value Support for Script Functions * Removed address-of operator on instruction_pointer parameter, as sol is pass-by-value. * Fixed reset_session_data & start_creator_script * Added support for Vector3 and updated casting for bool type for proper conversion to Lua boolean * Updated documentation for scr_function * Added get_int method and updated param names for script functions * Fix #3497 graceful landing not saved. * Added a check in view_lsc to see if the vehicle can accept clan logos first. * Fixed vehicle clan logo SP bypass not working properly. * Fixed COPY VEHICLE not giving persist_car_service::spawn_vehicle_json the target's ped so it can copy their clan logo and not ours. Fixed spawn_vehicle_json calling add_clan_logo_to_vehicle with our logo and not the ped parameter's logo. * Added Clone Player Car. * Fixed has_clan_logo check in view_lsc being given the wrong parameter. --------- Co-authored-by: gir489 <100792176+gir489returns@users.noreply.github.com>
This commit is contained in:
@ -625,12 +625,14 @@ namespace lua::memory
|
||||
pointer_ut["add"] = &pointer::add;
|
||||
pointer_ut["sub"] = &pointer::sub;
|
||||
pointer_ut["rip"] = &pointer::rip;
|
||||
pointer_ut["get_int"] = &pointer::get<int32_t>;
|
||||
pointer_ut["get_byte"] = &pointer::get<uint8_t>;
|
||||
pointer_ut["get_word"] = &pointer::get<uint16_t>;
|
||||
pointer_ut["get_dword"] = &pointer::get<uint32_t>;
|
||||
pointer_ut["get_qword"] = &pointer::get<uint64_t>;
|
||||
pointer_ut["get_float"] = &pointer::get<float>;
|
||||
pointer_ut["get_string"] = &pointer::get_string;
|
||||
pointer_ut["set_int"] = &pointer::set<int32_t>;
|
||||
pointer_ut["set_byte"] = &pointer::set<uint8_t>;
|
||||
pointer_ut["set_word"] = &pointer::set<uint16_t>;
|
||||
pointer_ut["set_dword"] = &pointer::set<uint32_t>;
|
||||
|
@ -137,6 +137,10 @@ namespace lua::memory
|
||||
{
|
||||
return type_info_t::double_;
|
||||
}
|
||||
else if (s.contains("vector3"))
|
||||
{
|
||||
return type_info_t::vector3_;
|
||||
}
|
||||
else
|
||||
{
|
||||
return type_info_t::integer_;
|
||||
|
360
src/lua/bindings/scr_function.cpp
Normal file
360
src/lua/bindings/scr_function.cpp
Normal file
@ -0,0 +1,360 @@
|
||||
#include "scr_function.hpp"
|
||||
|
||||
#include "gta_util.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "memory/pattern.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "util/scripts.hpp"
|
||||
|
||||
namespace lua::scr_function
|
||||
{
|
||||
template<typename Arg>
|
||||
void push_arg(uint64_t* stack, uint32_t& stack_pointer, Arg&& value)
|
||||
{
|
||||
*reinterpret_cast<std::remove_cv_t<std::remove_reference_t<Arg>>*>(reinterpret_cast<uint64_t*>(stack) + (stack_pointer++)) = std::forward<Arg>(value);
|
||||
}
|
||||
|
||||
// Lua API: Table
|
||||
// Name: scr_function
|
||||
// Table for calling GTA script functions. Needs to be called in the fiber pool. Only call the function when necessary.
|
||||
|
||||
// Lua API: function
|
||||
// Table: scr_function
|
||||
// Name: call_script_function
|
||||
// Param: script_name: string: Name of the script.
|
||||
// Param: function_name: string: Name of the function. This parameter needs to be unique.
|
||||
// Param: pattern: string: Pattern to scan for within the script.
|
||||
// Param: return_type_string: string: Return type of the function. Supported types are **"int"**, **"bool"**, **"const char\*/string"**, **"ptr/pointer/*"**, **"float"**, and **"vector3"**. Anything different will be rejected.
|
||||
// Param: args_: table: Arguments to pass to the function. Supported types are the same as return types.
|
||||
// Calls a script function with the given arguments. Returns the return value as the given type.
|
||||
// **Example Usage:**
|
||||
// ```lua
|
||||
// local value = scr_function.call_script_function("freemode", "wear_sunglasses_at_night", "69 42 06 66", "bool", {
|
||||
// { "int", 69 },
|
||||
// { "float", 4.20 },
|
||||
// { "int", 666 }
|
||||
// })
|
||||
// ```
|
||||
|
||||
static sol::object call_script_function_by_signature(const std::string& script_name, const std::string& function_name, const std::string& pattern, const std::string& return_type_string, sol::table args_, sol::this_state state_)
|
||||
{
|
||||
std::vector<lua::memory::type_info_t> param_types;
|
||||
std::vector<sol::object> actual_args;
|
||||
static std::unordered_map<std::string, int32_t> pattern_results;
|
||||
for (const auto& [k, v_] : args_)
|
||||
{
|
||||
if (v_.is<sol::table>())
|
||||
{
|
||||
auto v = v_.as<sol::table>();
|
||||
param_types.push_back(lua::memory::get_type_info_from_string(v[1].get<const char*>()));
|
||||
actual_args.push_back(v[2].get<sol::object>());
|
||||
}
|
||||
}
|
||||
|
||||
const auto return_type = lua::memory::get_type_info_from_string(return_type_string);
|
||||
|
||||
auto thread = big::gta_util::find_script_thread(rage::joaat(script_name));
|
||||
auto program = big::gta_util::find_script_program(rage::joaat(script_name));
|
||||
|
||||
if (!thread || !program)
|
||||
{
|
||||
LOG(FATAL) << "Failed to find " << script_name << " for " << function_name;
|
||||
return sol::lua_nil;
|
||||
}
|
||||
|
||||
int32_t instruction_pointer;
|
||||
|
||||
if (pattern_results.contains(function_name))
|
||||
{
|
||||
instruction_pointer = pattern_results[function_name];
|
||||
}
|
||||
else
|
||||
{
|
||||
const ::memory::pattern pattern_scan(pattern);
|
||||
|
||||
auto location = big::scripts::get_code_location_by_pattern(program, pattern_scan);
|
||||
|
||||
if (!location)
|
||||
{
|
||||
LOG(FATAL) << "Failed to find pattern " << function_name << " in script " << script_name;
|
||||
return sol::lua_nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(VERBOSE) << "Found pattern for " << function_name << " at " << HEX_TO_UPPER(location.value()) << " in " << script_name;
|
||||
}
|
||||
|
||||
pattern_results[function_name] = instruction_pointer = location.value();
|
||||
}
|
||||
|
||||
auto tls_ctx = rage::tlsContext::get();
|
||||
auto stack = (uint64_t*)thread->m_stack;
|
||||
auto og_thread = tls_ctx->m_script_thread;
|
||||
tls_ctx->m_script_thread = thread;
|
||||
tls_ctx->m_is_script_thread_active = true;
|
||||
rage::scrThreadContext ctx = thread->m_context;
|
||||
auto top_stack = ctx.m_stack_pointer; // This will be the top item in the stack after the args and return address are cleaned off
|
||||
|
||||
for (size_t i = 0; i < param_types.size(); i++)
|
||||
{
|
||||
switch (param_types[i])
|
||||
{
|
||||
case lua::memory::type_info_t::boolean_:
|
||||
{
|
||||
const auto val = actual_args[i].as<std::optional<bool>>();
|
||||
if (val)
|
||||
{
|
||||
push_arg(stack, ctx.m_stack_pointer, *val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case lua::memory::type_info_t::string_:
|
||||
{
|
||||
const auto val = actual_args[i].as<std::optional<const char*>>();
|
||||
if (val)
|
||||
{
|
||||
push_arg(stack, ctx.m_stack_pointer, *val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case lua::memory::type_info_t::integer_:
|
||||
{
|
||||
const auto val = actual_args[i].as<std::optional<int>>();
|
||||
if (val)
|
||||
{
|
||||
push_arg(stack, ctx.m_stack_pointer, *val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case lua::memory::type_info_t::ptr_:
|
||||
{
|
||||
const auto val = actual_args[i].as<std::optional<lua::memory::pointer>>();
|
||||
if (val)
|
||||
{
|
||||
push_arg(stack, ctx.m_stack_pointer, (*val).get_address());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case lua::memory::type_info_t::float_:
|
||||
{
|
||||
const auto val = actual_args[i].as<std::optional<float>>();
|
||||
if (val)
|
||||
{
|
||||
push_arg(stack, ctx.m_stack_pointer, *val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case lua::memory::type_info_t::vector3_:
|
||||
{
|
||||
const auto val = actual_args[i].as<std::optional<Vector3>>();
|
||||
if (val)
|
||||
{
|
||||
push_arg(stack, ctx.m_stack_pointer, val.value().x);
|
||||
push_arg(stack, ctx.m_stack_pointer, val.value().y);
|
||||
push_arg(stack, ctx.m_stack_pointer, val.value().z);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
stack[ctx.m_stack_pointer++] = 0;
|
||||
ctx.m_instruction_pointer = instruction_pointer;
|
||||
ctx.m_state = rage::eThreadState::idle;
|
||||
|
||||
big::g_pointers->m_gta.m_script_vm(stack, big::g_pointers->m_gta.m_script_globals, program, &ctx);
|
||||
|
||||
tls_ctx->m_script_thread = og_thread;
|
||||
tls_ctx->m_is_script_thread_active = og_thread != nullptr;
|
||||
|
||||
if (return_type == lua::memory::type_info_t::boolean_)
|
||||
{
|
||||
return sol::make_object(state_, (bool)*reinterpret_cast<BOOL*>(stack + top_stack));
|
||||
}
|
||||
else if (return_type == lua::memory::type_info_t::string_)
|
||||
{
|
||||
return sol::make_object(state_, *reinterpret_cast<const char**>(stack + top_stack));
|
||||
}
|
||||
else if (return_type == lua::memory::type_info_t::integer_)
|
||||
{
|
||||
return sol::make_object(state_, *reinterpret_cast<int*>(stack + top_stack));
|
||||
}
|
||||
else if (return_type == lua::memory::type_info_t::ptr_)
|
||||
{
|
||||
return sol::make_object(state_, *reinterpret_cast<uint64_t*>(stack + top_stack));
|
||||
}
|
||||
else if (return_type == lua::memory::type_info_t::float_)
|
||||
{
|
||||
return sol::make_object(state_, *reinterpret_cast<float*>(stack + top_stack));
|
||||
}
|
||||
else if (return_type == lua::memory::type_info_t::vector3_)
|
||||
{
|
||||
return sol::make_object(state_, *reinterpret_cast<Vector3*>(stack + top_stack));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(FATAL) << "Unimplemented return type " << return_type_string;
|
||||
return sol::lua_nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Lua API: function
|
||||
// Table: scr_function
|
||||
// Name: call_script_function
|
||||
// Param: script_name: string: Name of the script.
|
||||
// Param: instruction_pointer: integer: Position of the function within the script.
|
||||
// Param: return_type_string: string: Return type of the function. Supported types are **"int"**, **"bool"**, **"const char\*/string"**, **"ptr/pointer/*"**, **"float"**, and **"vector3"**. Anything different will be rejected.
|
||||
// Param: args_: table: Arguments to pass to the function. Supported types are the same as return types.
|
||||
// Calls a script function directly using the function position with the given arguments. Returns the return value as the given type.
|
||||
// **Example Usage:**
|
||||
// ```lua
|
||||
// local value = scr_function.call_script_function("freemode", 0xE792, "string", {
|
||||
// { "int", 191 }
|
||||
// })
|
||||
// ```
|
||||
|
||||
static sol::object call_script_function_by_instruction_pointer(const std::string& script_name, const int instruction_pointer, const std::string& return_type_string, sol::table args_, sol::this_state state_)
|
||||
{
|
||||
std::vector<lua::memory::type_info_t> param_types;
|
||||
std::vector<sol::object> actual_args;
|
||||
for (const auto& [k, v_] : args_)
|
||||
{
|
||||
if (v_.is<sol::table>())
|
||||
{
|
||||
auto v = v_.as<sol::table>();
|
||||
param_types.push_back(lua::memory::get_type_info_from_string(v[1].get<const char*>()));
|
||||
actual_args.push_back(v[2].get<sol::object>());
|
||||
}
|
||||
}
|
||||
|
||||
const auto return_type = lua::memory::get_type_info_from_string(return_type_string);
|
||||
|
||||
auto thread = big::gta_util::find_script_thread(rage::joaat(script_name));
|
||||
auto program = big::gta_util::find_script_program(rage::joaat(script_name));
|
||||
|
||||
if (!thread || !program || !instruction_pointer)
|
||||
{
|
||||
LOG(FATAL) << "Failed to run " << script_name << " script function at " << HEX_TO_UPPER(instruction_pointer);
|
||||
return sol::lua_nil;
|
||||
}
|
||||
|
||||
auto tls_ctx = rage::tlsContext::get();
|
||||
auto stack = (uint64_t*)thread->m_stack;
|
||||
auto og_thread = tls_ctx->m_script_thread;
|
||||
tls_ctx->m_script_thread = thread;
|
||||
tls_ctx->m_is_script_thread_active = true;
|
||||
rage::scrThreadContext ctx = thread->m_context;
|
||||
auto top_stack = ctx.m_stack_pointer; // This will be the top item in the stack after the args and return address are cleaned off
|
||||
|
||||
for (size_t i = 0; i < param_types.size(); i++)
|
||||
{
|
||||
switch (param_types[i])
|
||||
{
|
||||
case lua::memory::type_info_t::boolean_:
|
||||
{
|
||||
const auto val = actual_args[i].as<std::optional<bool>>();
|
||||
if (val)
|
||||
{
|
||||
push_arg(stack, ctx.m_stack_pointer, *val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case lua::memory::type_info_t::string_:
|
||||
{
|
||||
const auto val = actual_args[i].as<std::optional<const char*>>();
|
||||
if (val)
|
||||
{
|
||||
push_arg(stack, ctx.m_stack_pointer, *val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case lua::memory::type_info_t::integer_:
|
||||
{
|
||||
const auto val = actual_args[i].as<std::optional<int>>();
|
||||
if (val)
|
||||
{
|
||||
push_arg(stack, ctx.m_stack_pointer, *val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case lua::memory::type_info_t::ptr_:
|
||||
{
|
||||
const auto val = actual_args[i].as<std::optional<lua::memory::pointer>>();
|
||||
if (val)
|
||||
{
|
||||
push_arg(stack, ctx.m_stack_pointer, (*val).get_address());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case lua::memory::type_info_t::float_:
|
||||
{
|
||||
const auto val = actual_args[i].as<std::optional<float>>();
|
||||
if (val)
|
||||
{
|
||||
push_arg(stack, ctx.m_stack_pointer, *val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case lua::memory::type_info_t::vector3_:
|
||||
{
|
||||
const auto val = actual_args[i].as<std::optional<Vector3>>();
|
||||
if (val)
|
||||
{
|
||||
push_arg(stack, ctx.m_stack_pointer, val.value().x);
|
||||
push_arg(stack, ctx.m_stack_pointer, val.value().y);
|
||||
push_arg(stack, ctx.m_stack_pointer, val.value().z);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
stack[ctx.m_stack_pointer++] = 0;
|
||||
ctx.m_instruction_pointer = instruction_pointer;
|
||||
ctx.m_state = rage::eThreadState::idle;
|
||||
|
||||
big::g_pointers->m_gta.m_script_vm(stack, big::g_pointers->m_gta.m_script_globals, program, &ctx);
|
||||
|
||||
tls_ctx->m_script_thread = og_thread;
|
||||
tls_ctx->m_is_script_thread_active = og_thread != nullptr;
|
||||
|
||||
if (return_type == lua::memory::type_info_t::boolean_)
|
||||
{
|
||||
return sol::make_object(state_, (bool)*reinterpret_cast<BOOL*>(stack + top_stack));
|
||||
}
|
||||
else if (return_type == lua::memory::type_info_t::string_)
|
||||
{
|
||||
return sol::make_object(state_, *reinterpret_cast<const char**>(stack + top_stack));
|
||||
}
|
||||
else if (return_type == lua::memory::type_info_t::integer_)
|
||||
{
|
||||
return sol::make_object(state_, *reinterpret_cast<int*>(stack + top_stack));
|
||||
}
|
||||
else if (return_type == lua::memory::type_info_t::ptr_)
|
||||
{
|
||||
return sol::make_object(state_, *reinterpret_cast<uint64_t*>(stack + top_stack));
|
||||
}
|
||||
else if (return_type == lua::memory::type_info_t::float_)
|
||||
{
|
||||
return sol::make_object(state_, *reinterpret_cast<float*>(stack + top_stack));
|
||||
}
|
||||
else if (return_type == lua::memory::type_info_t::vector3_)
|
||||
{
|
||||
return sol::make_object(state_, *reinterpret_cast<Vector3*>(stack + top_stack));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(FATAL) << "Unimplemented return type " << return_type_string;
|
||||
return sol::lua_nil;
|
||||
}
|
||||
}
|
||||
|
||||
void bind(sol::state& state)
|
||||
{
|
||||
auto ns = state["scr_function"].get_or_create<sol::table>();
|
||||
|
||||
ns["call_script_function"] = sol::overload(call_script_function_by_signature, call_script_function_by_instruction_pointer);
|
||||
}
|
||||
}
|
8
src/lua/bindings/scr_function.hpp
Normal file
8
src/lua/bindings/scr_function.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "script_function.hpp"
|
||||
|
||||
namespace lua::scr_function
|
||||
{
|
||||
void bind(sol::state& state);
|
||||
}
|
@ -3,7 +3,6 @@
|
||||
|
||||
#include "lua/lua_manager.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"
|
||||
@ -204,27 +203,6 @@ namespace lua::script
|
||||
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<uint64_t>(_args);
|
||||
|
||||
big::script_function script_function(name, rage::joaat(script_name), pattern, offset);
|
||||
script_function(args);
|
||||
}
|
||||
|
||||
// Lua API: function
|
||||
// Table: script
|
||||
@ -250,7 +228,6 @@ namespace lua::script
|
||||
ns["is_active"] = is_active;
|
||||
ns["execute_as_script"] = execute_as_script;
|
||||
ns["add_patch"] = add_patch;
|
||||
ns["call_function"] = call_function;
|
||||
ns["start_launcher_script"] = start_launcher_script;
|
||||
|
||||
auto usertype = state.new_usertype<script_util>("script_util");
|
||||
|
@ -10,6 +10,7 @@ namespace lua::memory
|
||||
integer_,
|
||||
ptr_,
|
||||
float_,
|
||||
double_
|
||||
double_,
|
||||
vector3_
|
||||
};
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
#include "bindings/native.hpp"
|
||||
#include "bindings/network.hpp"
|
||||
#include "bindings/script.hpp"
|
||||
#include "bindings/scr_function.hpp"
|
||||
#include "bindings/self.hpp"
|
||||
#include "bindings/stats.hpp"
|
||||
#include "bindings/tunables.hpp"
|
||||
@ -298,6 +299,7 @@ namespace big
|
||||
lua::log::bind(m_state);
|
||||
lua::globals::bind(m_state);
|
||||
lua::script::bind(m_state);
|
||||
lua::scr_function::bind(m_state);
|
||||
lua::native::bind(m_state);
|
||||
lua::memory::bind(m_state);
|
||||
lua::gui::bind(m_state);
|
||||
|
Reference in New Issue
Block a user