Add Return Value Support for Script Functions (#3479)
Some checks are pending
Nightly Build / Build Nightly (push) Blocked by required conditions
Nightly Build / Recreate Release (push) Blocked by required conditions
Nightly Build / Check Recent Commit (push) Successful in 20s

* 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:
Arthur 2024-08-06 15:46:48 +03:00 committed by GitHub
parent 013b463536
commit 09b91ca6d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 606 additions and 3549 deletions

View File

@ -63,6 +63,18 @@ Rips the current memory address and returns a new pointer object.
pointer = pointer:rip(offset)
```
### `get_int()`
Retrieves the value stored at the memory address as the specified type.
- **Returns:**
- `number`: the value stored at the memory address as the specified type.
**Example Usage:**
```lua
number = pointer:get_int()
```
### `get_byte()`
Retrieves the value stored at the memory address as the specified type.
@ -123,6 +135,18 @@ Retrieves the value stored at the memory address as the specified type.
number = pointer:get_qword()
```
### `set_int(value)`
Sets the value at the memory address to the specified value of the given type.
- **Parameters:**
- `value` (number): new value.
**Example Usage:**
```lua
pointer:set_int(value)
```
### `set_byte(value)`
Sets the value at the memory address to the specified value of the given type.

View File

@ -0,0 +1,43 @@
# Table: scr_function
Table for calling GTA script functions. Needs to be called in the fiber pool or a GTA script. Only call the function when necessary.
## Functions (2)
### `call_script_function(script_name, function_name, pattern, return_type_string, args_)`
Calls a script function with the given arguments. Returns the return value as the given type.
- **Parameters:**
- `script_name` (string): Name of the script.
- `function_name` (string): Name of the function. This parameter needs to be unique.
- `pattern` (string): Pattern to scan for within the script.
- `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.
- `_args` (table): Arguments to pass to the function. Supported types are the same as return types.
**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 }
})
```
### `call_script_function(script_name, instruction_pointer, return_type_string, args_)`
Calls a script function directly using the function position with the given arguments. Returns the return value as the given type.
- **Parameters:**
- `script_name` (string): Name of the script.
- `function_name` (string): Name of the function.
- `instruction_pointer` (integer): Position of the function within the script.
- `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.
- `_args` (table): Arguments to pass to the function. Supported types are the same as return types.
**Example Usage:**
```lua
local value = scr_function.call_script_function("freemode", 0xE792, "string", {
{ "int", 191 }
})
```

View File

@ -106,22 +106,6 @@ Adds a patch for the specified script.
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})
```
### `start_launcher_script(script_name)`
Tries to start a launcher script. Needs to be called in the fiber pool or a loop.

View File

@ -54,7 +54,6 @@ namespace big
{
looped::self_wanted();
looped::self_hud();
looped::self_dance_mode();
looped::self_persist_outfit();
script::get_current()->yield();
@ -149,21 +148,9 @@ namespace big
}
}
void backend::lscustoms_loop()
{
LOG(INFO) << "Starting script: Ls customs";
while (g_running)
{
looped::vehicle_ls_customs();
script::get_current()->yield();
}
}
void backend::rainbowpaint_loop()
{
LOG(INFO) << "Starting script: Rainbow paint";
LOG(INFO) << "Starting script: Rainbow Paint";
while (g_running)
{

View File

@ -13,7 +13,6 @@ namespace big
static void vehicles_loop();
static void misc_loop();
static void remote_loop();
static void lscustoms_loop();
static void rainbowpaint_loop();
static void disable_control_action_loop();
static void world_loop();

View File

@ -9,7 +9,7 @@ namespace big
virtual void execute(player_ptr player, const command_arguments& _args, const std::shared_ptr<command_context> ctx) override
{
scr_functions::join_ceo({player->id(), 0, false, false});
scr_functions::set_as_ceo.call<void>(player->id(), 0, false, false);
}
};

View File

@ -0,0 +1,29 @@
#include "backend/player_command.hpp"
#include "natives.hpp"
#include "services/vehicle/persist_car_service.hpp"
#include "util/entity.hpp"
namespace big
{
class clone_player_car : player_command
{
using player_command::player_command;
virtual void execute(player_ptr player, const command_arguments& _args, const std::shared_ptr<command_context> ctx) override
{
Player player_id = player->id();
Ped ped = PLAYER::GET_PLAYER_PED_SCRIPT_INDEX(player_id);
if (!PED::IS_PED_IN_ANY_VEHICLE(ped, true))
{
g_notification_service.push_warning("TOXIC"_T.data(), "ERROR_PLAYER_IS_NOT_IN_VEHICLE"_T.data());
}
else
{
Vehicle v = persist_car_service::clone_ped_car(ped, PED::GET_VEHICLE_PED_IS_USING(ped));
PED::SET_PED_INTO_VEHICLE(PLAYER::PLAYER_PED_ID(), v, -1);
}
}
};
clone_player_car g_clone_player_car("cloneplayercar", "SPAWN_CLONE", "", 0);
}

View File

@ -27,7 +27,6 @@ namespace big
static void self_wanted();
static void self_hud();
static void self_dance_mode();
static void self_persist_outfit();
static void session_pop_multiplier_areas();
@ -48,7 +47,6 @@ namespace big
static void vehicle_auto_drive();
static void vehicle_allow_all_weapons();
static void vehicle_boost_behavior();
static void vehicle_ls_customs();
static void vehicle_rainbow_paint();
static void weapons_tp_gun();

View File

@ -1,63 +0,0 @@
#include "backend/looped/looped.hpp"
#include "natives.hpp"
#include "script_function.hpp"
#include "services/script_patcher/script_patcher_service.hpp"
#include "util/scripts.hpp"
namespace big
{
bool bLastDanceMode = false;
void looped::self_dance_mode()
{
if (g.self.dance_mode && SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH("maintransition"_J) > 0)
g.self.dance_mode = false;
if (g.self.dance_mode && g.self.dance_mode != bLastDanceMode)
{
g_script_patcher_service->update();
scripts::request_script("am_mp_nightclub"_J);
if (!scripts::wait_till_loaded("am_mp_nightclub"_J))
return;
auto thread = SYSTEM::START_NEW_SCRIPT_WITH_NAME_HASH("am_mp_nightclub"_J, 19400 /*PROPERTY_INT*/);
SCRIPT::SET_SCRIPT_WITH_NAME_HASH_AS_NO_LONGER_NEEDED("am_mp_nightclub"_J);
if (!thread)
return;
g.m_dance_thread = gta_util::find_script_thread_by_id(thread);
g.m_dance_program = gta_util::find_script_program("am_mp_nightclub"_J);
g.m_dance_thread->m_context.m_state = rage::eThreadState::unk_3;
// perform initial setup
gta_util::execute_as_script(g.m_dance_thread, [] {
NETWORK::NETWORK_SET_THIS_SCRIPT_IS_NETWORK_SCRIPT(32, true, 32);
scr_functions::init_nightclub_script({});
});
scr_functions::dance_loop.populate_ip();
bLastDanceMode = true;
return;
}
if (!g.self.dance_mode && g.self.dance_mode != bLastDanceMode)
{
if (g.m_dance_thread)
g.m_dance_thread->kill();
g.m_dance_thread = nullptr;
g.m_dance_program = nullptr;
g_script_patcher_service->update();
bLastDanceMode = false;
return;
}
if (g.self.dance_mode && g.m_dance_thread->m_handler)
{
*scr_globals::dance_state.as<PINT>() = TRUE; //Never once do the scripts read this as a boolean. It seems to be some kind of state the player is in. Runs from 4 to 35.
scr_functions::dance_loop.call(g.m_dance_thread, g.m_dance_program, {});
}
}
}

View File

@ -1,102 +0,0 @@
#include "backend/looped/looped.hpp"
#include "backend/looped/looped.hpp"
#include "gta/enums.hpp"
#include "gta_util.hpp"
#include "script_function.hpp"
#include "script_local.hpp"
#include "services/script_patcher/script_patcher_service.hpp"
#include "util/math.hpp"
#include "util/scripts.hpp"
namespace big
{
static bool bLastLsCustoms = false;
static bool bModshopReady = false;
void looped::vehicle_ls_customs()
{
if (g.vehicle.ls_customs && g.vehicle.ls_customs != bLastLsCustoms)
{
g_fiber_pool->queue_job([] {
scripts::request_script("carmod_shop"_J);
if (scripts::wait_till_loaded("carmod_shop"_J))
{
HUD::REQUEST_ADDITIONAL_TEXT("MOD_MNU", 9);
while (!HUD::HAS_THIS_ADDITIONAL_TEXT_LOADED("MOD_MNU", 9))
script::get_current()->yield();
GRAPHICS::REQUEST_STREAMED_TEXTURE_DICT("CommonMenu", false);
GRAPHICS::REQUEST_STREAMED_TEXTURE_DICT("MPShopSale", false);
GRAPHICS::REQUEST_STREAMED_TEXTURE_DICT("ShopUI_Title_Los_Santos_Car_Meet", false);
// clang-format off
while (!GRAPHICS::HAS_STREAMED_TEXTURE_DICT_LOADED("CommonMenu")
|| !GRAPHICS::HAS_STREAMED_TEXTURE_DICT_LOADED("MPShopSale")
|| !GRAPHICS::HAS_STREAMED_TEXTURE_DICT_LOADED("ShopUI_Title_Los_Santos_Car_Meet"))
script::get_current()->yield();
// clang-format on
auto id = SYSTEM::START_NEW_SCRIPT_WITH_NAME_HASH("carmod_shop"_J, 5050);
if (!id)
return;
g.m_modshop_thread = gta_util::find_script_thread_by_id(id);
if (!g.m_modshop_thread)
return;
g.m_modshop_thread->m_context.m_state = rage::eThreadState::unk_3;
scr_functions::modshop_loop.populate_ip();
scr_functions::setup_modshop.populate_ip();
g_script_patcher_service->update();
scr_functions::setup_modshop.call_latent(g.m_modshop_thread, gta_util::find_script_program("carmod_shop"_J), {45, 0, 18, 0}, bModshopReady);
*scr_locals::carmod_shop::maintainer.set(g.m_modshop_thread->m_stack).at(scr_locals::carmod_shop::state).as<PINT>() = 2;
*scr_locals::carmod_shop::input_button.set(g.m_modshop_thread->m_stack).as<ControllerInputs*>() = ControllerInputs::INPUT_FRONTEND_LT;
}
});
bLastLsCustoms = true;
}
else if (!g.vehicle.ls_customs && g.vehicle.ls_customs != bLastLsCustoms)
{
if (g.m_modshop_thread)
g.m_modshop_thread->kill();
GRAPHICS::SET_STREAMED_TEXTURE_DICT_AS_NO_LONGER_NEEDED("CommonMenu");
GRAPHICS::SET_STREAMED_TEXTURE_DICT_AS_NO_LONGER_NEEDED("MPShopSale");
GRAPHICS::SET_STREAMED_TEXTURE_DICT_AS_NO_LONGER_NEEDED("ShopUI_Title_Los_Santos_Car_Meet");
g.m_modshop_thread = nullptr;
bLastLsCustoms = false;
bModshopReady = false;
g_script_patcher_service->update();
}
if (self::veh == 0 || SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH("maintransition"_J) > 0 || (!g.m_modshop_thread && bModshopReady))
{
g.vehicle.ls_customs = false;
return;
}
if (g.vehicle.ls_customs && g.m_modshop_thread)
{
PAD::DISABLE_CONTROL_ACTION(0, (int)ControllerInputs::INPUT_VEH_CIN_CAM, true);
if (*scr_locals::carmod_shop::enabled_state.set(g.m_modshop_thread->m_stack).as<bool*>() && PAD::IS_CONTROL_JUST_PRESSED(2, (int)ControllerInputs::INPUT_FRONTEND_ACCEPT))
g.vehicle.ls_customs = false;
}
if (g.vehicle.ls_customs && bModshopReady && g.m_modshop_thread && g.m_modshop_thread->m_stack)
{
*scr_locals::carmod_shop::ready.set(g.m_modshop_thread->m_stack).as<PBOOL>() = FALSE;
*scr_locals::carmod_shop::maintainer.set(g.m_modshop_thread->m_stack).at(scr_locals::carmod_shop::vehicle_state).as<PINT>() = -1;
*scr_locals::carmod_shop::maintainer.set(g.m_modshop_thread->m_stack).at(scr_locals::carmod_shop::vehicle_ent_id).as<Vehicle*>() = self::veh;
if (*scr_locals::carmod_shop::maintainer.set(g.m_modshop_thread->m_stack).at(scr_locals::carmod_shop::state).as<PINT>() == 0)
*scr_locals::carmod_shop::maintainer.set(g.m_modshop_thread->m_stack).at(scr_locals::carmod_shop::state).as<PINT>() = 2;
scr_functions::modshop_loop.call(g.m_modshop_thread, gta_util::find_script_program("carmod_shop"_J), {});
}
}
}

View File

@ -20,17 +20,13 @@ namespace big
g_script_patcher_service->add_patch({"freemode"_J, "prevent normal blip update 2", "2C ? ? ? 55 ? ? 71 2C ? ? ? 61", 7, std::vector<uint8_t>(16, 0x0), &g.spoofing.spoof_blip}); // prevent normal blip update 2
g_script_patcher_service->add_patch({"freemode"_J, "stop relinquishing invalid CEO slots", "2D 01 05 00 00 38 00 2C ? ? ? 39 03 38 03 2C ? ? ? 56", 5, {0x2E, 0x01, 0x00}, &g.session.block_ceo_creation}); // stop relinquishing invalid CEO slots
g_script_patcher_service->add_patch({"freemode"_J, "prevent NPC bounties", "2D 04 06 00 00 25 1D", 5, {0x2E, 0x04, 0x00}, &g.protections.script_events.bounty}); // disable NPCs putting bounties on us.
g_script_patcher_service->add_patch({"shop_controller"_J, "despawn bypass", "2D 01 04 00 00 2C ? ? ? 56 ? ? 71", 5, {0x71, 0x2E, 0x01, 0x01}, nullptr}); // despawn bypass
g_script_patcher_service->add_patch({"shop_controller"_J, "godmode/invisibility detection bypass", "2D 01 03 00 00 5D ? ? ? 06 56 ? ? 2E ? ? 2C", 5, {0x2E, 0x01, 0x00}, nullptr}); // godmode/invisibility detection bypass
g_script_patcher_service->add_patch({"am_mp_nightclub"_J, "am_mp_nightclub1", "2D 01 03 00 00 2C ? ? ? 56 ? ? 72 2E ? ? 38 00", 5, {0x72, 0x2E, 0x01, 0x01}, &g.self.dance_mode});
g_script_patcher_service->add_patch({"am_mp_nightclub"_J, "am_mp_nightclub2", "20 56 ? ? 4F ? ? 46 ? ? 41 ? 71", 0, {0x2B, 0x55}, &g.self.dance_mode});
g_script_patcher_service->add_patch({"freemode"_J, "freemode9", "5D ? ? ? 56 ? ? 72 39 05 38 04 2C ? ? ? 58", 0, {0x2B, 0x2B, 0x2B, 0x00, 0x55}, &g.self.invisibility});
g_script_patcher_service->add_patch({"freemode"_J, "freemode10", "2D 01 03 00 00 38 00 71 72 5D ? ? ? 06 56 ? ? 71 2E ? ? 2C ? ? ? 71", 5, {0x72, 0x2E, 0x01, 0x01}, &g.session.unhide_players_from_player_list});
g_script_patcher_service->add_patch({"carmod_shop"_J, "disable camera", "2D 01 0A 00 00 4F ? ? 40 ? 41 ? 39 03", 5, {0x2E, 0x01, 0x00}, &g.vehicle.ls_customs}); // disable camera
g_script_patcher_service->add_patch({"carmod_shop"_J, "carmod_shop1", "2D 02 10 00 00 2C", 5, {0x71, 0x2E, 0x02, 0x01}, &g.vehicle.ls_customs});
g_script_patcher_service->add_patch({"carmod_shop"_J, "carmod_shop2", "2D 00 B8 00 00", 5, {0x2E, 0x00, 0x00}, &g.vehicle.ls_customs});
g_script_patcher_service->add_patch({"shop_controller"_J, "despawn bypass", "2D 01 04 00 00 2C ? ? ? 56 ? ? 71", 5, {0x71, 0x2E, 0x01, 0x01}, nullptr}); // despawn bypass
g_script_patcher_service->add_patch({"shop_controller"_J, "godmode/invisibility detection bypass", "2D 01 03 00 00 5D ? ? ? 06 56 ? ? 2E ? ? 2C", 5, {0x2E, 0x01, 0x00}, nullptr}); // godmode/invisibility detection bypass
g_script_patcher_service->add_patch({"carmod_shop"_J, "allow all vehicles", "2D 03 16 00 00 38 00", 5, {0x72, 0x2E, 0x03, 0x01}, nullptr}); // allow all vehicles
g_script_patcher_service->add_patch({"carmod_shop"_J, "allow all vehicles 2", "2D 03 07 00 00 71 38 02", 5, {0x72, 0x2E, 0x03, 0x01}, nullptr}); // allow all vehicles 2
g_script_patcher_service->add_patch({"main_persistent"_J, "vehicle clan logo SP bypass", "56 04 00 72 2E 01 01 2C 01 04 1F 5D ? ? ? 74", 0, {0x55}, nullptr}); // vehicle clan logo SP bypass
for (auto& entry : *g_pointers->m_gta.m_script_program_table)
{

View File

@ -38,8 +38,6 @@ namespace big::scr_globals
static inline const script_global spawn_global(2696212);
static inline const script_global dance_state(1943520);
static inline const script_global transaction_overlimit(20913);
static inline const script_global stats(2359296);
@ -107,15 +105,4 @@ namespace big::scr_locals
// func_\d+\((&.Local_\d+(, )?){9}\);
inline static script_local mobile(19139);
}
namespace carmod_shop
{
inline static script_local maintainer(735); //P"4F ? ? 47 ? ? 73 58 ? ? 4F ? ? 25 ?" +1 W="maintainer" +3 W ="state"
inline static auto state = 446;
inline static script_local input_button(1867); //P"51 ? ? 70 51 ? ? 50 ? ? 51 ? ?" +1 W="input_button"
inline static script_local enabled_state(2097); //P"51 ? ? 71 51 ? ? 71 51 ? ? 71 61 ? ? ?" +1 W="enabled_state"
inline static script_local ready(2048); //P"51 ? ? 50 ? ? 2A" +1 W="ready"
inline static auto vehicle_state = 638; //P"4F ? ? 46 ? ? 4F ? ?" +4 W ="vehicle_state"
inline static auto vehicle_ent_id = 409; //P"4F ? ? 47 ? ? 39 ? 55 ? ? 38 ? 73" +4 W ="vehicle_ent_id"
}
}

View File

@ -80,9 +80,6 @@ namespace big
rage::scrThread* m_hunt_the_beast_thread = nullptr;
rage::scrThread* m_dance_thread = nullptr;
rage::scrProgram* m_dance_program = nullptr;
rage::scrThread* m_mission_creator_thread = nullptr;
struct script_block_opts
@ -114,7 +111,6 @@ namespace big
bool enabled = false;
} cmd_executor{};
rage::scrThread* m_modshop_thread = nullptr;
bool in_script_vm = false;
struct debug
@ -396,8 +392,6 @@ namespace big
NLOHMANN_DEFINE_TYPE_INTRUSIVE(hud, color_override, color_override_initialized, hud_color_overrides, hide_radar, hide_ammo, selected_hud_component, hud_components_states, force_show_hud_element, force_show_hud)
} hud{};
// do not save below entries
bool dance_mode = false;
struct super_hero_fly
{
@ -413,7 +407,7 @@ namespace big
NLOHMANN_DEFINE_TYPE_INTRUSIVE(super_hero_fly, gradual, explosions, auto_land, charge, ptfx, fly_speed, initial_launch)
} super_hero_fly{};
NLOHMANN_DEFINE_TYPE_INTRUSIVE(self, ipls, ptfx_effects, clean_player, never_wanted, force_wanted_level, passive, free_cam, invisibility, local_visibility, no_ragdoll, noclip, noclip_aim_speed_multiplier, noclip_speed_multiplier, off_radar, super_run, no_collision, unlimited_oxygen, no_water_collision, wanted_level, god_mode, proof_bullet, proof_fire, proof_collision, proof_melee, proof_explosion, proof_steam, proof_water, proof_mask, mobile_radio, fast_respawn, auto_tp, super_jump, beast_jump, healthregen, healthregenrate, hud, superman, custom_weapon_stop, prompt_ambient_animations, persist_outfit, persist_outfits_mis, interaction_menu_freedom, super_hero_fly)
NLOHMANN_DEFINE_TYPE_INTRUSIVE(self, ipls, ptfx_effects, clean_player, never_wanted, force_wanted_level, passive, free_cam, invisibility, local_visibility, no_ragdoll, noclip, noclip_aim_speed_multiplier, noclip_speed_multiplier, off_radar, super_run, no_collision, unlimited_oxygen, no_water_collision, wanted_level, god_mode, proof_bullet, proof_fire, proof_collision, proof_melee, proof_explosion, proof_steam, proof_water, proof_mask, mobile_radio, fast_respawn, auto_tp, super_jump, beast_jump, healthregen, healthregenrate, hud, superman, custom_weapon_stop, prompt_ambient_animations, persist_outfit, persist_outfits_mis, interaction_menu_freedom, super_hero_fly, graceful_landing)
} self{};
@ -806,7 +800,6 @@ namespace big
bool instant_brake = false;
bool infinite_veh_ammo = false;
bool block_homing = true;
bool ls_customs = false; // don't save this to disk
bool seatbelt = false;
bool turn_signals = false;
bool vehicle_jump = false;

File diff suppressed because it is too large Load Diff

View File

@ -9,8 +9,6 @@
#include <network/CNetGamePlayer.hpp>
#include <script/globals/GPBD_FM_3.hpp>
#include <script/globals/GlobalPlayerBD.hpp>
#include <script/CGameScriptHandlerNetComponent.hpp>
namespace big
{

View File

@ -17,15 +17,9 @@ namespace big
if (thread == g.m_hunt_the_beast_thread)
g.m_hunt_the_beast_thread = nullptr;
if (thread == g.m_dance_thread)
g.m_dance_thread = nullptr;
if (thread == g.m_mission_creator_thread)
g.m_mission_creator_thread = nullptr;
if (thread == g.m_modshop_thread)
g.m_modshop_thread = nullptr;
return result;
}
}

View File

@ -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>;

View File

@ -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_;

View 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);
}
}

View File

@ -0,0 +1,8 @@
#pragma once
#include "script_function.hpp"
namespace lua::scr_function
{
void bind(sol::state& state);
}

View File

@ -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"
@ -205,27 +204,6 @@ namespace lua::script
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
// Name: start_launcher_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");

View File

@ -10,6 +10,7 @@ namespace lua::memory
integer_,
ptr_,
float_,
double_
double_,
vector3_
};
}

View File

@ -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);

View File

@ -236,7 +236,6 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
g_script_mgr.add_script(std::make_unique<script>(&backend::vehicles_loop, "Vehicle"));
g_script_mgr.add_script(std::make_unique<script>(&backend::misc_loop, "Miscellaneous"));
g_script_mgr.add_script(std::make_unique<script>(&backend::remote_loop, "Remote"));
g_script_mgr.add_script(std::make_unique<script>(&backend::lscustoms_loop, "LS Customs"));
g_script_mgr.add_script(std::make_unique<script>(&backend::rainbowpaint_loop, "Rainbow Paint"));
g_script_mgr.add_script(std::make_unique<script>(&backend::disable_control_action_loop, "Disable Controls"));
g_script_mgr.add_script(std::make_unique<script>(&backend::world_loop, "World"));

View File

@ -1,5 +1,4 @@
#pragma once
#include "script_function.hpp"
namespace big
{

View File

@ -1,41 +0,0 @@
#pragma once
#include "native_hooks.hpp"
#include "natives.hpp"
namespace big
{
namespace carmod_shop
{
inline void SET_ENTITY_COORDS(rage::scrNativeCallContext* src)
{
if (!g.vehicle.ls_customs)
{
ENTITY::SET_ENTITY_COORDS(src->get_arg<Entity>(0), src->get_arg<float>(1), src->get_arg<float>(2), src->get_arg<float>(3), src->get_arg<BOOL>(4), src->get_arg<BOOL>(5), src->get_arg<BOOL>(6), src->get_arg<BOOL>(7));
}
}
inline void SET_ENTITY_HEADING(rage::scrNativeCallContext* src)
{
if (!g.vehicle.ls_customs)
{
ENTITY::SET_ENTITY_HEADING(src->get_arg<Entity>(0), src->get_arg<float>(1));
}
}
inline void SET_VEHICLE_LIGHTS(rage::scrNativeCallContext* src)
{
if (!g.vehicle.ls_customs)
{
VEHICLE::SET_VEHICLE_LIGHTS(src->get_arg<Vehicle>(0), src->get_arg<int>(1));
}
}
inline void DISABLE_ALL_CONTROL_ACTIONS(rage::scrNativeCallContext* src)
{
if (!g.vehicle.ls_customs)
{
PAD::DISABLE_ALL_CONTROL_ACTIONS(src->get_arg<int>(0));
}
}
}
}

View File

@ -73,7 +73,7 @@ namespace big
{
if (SCRIPT::GET_HASH_OF_THIS_SCRIPT_NAME() == "freemode"_J && g.session.fast_join)
{
scr_functions::set_freemode_session_active({});
scr_functions::set_freemode_session_active.call<void>();
src->set_return_value<BOOL>(TRUE);
}
else

View File

@ -3,7 +3,6 @@
#include "all_scripts.hpp"
#include "am_launcher.hpp"
#include "am_pi_menu.hpp"
#include "carmod_shop.hpp"
#include "creator.hpp"
#include "freemode.hpp"
#include "network_session_host.hpp"
@ -104,11 +103,6 @@ namespace big
add_native_detour("shop_controller"_J, NativeIndex::SET_WARNING_MESSAGE_WITH_HEADER, shop_controller::SET_WARNING_MESSAGE_WITH_HEADER);
add_native_detour("shop_controller"_J, NativeIndex::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT, shop_controller::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT);
add_native_detour("carmod_shop"_J, NativeIndex::SET_ENTITY_COORDS, carmod_shop::SET_ENTITY_COORDS);
add_native_detour("carmod_shop"_J, NativeIndex::SET_ENTITY_HEADING, carmod_shop::SET_ENTITY_HEADING);
add_native_detour("carmod_shop"_J, NativeIndex::SET_VEHICLE_LIGHTS, carmod_shop::SET_VEHICLE_LIGHTS);
add_native_detour("carmod_shop"_J, NativeIndex::DISABLE_ALL_CONTROL_ACTIONS, carmod_shop::DISABLE_ALL_CONTROL_ACTIONS);
add_native_detour("freemode"_J, NativeIndex::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH, freemode::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH);
add_native_detour("freemode"_J, NativeIndex::STAT_GET_INT, freemode::STAT_GET_INT);
add_native_detour("freemode"_J, NativeIndex::IS_PLAYER_PLAYING, freemode::IS_PLAYER_PLAYING);

View File

@ -1,136 +1,30 @@
#include "script_function.hpp"
#include "gta_util.hpp"
#include "pointers.hpp"
#include "util/scripts.hpp"
#include <script/scrProgram.hpp>
namespace big
{
script_function::script_function(const std::string& name, const rage::joaat_t script, const std::string& pattern, int32_t offset) :
script_function::script_function(const std::string& name, const rage::joaat_t script, const std::string& pattern) :
m_name(name),
m_script(script),
m_pattern(pattern),
m_offset(offset),
m_ip(0)
{
}
void script_function::populate_ip()
uint32_t script_function::get_ip(rage::scrProgram* program)
{
if (m_ip == 0)
if (m_ip != 0)
return m_ip;
if (auto location = scripts::get_code_location_by_pattern(program, m_pattern))
{
auto program = gta_util::find_script_program(m_script);
if (!program)
return;
auto location = scripts::get_code_location_by_pattern(program, m_pattern);
if (!location)
LOG(FATAL) << "Failed to find pattern " << m_name << " in script " << program->m_name;
m_ip = *location;
LOG(VERBOSE) << "Found pattern " << m_name << " at " << HEX_TO_UPPER(m_ip) << " in script " << program->m_name;
}
else
LOG(VERBOSE) << "Found pattern " << m_name << " in script " << program->m_name;
m_ip = location.value() + m_offset;
}
}
void script_function::call(rage::scrThread* thread, rage::scrProgram* program, const std::vector<uint64_t>& args)
{
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;
for (const auto& arg : args)
stack[ctx.m_stack_pointer++] = arg;
stack[ctx.m_stack_pointer++] = 0;
ctx.m_instruction_pointer = m_ip;
ctx.m_state = rage::eThreadState::idle;
g_pointers->m_gta.m_script_vm(stack, 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;
LOG(FATAL) << "Failed to find pattern " << m_name << " in script " << program->m_name;
}
void script_function::call_latent(rage::scrThread* thread, rage::scrProgram* program, const std::vector<uint64_t>& args, bool& done)
{
g_fiber_pool->queue_job([this, thread, program, args, &done] {
auto stack = (uint64_t*)thread->m_stack;
rage::eThreadState result = rage::eThreadState::idle;
rage::scrThreadContext ctx = thread->m_context;
for (const auto& arg : args)
stack[ctx.m_stack_pointer++] = arg;
stack[ctx.m_stack_pointer++] = 0;
ctx.m_instruction_pointer = m_ip;
ctx.m_state = rage::eThreadState::idle;
while (result != rage::eThreadState::killed)
{
auto tls_ctx = rage::tlsContext::get();
auto og_thread = tls_ctx->m_script_thread;
tls_ctx->m_script_thread = thread;
tls_ctx->m_is_script_thread_active = true;
auto old_ctx = thread->m_context;
thread->m_context = ctx;
result = g_pointers->m_gta.m_script_vm(stack, g_pointers->m_gta.m_script_globals, program, &thread->m_context);
thread->m_context = old_ctx;
tls_ctx->m_script_thread = og_thread;
tls_ctx->m_is_script_thread_active = og_thread != nullptr;
script::get_current()->yield();
}
done = true;
});
}
void script_function::static_call(const std::vector<uint64_t>& args)
{
populate_ip();
rage::scrThread* thread = (rage::scrThread*)new uint8_t[sizeof(rage::scrThread)];
memcpy(thread, rage::tlsContext::get()->m_script_thread, sizeof(rage::scrThread));
void* stack = new uint64_t[25000];
thread->m_stack = (rage::scrValue*)stack;
thread->m_context.m_stack_size = 25000;
thread->m_context.m_stack_pointer = 1;
call(thread, gta_util::find_script_program(m_script), args);
delete[] stack;
delete[] (uint8_t*)thread; // without the cast it ends up calling the destructor which leads to some pretty funny crashes
}
void script_function::operator()(const std::vector<uint64_t>& args)
{
populate_ip();
if (m_ip == 0)
return;
auto thread = gta_util::find_script_thread(m_script);
auto program = gta_util::find_script_program(m_script);
if (thread && program)
{
call(thread, program, args);
}
return m_ip;
}
}

View File

@ -1,43 +1,74 @@
#pragma once
#include "gta/joaat.hpp"
#include "memory/pattern.hpp"
#include "gta_util.hpp"
#include "pointers.hpp"
#include "util/scripts.hpp"
#include <script/scrProgram.hpp>
namespace big
{
// a lightweight script function wrapper inspired by https://github.com/Parik27/V.Rainbomizer/blob/master/src/mission/missions_YscUtils.hh
class script_function
{
rage::joaat_t m_script;
const memory::pattern m_pattern;
int32_t m_offset;
int32_t m_ip;
uint32_t m_ip;
std::string m_name;
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, const std::vector<uint64_t>& args);
void call_latent(rage::scrThread* thread, rage::scrProgram* program, const std::vector<uint64_t>& args, bool& done);
script_function(const std::string& name, const rage::joaat_t script, const std::string& pattern);
uint32_t get_ip(rage::scrProgram* program);
// for pure functions that do not need access to thread stack
void static_call(const std::vector<uint64_t>& args);
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);
}
void operator()(const std::vector<uint64_t>& args);
template<typename Ret, typename... Args>
Ret call(Args... args)
{
auto thread = gta_util::find_script_thread(m_script);
auto program = gta_util::find_script_program(m_script);
auto ip = get_ip(program);
if (!thread || !program || !ip)
return Ret();
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
(push_arg(stack, ctx.m_stack_pointer, std::forward<Args>(args)), ...);
stack[ctx.m_stack_pointer++] = 0;
ctx.m_instruction_pointer = ip;
ctx.m_state = rage::eThreadState::idle;
g_pointers->m_gta.m_script_vm(stack, 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 constexpr (!std::is_same_v<Ret, void>)
{
return *reinterpret_cast<Ret*>(stack + top_stack);
}
}
};
namespace scr_functions
{
static inline script_function join_ceo("JC", "freemode"_J, "2D 04 1D 00 00 5D", 0);
static inline script_function set_freemode_session_active("SFSA", "freemode"_J, "2D 00 02 00 00 75 5D ? ? ? 50", 0);
static inline script_function dance_loop("DL", "am_mp_nightclub"_J, "2D 00 14 00 00 4F ? ? 47 ? ? 5D ? ? ? 56", 0);
static inline script_function init_nightclub_script("INS", "am_mp_nightclub"_J, "2D 00 11 00 00 4F", 0);
static inline script_function save_to_datafile("STD", "fm_race_creator"_J, "2D 01 03 00 00 71 2C", 0);
static inline script_function load_from_datafile("LFD", "fm_race_creator"_J, "2D 04 0D 00 00 71 2C", 0);
static inline script_function modshop_loop("ML", "carmod_shop"_J, "2D 00 07 00 00 71 51", 0);
static inline script_function setup_modshop("SM", "carmod_shop"_J, "2D 04 12 00 00 38 00 51", 0);
static inline script_function reset_session_data("RSD", "pausemenu_multiplayer"_J, "2D 02 7D 00 00", 0);
inline script_function set_as_ceo("SAC", "freemode"_J, "2D 04 1D 00 00 5D");
inline script_function set_freemode_session_active("SFSA", "freemode"_J, "2D 00 02 00 00 75 5D ? ? ? 50");
inline script_function save_to_datafile("STD", "fm_race_creator"_J, "2D 01 03 00 00 71 2C");
inline script_function load_from_datafile("LFD", "fm_race_creator"_J, "2D 03 0C 00 00 71 2C");
inline script_function reset_session_data("RSD", "main_persistent"_J, "2D 02 7D 00 00");
inline script_function add_clan_logo_to_vehicle("ACLTV", "main_persistent"_J, "2D 02 04 00 00 5D ? ? ? 61");
inline script_function vehicle_cannot_accept_clan_logo("CVACL", "main_persistent"_J, "2D 01 03 00 00 2C 01 00 A1 06 ? 04");
}
}

View File

@ -109,7 +109,7 @@ namespace big
}},
{"COPY VEHICLE",
[this] {
Vehicle v = persist_car_service::clone_ped_car(PLAYER::PLAYER_PED_ID(), m_handle);
Vehicle v = persist_car_service::clone_ped_car(VEHICLE::GET_PED_IN_VEHICLE_SEAT(m_handle, -1, 0), m_handle);
script::get_current()->yield();
PED::SET_PED_INTO_VEHICLE(PLAYER::PLAYER_PED_ID(), v, -1);
}},

View File

@ -4,8 +4,8 @@
#include "natives.hpp"
#include "pointers.hpp"
#include "script.hpp"
#include "script/tlsContext.hpp"
#include "script_function.hpp"
#include "script/tlsContext.hpp"
namespace big
{
@ -37,7 +37,7 @@ namespace big
}
char* storage = new char[0x50000];
scr_functions::save_to_datafile.static_call({(uint64_t)storage});
scr_functions::save_to_datafile.call<void>((uint64_t)storage);
delete[] storage;
SCRIPT::SET_SCRIPT_WITH_NAME_HASH_AS_NO_LONGER_NEEDED("fm_race_creator"_J);
@ -77,7 +77,9 @@ namespace big
script::get_current()->yield();
}
scr_functions::load_from_datafile.static_call({1, true, false, 0});
int load_stage = 0; // Will be incremented at the end of each case in the function
int ugc_language = NETWORK::UGC_GET_CONTENT_LANGUAGE(0);
scr_functions::load_from_datafile.call<bool>(&load_stage, ugc_language, false);
SCRIPT::SET_SCRIPT_WITH_NAME_HASH_AS_NO_LONGER_NEEDED("fm_race_creator"_J);
file_stream.close();

View File

@ -2,6 +2,7 @@
#include "base/CObject.hpp"
#include "pointers.hpp"
#include "script_function.hpp"
#include "util/misc.hpp"
#include "util/vehicle.hpp"
#include "util/pools.hpp"
@ -327,9 +328,11 @@ namespace big
VEHICLE::SET_VEHICLE_EXTRA_COLOUR_6(vehicle, vehicle_json[dash_color_key]);
const BOOL have_clan_logo = vehicle_json[clan_logo_key];
if (have_clan_logo)
vehicle_helper::add_clan_logo_to_vehicle(vehicle, ped);
const BOOL needs_clan_logo = vehicle_json[clan_logo_key];
if (needs_clan_logo)
{
scr_functions::add_clan_logo_to_vehicle.call<bool>(&vehicle, NETWORK::NETWORK_GET_PLAYER_INDEX_FROM_PED(ped));
}
VEHICLE::SET_VEHICLE_XENON_LIGHT_COLOR_INDEX(vehicle, vehicle_json[headlight_color_key]);
}

View File

@ -6,23 +6,6 @@
namespace big
{
void vehicle_helper::add_clan_logo_to_vehicle(Vehicle vehicle, Ped ped)
{
rage::fvector3 x, y, z;
float scale;
Hash modelHash = ENTITY::GET_ENTITY_MODEL(vehicle);
if (GetVehicleInfoForClanLogo(modelHash, x, y, z, scale))
{
int alpha = 200;
if (modelHash == VEHICLE_WINDSOR || modelHash == VEHICLE_COMET4)
alpha = 255;
GRAPHICS::ADD_VEHICLE_CREW_EMBLEM(vehicle, ped, ENTITY::GET_ENTITY_BONE_INDEX_BY_NAME(vehicle, "chassis_dummy"), x.x, x.y, x.z, y.x, y.y, y.z, z.x, z.y, z.z, scale, 0, alpha);
if (y.z >= 0.0f)
GRAPHICS::ADD_VEHICLE_CREW_EMBLEM(vehicle, ped, ENTITY::GET_ENTITY_BONE_INDEX_BY_NAME(vehicle, "chassis_dummy"), x.x * -1.0f, x.y, x.z, y.x * -1.0f, y.y, y.z, z.x * -1.0f, z.y * -1.0f, z.z, scale, 1, alpha);
}
}
const char* vehicle_helper::get_mod_slot_name(Hash model, Vehicle vehicle, int mod_slot)
{
switch (mod_slot)

View File

@ -10,6 +10,5 @@ namespace big
static bool check_mod_blacklist(Hash model, int mod_slot, int mod);
static const char* get_mod_slot_name(Hash model, Vehicle vehicle, int mod_slot);
static const char* get_mod_name(Hash model, Vehicle vehicle, int mod_slot, int mod, int mod_count);
static void add_clan_logo_to_vehicle(Vehicle vehicle, Ped ped);
};
}

View File

@ -244,16 +244,15 @@ namespace big::scripts
if (g.m_mission_creator_thread || SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH("creator"_J) != 0 || SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH("maintransition"_J) != 0 || STREAMING::IS_PLAYER_SWITCH_IN_PROGRESS() || CUTSCENE::IS_CUTSCENE_ACTIVE())
{
g_notification_service.push_warning("Creator", "Cannot start creator now");
g_notification_service.push_warning("GUI_TAB_CREATOR"_T.data(), "CREATOR_CANNOT_START"_T.data());
return;
}
if (MISC::GET_NUMBER_OF_FREE_STACKS_OF_THIS_SIZE(60500) == 0)
if (MISC::GET_NUMBER_OF_FREE_STACKS_OF_THIS_SIZE(62500) == 0)
{
g_notification_service.push_warning("Creator", "No free stacks for MISSION stack size");
g_notification_service.push_warning("GUI_TAB_CREATOR"_T.data(), "CREATOR_NO_FREE_STACKS"_T.data());
}
while (!SCRIPT::HAS_SCRIPT_WITH_NAME_HASH_LOADED(hash))
{
SCRIPT::REQUEST_SCRIPT_WITH_NAME_HASH(hash);
@ -264,7 +263,7 @@ namespace big::scripts
*scr_globals::mission_creator_exited.as<bool*>() = false;
*scr_globals::mission_creator_radar_follows_camera.as<bool*>() = true;
if (SYSTEM::START_NEW_SCRIPT_WITH_NAME_HASH(hash, 60500))
if (SYSTEM::START_NEW_SCRIPT_WITH_NAME_HASH(hash, 62500))
{
g.m_mission_creator_thread = gta_util::find_script_thread(hash);
}

View File

@ -8,13 +8,13 @@
#include "pointers.hpp"
#include "rage/rlSessionByGamerTaskResult.hpp"
#include "script.hpp"
#include "script_function.hpp"
#include "services/api/api_service.hpp"
#include "services/player_database/player_database_service.hpp"
#include "services/players/player_service.hpp"
#include "thread_pool.hpp"
#include "util/globals.hpp"
#include "util/misc.hpp"
#include "script_function.hpp"
#include <network/Network.hpp>
#include <network/snConnectToPeerTask.hpp>
@ -26,11 +26,6 @@ namespace big::session
{
inline bool join_type(eSessionType session)
{
SCRIPT::REQUEST_SCRIPT_WITH_NAME_HASH("pausemenu_multiplayer"_J);
while (!SCRIPT::HAS_SCRIPT_WITH_NAME_HASH_LOADED("pausemenu_multiplayer"_J))
script::get_current()->yield();
*scr_globals::sctv_spectator.as<int*>() = (session == eSessionType::SC_TV ? 1 : 0); // If SCTV then enable spectator mode
if (session == eSessionType::LEAVE_ONLINE)
@ -52,7 +47,7 @@ namespace big::session
*scr_globals::transition_state.as<eTransitionState*>() = eTransitionState::TRANSITION_STATE_RETURN_TO_SINGLEPLAYER;
}
scr_functions::reset_session_data({true, true});
scr_functions::reset_session_data.call<void>(true, true);
*scr_globals::session3.as<int*>() = 0;
*scr_globals::session4.as<int*>() = 1;
*scr_globals::session5.as<int*>() = 32;
@ -64,7 +59,6 @@ namespace big::session
*scr_globals::session.as<int*>() = 0;
}
SCRIPT::SET_SCRIPT_WITH_NAME_HASH_AS_NO_LONGER_NEEDED("pausemenu_multiplayer"_J);
return true;
}

View File

@ -1,5 +1,6 @@
#include "vehicle.hpp"
#include "pools.hpp"
#include "script_function.hpp"
namespace big::vehicle
{
@ -459,7 +460,7 @@ namespace big::vehicle
if (owned_mods[MOD_HAS_CLAN_LOGO] != 0)
{
vehicle_helper::add_clan_logo_to_vehicle(vehicle, self::ped);
scr_functions::add_clan_logo_to_vehicle.call<bool>(&vehicle, self::id);
}
return vehicle;

View File

@ -54,10 +54,6 @@ namespace big
DLC::ON_ENTER_SP();
});
components::button("START_LS_CUSTOMS"_T, [] {
g.vehicle.ls_customs = true;
});
components::button("SKIP_CUTSCENE"_T, [] {
CUTSCENE::STOP_CUTSCENE_IMMEDIATELY();
});

View File

@ -20,6 +20,7 @@ namespace big
ImGui::BeginGroup();
components::player_command_button<"killengine">(g_player_service->get_selected(), {});
components::player_command_button<"cloneplayercar">(g_player_service->get_selected(), {});
components::player_command_button<"burstwheels">(g_player_service->get_selected(), {});
components::player_command_button<"smashwindows">(g_player_service->get_selected(), {});
components::player_command_button<"blacktint">(g_player_service->get_selected(), {});

View File

@ -105,8 +105,6 @@ namespace big
components::command_float_input<"superheroflyinitiallaunch">();
});
ImGui::Checkbox("DANCE_MODE"_T.data(), &g.self.dance_mode);
components::command_checkbox<"orbitaldrone">();
components::options_modal("VIEW_SELF_ORBITAL_DRONE"_T.data(), [] {
ImGui::Separator();

View File

@ -2,7 +2,7 @@
#include "fiber_pool.hpp"
#include "natives.hpp"
#include "script.hpp"
#include "services/vehicle_helper/vehicle_helper.hpp"
#include "script_function.hpp"
#include "util/vehicle.hpp"
#include "views/view.hpp"
@ -24,6 +24,8 @@ namespace big
static int selected_slot = -1;
static bool is_bennys = false;
static bool has_clan_logo = false;
static bool vehicle_cannot_accept_clan_logo = false;
static int front_wheel_stock_mod = -1;
static int rear_wheel_stock_mod = -1;
@ -78,6 +80,8 @@ namespace big
tmp_mod_display_names[MOD_WHEEL_TYPE].insert(lsc_wheel_styles.begin(), lsc_wheel_styles.end());
is_bennys = owned_mods[MOD_WHEEL_TYPE] == WHEEL_TYPE_BENNYS_ORIGINAL || owned_mods[MOD_WHEEL_TYPE] == WHEEL_TYPE_BENNYS_BESPOKE || owned_mods[MOD_WHEEL_TYPE] == WHEEL_TYPE_OPEN_WHEEL || owned_mods[MOD_WHEEL_TYPE] == WHEEL_TYPE_STREET || owned_mods[MOD_WHEEL_TYPE] == WHEEL_TYPE_TRACK;
has_clan_logo = GRAPHICS::DOES_VEHICLE_HAVE_CREW_EMBLEM(player_vehicle, 0);
vehicle_cannot_accept_clan_logo = scr_functions::vehicle_cannot_accept_clan_logo.call<bool>(player_vehicle);
for (int slot = MOD_SPOILERS; slot <= MOD_LIGHTBAR; slot++)
{
@ -260,26 +264,21 @@ namespace big
VEHICLE::TOGGLE_VEHICLE_MOD(player_vehicle, MOD_TYRE_SMOKE, owned_mods[MOD_TYRE_SMOKE]);
});
}
rage::fvector3 blank;
float scale;
if (GetVehicleInfoForClanLogo(model, blank, blank, blank, scale))
if (!vehicle_cannot_accept_clan_logo)
{
auto has_clan_logo = (bool*)&owned_mods[MOD_HAS_CLAN_LOGO];
if (ImGui::Checkbox("CLAN_LOGO"_T.data(), has_clan_logo))
{
if (*has_clan_logo)
if (ImGui::Checkbox("CLAN_LOGO"_T.data(), &has_clan_logo))
{
g_fiber_pool->queue_job([] {
vehicle_helper::add_clan_logo_to_vehicle(player_vehicle, self::ped);
});
if (has_clan_logo)
{
scr_functions::add_clan_logo_to_vehicle.call<bool>(&player_vehicle, self::id);
}
else
{
g_fiber_pool->queue_job([] {
GRAPHICS::REMOVE_VEHICLE_CREW_EMBLEM(player_vehicle, 0);
GRAPHICS::REMOVE_VEHICLE_CREW_EMBLEM(player_vehicle, 1);
});
}
});
}
}