Lua: refactor script api, more doc, add button for Open Lua Scripts Folder (#1588)

* lua manager: pass down the scripts folder to the instance instead of hard coding calls to the file manager everywhere
* lua: add open lua scripts folder button
* lua api: change script binding so that user cannot by mistake try to sleep or yield in a non script context
This commit is contained in:
Quentin 2023-07-03 13:01:12 +02:00 committed by GitHub
parent 71960eddbb
commit ed18f7e70d
8 changed files with 186 additions and 58 deletions

View File

@ -0,0 +1,28 @@
# Class: script_util
Class for gta script utils, the instance is usually given to you.
## Functions (2)
### `yield()`
Yield execution.
**Exemple Usage:**
```lua
script_util:yield()
```
### `sleep(ms)`
Sleep for the given amount of time, time is in milliseconds.
- **Parameters:**
- `ms` (integer): The amount of time in milliseconds that we will sleep for.
**Exemple Usage:**
```lua
script_util:sleep(ms)
```

View File

@ -2,11 +2,34 @@
Table containing helper functions related to gta scripts. Table containing helper functions related to gta scripts.
## Functions (4) ## Functions (2)
### `register_looped(name, func)` ### `register_looped(name, func)`
Registers a function that will be looped as a gta script. Registers a function that will be looped as a gta script.
**Exemple Usage:**
```lua
script.register_looped("nameOfMyLoopedScript", function (script)
-- sleep until next game frame
script:yield()
local ModelHash = joaat("adder")
if not STREAMING.IS_MODEL_IN_CDIMAGE(ModelHash) then return end
STREAMING.REQUEST_MODEL(ModelHash) -- Request the model
while not STREAMING.HAS_MODEL_LOADED(ModelHash) do -- Waits for the model to load
script:yield()
end
local myPed = PLAYER.PLAYER_PED_ID()
local myCoords = ENTITY.GET_ENTITY_COORDS(myPed, true)
-- Spawns a networked vehicle on your current coords
local spawnedVehicle = VEHICLE.CREATE_VEHICLE(ModelHash, myCoords.x, myCoords.y, myCoords.z, ENTITY.GET_ENTITY_HEADING(myPed), true, false)
-- removes model from game memory as we no longer need it
STREAMING.SET_MODEL_AS_NO_LONGER_NEEDED(ModelHash)
-- sleep for 2s
script:sleep(2000)
ENTITY.DELETE_ENTITY(spawnedVehicle)
end)
```
- **Parameters:** - **Parameters:**
- `name` (string): name of your new looped script - `name` (string): name of your new looped script
@ -19,35 +42,37 @@ script.register_looped(name, func)
### `run_in_fiber(func)` ### `run_in_fiber(func)`
Executes a function inside the fiber pool, you can call natives inside it. Executes a function once inside the fiber pool, you can call natives inside it and yield or sleep.
**Exemple Usage:**
```lua
script.run_in_fiber(function (script)
-- sleep until next game frame
script:yield()
local ModelHash = joaat("adder")
if not STREAMING.IS_MODEL_IN_CDIMAGE(ModelHash) then return end
STREAMING.REQUEST_MODEL(ModelHash) -- Request the model
while not STREAMING.HAS_MODEL_LOADED(ModelHash) do -- Waits for the model to load
script:yield()
end
local myPed = PLAYER.PLAYER_PED_ID()
local myCoords = ENTITY.GET_ENTITY_COORDS(myPed, true)
-- Spawns a networked vehicle on your current coords
local spawnedVehicle = VEHICLE.CREATE_VEHICLE(ModelHash, myCoords.x, myCoords.y, myCoords.z, ENTITY.GET_ENTITY_HEADING(myPed), true, false)
-- removes model from game memory as we no longer need it
STREAMING.SET_MODEL_AS_NO_LONGER_NEEDED(ModelHash)
-- sleep for 2s
script:sleep(2000)
ENTITY.DELETE_ENTITY(spawnedVehicle)
end)
```
- **Parameters:** - **Parameters:**
- `func` (function): function that will be executed once in the fiber pool, you can call natives inside it. - `func` (function): function that will be executed once in the fiber pool.
**Exemple Usage:** **Exemple Usage:**
```lua ```lua
script.run_in_fiber(func) script.run_in_fiber(func)
``` ```
### `yield()`
Yield execution.
**Exemple Usage:**
```lua
script.yield()
```
### `sleep(ms)`
Sleep for the given amount of time, time is in milliseconds.
- **Parameters:**
- `ms` (integer): The amount of time in milliseconds that we will sleep for.
**Exemple Usage:**
```lua
script.sleep(ms)
```

View File

@ -6,6 +6,33 @@
namespace lua::script namespace lua::script
{ {
// Lua API: Class
// Name: script_util
// Class for gta script utils, the instance is usually given to you.
class script_util
{
public:
// Lua API: Function
// Class: script_util
// Name: yield
// Yield execution.
void yield()
{
big::script::get_current()->yield();
}
// Lua API: Function
// Class: script_util
// Name: sleep
// Param: ms: integer: The amount of time in milliseconds that we will sleep for.
// Sleep for the given amount of time, time is in milliseconds.
void sleep(int ms)
{
big::script::get_current()->yield(std::chrono::milliseconds(ms));
}
};
static script_util dummy_script_util;
// Lua API: Table // Lua API: Table
// Name: script // Name: script
// Table containing helper functions related to gta scripts. // Table containing helper functions related to gta scripts.
@ -16,6 +43,29 @@ namespace lua::script
// Param: name: string: name of your new looped script // Param: name: string: name of your new looped script
// Param: func: function: function that will be executed in a forever loop. // Param: func: function: function that will be executed in a forever loop.
// Registers a function that will be looped as a gta script. // Registers a function that will be looped as a gta script.
// **Exemple Usage:**
// ```lua
// script.register_looped("nameOfMyLoopedScript", function (script)
// -- sleep until next game frame
// script:yield()
//
// local ModelHash = joaat("adder")
// if not STREAMING.IS_MODEL_IN_CDIMAGE(ModelHash) then return end
// STREAMING.REQUEST_MODEL(ModelHash) -- Request the model
// while not STREAMING.HAS_MODEL_LOADED(ModelHash) do -- Waits for the model to load
// script:yield()
// end
// local myPed = PLAYER.PLAYER_PED_ID()
// local myCoords = ENTITY.GET_ENTITY_COORDS(myPed, true)
// -- Spawns a networked vehicle on your current coords
// local spawnedVehicle = VEHICLE.CREATE_VEHICLE(ModelHash, myCoords.x, myCoords.y, myCoords.z, ENTITY.GET_ENTITY_HEADING(myPed), true, false)
// -- removes model from game memory as we no longer need it
// STREAMING.SET_MODEL_AS_NO_LONGER_NEEDED(ModelHash)
// -- sleep for 2s
// script:sleep(2000)
// ENTITY.DELETE_ENTITY(spawnedVehicle)
// end)
// ```
static void register_looped(const std::string& name, sol::function func, sol::this_state state) static void register_looped(const std::string& name, sol::function func, sol::this_state state)
{ {
auto module = sol::state_view(state)["!this"].get<big::lua_module*>(); auto module = sol::state_view(state)["!this"].get<big::lua_module*>();
@ -24,7 +74,8 @@ namespace lua::script
[func] { [func] {
while (big::g_running) while (big::g_running)
{ {
auto res = func(); auto res = func(dummy_script_util);
if (!res.valid()) if (!res.valid())
big::g_lua_manager->handle_error(res, res.lua_state()); big::g_lua_manager->handle_error(res, res.lua_state());
@ -37,42 +88,51 @@ namespace lua::script
// Lua API: Function // Lua API: Function
// Table: script // Table: script
// Name: run_in_fiber // Name: run_in_fiber
// Param: func: function: function that will be executed once in the fiber pool, you can call natives inside it. // Param: func: function: function that will be executed once in the fiber pool.
// Executes a function inside the fiber pool, you can call natives inside it. // Executes a function once inside the fiber pool, you can call natives inside it and yield or sleep.
// **Exemple Usage:**
// ```lua
// script.run_in_fiber(function (script)
// -- sleep until next game frame
// script:yield()
//
// local ModelHash = joaat("adder")
// if not STREAMING.IS_MODEL_IN_CDIMAGE(ModelHash) then return end
// STREAMING.REQUEST_MODEL(ModelHash) -- Request the model
// while not STREAMING.HAS_MODEL_LOADED(ModelHash) do -- Waits for the model to load
// script:yield()
// end
// local myPed = PLAYER.PLAYER_PED_ID()
// local myCoords = ENTITY.GET_ENTITY_COORDS(myPed, true)
// -- Spawns a networked vehicle on your current coords
// local spawnedVehicle = VEHICLE.CREATE_VEHICLE(ModelHash, myCoords.x, myCoords.y, myCoords.z, ENTITY.GET_ENTITY_HEADING(myPed), true, false)
// -- removes model from game memory as we no longer need it
// STREAMING.SET_MODEL_AS_NO_LONGER_NEEDED(ModelHash)
// -- sleep for 2s
// script:sleep(2000)
// ENTITY.DELETE_ENTITY(spawnedVehicle)
// end)
// ```
static void run_in_fiber(sol::function func) static void run_in_fiber(sol::function func)
{ {
big::g_fiber_pool->queue_job([func] { big::g_fiber_pool->queue_job([func] {
auto res = func(); auto res = func(dummy_script_util);
if (!res.valid()) if (!res.valid())
big::g_lua_manager->handle_error(res, res.lua_state()); big::g_lua_manager->handle_error(res, res.lua_state());
}); });
} }
// Lua API: Function
// Table: script
// Name: yield
// Yield execution.
static void yield()
{
big::script::get_current()->yield();
}
// Lua API: Function
// Table: script
// Name: sleep
// Param: ms: integer: The amount of time in milliseconds that we will sleep for.
// Sleep for the given amount of time, time is in milliseconds.
static void sleep(int ms)
{
big::script::get_current()->yield(std::chrono::milliseconds(ms));
}
static void bind(sol::state& state) static void bind(sol::state& state)
{ {
auto ns = state["script"].get_or_create<sol::table>(); auto ns = state["script"].get_or_create<sol::table>();
ns["register_looped"] = register_looped; ns["register_looped"] = register_looped;
ns["run_in_fiber"] = run_in_fiber; ns["run_in_fiber"] = run_in_fiber;
ns["yield"] = yield;
ns["sleep"] = sleep; //clang-format off
state.new_usertype<script_util>("script_util",
"yield", &script_util::yield,
"sleep", &script_util::sleep);
//clang-format on
} }
} }

View File

@ -4,16 +4,17 @@
namespace big namespace big
{ {
lua_manager::lua_manager() lua_manager::lua_manager(folder scripts_folder) :
m_scripts_folder(scripts_folder)
{ {
m_schedule_reload_modules = false; m_schedule_reload_modules = false;
m_wake_time_changed_scripts_check = m_wake_time_changed_scripts_check =
std::chrono::high_resolution_clock::now() + m_delay_between_changed_scripts_check; std::chrono::high_resolution_clock::now() + m_delay_between_changed_scripts_check;
load_all_modules();
g_lua_manager = this; g_lua_manager = this;
load_all_modules();
} }
lua_manager::~lua_manager() lua_manager::~lua_manager()
@ -70,7 +71,7 @@ namespace big
if (m_wake_time_changed_scripts_check <= std::chrono::high_resolution_clock::now()) if (m_wake_time_changed_scripts_check <= std::chrono::high_resolution_clock::now())
{ {
for (const auto& entry : std::filesystem::directory_iterator(g_file_manager->get_project_folder("scripts").get_path())) for (const auto& entry : std::filesystem::directory_iterator(m_scripts_folder.get_path()))
{ {
if (entry.is_regular_file()) if (entry.is_regular_file())
{ {
@ -134,7 +135,7 @@ namespace big
void lua_manager::load_all_modules() void lua_manager::load_all_modules()
{ {
for (const auto& entry : std::filesystem::directory_iterator(g_file_manager->get_project_folder("scripts").get_path())) for (const auto& entry : std::filesystem::directory_iterator(m_scripts_folder.get_path()))
if (entry.is_regular_file()) if (entry.is_regular_file())
load_module(entry.path().filename().string()); load_module(entry.path().filename().string());
} }

View File

@ -20,11 +20,13 @@ namespace big
static constexpr std::chrono::seconds m_delay_between_changed_scripts_check = 3s; static constexpr std::chrono::seconds m_delay_between_changed_scripts_check = 3s;
std::chrono::high_resolution_clock::time_point m_wake_time_changed_scripts_check; std::chrono::high_resolution_clock::time_point m_wake_time_changed_scripts_check;
folder m_scripts_folder;
public: public:
bool m_schedule_reload_modules; bool m_schedule_reload_modules;
public: public:
lua_manager(); lua_manager(folder scripts_folder);
~lua_manager(); ~lua_manager();
void load_all_modules(); void load_all_modules();
@ -35,6 +37,11 @@ namespace big
return m_modules.size(); return m_modules.size();
} }
inline const folder& get_scripts_folder() const
{
return m_scripts_folder;
}
void draw_gui(rage::joaat_t tab_hash); void draw_gui(rage::joaat_t tab_hash);
void unload_module(rage::joaat_t module_id); void unload_module(rage::joaat_t module_id);

View File

@ -51,7 +51,7 @@ namespace big
{ {
m_state.open_libraries(); m_state.open_libraries();
const auto scripts_folder = g_file_manager->get_project_folder("scripts"); const auto& scripts_folder = g_lua_manager->get_scripts_folder();
add_folder_to_require_available_paths(scripts_folder); add_folder_to_require_available_paths(scripts_folder);

View File

@ -136,7 +136,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
auto native_hooks_instance = std::make_unique<native_hooks>(); auto native_hooks_instance = std::make_unique<native_hooks>();
LOG(INFO) << "Dynamic native hooker initialized."; LOG(INFO) << "Dynamic native hooker initialized.";
auto lua_manager_instance = std::make_unique<lua_manager>(); auto lua_manager_instance = std::make_unique<lua_manager>(g_file_manager->get_project_folder("scripts"));
LOG(INFO) << "Lua manager initialized."; LOG(INFO) << "Lua manager initialized.";
g_running = true; g_running = true;

View File

@ -53,7 +53,14 @@ namespace big
{ {
g_lua_manager->m_schedule_reload_modules = true; g_lua_manager->m_schedule_reload_modules = true;
} }
ImGui::SameLine();
ImGui::Checkbox("Auto Reload Changed Scripts", &g.lua.enable_auto_reload_changed_scripts); ImGui::Checkbox("Auto Reload Changed Scripts", &g.lua.enable_auto_reload_changed_scripts);
if (components::button("Open Lua Scripts Folder"))
{
std::string command = "explorer.exe /select," + g_lua_manager->get_scripts_folder().get_path().string();
std::system(command.c_str());
}
} }
} }