2023-06-06 07:40:40 +00:00
# include "lua_module.hpp"
# include "bindings/command.hpp"
2023-07-21 21:21:51 +02:00
# include "bindings/entities.hpp"
2023-06-06 07:40:40 +00:00
# include "bindings/event.hpp"
2023-07-15 22:07:10 +02:00
# include "bindings/global_table.hpp"
2023-06-06 07:40:40 +00:00
# include "bindings/globals.hpp"
# include "bindings/gui.hpp"
2023-07-21 21:21:51 +02:00
# include "bindings/imgui.hpp"
2023-06-06 07:40:40 +00:00
# include "bindings/locals.hpp"
# include "bindings/log.hpp"
# include "bindings/memory.hpp"
# include "bindings/native.hpp"
# include "bindings/network.hpp"
# include "bindings/script.hpp"
2024-01-28 17:18:44 +03:00
# include "bindings/self.hpp"
2023-07-28 10:07:17 +02:00
# include "bindings/stats.hpp"
2023-06-06 07:40:40 +00:00
# include "bindings/tunables.hpp"
# include "bindings/vector.hpp"
2023-12-05 03:58:35 -05:00
# include "bindings/vehicles.hpp"
2023-12-08 09:14:57 +01:00
# include "bindings/weapons.hpp"
2023-06-06 07:40:40 +00:00
# include "file_manager.hpp"
# include "script_mgr.hpp"
namespace big
{
2023-07-16 23:32:34 +02:00
// https://sol2.readthedocs.io/en/latest/exceptions.html
int exception_handler ( lua_State * L , sol : : optional < const std : : exception & > maybe_exception , sol : : string_view description )
2023-06-06 07:40:40 +00:00
{
2023-07-16 23:32:34 +02:00
// L is the lua state, which you can wrap in a state_view if necessary
// maybe_exception will contain exception, if it exists
// description will either be the what() of the exception or a description saying that we hit the general-case catch(...)
if ( maybe_exception )
{
const std : : exception & ex = * maybe_exception ;
LOG ( FATAL ) < < ex . what ( ) ;
}
2023-06-06 07:40:40 +00:00
else
2023-07-16 23:32:34 +02:00
{
LOG ( FATAL ) < < description ;
}
Logger : : FlushQueue ( ) ;
2023-06-06 07:40:40 +00:00
2023-07-16 23:32:34 +02:00
// you must push 1 element onto the stack to be
// transported through as the error object in Lua
// note that Lua -- and 99.5% of all Lua users and libraries -- expects a string
// so we push a single string (in our case, the description of the error)
return sol : : stack : : push ( L , description ) ;
2023-06-06 07:40:40 +00:00
}
2023-07-16 23:32:34 +02:00
inline void panic_handler ( sol : : optional < std : : string > maybe_msg )
2023-06-06 07:40:40 +00:00
{
2023-07-16 23:32:34 +02:00
LOG ( FATAL ) < < " Lua is in a panic state and will now abort() the application " ;
if ( maybe_msg )
2023-06-06 07:40:40 +00:00
{
2023-07-16 23:32:34 +02:00
const std : : string & msg = maybe_msg . value ( ) ;
LOG ( FATAL ) < < " error message: " < < msg ;
2023-06-06 07:40:40 +00:00
}
2023-07-16 23:32:34 +02:00
Logger : : FlushQueue ( ) ;
// When this function exits, Lua will exhibit default behavior and abort()
2023-06-06 07:40:40 +00:00
}
2023-12-08 09:11:36 +01:00
static int traceback_error_handler ( lua_State * L )
{
std : : string msg = " An unknown error has triggered the error handler " ;
sol : : optional < sol : : string_view > maybetopmsg = sol : : stack : : unqualified_check_get < sol : : string_view > ( L , 1 , & sol : : no_panic ) ;
if ( maybetopmsg )
{
const sol : : string_view & topmsg = maybetopmsg . value ( ) ;
msg . assign ( topmsg . data ( ) , topmsg . size ( ) ) ;
}
luaL_traceback ( L , L , msg . c_str ( ) , 1 ) ;
sol : : optional < sol : : string_view > maybetraceback = sol : : stack : : unqualified_check_get < sol : : string_view > ( L , - 1 , & sol : : no_panic ) ;
if ( maybetraceback )
{
const sol : : string_view & traceback = maybetraceback . value ( ) ;
msg . assign ( traceback . data ( ) , traceback . size ( ) ) ;
}
LOG ( FATAL ) < < msg ;
return sol : : stack : : push ( L , msg ) ;
}
2023-12-13 23:19:52 +01:00
lua_module : : lua_module ( const std : : filesystem : : path & module_path , folder & scripts_folder , bool disabled ) :
2023-07-22 13:05:43 +02:00
m_state ( ) ,
m_module_path ( module_path ) ,
m_module_name ( module_path . filename ( ) . string ( ) ) ,
2023-12-13 23:19:52 +01:00
m_module_id ( rage : : joaat ( m_module_name ) ) ,
m_disabled ( disabled )
2023-06-06 07:40:40 +00:00
{
2023-12-13 23:19:52 +01:00
if ( ! m_disabled )
{
// clang-format off
m_state . open_libraries (
sol : : lib : : base ,
sol : : lib : : package ,
sol : : lib : : coroutine ,
sol : : lib : : string ,
sol : : lib : : os ,
sol : : lib : : math ,
sol : : lib : : table ,
sol : : lib : : bit32 ,
sol : : lib : : io ,
sol : : lib : : utf8
) ;
// clang-format on
init_lua_api ( scripts_folder ) ;
m_state [ " !module_name " ] = m_module_name ;
m_state [ " !this " ] = this ;
m_state . set_exception_handler ( exception_handler ) ;
m_state . set_panic ( sol : : c_call < decltype ( & panic_handler ) , & panic_handler > ) ;
lua_CFunction traceback_function = sol : : c_call < decltype ( & traceback_error_handler ) , & traceback_error_handler > ;
sol : : protected_function : : set_default_handler ( sol : : object ( m_state . lua_state ( ) , sol : : in_place , traceback_function ) ) ;
m_last_write_time = std : : filesystem : : last_write_time ( m_module_path ) ;
}
2023-06-06 07:40:40 +00:00
}
lua_module : : ~ lua_module ( )
{
2023-07-05 00:30:57 +02:00
{
2023-07-22 13:05:43 +02:00
std : : lock_guard guard ( m_registered_scripts_mutex ) ;
m_registered_scripts . clear ( ) ;
2023-07-05 00:30:57 +02:00
}
2023-07-22 13:05:43 +02:00
for ( const auto owned_tab : m_owned_tabs )
2023-07-13 09:36:13 +02:00
{
2023-07-22 13:05:43 +02:00
big : : g_gui_service - > remove_from_nav ( owned_tab ) ;
2023-07-13 09:36:13 +02:00
}
2023-06-06 07:40:40 +00:00
for ( auto memory : m_allocated_memory )
delete [ ] memory ;
}
2023-07-02 22:32:46 +02:00
rage : : joaat_t lua_module : : module_id ( ) const
2023-06-06 07:40:40 +00:00
{
return m_module_id ;
}
2023-07-02 22:32:46 +02:00
const std : : string & lua_module : : module_name ( ) const
2023-06-06 07:40:40 +00:00
{
return m_module_name ;
}
2023-06-27 20:13:05 +02:00
2023-07-22 13:05:43 +02:00
const std : : filesystem : : path & lua_module : : module_path ( ) const
{
return m_module_path ;
}
2023-07-02 22:32:46 +02:00
const std : : chrono : : time_point < std : : chrono : : file_clock > lua_module : : last_write_time ( ) const
{
return m_last_write_time ;
}
2023-12-13 23:19:52 +01:00
const bool lua_module : : is_disabled ( ) const
{
return m_disabled ;
}
2023-07-18 13:07:33 +02:00
void lua_module : : set_folder_for_lua_require ( folder & scripts_folder )
2023-07-13 09:36:13 +02:00
{
2023-07-22 13:05:43 +02:00
std : : string scripts_search_path = scripts_folder . get_path ( ) . string ( ) + " /?.lua; " ;
2023-07-13 09:36:13 +02:00
2023-07-22 13:05:43 +02:00
for ( const auto & entry : std : : filesystem : : recursive_directory_iterator ( scripts_folder . get_path ( ) , std : : filesystem : : directory_options : : skip_permission_denied ) )
{
if ( ! entry . is_directory ( ) )
continue ;
scripts_search_path + = entry . path ( ) . string ( ) + " /?.lua; " ;
}
// Remove final ';'
scripts_search_path . pop_back ( ) ;
m_state [ " package " ] [ " path " ] = scripts_search_path ;
2023-07-13 09:36:13 +02:00
}
void lua_module : : sandbox_lua_os_library ( )
2023-06-27 20:13:05 +02:00
{
2023-07-22 13:05:43 +02:00
const auto & os = m_state [ " os " ] ;
sol : : table sandbox_os ( m_state , sol : : create ) ;
2023-07-13 09:36:13 +02:00
2023-07-15 22:07:10 +02:00
sandbox_os [ " clock " ] = os [ " clock " ] ;
sandbox_os [ " date " ] = os [ " date " ] ;
sandbox_os [ " difftime " ] = os [ " difftime " ] ;
sandbox_os [ " time " ] = os [ " time " ] ;
2023-07-13 09:36:13 +02:00
2023-07-22 13:05:43 +02:00
m_state [ " os " ] = sandbox_os ;
2023-07-13 09:36:13 +02:00
}
2024-02-24 11:10:58 +01:00
static std : : optional < std : : filesystem : : path > make_absolute ( const std : : filesystem : : path & root , const std : : filesystem : : path & user_path )
{
auto final_path = std : : filesystem : : weakly_canonical ( root / user_path ) ;
auto [ root_end , nothing ] = std : : mismatch ( root . begin ( ) , root . end ( ) , final_path . begin ( ) ) ;
if ( root_end ! = root . end ( ) )
return std : : nullopt ;
return final_path ;
} ;
2023-12-08 09:14:57 +01:00
void lua_module : : sandbox_lua_io_library ( )
{
auto io = m_state [ " io " ] ;
sol : : table sandbox_io ( m_state , sol : : create ) ;
2024-02-24 11:10:58 +01:00
m_io_open = io [ " open " ] ;
2023-12-08 09:14:57 +01:00
2024-02-24 11:10:58 +01:00
// Lua API: Table
// Name: io
// Table for file manipulation. Modified for security purposes.
2023-12-08 09:14:57 +01:00
2024-02-24 11:10:58 +01:00
// Lua API: Function
// Table: io
// Name: open
// Returns: file_handle: file handle or nil if can't read / write to the given path.
sandbox_io [ " open " ] = [ this ] ( const std : : string & filename , const std : : string & mode ) {
2023-12-08 09:14:57 +01:00
const auto scripts_config_sub_path = make_absolute ( g_lua_manager - > get_scripts_config_folder ( ) . get_path ( ) , filename ) ;
if ( ! scripts_config_sub_path )
{
LOG ( WARNING ) < < " io.open is restricted to the scripts_config folder, and the filename provided ( " < < filename < < " ) is outside of it. " ;
return sol : : reference ( sol : : lua_nil ) ;
}
const auto res = m_io_open ( scripts_config_sub_path . value ( ) . u8string ( ) . c_str ( ) , mode ) . get < sol : : reference > ( ) ;
if ( res . get_type ( ) = = sol : : type : : lua_nil )
{
2024-02-24 11:10:58 +01:00
LOG ( WARNING ) < < " Couldn't io.open a file called " < < filename < < " mode ( " < < mode < < " ). Note that io.open is restricted to the scripts_config folder. If you want to check if a file exists, use io.exists " ;
2023-12-08 09:14:57 +01:00
}
return res ;
} ;
2024-02-24 11:10:58 +01:00
// Lua API: Function
// Table: io
// Name: exists
// Returns: boolean: True if the passed file path exists
sandbox_io [ " exists " ] = [ ] ( const std : : string & filename ) - > bool {
const auto scripts_config_sub_path = make_absolute ( g_lua_manager - > get_scripts_config_folder ( ) . get_path ( ) , filename ) ;
if ( ! scripts_config_sub_path )
{
LOG ( WARNING ) < < " io.open is restricted to the scripts_config folder, and the filename provided ( " < < filename < < " ) is outside of it. " ;
return false ;
}
return std : : filesystem : : exists ( * scripts_config_sub_path ) ;
} ;
2023-12-08 09:14:57 +01:00
m_state [ " io " ] = sandbox_io ;
}
2023-07-15 22:07:10 +02:00
template < size_t N >
static constexpr auto not_supported_lua_function ( const char ( & function_name ) [ N ] )
{
2023-07-22 13:05:43 +02:00
return [ function_name ] ( sol : : this_state state , sol : : variadic_args args ) {
big : : lua_module * module = sol : : state_view ( state ) [ " !this " ] ;
2023-07-15 22:07:10 +02:00
LOG ( FATAL ) < < module - > module_name ( ) < < " tried calling a currently not supported lua function: " < < function_name ;
2023-07-16 23:32:34 +02:00
Logger : : FlushQueue ( ) ;
2023-07-15 22:07:10 +02:00
} ;
}
2023-07-18 13:07:33 +02:00
void lua_module : : sandbox_lua_loads ( folder & scripts_folder )
2023-07-13 09:36:13 +02:00
{
// That's from lua base lib, luaB
2023-07-22 13:05:43 +02:00
m_state [ " load " ] = not_supported_lua_function ( " load " ) ;
m_state [ " loadstring " ] = not_supported_lua_function ( " loadstring " ) ;
m_state [ " loadfile " ] = not_supported_lua_function ( " loadfile " ) ;
m_state [ " dofile " ] = not_supported_lua_function ( " dofile " ) ;
2023-07-13 09:36:13 +02:00
// That's from lua package lib.
// We only allow dependencies between .lua files, no DLLs.
2023-07-22 13:05:43 +02:00
m_state [ " package " ] [ " loadlib " ] = not_supported_lua_function ( " package.loadlib " ) ;
m_state [ " package " ] [ " cpath " ] = " " ;
2023-07-13 09:36:13 +02:00
// 1 2 3 4
// {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL};
2023-07-22 13:05:43 +02:00
m_state [ " package " ] [ " searchers " ] [ 3 ] = not_supported_lua_function ( " package.searcher C " ) ;
m_state [ " package " ] [ " searchers " ] [ 4 ] = not_supported_lua_function ( " package.searcher Croot " ) ;
2023-07-13 09:36:13 +02:00
2023-07-18 13:07:33 +02:00
set_folder_for_lua_require ( scripts_folder ) ;
2023-06-27 20:13:05 +02:00
}
2023-07-18 13:07:33 +02:00
void lua_module : : init_lua_api ( folder & scripts_folder )
2023-06-27 20:13:05 +02:00
{
2023-07-13 09:36:13 +02:00
// https://blog.rubenwardy.com/2020/07/26/sol3-script-sandbox/
// https://www.lua.org/manual/5.4/manual.html#pdf-require
sandbox_lua_os_library ( ) ;
2023-12-08 09:14:57 +01:00
sandbox_lua_io_library ( ) ;
2023-07-18 13:07:33 +02:00
sandbox_lua_loads ( scripts_folder ) ;
2023-07-13 09:36:13 +02:00
2023-07-22 13:05:43 +02:00
lua : : log : : bind ( m_state ) ;
lua : : globals : : bind ( m_state ) ;
lua : : script : : bind ( m_state ) ;
lua : : native : : bind ( m_state ) ;
lua : : memory : : bind ( m_state ) ;
lua : : gui : : bind ( m_state ) ;
lua : : network : : bind ( m_state ) ;
lua : : command : : bind ( m_state ) ;
lua : : tunables : : bind ( m_state ) ;
lua : : locals : : bind ( m_state ) ;
lua : : event : : bind ( m_state ) ;
lua : : vector : : bind ( m_state ) ;
lua : : global_table : : bind ( m_state ) ;
lua : : imgui : : bind ( m_state , m_state . globals ( ) ) ;
lua : : entities : : bind ( m_state ) ;
2024-01-28 17:18:44 +03:00
lua : : self : : bind ( m_state ) ;
2023-07-28 10:07:17 +02:00
lua : : stats : : bind ( m_state ) ;
2023-12-05 03:58:35 -05:00
lua : : weapons : : bind ( m_state ) ;
lua : : vehicles : : bind ( m_state ) ;
2023-07-22 13:05:43 +02:00
}
void lua_module : : load_and_call_script ( )
{
auto result = m_state . safe_script_file ( m_module_path . string ( ) , & sol : : script_pass_on_error , sol : : load_mode : : text ) ;
if ( ! result . valid ( ) )
{
LOG ( FATAL ) < < m_module_name < < " failed to load: " < < result . get < sol : : error > ( ) . what ( ) ;
Logger : : FlushQueue ( ) ;
}
else
{
LOG ( INFO ) < < " Loaded " < < m_module_name ;
}
}
void lua_module : : tick_scripts ( )
{
std : : lock_guard guard ( m_registered_scripts_mutex ) ;
2023-07-29 11:22:04 +02:00
const auto script_count = m_registered_scripts . size ( ) ;
for ( size_t i = 0 ; i < script_count ; i + + )
2023-07-22 13:05:43 +02:00
{
2023-07-29 11:22:04 +02:00
const auto script = m_registered_scripts [ i ] . get ( ) ;
2023-07-22 13:05:43 +02:00
if ( script - > is_enabled ( ) )
{
script - > tick ( ) ;
}
}
}
void lua_module : : cleanup_done_scripts ( )
{
std : : lock_guard guard ( m_registered_scripts_mutex ) ;
std : : erase_if ( m_registered_scripts , [ ] ( auto & script ) {
return script - > is_done ( ) ;
} ) ;
2023-06-27 20:13:05 +02:00
}
2024-02-24 11:10:58 +01:00
}