2013-08-04 17:11:25 -04:00
// LuaState.cpp
// Implements the cLuaState class representing the wrapper over lua_State *, provides associated helper functions
# include "Globals.h"
# include "LuaState.h"
extern " C "
{
2013-11-26 12:14:46 -05:00
# include "lua/src/lualib.h"
2013-08-04 17:11:25 -04:00
}
2014-03-25 13:15:05 -04:00
# undef TOLUA_TEMPLATE_BIND
2013-12-08 12:24:56 -05:00
# include "tolua++/include/tolua++.h"
2013-08-04 17:11:25 -04:00
# include "Bindings.h"
# include "ManualBindings.h"
2014-03-02 04:12:29 -05:00
# include "DeprecatedBindings.h"
2015-12-23 05:08:39 -05:00
# include "LuaJson.h"
2014-10-19 05:46:38 -04:00
# include "../Entities/Entity.h"
# include "../BlockEntities/BlockEntity.h"
2017-01-17 16:38:04 -05:00
# include "../DeadlockDetect.h"
2013-08-04 17:11:25 -04:00
2016-03-02 05:34:39 -05:00
2015-05-09 03:25:09 -04:00
// fwd: "SQLite/lsqlite3.c"
2013-08-04 17:11:25 -04:00
extern " C "
{
2013-12-27 05:51:08 -05:00
int luaopen_lsqlite3 ( lua_State * L ) ;
2013-08-04 17:11:25 -04:00
}
2015-05-09 03:25:09 -04:00
// fwd: "LuaExpat/lxplib.c":
2013-08-04 17:11:25 -04:00
extern " C "
{
int luaopen_lxp ( lua_State * L ) ;
}
2013-08-15 07:05:24 -04:00
const cLuaState : : cRet cLuaState : : Return = { } ;
2016-08-16 07:02:08 -04:00
const cLuaState : : cNil cLuaState : : Nil = { } ;
2013-08-08 08:08:21 -04:00
2017-01-09 09:56:16 -05:00
////////////////////////////////////////////////////////////////////////////////
// cCanonLuaStates:
/** Tracks the canon cLuaState instances for each lua_State pointer.
Used for tracked refs - the ref needs to be tracked by a single cLuaState ( the canon state ) , despite being created from a different ( attached ) cLuaState .
The canon state must be available without accessing the Lua state itself ( so it cannot be stored within Lua ) . */
class cCanonLuaStates
{
public :
/** Returns the canon Lua state for the specified lua_State pointer. */
static cLuaState * GetCanonState ( lua_State * a_LuaState )
{
auto & inst = GetInstance ( ) ;
cCSLock lock ( inst . m_CS ) ;
auto itr = inst . m_CanonStates . find ( a_LuaState ) ;
if ( itr = = inst . m_CanonStates . end ( ) )
{
return nullptr ;
}
return itr - > second ;
}
/** Adds a new canon cLuaState instance to the map.
Used when a new Lua state is created , this informs the map that a new canon Lua state should be tracked . */
static void Add ( cLuaState & a_LuaState )
{
auto & inst = GetInstance ( ) ;
cCSLock lock ( inst . m_CS ) ;
ASSERT ( inst . m_CanonStates . find ( a_LuaState ) = = inst . m_CanonStates . end ( ) ) ;
inst . m_CanonStates [ a_LuaState . operator lua_State * ( ) ] = & a_LuaState ;
}
/** Removes the bindings between the specified canon state and its lua_State pointer.
Used when a Lua state is being closed . */
static void Remove ( cLuaState & a_LuaState )
{
auto & inst = GetInstance ( ) ;
cCSLock lock ( inst . m_CS ) ;
auto itr = inst . m_CanonStates . find ( a_LuaState ) ;
ASSERT ( itr ! = inst . m_CanonStates . end ( ) ) ;
inst . m_CanonStates . erase ( itr ) ;
}
protected :
/** The mutex protecting m_CanonStates against multithreaded access. */
cCriticalSection m_CS ;
/** Map of lua_State pointers to their canon cLuaState instances. */
std : : map < lua_State * , cLuaState * > m_CanonStates ;
/** Returns the singleton instance of this class. */
static cCanonLuaStates & GetInstance ( void )
{
static cCanonLuaStates canonLuaStates ;
return canonLuaStates ;
}
} ;
2016-03-02 05:34:39 -05:00
2013-08-08 08:08:21 -04:00
2015-09-28 15:30:31 -04:00
////////////////////////////////////////////////////////////////////////////////
// cLuaStateTracker:
void cLuaStateTracker : : Add ( cLuaState & a_LuaState )
{
auto & Instance = Get ( ) ;
cCSLock Lock ( Instance . m_CSLuaStates ) ;
Instance . m_LuaStates . push_back ( & a_LuaState ) ;
}
void cLuaStateTracker : : Del ( cLuaState & a_LuaState )
{
auto & Instance = Get ( ) ;
cCSLock Lock ( Instance . m_CSLuaStates ) ;
Instance . m_LuaStates . erase (
std : : remove_if (
Instance . m_LuaStates . begin ( ) , Instance . m_LuaStates . end ( ) ,
[ & a_LuaState ] ( cLuaStatePtr a_StoredLuaState )
{
return ( & a_LuaState = = a_StoredLuaState ) ;
}
) ,
Instance . m_LuaStates . end ( )
) ;
}
AString cLuaStateTracker : : GetStats ( void )
{
auto & Instance = Get ( ) ;
cCSLock Lock ( Instance . m_CSLuaStates ) ;
AString res ;
int Total = 0 ;
for ( auto state : Instance . m_LuaStates )
{
int Mem = 0 ;
if ( ! state - > Call ( " collectgarbage " , " count " , cLuaState : : Return , Mem ) )
{
res . append ( Printf ( " Cannot query memory for state \" %s \" \n " , state - > GetSubsystemName ( ) . c_str ( ) ) ) ;
}
else
{
res . append ( Printf ( " State \" %s \" is using %d KiB of memory \n " , state - > GetSubsystemName ( ) . c_str ( ) , Mem ) ) ;
Total + = Mem ;
}
}
res . append ( Printf ( " Total memory used by Lua: %d KiB \n " , Total ) ) ;
return res ;
}
cLuaStateTracker & cLuaStateTracker : : Get ( void )
{
static cLuaStateTracker Inst ; // The singleton
return Inst ;
}
2016-03-02 05:34:39 -05:00
////////////////////////////////////////////////////////////////////////////////
2016-07-06 14:52:04 -04:00
// cLuaState::cTrackedRef:
2016-03-02 05:34:39 -05:00
2016-07-06 14:52:04 -04:00
cLuaState : : cTrackedRef : : cTrackedRef ( void ) :
2016-06-12 10:53:24 -04:00
m_CS ( nullptr )
{
}
2016-07-06 14:52:04 -04:00
bool cLuaState : : cTrackedRef : : RefStack ( cLuaState & a_LuaState , int a_StackPos )
2016-03-02 05:34:39 -05:00
{
// Clear any previous callback:
Clear ( ) ;
// Add self to LuaState's callback-tracking:
2016-06-12 10:53:24 -04:00
auto canonState = a_LuaState . QueryCanonLuaState ( ) ;
2016-07-06 14:52:04 -04:00
canonState - > TrackRef ( * this ) ;
2016-03-02 05:34:39 -05:00
// Store the new callback:
2016-06-12 10:53:24 -04:00
m_CS = & ( canonState - > m_CS ) ;
m_Ref . RefStack ( * canonState , a_StackPos ) ;
2016-03-02 05:34:39 -05:00
return true ;
}
2016-07-06 14:52:04 -04:00
void cLuaState : : cTrackedRef : : Clear ( void )
2016-03-02 05:34:39 -05:00
{
2016-07-06 14:52:04 -04:00
// Free the reference:
2016-03-02 05:34:39 -05:00
lua_State * luaState = nullptr ;
{
2017-01-13 04:31:05 -05:00
auto cs = m_CS . load ( ) ;
2016-06-12 10:53:24 -04:00
if ( cs ! = nullptr )
2016-03-02 05:34:39 -05:00
{
2016-06-12 10:53:24 -04:00
cCSLock Lock ( * cs ) ;
if ( ! m_Ref . IsValid ( ) )
{
return ;
}
luaState = m_Ref . GetLuaState ( ) ;
m_Ref . UnRef ( ) ;
2016-03-02 05:34:39 -05:00
}
}
2016-07-06 14:52:04 -04:00
m_CS = nullptr ;
2016-03-02 05:34:39 -05:00
// Remove from LuaState's callback-tracking:
2016-06-12 10:53:24 -04:00
if ( luaState = = nullptr )
{
return ;
}
2016-07-06 14:52:04 -04:00
cLuaState ( luaState ) . UntrackRef ( * this ) ;
2016-03-02 05:34:39 -05:00
}
2016-07-06 14:52:04 -04:00
bool cLuaState : : cTrackedRef : : IsValid ( void )
2016-03-02 04:12:43 -05:00
{
2017-01-13 04:31:05 -05:00
auto cs = m_CS . load ( ) ;
2016-06-12 10:53:24 -04:00
if ( cs = = nullptr )
{
return false ;
}
cCSLock lock ( * cs ) ;
2016-03-02 04:12:43 -05:00
return m_Ref . IsValid ( ) ;
}
2016-07-06 14:52:04 -04:00
bool cLuaState : : cTrackedRef : : IsSameLuaState ( cLuaState & a_LuaState )
2016-06-10 15:30:07 -04:00
{
2017-01-13 04:31:05 -05:00
auto cs = m_CS . load ( ) ;
2016-06-12 10:53:24 -04:00
if ( cs = = nullptr )
{
return false ;
}
cCSLock lock ( * cs ) ;
2016-06-10 15:30:07 -04:00
if ( ! m_Ref . IsValid ( ) )
{
return false ;
}
auto canonState = a_LuaState . QueryCanonLuaState ( ) ;
if ( canonState = = nullptr )
{
return false ;
}
return ( m_Ref . GetLuaState ( ) = = static_cast < lua_State * > ( * canonState ) ) ;
}
2016-07-06 14:52:04 -04:00
void cLuaState : : cTrackedRef : : Invalidate ( void )
2016-03-02 05:34:39 -05:00
{
2017-01-13 04:31:05 -05:00
auto cs = m_CS . load ( ) ;
2016-06-12 10:53:24 -04:00
if ( cs = = nullptr )
{
// Already invalid
return ;
}
cCSLock Lock ( * cs ) ;
2016-03-02 05:34:39 -05:00
if ( ! m_Ref . IsValid ( ) )
{
2016-06-12 10:53:24 -04:00
LOGD ( " %s: Inconsistent callback at %p, has a CS but an invalid Ref. This should not happen " ,
2016-03-02 05:34:39 -05:00
__FUNCTION__ , reinterpret_cast < void * > ( this )
) ;
return ;
}
m_Ref . UnRef ( ) ;
2016-07-06 14:52:04 -04:00
m_CS = nullptr ;
}
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cCallback:
bool cLuaState : : cCallback : : RefStack ( cLuaState & a_LuaState , int a_StackPos )
{
// Check if the stack contains a function:
if ( ! lua_isfunction ( a_LuaState , a_StackPos ) )
{
return false ;
}
return Super : : RefStack ( a_LuaState , a_StackPos ) ;
}
2016-08-14 10:26:31 -04:00
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cOptionalCallback:
bool cLuaState : : cOptionalCallback : : RefStack ( cLuaState & a_LuaState , int a_StackPos )
{
// If the stack pos is nil, make this an empty callback:
if ( lua_isnil ( a_LuaState , a_StackPos ) )
{
Clear ( ) ;
return true ;
}
// Use default cCallback implementation:
return Super : : RefStack ( a_LuaState , a_StackPos ) ;
}
2016-07-06 14:52:04 -04:00
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cTableRef:
bool cLuaState : : cTableRef : : RefStack ( cLuaState & a_LuaState , int a_StackPos )
{
// Check if the stack contains a table:
if ( ! lua_istable ( a_LuaState , a_StackPos ) )
{
return false ;
}
return Super : : RefStack ( a_LuaState , a_StackPos ) ;
2016-03-02 05:34:39 -05:00
}
2016-08-14 10:26:31 -04:00
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cStackTable:
cLuaState : : cStackTable : : cStackTable ( cLuaState & a_LuaState , int a_StackPos ) :
m_LuaState ( a_LuaState ) ,
m_StackPos ( a_StackPos )
{
ASSERT ( lua_istable ( a_LuaState , a_StackPos ) ) ;
}
void cLuaState : : cStackTable : : ForEachArrayElement ( std : : function < bool ( cLuaState & a_LuaState , int a_Index ) > a_ElementCallback ) const
{
auto numElements = luaL_getn ( m_LuaState , m_StackPos ) ;
# ifdef _DEBUG
auto stackTop = lua_gettop ( m_LuaState ) ;
# endif
for ( int idx = 1 ; idx < = numElements ; idx + + )
{
// Push the idx-th element of the array onto stack top and call the callback:
lua_rawgeti ( m_LuaState , m_StackPos , idx ) ;
auto shouldAbort = a_ElementCallback ( m_LuaState , idx ) ;
ASSERT ( lua_gettop ( m_LuaState ) = = stackTop + 1 ) ; // The element callback must not change the Lua stack below the value
lua_pop ( m_LuaState , 1 ) ;
if ( shouldAbort )
{
// The callback wants to abort
return ;
}
}
}
2016-08-16 05:55:12 -04:00
void cLuaState : : cStackTable : : ForEachElement ( std : : function < bool ( cLuaState & a_LuaState ) > a_ElementCallback ) const
{
# ifdef _DEBUG
auto stackTop = lua_gettop ( m_LuaState ) ;
# endif
lua_pushvalue ( m_LuaState , m_StackPos ) ; // Stk: <table>
lua_pushnil ( m_LuaState ) ; // Stk: <table> nil
while ( lua_next ( m_LuaState , - 2 ) ) // Stk: <table> <key> <val>
{
auto shouldAbort = a_ElementCallback ( m_LuaState ) ;
ASSERT ( lua_gettop ( m_LuaState ) = = stackTop + 3 ) ; // The element callback must not change the Lua stack below the value
lua_pop ( m_LuaState , 1 ) ; // Stk: <table> <key>
if ( shouldAbort )
{
// The callback wants to abort
lua_pop ( m_LuaState , 2 ) ; // Stk: empty
return ;
}
}
// Stk: <table>
lua_pop ( m_LuaState , 1 ) ; // Stk: empty
}
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2013-08-04 17:11:25 -04:00
// cLuaState:
cLuaState : : cLuaState ( const AString & a_SubsystemName ) :
2014-10-20 16:55:07 -04:00
m_LuaState ( nullptr ) ,
2013-08-06 02:01:00 -04:00
m_IsOwned ( false ) ,
2013-08-06 02:59:54 -04:00
m_SubsystemName ( a_SubsystemName ) ,
m_NumCurrentFunctionArgs ( - 1 )
2013-08-04 17:11:25 -04:00
{
}
2013-08-06 02:01:00 -04:00
cLuaState : : cLuaState ( lua_State * a_AttachState ) :
m_LuaState ( a_AttachState ) ,
m_IsOwned ( false ) ,
2013-08-06 02:59:54 -04:00
m_SubsystemName ( " <attached> " ) ,
m_NumCurrentFunctionArgs ( - 1 )
2013-08-06 02:01:00 -04:00
{
}
2013-08-04 17:11:25 -04:00
cLuaState : : ~ cLuaState ( )
{
if ( IsValid ( ) )
{
2013-08-07 08:26:18 -04:00
if ( m_IsOwned )
{
Close ( ) ;
}
else
{
Detach ( ) ;
}
2013-08-04 17:11:25 -04:00
}
}
void cLuaState : : Create ( void )
{
2014-10-20 16:55:07 -04:00
if ( m_LuaState ! = nullptr )
2013-08-04 17:11:25 -04:00
{
LOGWARNING ( " %s: Trying to create an already-existing LuaState, ignoring. " , __FUNCTION__ ) ;
return ;
}
m_LuaState = lua_open ( ) ;
luaL_openlibs ( m_LuaState ) ;
2014-03-12 08:05:28 -04:00
m_IsOwned = true ;
2015-09-28 15:30:31 -04:00
cLuaStateTracker : : Add ( * this ) ;
2017-01-09 09:56:16 -05:00
cCanonLuaStates : : Add ( * this ) ;
2014-03-12 08:05:28 -04:00
}
void cLuaState : : RegisterAPILibs ( void )
{
2016-08-11 15:04:29 -04:00
auto top = lua_gettop ( m_LuaState ) ;
2013-08-04 17:11:25 -04:00
tolua_AllToLua_open ( m_LuaState ) ;
2016-08-11 15:04:29 -04:00
ASSERT ( top = = lua_gettop ( m_LuaState ) ) ;
2015-05-13 05:30:57 -04:00
cManualBindings : : Bind ( m_LuaState ) ;
2016-08-11 15:04:29 -04:00
ASSERT ( top = = lua_gettop ( m_LuaState ) ) ;
2014-03-02 04:12:29 -05:00
DeprecatedBindings : : Bind ( m_LuaState ) ;
2016-08-11 15:04:29 -04:00
ASSERT ( top = = lua_gettop ( m_LuaState ) ) ;
2015-12-23 05:08:39 -05:00
cLuaJson : : Bind ( * this ) ;
2016-08-11 15:04:29 -04:00
ASSERT ( top = = lua_gettop ( m_LuaState ) ) ;
2013-08-04 17:11:25 -04:00
luaopen_lsqlite3 ( m_LuaState ) ;
2016-08-11 15:04:29 -04:00
if ( top = = lua_gettop ( m_LuaState ) - 1 )
{
// lsqlite3 left the "sqlite3" table on the stack, pop it:
lua_pop ( m_LuaState , 1 ) ;
}
ASSERT ( top = = lua_gettop ( m_LuaState ) ) ;
2013-08-04 17:11:25 -04:00
luaopen_lxp ( m_LuaState ) ;
2016-08-11 15:04:29 -04:00
if ( top = = lua_gettop ( m_LuaState ) - 1 )
{
// lxp left the unregistered "lxp" table on the stack, register and pop it (#3304):
lua_setglobal ( m_LuaState , " lxp " ) ;
}
ASSERT ( top = = lua_gettop ( m_LuaState ) ) ;
2013-08-04 17:11:25 -04:00
}
void cLuaState : : Close ( void )
{
2014-10-20 16:55:07 -04:00
if ( m_LuaState = = nullptr )
2013-08-04 17:11:25 -04:00
{
LOGWARNING ( " %s: Trying to close an invalid LuaState, ignoring. " , __FUNCTION__ ) ;
return ;
}
2013-08-06 02:01:00 -04:00
if ( ! m_IsOwned )
{
LOGWARNING (
" %s: Detected mis-use, calling Close() on an attached state (0x%p). Detaching instead. " ,
2015-09-24 10:04:44 -04:00
__FUNCTION__ , static_cast < void * > ( m_LuaState )
2013-08-06 02:01:00 -04:00
) ;
Detach ( ) ;
return ;
}
2016-03-02 05:34:39 -05:00
2016-07-06 14:52:04 -04:00
// Invalidate all tracked refs:
2016-03-02 05:34:39 -05:00
{
2016-07-06 14:52:04 -04:00
cCSLock Lock ( m_CSTrackedRefs ) ;
for ( auto & r : m_TrackedRefs )
2016-03-02 05:34:39 -05:00
{
2016-07-06 14:52:04 -04:00
r - > Invalidate ( ) ;
2016-03-02 05:34:39 -05:00
}
2017-01-13 04:31:05 -05:00
m_TrackedRefs . clear ( ) ;
2016-03-02 05:34:39 -05:00
}
2017-01-09 09:56:16 -05:00
cCanonLuaStates : : Remove ( * this ) ;
2015-09-28 15:30:31 -04:00
cLuaStateTracker : : Del ( * this ) ;
2013-08-04 17:11:25 -04:00
lua_close ( m_LuaState ) ;
2014-10-20 16:55:07 -04:00
m_LuaState = nullptr ;
2013-08-06 02:01:00 -04:00
m_IsOwned = false ;
}
void cLuaState : : Attach ( lua_State * a_State )
{
2014-10-20 16:55:07 -04:00
if ( m_LuaState ! = nullptr )
2013-08-06 02:01:00 -04:00
{
2015-09-24 10:04:44 -04:00
LOGINFO ( " %s: Already contains a LuaState (0x%p), will be closed / detached. " , __FUNCTION__ , static_cast < void * > ( m_LuaState ) ) ;
2013-08-06 02:01:00 -04:00
if ( m_IsOwned )
{
Close ( ) ;
}
else
{
Detach ( ) ;
}
}
m_LuaState = a_State ;
m_IsOwned = false ;
}
void cLuaState : : Detach ( void )
{
2014-10-20 16:55:07 -04:00
if ( m_LuaState = = nullptr )
2013-08-06 02:01:00 -04:00
{
return ;
}
if ( m_IsOwned )
{
LOGWARNING (
" %s: Detected a mis-use, calling Detach() when the state is owned. Closing the owned state (0x%p). " ,
2015-09-24 10:04:44 -04:00
__FUNCTION__ , static_cast < void * > ( m_LuaState )
2013-08-06 02:01:00 -04:00
) ;
Close ( ) ;
return ;
}
2014-10-20 16:55:07 -04:00
m_LuaState = nullptr ;
2013-08-04 17:11:25 -04:00
}
2014-02-17 17:12:46 -05:00
void cLuaState : : AddPackagePath ( const AString & a_PathVariable , const AString & a_Path )
{
2016-11-10 10:46:31 -05:00
ASSERT_LUA_STACK_BALANCE ( m_LuaState ) ;
2014-02-17 17:12:46 -05:00
// Get the current path:
lua_getfield ( m_LuaState , LUA_GLOBALSINDEX , " package " ) ; // Stk: <package>
lua_getfield ( m_LuaState , - 1 , a_PathVariable . c_str ( ) ) ; // Stk: <package> <package.path>
size_t len = 0 ;
const char * PackagePath = lua_tolstring ( m_LuaState , - 1 , & len ) ;
2016-02-05 16:45:45 -05:00
2014-02-17 17:12:46 -05:00
// Append the new path:
AString NewPackagePath ( PackagePath , len ) ;
NewPackagePath . append ( LUA_PATHSEP ) ;
NewPackagePath . append ( a_Path ) ;
2016-02-05 16:45:45 -05:00
2014-02-17 17:12:46 -05:00
// Set the new path to the environment:
lua_pop ( m_LuaState , 1 ) ; // Stk: <package>
lua_pushlstring ( m_LuaState , NewPackagePath . c_str ( ) , NewPackagePath . length ( ) ) ; // Stk: <package> <NewPackagePath>
lua_setfield ( m_LuaState , - 2 , a_PathVariable . c_str ( ) ) ; // Stk: <package>
lua_pop ( m_LuaState , 1 ) ; // Stk: -
}
2015-06-17 09:21:20 -04:00
bool cLuaState : : LoadFile ( const AString & a_FileName , bool a_LogWarnings )
2013-08-04 17:11:25 -04:00
{
ASSERT ( IsValid ( ) ) ;
2016-11-10 10:46:31 -05:00
ASSERT_LUA_STACK_BALANCE ( m_LuaState ) ;
2015-11-11 04:32:42 -05:00
2013-08-04 17:11:25 -04:00
// Load the file:
int s = luaL_loadfile ( m_LuaState , a_FileName . c_str ( ) ) ;
2015-06-17 09:21:20 -04:00
if ( s ! = 0 )
2013-08-04 17:11:25 -04:00
{
2015-06-17 09:21:20 -04:00
if ( a_LogWarnings )
{
LOGWARNING ( " Can't load %s because of a load error in file %s: %d (%s) " , m_SubsystemName . c_str ( ) , a_FileName . c_str ( ) , s , lua_tostring ( m_LuaState , - 1 ) ) ;
}
lua_pop ( m_LuaState , 1 ) ;
2013-08-04 17:11:25 -04:00
return false ;
}
// Execute the globals:
2016-11-10 10:46:31 -05:00
s = lua_pcall ( m_LuaState , 0 , 0 , 0 ) ;
2015-06-17 09:21:20 -04:00
if ( s ! = 0 )
2013-08-04 17:11:25 -04:00
{
2015-06-17 09:21:20 -04:00
if ( a_LogWarnings )
{
LOGWARNING ( " Can't load %s because of an initialization error in file %s: %d (%s) " , m_SubsystemName . c_str ( ) , a_FileName . c_str ( ) , s , lua_tostring ( m_LuaState , - 1 ) ) ;
}
lua_pop ( m_LuaState , 1 ) ;
2013-08-04 17:11:25 -04:00
return false ;
}
2015-11-11 04:32:42 -05:00
return true ;
}
bool cLuaState : : LoadString ( const AString & a_StringToLoad , const AString & a_FileName , bool a_LogWarnings )
{
ASSERT ( IsValid ( ) ) ;
2016-11-10 10:46:31 -05:00
ASSERT_LUA_STACK_BALANCE ( m_LuaState ) ;
2015-11-11 04:32:42 -05:00
// Load the file:
int s = luaL_loadstring ( m_LuaState , a_StringToLoad . c_str ( ) ) ;
if ( s ! = 0 )
{
if ( a_LogWarnings )
{
LOGWARNING ( " Can't load %s because of a load error in string from \" %s \" : %d (%s) " , m_SubsystemName . c_str ( ) , a_FileName . c_str ( ) , s , lua_tostring ( m_LuaState , - 1 ) ) ;
}
lua_pop ( m_LuaState , 1 ) ;
return false ;
}
// Execute the globals:
s = lua_pcall ( m_LuaState , 0 , LUA_MULTRET , 0 ) ;
if ( s ! = 0 )
{
if ( a_LogWarnings )
{
LOGWARNING ( " Can't load %s because of an initialization error in string from \" %s \" : %d (%s) " , m_SubsystemName . c_str ( ) , a_FileName . c_str ( ) , s , lua_tostring ( m_LuaState , - 1 ) ) ;
}
lua_pop ( m_LuaState , 1 ) ;
return false ;
}
2013-08-04 17:11:25 -04:00
return true ;
}
2013-08-08 10:02:07 -04:00
bool cLuaState : : HasFunction ( const char * a_FunctionName )
{
if ( ! IsValid ( ) )
{
// This happens if cPlugin::Initialize() fails with an error
return false ;
}
2016-11-10 10:46:31 -05:00
ASSERT_LUA_STACK_BALANCE ( m_LuaState ) ;
2013-08-08 10:02:07 -04:00
lua_getglobal ( m_LuaState , a_FunctionName ) ;
bool res = ( ! lua_isnil ( m_LuaState , - 1 ) & & lua_isfunction ( m_LuaState , - 1 ) ) ;
lua_pop ( m_LuaState , 1 ) ;
return res ;
}
bool cLuaState : : PushFunction ( const char * a_FunctionName )
2013-08-04 17:11:25 -04:00
{
2013-08-06 02:59:54 -04:00
ASSERT ( m_NumCurrentFunctionArgs = = - 1 ) ; // If not, there's already something pushed onto the stack
2013-08-04 17:11:25 -04:00
if ( ! IsValid ( ) )
{
// This happens if cPlugin::Initialize() fails with an error
return false ;
}
2016-02-05 16:45:45 -05:00
2014-01-11 17:10:40 -05:00
// Push the error handler for lua_pcall()
lua_pushcfunction ( m_LuaState , & ReportFnCallErrors ) ;
2016-02-05 16:45:45 -05:00
2013-08-04 17:11:25 -04:00
lua_getglobal ( m_LuaState , a_FunctionName ) ;
if ( ! lua_isfunction ( m_LuaState , - 1 ) )
{
2013-08-08 10:02:07 -04:00
LOGWARNING ( " Error in %s: Could not find function %s() " , m_SubsystemName . c_str ( ) , a_FunctionName ) ;
2014-01-21 16:59:08 -05:00
lua_pop ( m_LuaState , 2 ) ;
2013-08-04 17:11:25 -04:00
return false ;
}
2013-08-06 02:59:54 -04:00
m_CurrentFunctionName . assign ( a_FunctionName ) ;
m_NumCurrentFunctionArgs = 0 ;
2013-08-04 17:11:25 -04:00
return true ;
}
2016-06-12 12:24:01 -04:00
bool cLuaState : : PushFunction ( const cRef & a_FnRef )
2013-08-04 17:11:25 -04:00
{
2013-08-06 02:59:54 -04:00
ASSERT ( IsValid ( ) ) ;
ASSERT ( m_NumCurrentFunctionArgs = = - 1 ) ; // If not, there's already something pushed onto the stack
2016-02-05 16:45:45 -05:00
2014-01-11 17:10:40 -05:00
// Push the error handler for lua_pcall()
lua_pushcfunction ( m_LuaState , & ReportFnCallErrors ) ;
2016-02-05 16:45:45 -05:00
2016-06-12 12:24:01 -04:00
lua_rawgeti ( m_LuaState , LUA_REGISTRYINDEX , static_cast < int > ( a_FnRef ) ) ; // same as lua_getref()
2013-08-04 17:11:25 -04:00
if ( ! lua_isfunction ( m_LuaState , - 1 ) )
{
2014-01-21 16:59:08 -05:00
lua_pop ( m_LuaState , 2 ) ;
2013-08-04 17:11:25 -04:00
return false ;
}
2013-08-06 02:59:54 -04:00
m_CurrentFunctionName = " <callback> " ;
m_NumCurrentFunctionArgs = 0 ;
2013-08-04 17:11:25 -04:00
return true ;
}
2016-07-06 14:52:04 -04:00
bool cLuaState : : PushFunction ( const cRef & a_TableRef , const char * a_FnName )
2013-08-07 08:26:18 -04:00
{
ASSERT ( IsValid ( ) ) ;
ASSERT ( m_NumCurrentFunctionArgs = = - 1 ) ; // If not, there's already something pushed onto the stack
2016-02-05 16:45:45 -05:00
2016-07-06 14:52:04 -04:00
if ( ! a_TableRef . IsValid ( ) )
{
return false ;
}
2014-01-11 17:10:40 -05:00
// Push the error handler for lua_pcall()
lua_pushcfunction ( m_LuaState , & ReportFnCallErrors ) ;
2016-02-05 16:45:45 -05:00
2016-07-06 14:52:04 -04:00
// Get the function from the table:
lua_rawgeti ( m_LuaState , LUA_REGISTRYINDEX , static_cast < int > ( a_TableRef ) ) ;
2013-08-07 08:26:18 -04:00
if ( ! lua_istable ( m_LuaState , - 1 ) )
{
// Not a table, bail out
2014-01-21 16:59:08 -05:00
lua_pop ( m_LuaState , 2 ) ;
2013-08-07 08:26:18 -04:00
return false ;
}
2016-07-06 14:52:04 -04:00
lua_getfield ( m_LuaState , - 1 , a_FnName ) ;
2013-08-07 08:26:18 -04:00
if ( lua_isnil ( m_LuaState , - 1 ) | | ! lua_isfunction ( m_LuaState , - 1 ) )
{
// Not a valid function, bail out
2014-01-29 16:56:23 -05:00
lua_pop ( m_LuaState , 3 ) ;
2013-08-07 08:26:18 -04:00
return false ;
}
2016-02-05 16:45:45 -05:00
2014-01-29 16:56:23 -05:00
// Pop the table off the stack:
lua_remove ( m_LuaState , - 2 ) ;
2016-02-05 16:45:45 -05:00
2016-07-06 14:52:04 -04:00
Printf ( m_CurrentFunctionName , " <table-callback %s> " , a_FnName ) ;
2013-08-07 08:26:18 -04:00
m_NumCurrentFunctionArgs = 0 ;
return true ;
}
2013-08-08 08:08:21 -04:00
void cLuaState : : Push ( const AString & a_String )
{
ASSERT ( IsValid ( ) ) ;
2014-01-07 09:55:23 -05:00
lua_pushlstring ( m_LuaState , a_String . data ( ) , a_String . size ( ) ) ;
2013-08-08 08:08:21 -04:00
}
2016-08-16 05:55:12 -04:00
void cLuaState : : Push ( const AStringMap & a_Dictionary )
{
ASSERT ( IsValid ( ) ) ;
lua_createtable ( m_LuaState , 0 , static_cast < int > ( a_Dictionary . size ( ) ) ) ;
int newTable = lua_gettop ( m_LuaState ) ;
for ( const auto & item : a_Dictionary )
{
Push ( item . first ) ; // key
Push ( item . second ) ; // value
lua_rawset ( m_LuaState , newTable ) ;
}
}
2013-08-08 08:08:21 -04:00
void cLuaState : : Push ( const AStringVector & a_Vector )
2013-08-04 17:11:25 -04:00
{
2013-08-06 02:59:54 -04:00
ASSERT ( IsValid ( ) ) ;
2015-07-29 11:04:03 -04:00
lua_createtable ( m_LuaState , static_cast < int > ( a_Vector . size ( ) ) , 0 ) ;
2013-08-04 17:11:25 -04:00
int newTable = lua_gettop ( m_LuaState ) ;
int index = 1 ;
for ( AStringVector : : const_iterator itr = a_Vector . begin ( ) , end = a_Vector . end ( ) ; itr ! = end ; + + itr , + + index )
{
tolua_pushstring ( m_LuaState , itr - > c_str ( ) ) ;
lua_rawseti ( m_LuaState , newTable , index ) ;
}
2013-08-06 02:59:54 -04:00
}
2014-06-03 13:29:04 -04:00
void cLuaState : : Push ( const char * a_Value )
2013-08-06 02:59:54 -04:00
{
ASSERT ( IsValid ( ) ) ;
2014-06-03 13:29:04 -04:00
tolua_pushstring ( m_LuaState , a_Value ) ;
2013-08-06 02:59:54 -04:00
}
2016-08-16 07:02:08 -04:00
void cLuaState : : Push ( const cNil & a_Nil )
{
ASSERT ( IsValid ( ) ) ;
lua_pushnil ( m_LuaState ) ;
2013-08-06 02:59:54 -04:00
}
2015-06-17 09:21:20 -04:00
void cLuaState : : Push ( const cLuaState : : cRef & a_Ref )
{
ASSERT ( IsValid ( ) ) ;
lua_rawgeti ( m_LuaState , LUA_REGISTRYINDEX , static_cast < int > ( a_Ref ) ) ;
}
2014-06-03 13:29:04 -04:00
void cLuaState : : Push ( const Vector3d & a_Vector )
2013-08-08 08:08:21 -04:00
{
ASSERT ( IsValid ( ) ) ;
2017-05-09 07:21:03 -04:00
auto c = new Vector3d ( a_Vector ) ;
tolua_pushusertype_and_takeownership ( m_LuaState , c , " Vector3<double> " ) ;
2014-08-22 04:33:15 -04:00
}
void cLuaState : : Push ( const Vector3i & a_Vector )
{
ASSERT ( IsValid ( ) ) ;
2017-05-09 07:21:03 -04:00
auto c = new Vector3i ( a_Vector ) ;
tolua_pushusertype_and_takeownership ( m_LuaState , c , " Vector3<int> " ) ;
2013-08-08 08:08:21 -04:00
}
2014-06-03 13:29:04 -04:00
void cLuaState : : Push ( bool a_Value )
2013-08-06 02:59:54 -04:00
{
ASSERT ( IsValid ( ) ) ;
2014-06-03 13:29:04 -04:00
tolua_pushboolean ( m_LuaState , a_Value ? 1 : 0 ) ;
2013-08-06 02:59:54 -04:00
}
2014-06-03 13:29:04 -04:00
void cLuaState : : Push ( cEntity * a_Entity )
{
ASSERT ( IsValid ( ) ) ;
2014-10-20 11:32:09 -04:00
if ( a_Entity = = nullptr )
{
lua_pushnil ( m_LuaState ) ;
}
2014-10-19 06:49:54 -04:00
else
{
2014-10-21 06:43:06 -04:00
switch ( a_Entity - > GetEntityType ( ) )
{
case cEntity : : etMonster :
{
// Don't push specific mob types, as those are not exported in the API:
tolua_pushusertype ( m_LuaState , a_Entity , " cMonster " ) ;
break ;
}
case cEntity : : etPlayer :
{
tolua_pushusertype ( m_LuaState , a_Entity , " cPlayer " ) ;
break ;
}
case cEntity : : etPickup :
{
tolua_pushusertype ( m_LuaState , a_Entity , " cPickup " ) ;
break ;
}
case cEntity : : etTNT :
{
tolua_pushusertype ( m_LuaState , a_Entity , " cTNTEntity " ) ;
break ;
}
case cEntity : : etProjectile :
{
2014-10-22 10:06:16 -04:00
tolua_pushusertype ( m_LuaState , a_Entity , a_Entity - > GetClass ( ) ) ;
2014-10-21 06:43:06 -04:00
break ;
}
case cEntity : : etFloater :
{
tolua_pushusertype ( m_LuaState , a_Entity , " cFloater " ) ;
break ;
}
case cEntity : : etEntity :
case cEntity : : etEnderCrystal :
case cEntity : : etFallingBlock :
case cEntity : : etMinecart :
case cEntity : : etBoat :
case cEntity : : etExpOrb :
case cEntity : : etItemFrame :
case cEntity : : etPainting :
{
// Push the generic entity class type:
tolua_pushusertype ( m_LuaState , a_Entity , " cEntity " ) ;
}
} // switch (EntityType)
2014-10-19 06:49:54 -04:00
}
2014-06-03 13:29:04 -04:00
}
2015-01-30 15:24:02 -05:00
void cLuaState : : Push ( cLuaServerHandle * a_ServerHandle )
{
ASSERT ( IsValid ( ) ) ;
tolua_pushusertype ( m_LuaState , a_ServerHandle , " cServerHandle " ) ;
}
2015-01-28 09:14:05 -05:00
void cLuaState : : Push ( cLuaTCPLink * a_TCPLink )
{
ASSERT ( IsValid ( ) ) ;
tolua_pushusertype ( m_LuaState , a_TCPLink , " cTCPLink " ) ;
}
2015-02-20 08:28:05 -05:00
void cLuaState : : Push ( cLuaUDPEndpoint * a_UDPEndpoint )
{
ASSERT ( IsValid ( ) ) ;
tolua_pushusertype ( m_LuaState , a_UDPEndpoint , " cUDPEndpoint " ) ;
}
2014-06-03 13:29:04 -04:00
void cLuaState : : Push ( double a_Value )
2013-08-08 10:02:07 -04:00
{
ASSERT ( IsValid ( ) ) ;
2014-06-03 13:29:04 -04:00
tolua_pushnumber ( m_LuaState , a_Value ) ;
2013-08-08 10:02:07 -04:00
}
2014-06-03 13:29:04 -04:00
void cLuaState : : Push ( int a_Value )
2013-08-08 10:30:02 -04:00
{
ASSERT ( IsValid ( ) ) ;
2014-06-03 13:29:04 -04:00
tolua_pushnumber ( m_LuaState , a_Value ) ;
2013-08-08 10:30:02 -04:00
}
2015-09-26 16:54:18 -04:00
void cLuaState : : Push ( long a_Value )
{
ASSERT ( IsValid ( ) ) ;
tolua_pushnumber ( m_LuaState , static_cast < lua_Number > ( a_Value ) ) ;
}
2015-07-29 11:04:03 -04:00
void cLuaState : : Push ( UInt32 a_Value )
{
ASSERT ( IsValid ( ) ) ;
tolua_pushnumber ( m_LuaState , a_Value ) ;
}
2015-01-11 16:12:26 -05:00
void cLuaState : : Push ( std : : chrono : : milliseconds a_Value )
{
ASSERT ( IsValid ( ) ) ;
2015-01-18 05:02:17 -05:00
tolua_pushnumber ( m_LuaState , static_cast < lua_Number > ( a_Value . count ( ) ) ) ;
2015-01-11 16:12:26 -05:00
}
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
2013-08-11 08:57:07 -04:00
2016-06-10 15:30:07 -04:00
void cLuaState : : Pop ( int a_NumValuesToPop )
{
ASSERT ( IsValid ( ) ) ;
lua_pop ( m_LuaState , a_NumValuesToPop ) ;
}
2015-05-19 10:25:18 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , AString & a_Value )
2015-04-23 13:41:01 -04:00
{
size_t len = 0 ;
const char * data = lua_tolstring ( m_LuaState , a_StackPos , & len ) ;
if ( data ! = nullptr )
{
a_Value . assign ( data , len ) ;
2015-05-19 10:25:18 -04:00
return true ;
2015-04-23 13:41:01 -04:00
}
2015-05-19 10:25:18 -04:00
return false ;
2015-04-23 13:41:01 -04:00
}
2016-08-16 05:55:12 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , AStringMap & a_Value )
{
// Retrieve all values in a string => string dictionary table:
if ( ! lua_istable ( m_LuaState , a_StackPos ) )
{
return false ;
}
cStackTable tbl ( * this , a_StackPos ) ;
bool isValid = true ;
tbl . ForEachElement ( [ & isValid , & a_Value ] ( cLuaState & a_LuaState )
{
AString key , val ;
if ( a_LuaState . GetStackValues ( - 2 , key , val ) )
{
a_Value [ key ] = val ;
}
else
{
isValid = false ;
return true ;
}
return false ;
}
) ;
return isValid ;
}
2015-05-19 10:25:18 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , bool & a_ReturnedVal )
2013-08-08 08:08:21 -04:00
{
a_ReturnedVal = ( tolua_toboolean ( m_LuaState , a_StackPos , a_ReturnedVal ? 1 : 0 ) > 0 ) ;
2015-05-19 10:25:18 -04:00
return true ;
2013-08-08 08:08:21 -04:00
}
2016-03-02 04:12:43 -05:00
bool cLuaState : : GetStackValue ( int a_StackPos , cCallback & a_Callback )
{
return a_Callback . RefStack ( * this , a_StackPos ) ;
}
bool cLuaState : : GetStackValue ( int a_StackPos , cCallbackPtr & a_Callback )
2016-06-27 14:49:59 -04:00
{
if ( a_Callback = = nullptr )
{
a_Callback = cpp14 : : make_unique < cCallback > ( ) ;
}
return a_Callback - > RefStack ( * this , a_StackPos ) ;
}
2016-08-14 10:26:31 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , cOptionalCallback & a_Callback )
2016-03-02 04:12:43 -05:00
{
2016-08-14 10:26:31 -04:00
return a_Callback . RefStack ( * this , a_StackPos ) ;
2016-03-02 04:12:43 -05:00
}
2016-08-14 10:26:31 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , cOptionalCallbackPtr & a_Callback )
2016-07-06 14:52:04 -04:00
{
2016-08-14 10:26:31 -04:00
if ( a_Callback = = nullptr )
{
a_Callback = cpp14 : : make_unique < cOptionalCallback > ( ) ;
}
return a_Callback - > RefStack ( * this , a_StackPos ) ;
2016-07-06 14:52:04 -04:00
}
2016-08-14 10:26:31 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , cCallbackSharedPtr & a_Callback )
2016-07-06 14:52:04 -04:00
{
2016-08-14 10:26:31 -04:00
if ( a_Callback = = nullptr )
2016-07-06 14:52:04 -04:00
{
2016-08-14 10:26:31 -04:00
a_Callback = std : : make_shared < cCallback > ( ) ;
2016-07-06 14:52:04 -04:00
}
2016-08-14 10:26:31 -04:00
return a_Callback - > RefStack ( * this , a_StackPos ) ;
2016-07-06 14:52:04 -04:00
}
2015-05-19 10:25:18 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , cPluginManager : : CommandResult & a_Result )
2015-05-10 17:11:30 -04:00
{
if ( lua_isnumber ( m_LuaState , a_StackPos ) )
{
a_Result = static_cast < cPluginManager : : CommandResult > ( static_cast < int > ( ( tolua_tonumber ( m_LuaState , a_StackPos , a_Result ) ) ) ) ;
2015-05-19 10:25:18 -04:00
return true ;
2015-05-10 17:11:30 -04:00
}
2015-05-19 10:25:18 -04:00
return false ;
2015-05-10 17:11:30 -04:00
}
2015-05-19 10:25:18 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , cRef & a_Ref )
2013-08-08 08:08:21 -04:00
{
2015-04-23 13:41:01 -04:00
a_Ref . RefStack ( * this , a_StackPos ) ;
2015-05-19 10:25:18 -04:00
return true ;
2013-08-08 08:08:21 -04:00
}
2016-08-14 10:26:31 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , cStackTablePtr & a_StackTable )
{
// Only allow tables to be stored in a_StackTable:
if ( ! lua_istable ( m_LuaState , a_StackPos ) )
{
return false ;
}
// Assign the StackTable to the specified stack position:
a_StackTable = cpp14 : : make_unique < cStackTable > ( * this , a_StackPos ) ;
return true ;
}
bool cLuaState : : GetStackValue ( int a_StackPos , cTableRef & a_TableRef )
{
return a_TableRef . RefStack ( * this , a_StackPos ) ;
}
bool cLuaState : : GetStackValue ( int a_StackPos , cTableRefPtr & a_TableRef )
{
if ( a_TableRef = = nullptr )
{
a_TableRef = cpp14 : : make_unique < cTableRef > ( ) ;
}
return a_TableRef - > RefStack ( * this , a_StackPos ) ;
}
2016-07-06 14:52:04 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , cTrackedRef & a_Ref )
{
return a_Ref . RefStack ( * this , a_StackPos ) ;
}
bool cLuaState : : GetStackValue ( int a_StackPos , cTrackedRefPtr & a_Ref )
{
if ( a_Ref = = nullptr )
{
a_Ref = cpp14 : : make_unique < cTrackedRef > ( ) ;
}
return a_Ref - > RefStack ( * this , a_StackPos ) ;
}
bool cLuaState : : GetStackValue ( int a_StackPos , cTrackedRefSharedPtr & a_Ref )
{
if ( a_Ref = = nullptr )
{
a_Ref = std : : make_shared < cTrackedRef > ( ) ;
}
return a_Ref - > RefStack ( * this , a_StackPos ) ;
}
2015-05-19 10:25:18 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , double & a_ReturnedVal )
2013-08-08 08:08:21 -04:00
{
if ( lua_isnumber ( m_LuaState , a_StackPos ) )
{
2015-04-23 13:41:01 -04:00
a_ReturnedVal = tolua_tonumber ( m_LuaState , a_StackPos , a_ReturnedVal ) ;
2015-05-19 10:25:18 -04:00
return true ;
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
}
2015-05-19 10:25:18 -04:00
return false ;
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
}
2015-06-17 09:21:20 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , eBlockFace & a_ReturnedVal )
2015-05-08 18:32:02 -04:00
{
2015-06-17 09:21:20 -04:00
if ( ! lua_isnumber ( m_LuaState , a_StackPos ) )
2015-05-08 18:32:02 -04:00
{
2015-06-17 09:21:20 -04:00
return false ;
2015-05-08 18:32:02 -04:00
}
2015-06-17 09:21:20 -04:00
a_ReturnedVal = static_cast < eBlockFace > ( Clamp (
static_cast < int > ( tolua_tonumber ( m_LuaState , a_StackPos , a_ReturnedVal ) ) ,
static_cast < int > ( BLOCK_FACE_MIN ) , static_cast < int > ( BLOCK_FACE_MAX ) )
) ;
return true ;
2015-05-08 18:32:02 -04:00
}
2015-05-19 10:25:18 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , eWeather & a_ReturnedVal )
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
{
2015-04-23 13:41:01 -04:00
if ( ! lua_isnumber ( m_LuaState , a_StackPos ) )
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
{
2015-05-19 10:25:18 -04:00
return false ;
2013-08-08 08:08:21 -04:00
}
2015-04-23 13:41:01 -04:00
a_ReturnedVal = static_cast < eWeather > ( Clamp (
static_cast < int > ( tolua_tonumber ( m_LuaState , a_StackPos , a_ReturnedVal ) ) ,
static_cast < int > ( wSunny ) , static_cast < int > ( wThunderstorm ) )
) ;
2015-05-19 10:25:18 -04:00
return true ;
2014-07-03 11:49:21 -04:00
}
2015-06-17 09:21:20 -04:00
bool cLuaState : : GetStackValue ( int a_StackPos , float & a_ReturnedVal )
{
if ( lua_isnumber ( m_LuaState , a_StackPos ) )
{
a_ReturnedVal = static_cast < float > ( tolua_tonumber ( m_LuaState , a_StackPos , a_ReturnedVal ) ) ;
return true ;
}
return false ;
}
cLuaState : : cStackValue cLuaState : : WalkToValue ( const AString & a_Name )
{
2015-06-20 09:37:41 -04:00
// There needs to be at least one value on the stack:
ASSERT ( lua_gettop ( m_LuaState ) > 0 ) ;
// Iterate over path and replace the top of the stack with the walked element
2015-06-17 09:21:20 -04:00
lua_pushvalue ( m_LuaState , - 1 ) ; // Copy the stack value into the "working area"
2015-06-20 09:37:41 -04:00
auto path = StringSplit ( a_Name , " . " ) ;
2015-06-17 09:21:20 -04:00
for ( const auto & elem : path )
{
// If the value is not a table, bail out (error):
if ( ! lua_istable ( m_LuaState , - 1 ) )
{
lua_pop ( m_LuaState , 1 ) ;
return cStackValue ( ) ;
}
// Get the next part of the path:
lua_getfield ( m_LuaState , - 1 , elem . c_str ( ) ) ;
// Remove the previous value from the stack (keep only the new one):
lua_remove ( m_LuaState , - 2 ) ;
} // for elem - path[]
2015-11-11 04:32:42 -05:00
if ( lua_isnil ( m_LuaState , - 1 ) )
{
lua_pop ( m_LuaState , 1 ) ;
return cStackValue ( ) ;
}
2015-07-21 18:40:13 -04:00
return cStackValue ( * this ) ;
2015-06-17 09:21:20 -04:00
}
2015-11-11 04:32:42 -05:00
cLuaState : : cStackValue cLuaState : : WalkToNamedGlobal ( const AString & a_Name )
{
// Iterate over path and replace the top of the stack with the walked element
lua_getglobal ( m_LuaState , " _G " ) ;
auto path = StringSplit ( a_Name , " . " ) ;
for ( const auto & elem : path )
{
// If the value is not a table, bail out (error):
if ( ! lua_istable ( m_LuaState , - 1 ) )
{
lua_pop ( m_LuaState , 1 ) ;
return cStackValue ( ) ;
}
// Get the next part of the path:
lua_getfield ( m_LuaState , - 1 , elem . c_str ( ) ) ;
// Remove the previous value from the stack (keep only the new one):
lua_remove ( m_LuaState , - 2 ) ;
} // for elem - path[]
if ( lua_isnil ( m_LuaState , - 1 ) )
{
lua_pop ( m_LuaState , 1 ) ;
return cStackValue ( ) ;
}
2015-12-19 16:41:17 -05:00
return cStackValue ( * this ) ;
2015-11-11 04:32:42 -05:00
}
2013-08-06 02:59:54 -04:00
bool cLuaState : : CallFunction ( int a_NumResults )
2013-08-04 17:11:25 -04:00
{
2013-08-06 02:59:54 -04:00
ASSERT ( m_NumCurrentFunctionArgs > = 0 ) ; // A function must be pushed to stack first
2014-01-11 17:10:40 -05:00
ASSERT ( lua_isfunction ( m_LuaState , - m_NumCurrentFunctionArgs - 1 ) ) ; // The function to call
ASSERT ( lua_isfunction ( m_LuaState , - m_NumCurrentFunctionArgs - 2 ) ) ; // The error handler
2016-02-05 16:45:45 -05:00
2014-02-11 02:52:14 -05:00
// Save the current "stack" state and reset, in case the callback calls another function:
AString CurrentFunctionName ;
std : : swap ( m_CurrentFunctionName , CurrentFunctionName ) ;
int NumArgs = m_NumCurrentFunctionArgs ;
m_NumCurrentFunctionArgs = - 1 ;
2016-02-05 16:45:45 -05:00
2014-02-11 02:52:14 -05:00
// Call the function:
int s = lua_pcall ( m_LuaState , NumArgs , a_NumResults , - NumArgs - 2 ) ;
2014-01-11 17:10:40 -05:00
if ( s ! = 0 )
2013-08-04 17:11:25 -04:00
{
2014-01-11 17:10:40 -05:00
// The error has already been printed together with the stacktrace
2014-02-11 02:52:14 -05:00
LOGWARNING ( " Error in %s calling function %s() " , m_SubsystemName . c_str ( ) , CurrentFunctionName . c_str ( ) ) ;
2016-11-10 10:46:31 -05:00
// Remove the error handler and error message from the stack:
2017-06-09 06:16:31 -04:00
auto top = lua_gettop ( m_LuaState ) ;
if ( top < 2 )
{
LogStackValues ( Printf ( " The Lua stack is in an unexpected state, expected at least two values there, but got %d " , top ) . c_str ( ) ) ;
}
lua_pop ( m_LuaState , std : : min ( 2 , top ) ) ;
2013-08-04 17:11:25 -04:00
return false ;
}
2016-02-05 16:45:45 -05:00
2014-01-21 16:59:08 -05:00
// Remove the error handler from the stack:
lua_remove ( m_LuaState , - a_NumResults - 1 ) ;
2013-08-04 17:11:25 -04:00
return true ;
}
2013-11-22 10:49:38 -05:00
bool cLuaState : : CheckParamUserTable ( int a_StartParam , const char * a_UserTable , int a_EndParam )
{
ASSERT ( IsValid ( ) ) ;
2016-02-05 16:45:45 -05:00
2013-11-22 10:49:38 -05:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 16:45:45 -05:00
2013-11-22 10:49:38 -05:00
tolua_Error tolua_err ;
for ( int i = a_StartParam ; i < = a_EndParam ; i + + )
{
if ( tolua_isusertable ( m_LuaState , i , a_UserTable , 0 , & tolua_err ) )
{
continue ;
}
// Not the correct parameter
lua_Debug entry ;
VERIFY ( lua_getstack ( m_LuaState , 0 , & entry ) ) ;
VERIFY ( lua_getinfo ( m_LuaState , " n " , & entry ) ) ;
2014-10-20 16:55:07 -04:00
AString ErrMsg = Printf ( " #ferror in function '%s'. " , ( entry . name ! = nullptr ) ? entry . name : " ? " ) ;
2013-11-22 10:49:38 -05:00
tolua_error ( m_LuaState , ErrMsg . c_str ( ) , & tolua_err ) ;
return false ;
} // for i - Param
2016-02-05 16:45:45 -05:00
2013-11-22 10:49:38 -05:00
// All params checked ok
return true ;
}
2013-08-07 08:26:18 -04:00
bool cLuaState : : CheckParamUserType ( int a_StartParam , const char * a_UserType , int a_EndParam )
{
ASSERT ( IsValid ( ) ) ;
2016-02-05 16:45:45 -05:00
2013-08-07 08:26:18 -04:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 16:45:45 -05:00
2013-08-07 08:26:18 -04:00
tolua_Error tolua_err ;
for ( int i = a_StartParam ; i < = a_EndParam ; i + + )
{
if ( tolua_isusertype ( m_LuaState , i , a_UserType , 0 , & tolua_err ) )
{
continue ;
}
// Not the correct parameter
lua_Debug entry ;
VERIFY ( lua_getstack ( m_LuaState , 0 , & entry ) ) ;
VERIFY ( lua_getinfo ( m_LuaState , " n " , & entry ) ) ;
2014-10-20 16:55:07 -04:00
AString ErrMsg = Printf ( " #ferror in function '%s'. " , ( entry . name ! = nullptr ) ? entry . name : " ? " ) ;
2013-08-07 08:26:18 -04:00
tolua_error ( m_LuaState , ErrMsg . c_str ( ) , & tolua_err ) ;
return false ;
} // for i - Param
2016-02-05 16:45:45 -05:00
2013-08-07 08:26:18 -04:00
// All params checked ok
return true ;
}
bool cLuaState : : CheckParamTable ( int a_StartParam , int a_EndParam )
{
ASSERT ( IsValid ( ) ) ;
2016-02-05 16:45:45 -05:00
2013-08-07 08:26:18 -04:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 16:45:45 -05:00
2013-08-07 08:26:18 -04:00
tolua_Error tolua_err ;
for ( int i = a_StartParam ; i < = a_EndParam ; i + + )
{
if ( tolua_istable ( m_LuaState , i , 0 , & tolua_err ) )
{
continue ;
}
// Not the correct parameter
lua_Debug entry ;
VERIFY ( lua_getstack ( m_LuaState , 0 , & entry ) ) ;
VERIFY ( lua_getinfo ( m_LuaState , " n " , & entry ) ) ;
2014-10-20 16:55:07 -04:00
AString ErrMsg = Printf ( " #ferror in function '%s'. " , ( entry . name ! = nullptr ) ? entry . name : " ? " ) ;
2015-05-16 10:19:18 -04:00
BreakIntoDebugger ( m_LuaState ) ;
2013-08-07 08:26:18 -04:00
tolua_error ( m_LuaState , ErrMsg . c_str ( ) , & tolua_err ) ;
return false ;
} // for i - Param
2016-02-05 16:45:45 -05:00
2013-08-07 08:26:18 -04:00
// All params checked ok
return true ;
}
bool cLuaState : : CheckParamNumber ( int a_StartParam , int a_EndParam )
{
ASSERT ( IsValid ( ) ) ;
2016-02-05 16:45:45 -05:00
2013-08-07 08:26:18 -04:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 16:45:45 -05:00
2013-08-07 08:26:18 -04:00
tolua_Error tolua_err ;
for ( int i = a_StartParam ; i < = a_EndParam ; i + + )
{
if ( tolua_isnumber ( m_LuaState , i , 0 , & tolua_err ) )
{
continue ;
}
// Not the correct parameter
lua_Debug entry ;
VERIFY ( lua_getstack ( m_LuaState , 0 , & entry ) ) ;
VERIFY ( lua_getinfo ( m_LuaState , " n " , & entry ) ) ;
2014-10-20 16:55:07 -04:00
AString ErrMsg = Printf ( " #ferror in function '%s'. " , ( entry . name ! = nullptr ) ? entry . name : " ? " ) ;
2013-08-07 08:26:18 -04:00
tolua_error ( m_LuaState , ErrMsg . c_str ( ) , & tolua_err ) ;
return false ;
} // for i - Param
2016-02-05 16:45:45 -05:00
2013-08-07 08:26:18 -04:00
// All params checked ok
return true ;
}
2015-12-16 09:04:47 -05:00
bool cLuaState : : CheckParamBool ( int a_StartParam , int a_EndParam )
{
ASSERT ( IsValid ( ) ) ;
2016-02-05 16:45:45 -05:00
2015-12-16 09:04:47 -05:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 16:45:45 -05:00
2015-12-16 09:04:47 -05:00
tolua_Error tolua_err ;
for ( int i = a_StartParam ; i < = a_EndParam ; i + + )
{
if ( tolua_isboolean ( m_LuaState , i , 0 , & tolua_err ) )
{
continue ;
}
// Not the correct parameter
lua_Debug entry ;
VERIFY ( lua_getstack ( m_LuaState , 0 , & entry ) ) ;
VERIFY ( lua_getinfo ( m_LuaState , " n " , & entry ) ) ;
AString ErrMsg = Printf ( " #ferror in function '%s'. " , ( entry . name ! = nullptr ) ? entry . name : " ? " ) ;
tolua_error ( m_LuaState , ErrMsg . c_str ( ) , & tolua_err ) ;
return false ;
} // for i - Param
2016-02-05 16:45:45 -05:00
2015-12-16 09:04:47 -05:00
// All params checked ok
return true ;
}
2013-11-22 14:11:24 -05:00
bool cLuaState : : CheckParamString ( int a_StartParam , int a_EndParam )
{
ASSERT ( IsValid ( ) ) ;
2016-02-05 16:45:45 -05:00
2013-11-22 14:11:24 -05:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 16:45:45 -05:00
2013-11-22 14:11:24 -05:00
tolua_Error tolua_err ;
for ( int i = a_StartParam ; i < = a_EndParam ; i + + )
{
2015-09-28 08:53:50 -04:00
if ( lua_isstring ( m_LuaState , i ) )
2013-11-22 14:11:24 -05:00
{
continue ;
}
// Not the correct parameter
lua_Debug entry ;
VERIFY ( lua_getstack ( m_LuaState , 0 , & entry ) ) ;
VERIFY ( lua_getinfo ( m_LuaState , " n " , & entry ) ) ;
2015-09-28 08:53:50 -04:00
tolua_err . array = 0 ;
tolua_err . type = " string " ;
tolua_err . index = i ;
2014-10-20 16:55:07 -04:00
AString ErrMsg = Printf ( " #ferror in function '%s'. " , ( entry . name ! = nullptr ) ? entry . name : " ? " ) ;
2013-11-22 14:11:24 -05:00
tolua_error ( m_LuaState , ErrMsg . c_str ( ) , & tolua_err ) ;
return false ;
} // for i - Param
2016-02-05 16:45:45 -05:00
2013-11-22 14:11:24 -05:00
// All params checked ok
return true ;
}
2014-01-19 17:45:26 -05:00
bool cLuaState : : CheckParamFunction ( int a_StartParam , int a_EndParam )
{
ASSERT ( IsValid ( ) ) ;
2016-02-05 16:45:45 -05:00
2014-01-19 17:45:26 -05:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 16:45:45 -05:00
2014-01-19 17:45:26 -05:00
for ( int i = a_StartParam ; i < = a_EndParam ; i + + )
{
if ( lua_isfunction ( m_LuaState , i ) )
{
continue ;
}
// Not the correct parameter
lua_Debug entry ;
VERIFY ( lua_getstack ( m_LuaState , 0 , & entry ) ) ;
VERIFY ( lua_getinfo ( m_LuaState , " n " , & entry ) ) ;
2014-02-11 09:03:35 -05:00
luaL_error ( m_LuaState , " Error in function '%s' parameter #%d. Function expected, got %s " ,
2014-10-20 16:55:07 -04:00
( entry . name ! = nullptr ) ? entry . name : " ? " , i , GetTypeText ( i ) . c_str ( )
2014-02-11 09:03:35 -05:00
) ;
return false ;
} // for i - Param
2016-02-05 16:45:45 -05:00
2014-02-11 09:03:35 -05:00
// All params checked ok
return true ;
}
bool cLuaState : : CheckParamFunctionOrNil ( int a_StartParam , int a_EndParam )
{
ASSERT ( IsValid ( ) ) ;
2016-02-05 16:45:45 -05:00
2014-02-11 09:03:35 -05:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 16:45:45 -05:00
2014-02-11 09:03:35 -05:00
for ( int i = a_StartParam ; i < = a_EndParam ; i + + )
{
if ( lua_isfunction ( m_LuaState , i ) | | lua_isnil ( m_LuaState , i ) )
{
continue ;
}
// Not the correct parameter
lua_Debug entry ;
VERIFY ( lua_getstack ( m_LuaState , 0 , & entry ) ) ;
VERIFY ( lua_getinfo ( m_LuaState , " n " , & entry ) ) ;
luaL_error ( m_LuaState , " Error in function '%s' parameter #%d. Function expected, got %s " ,
2014-10-20 16:55:07 -04:00
( entry . name ! = nullptr ) ? entry . name : " ? " , i , GetTypeText ( i ) . c_str ( )
2014-01-19 17:45:26 -05:00
) ;
return false ;
} // for i - Param
2016-02-05 16:45:45 -05:00
2014-01-19 17:45:26 -05:00
// All params checked ok
return true ;
}
2013-08-07 08:26:18 -04:00
bool cLuaState : : CheckParamEnd ( int a_Param )
{
tolua_Error tolua_err ;
2015-05-08 18:32:02 -04:00
if ( tolua_isnoobj ( m_LuaState , a_Param , & tolua_err ) = = 1 )
2013-08-07 08:26:18 -04:00
{
return true ;
}
// Not the correct parameter
lua_Debug entry ;
VERIFY ( lua_getstack ( m_LuaState , 0 , & entry ) ) ;
VERIFY ( lua_getinfo ( m_LuaState , " n " , & entry ) ) ;
2014-10-20 16:55:07 -04:00
AString ErrMsg = Printf ( " #ferror in function '%s': Too many arguments. " , ( entry . name ! = nullptr ) ? entry . name : " ? " ) ;
2013-08-07 08:26:18 -04:00
tolua_error ( m_LuaState , ErrMsg . c_str ( ) , & tolua_err ) ;
return false ;
}
2015-03-21 13:17:26 -04:00
bool cLuaState : : IsParamUserType ( int a_Param , AString a_UserType )
{
ASSERT ( IsValid ( ) ) ;
2016-02-05 16:45:45 -05:00
2015-03-21 13:17:26 -04:00
tolua_Error tolua_err ;
2015-05-08 18:32:02 -04:00
return ( tolua_isusertype ( m_LuaState , a_Param , a_UserType . c_str ( ) , 0 , & tolua_err ) = = 1 ) ;
2015-03-21 13:17:26 -04:00
}
bool cLuaState : : IsParamNumber ( int a_Param )
{
ASSERT ( IsValid ( ) ) ;
2016-02-05 16:45:45 -05:00
2015-03-21 13:17:26 -04:00
tolua_Error tolua_err ;
2015-05-08 18:32:02 -04:00
return ( tolua_isnumber ( m_LuaState , a_Param , 0 , & tolua_err ) = = 1 ) ;
2015-03-21 13:17:26 -04:00
}
2013-08-04 17:11:25 -04:00
bool cLuaState : : ReportErrors ( int a_Status )
{
return ReportErrors ( m_LuaState , a_Status ) ;
}
bool cLuaState : : ReportErrors ( lua_State * a_LuaState , int a_Status )
{
if ( a_Status = = 0 )
{
// No error to report
return false ;
}
2016-02-05 16:45:45 -05:00
2013-08-04 17:11:25 -04:00
LOGWARNING ( " LUA: %d - %s " , a_Status , lua_tostring ( a_LuaState , - 1 ) ) ;
lua_pop ( a_LuaState , 1 ) ;
return true ;
}
2013-08-07 08:26:18 -04:00
2014-03-19 17:55:47 -04:00
void cLuaState : : LogStackTrace ( int a_StartingDepth )
2014-01-11 16:51:10 -05:00
{
2014-03-19 17:55:47 -04:00
LogStackTrace ( m_LuaState , a_StartingDepth ) ;
2014-01-11 16:51:10 -05:00
}
2014-03-19 17:55:47 -04:00
void cLuaState : : LogStackTrace ( lua_State * a_LuaState , int a_StartingDepth )
2013-08-21 14:06:37 -04:00
{
LOGWARNING ( " Stack trace: " ) ;
lua_Debug entry ;
2014-03-19 17:55:47 -04:00
int depth = a_StartingDepth ;
2014-01-11 16:51:10 -05:00
while ( lua_getstack ( a_LuaState , depth , & entry ) )
2013-08-21 14:06:37 -04:00
{
2014-02-04 16:24:03 -05:00
lua_getinfo ( a_LuaState , " Sln " , & entry ) ;
2014-01-11 16:51:10 -05:00
LOGWARNING ( " %s(%d): %s " , entry . short_src , entry . currentline , entry . name ? entry . name : " (no name) " ) ;
2013-08-21 14:06:37 -04:00
depth + + ;
}
LOGWARNING ( " Stack trace end " ) ;
}
AString cLuaState : : GetTypeText ( int a_StackPos )
{
2014-01-21 16:59:08 -05:00
return lua_typename ( m_LuaState , lua_type ( m_LuaState , a_StackPos ) ) ;
}
int cLuaState : : CallFunctionWithForeignParams (
const AString & a_FunctionName ,
cLuaState & a_SrcLuaState ,
int a_SrcParamStart ,
int a_SrcParamEnd
)
{
ASSERT ( IsValid ( ) ) ;
ASSERT ( a_SrcLuaState . IsValid ( ) ) ;
2016-02-05 16:45:45 -05:00
2014-01-21 16:59:08 -05:00
// Store the stack position before any changes
int OldTop = lua_gettop ( m_LuaState ) ;
2016-02-05 16:45:45 -05:00
2014-01-21 16:59:08 -05:00
// Push the function to call, including the error handler:
if ( ! PushFunction ( a_FunctionName . c_str ( ) ) )
{
LOGWARNING ( " Function '%s' not found " , a_FunctionName . c_str ( ) ) ;
2015-05-07 17:02:18 -04:00
lua_settop ( m_LuaState , OldTop ) ;
2014-01-21 16:59:08 -05:00
return - 1 ;
}
// Copy the function parameters to the target state
if ( CopyStackFrom ( a_SrcLuaState , a_SrcParamStart , a_SrcParamEnd ) < 0 )
2013-08-21 14:06:37 -04:00
{
2014-01-21 16:59:08 -05:00
// Something went wrong, fix the stack and exit
2015-05-07 17:02:18 -04:00
lua_settop ( m_LuaState , OldTop ) ;
2014-02-04 03:18:32 -05:00
m_NumCurrentFunctionArgs = - 1 ;
m_CurrentFunctionName . clear ( ) ;
2014-01-21 16:59:08 -05:00
return - 1 ;
}
2016-02-05 16:45:45 -05:00
2014-01-21 16:59:08 -05:00
// Call the function, with an error handler:
2014-02-04 04:29:10 -05:00
int s = lua_pcall ( m_LuaState , a_SrcParamEnd - a_SrcParamStart + 1 , LUA_MULTRET , OldTop + 1 ) ;
2014-01-21 16:59:08 -05:00
if ( ReportErrors ( s ) )
{
LOGWARN ( " Error while calling function '%s' in '%s' " , a_FunctionName . c_str ( ) , m_SubsystemName . c_str ( ) ) ;
2015-05-07 17:02:18 -04:00
// Reset the stack:
lua_settop ( m_LuaState , OldTop ) ;
2016-02-05 16:45:45 -05:00
2014-02-04 03:18:32 -05:00
// Reset the internal checking mechanisms:
m_NumCurrentFunctionArgs = - 1 ;
m_CurrentFunctionName . clear ( ) ;
2016-02-05 16:45:45 -05:00
2014-02-04 04:29:10 -05:00
// Make Lua think everything is okay and return 0 values, so that plugins continue executing.
// The failure is indicated by the zero return values.
return 0 ;
2013-08-21 14:06:37 -04:00
}
2016-02-05 16:45:45 -05:00
2014-01-21 16:59:08 -05:00
// Reset the internal checking mechanisms:
m_NumCurrentFunctionArgs = - 1 ;
2014-02-04 03:18:32 -05:00
m_CurrentFunctionName . clear ( ) ;
2016-02-05 16:45:45 -05:00
2014-01-21 16:59:08 -05:00
// Remove the error handler from the stack:
lua_remove ( m_LuaState , OldTop + 1 ) ;
2016-02-05 16:45:45 -05:00
2014-01-21 16:59:08 -05:00
// Return the number of return values:
return lua_gettop ( m_LuaState ) - OldTop ;
}
2016-05-30 19:01:55 -04:00
int cLuaState : : CopyStackFrom ( cLuaState & a_SrcLuaState , int a_SrcStart , int a_SrcEnd , int a_NumAllowedNestingLevels )
2014-01-21 16:59:08 -05:00
{
/*
// DEBUG:
LOGD ( " Copying stack values from %d to %d " , a_SrcStart , a_SrcEnd ) ;
a_SrcLuaState . LogStack ( " Src stack before copying: " ) ;
LogStack ( " Dst stack before copying: " ) ;
*/
for ( int i = a_SrcStart ; i < = a_SrcEnd ; + + i )
{
2016-05-30 19:01:55 -04:00
if ( ! CopySingleValueFrom ( a_SrcLuaState , i , a_NumAllowedNestingLevels ) )
2014-01-21 16:59:08 -05:00
{
2016-05-30 19:01:55 -04:00
lua_pop ( m_LuaState , i - a_SrcStart ) ;
return - 1 ;
}
}
return a_SrcEnd - a_SrcStart + 1 ;
}
bool cLuaState : : CopyTableFrom ( cLuaState & a_SrcLuaState , int a_SrcStackIdx , int a_NumAllowedNestingLevels )
{
// Create the dest table:
# ifdef _DEBUG
auto srcTop = lua_gettop ( a_SrcLuaState ) ;
auto dstTop = lua_gettop ( m_LuaState ) ;
# endif
lua_createtable ( m_LuaState , 0 , 0 ) ; // DST: <table>
lua_pushvalue ( a_SrcLuaState , a_SrcStackIdx ) ; // SRC: <table>
lua_pushnil ( a_SrcLuaState ) ; // SRC: <table> <key>
while ( lua_next ( a_SrcLuaState , - 2 ) ! = 0 ) // SRC: <table> <key> <value>
{
2016-08-02 07:12:34 -04:00
ASSERT ( lua_gettop ( a_SrcLuaState ) = = srcTop + 3 ) ;
ASSERT ( lua_gettop ( m_LuaState ) = = dstTop + 1 ) ;
2016-05-30 19:01:55 -04:00
// Copy the key:
if ( ! CopySingleValueFrom ( a_SrcLuaState , - 2 , a_NumAllowedNestingLevels ) ) // DST: <table> <key>
{
lua_pop ( m_LuaState , 1 ) ;
lua_pop ( a_SrcLuaState , 3 ) ;
2016-08-02 07:12:34 -04:00
ASSERT ( lua_gettop ( a_SrcLuaState ) = = srcTop ) ;
ASSERT ( lua_gettop ( m_LuaState ) = = dstTop ) ;
2016-05-30 19:01:55 -04:00
return false ;
}
2016-08-02 07:12:34 -04:00
ASSERT ( lua_gettop ( a_SrcLuaState ) = = srcTop + 3 ) ;
ASSERT ( lua_gettop ( m_LuaState ) = = dstTop + 2 ) ;
2016-05-30 19:01:55 -04:00
// Copy the value:
if ( ! CopySingleValueFrom ( a_SrcLuaState , - 1 , a_NumAllowedNestingLevels - 1 ) ) // DST: <table> <key> <value>
{
lua_pop ( m_LuaState , 2 ) ; // DST: empty
lua_pop ( a_SrcLuaState , 3 ) ; // SRC: empty
2016-08-02 07:12:34 -04:00
ASSERT ( lua_gettop ( a_SrcLuaState ) = = srcTop ) ;
ASSERT ( lua_gettop ( m_LuaState ) = = dstTop ) ;
2016-05-30 19:01:55 -04:00
return false ;
}
2016-08-02 07:12:34 -04:00
ASSERT ( lua_gettop ( a_SrcLuaState ) = = srcTop + 3 ) ;
ASSERT ( lua_gettop ( m_LuaState ) = = dstTop + 3 ) ;
2016-05-30 19:01:55 -04:00
// Set the value and fix up stacks:
lua_rawset ( m_LuaState , - 3 ) ; // DST: <table>
lua_pop ( a_SrcLuaState , 1 ) ; // SRC: <table> <key>
2016-08-02 07:12:34 -04:00
ASSERT ( lua_gettop ( a_SrcLuaState ) = = srcTop + 2 ) ;
ASSERT ( lua_gettop ( m_LuaState ) = = dstTop + 1 ) ;
2016-05-30 19:01:55 -04:00
}
lua_pop ( a_SrcLuaState , 1 ) ; // SRC: empty
2016-08-02 07:12:34 -04:00
ASSERT ( lua_gettop ( a_SrcLuaState ) = = srcTop ) ;
ASSERT ( lua_gettop ( m_LuaState ) = = dstTop + 1 ) ;
2016-05-30 19:01:55 -04:00
return true ;
}
bool cLuaState : : CopySingleValueFrom ( cLuaState & a_SrcLuaState , int a_StackIdx , int a_NumAllowedNestingLevels )
{
int t = lua_type ( a_SrcLuaState , a_StackIdx ) ;
switch ( t )
{
case LUA_TNIL :
{
lua_pushnil ( m_LuaState ) ;
return true ;
}
case LUA_TSTRING :
{
AString s ;
a_SrcLuaState . ToString ( a_StackIdx , s ) ;
Push ( s ) ;
return true ;
}
case LUA_TBOOLEAN :
{
bool b = ( tolua_toboolean ( a_SrcLuaState , a_StackIdx , false ) ! = 0 ) ;
Push ( b ) ;
return true ;
}
case LUA_TNUMBER :
{
lua_Number d = tolua_tonumber ( a_SrcLuaState , a_StackIdx , 0 ) ;
Push ( d ) ;
return true ;
}
case LUA_TUSERDATA :
{
// Get the class name:
const char * type = nullptr ;
if ( lua_getmetatable ( a_SrcLuaState , a_StackIdx ) = = 0 )
2014-01-21 16:59:08 -05:00
{
2016-05-30 19:01:55 -04:00
LOGWARNING ( " %s: Unknown class in pos %d, cannot copy. " , __FUNCTION__ , a_StackIdx ) ;
return false ;
2014-01-21 16:59:08 -05:00
}
2016-05-30 19:01:55 -04:00
lua_rawget ( a_SrcLuaState , LUA_REGISTRYINDEX ) ; // Stack +1
type = lua_tostring ( a_SrcLuaState , - 1 ) ;
lua_pop ( a_SrcLuaState , 1 ) ; // Stack -1
// Copy the value:
void * ud = tolua_touserdata ( a_SrcLuaState , a_StackIdx , nullptr ) ;
tolua_pushusertype ( m_LuaState , ud , type ) ;
return true ;
}
case LUA_TTABLE :
{
if ( ! CopyTableFrom ( a_SrcLuaState , a_StackIdx , a_NumAllowedNestingLevels - 1 ) )
2014-01-21 16:59:08 -05:00
{
2016-05-30 19:01:55 -04:00
LOGWARNING ( " %s: Failed to copy table in pos %d. " , __FUNCTION__ , a_StackIdx ) ;
return false ;
2014-01-21 16:59:08 -05:00
}
2016-05-30 19:01:55 -04:00
return true ;
}
default :
{
LOGWARNING ( " %s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools, classes and simple tables! " ,
__FUNCTION__ , lua_typename ( a_SrcLuaState , t ) , a_StackIdx
) ;
return false ;
2014-01-21 16:59:08 -05:00
}
}
}
void cLuaState : : ToString ( int a_StackPos , AString & a_String )
{
size_t len ;
const char * s = lua_tolstring ( m_LuaState , a_StackPos , & len ) ;
2014-10-20 16:55:07 -04:00
if ( s ! = nullptr )
2014-01-21 16:59:08 -05:00
{
a_String . assign ( s , len ) ;
}
}
2016-06-10 15:30:07 -04:00
void cLuaState : : LogStackValues ( const char * a_Header )
2014-01-21 16:59:08 -05:00
{
2016-06-10 15:30:07 -04:00
LogStackValues ( m_LuaState , a_Header ) ;
2014-01-21 16:59:08 -05:00
}
2016-06-10 15:30:07 -04:00
void cLuaState : : LogStackValues ( lua_State * a_LuaState , const char * a_Header )
2014-01-21 16:59:08 -05:00
{
2014-03-11 17:16:08 -04:00
// Format string consisting only of %s is used to appease the compiler
2016-11-10 10:46:31 -05:00
ASSERT_LUA_STACK_BALANCE ( a_LuaState , false ) ;
2014-10-20 16:55:07 -04:00
LOG ( " %s " , ( a_Header ! = nullptr ) ? a_Header : " Lua C API Stack contents: " ) ;
2014-02-10 16:44:56 -05:00
for ( int i = lua_gettop ( a_LuaState ) ; i > 0 ; i - - )
2014-01-21 16:59:08 -05:00
{
AString Value ;
int Type = lua_type ( a_LuaState , i ) ;
switch ( Type )
{
case LUA_TBOOLEAN : Value . assign ( ( lua_toboolean ( a_LuaState , i ) ! = 0 ) ? " true " : " false " ) ; break ;
case LUA_TLIGHTUSERDATA : Printf ( Value , " %p " , lua_touserdata ( a_LuaState , i ) ) ; break ;
2015-07-29 11:04:03 -04:00
case LUA_TNUMBER : Printf ( Value , " %f " , static_cast < double > ( lua_tonumber ( a_LuaState , i ) ) ) ; break ;
2016-11-10 10:46:31 -05:00
case LUA_TSTRING :
{
size_t len ;
const char * txt = lua_tolstring ( a_LuaState , i , & len ) ;
Value . assign ( txt , std : : min < size_t > ( len , 50 ) ) ; // Only log up to 50 characters of the string
break ;
}
2014-02-17 17:12:46 -05:00
case LUA_TTABLE : Printf ( Value , " %p " , lua_topointer ( a_LuaState , i ) ) ; break ;
2016-11-10 10:46:31 -05:00
case LUA_TFUNCTION : Printf ( Value , " %p " , lua_topointer ( a_LuaState , i ) ) ; break ;
case LUA_TUSERDATA :
{
Printf ( Value , " %p (%s) " , lua_touserdata ( a_LuaState , i ) , tolua_typename ( a_LuaState , i ) ) ;
// tolua_typename pushes the string onto Lua stack, pop it off again:
lua_pop ( a_LuaState , 1 ) ;
break ;
}
2014-01-21 16:59:08 -05:00
default : break ;
}
LOGD ( " Idx %d: type %d (%s) %s " , i , Type , lua_typename ( a_LuaState , Type ) , Value . c_str ( ) ) ;
} // for i - stack idx
2013-08-21 14:06:37 -04:00
}
2017-01-09 09:56:16 -05:00
cLuaState * cLuaState : : QueryCanonLuaState ( void ) const
2016-06-10 15:30:07 -04:00
{
2017-01-09 09:56:16 -05:00
return cCanonLuaStates : : GetCanonState ( m_LuaState ) ;
2016-06-10 15:30:07 -04:00
}
2016-08-23 07:20:43 -04:00
void cLuaState : : LogApiCallParamFailure ( const char * a_FnName , const char * a_ParamNames )
{
LOGWARNING ( " %s: Cannot read params: %s, bailing out. " , a_FnName , a_ParamNames ) ;
LogStackTrace ( ) ;
LogStackValues ( " Values on the stack " ) ;
}
2017-01-17 16:38:04 -05:00
void cLuaState : : TrackInDeadlockDetect ( cDeadlockDetect & a_DeadlockDetect )
{
a_DeadlockDetect . TrackCriticalSection ( m_CS , Printf ( " cLuaState %s " , m_SubsystemName . c_str ( ) ) ) ;
}
void cLuaState : : UntrackInDeadlockDetect ( cDeadlockDetect & a_DeadlockDetect )
{
a_DeadlockDetect . UntrackCriticalSection ( m_CS ) ;
}
2014-01-11 17:10:40 -05:00
int cLuaState : : ReportFnCallErrors ( lua_State * a_LuaState )
{
LOGWARNING ( " LUA: %s " , lua_tostring ( a_LuaState , - 1 ) ) ;
2014-03-19 17:55:47 -04:00
LogStackTrace ( a_LuaState , 1 ) ;
2015-05-16 10:19:18 -04:00
BreakIntoDebugger ( a_LuaState ) ;
2014-01-11 17:10:40 -05:00
return 1 ; // We left the error message on the stack as the return value
}
2015-05-16 10:19:18 -04:00
int cLuaState : : BreakIntoDebugger ( lua_State * a_LuaState )
{
2016-11-10 10:46:31 -05:00
ASSERT_LUA_STACK_BALANCE ( a_LuaState ) ;
2015-05-16 10:19:18 -04:00
// Call the BreakIntoDebugger function, if available:
lua_getglobal ( a_LuaState , " BreakIntoDebugger " ) ;
if ( ! lua_isfunction ( a_LuaState , - 1 ) )
{
LOGD ( " LUA: BreakIntoDebugger() not found / not a function " ) ;
lua_pop ( a_LuaState , 1 ) ;
return 1 ;
}
2016-11-10 10:46:31 -05:00
lua_pushvalue ( a_LuaState , - 2 ) ; // Copy the string that has been passed to us
2015-05-16 10:19:18 -04:00
LOGD ( " Calling BreakIntoDebugger()... " ) ;
lua_call ( a_LuaState , 1 , 0 ) ;
LOGD ( " Returned from BreakIntoDebugger(). " ) ;
return 0 ;
}
2016-07-06 14:52:04 -04:00
void cLuaState : : TrackRef ( cTrackedRef & a_Ref )
2016-03-02 05:34:39 -05:00
{
// Get the CanonLuaState global from Lua:
2016-06-10 15:30:07 -04:00
auto canonState = QueryCanonLuaState ( ) ;
if ( canonState = = nullptr )
2016-03-02 05:34:39 -05:00
{
LOGWARNING ( " %s: Lua state %p has invalid CanonLuaState! " , __FUNCTION__ , reinterpret_cast < void * > ( m_LuaState ) ) ;
return ;
}
// Add the callback:
2016-07-06 14:52:04 -04:00
cCSLock Lock ( canonState - > m_CSTrackedRefs ) ;
canonState - > m_TrackedRefs . push_back ( & a_Ref ) ;
2016-03-02 05:34:39 -05:00
}
2016-07-06 14:52:04 -04:00
void cLuaState : : UntrackRef ( cTrackedRef & a_Ref )
2016-03-02 05:34:39 -05:00
{
// Get the CanonLuaState global from Lua:
2016-06-10 15:30:07 -04:00
auto canonState = QueryCanonLuaState ( ) ;
if ( canonState = = nullptr )
2016-03-02 05:34:39 -05:00
{
LOGWARNING ( " %s: Lua state %p has invalid CanonLuaState! " , __FUNCTION__ , reinterpret_cast < void * > ( m_LuaState ) ) ;
return ;
}
2017-01-15 06:54:46 -05:00
// Remove the callback (note that another thread may have cleared the callbacks by closing the LuaState):
2016-07-06 14:52:04 -04:00
cCSLock Lock ( canonState - > m_CSTrackedRefs ) ;
auto & trackedRefs = canonState - > m_TrackedRefs ;
2017-01-15 06:54:46 -05:00
for ( auto itr = trackedRefs . begin ( ) , end = trackedRefs . end ( ) ; itr ! = end ; + + itr )
{
if ( * itr = = & a_Ref )
2016-03-02 05:34:39 -05:00
{
2017-01-15 06:54:46 -05:00
trackedRefs . erase ( itr ) ;
break ;
2016-03-02 05:34:39 -05:00
}
2017-01-15 06:54:46 -05:00
}
2016-03-02 05:34:39 -05:00
}
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2013-08-07 08:26:18 -04:00
// cLuaState::cRef:
2014-02-09 12:39:22 -05:00
cLuaState : : cRef : : cRef ( void ) :
2014-10-20 16:55:07 -04:00
m_LuaState ( nullptr ) ,
2014-02-09 12:39:22 -05:00
m_Ref ( LUA_REFNIL )
{
}
2013-08-07 08:26:18 -04:00
cLuaState : : cRef : : cRef ( cLuaState & a_LuaState , int a_StackPos ) :
2014-10-20 16:55:07 -04:00
m_LuaState ( nullptr ) ,
2014-02-09 12:39:22 -05:00
m_Ref ( LUA_REFNIL )
2013-08-07 08:26:18 -04:00
{
2014-02-09 12:39:22 -05:00
RefStack ( a_LuaState , a_StackPos ) ;
2013-08-07 08:26:18 -04:00
}
2015-01-30 15:24:02 -05:00
cLuaState : : cRef : : cRef ( cRef & & a_FromRef ) :
m_LuaState ( a_FromRef . m_LuaState ) ,
m_Ref ( a_FromRef . m_Ref )
{
a_FromRef . m_LuaState = nullptr ;
a_FromRef . m_Ref = LUA_REFNIL ;
}
2013-08-07 08:26:18 -04:00
cLuaState : : cRef : : ~ cRef ( )
{
2014-10-20 16:55:07 -04:00
if ( m_LuaState ! = nullptr )
2014-02-09 12:39:22 -05:00
{
UnRef ( ) ;
}
}
void cLuaState : : cRef : : RefStack ( cLuaState & a_LuaState , int a_StackPos )
{
ASSERT ( a_LuaState . IsValid ( ) ) ;
2014-10-20 16:55:07 -04:00
if ( m_LuaState ! = nullptr )
2014-02-09 12:39:22 -05:00
{
UnRef ( ) ;
}
2017-01-13 04:31:05 -05:00
ASSERT ( cCanonLuaStates : : GetCanonState ( a_LuaState ) - > m_CS . IsLockedByCurrentThread ( ) ) ;
2016-03-02 05:34:39 -05:00
m_LuaState = a_LuaState ;
2014-02-09 12:39:22 -05:00
lua_pushvalue ( a_LuaState , a_StackPos ) ; // Push a copy of the value at a_StackPos onto the stack
m_Ref = luaL_ref ( a_LuaState , LUA_REGISTRYINDEX ) ;
}
void cLuaState : : cRef : : UnRef ( void )
{
2013-08-07 08:26:18 -04:00
if ( IsValid ( ) )
{
2017-01-13 04:31:05 -05:00
ASSERT ( cCanonLuaStates : : GetCanonState ( m_LuaState ) - > m_CS . IsLockedByCurrentThread ( ) ) ;
2016-03-02 05:34:39 -05:00
luaL_unref ( m_LuaState , LUA_REGISTRYINDEX , m_Ref ) ;
2013-08-07 08:26:18 -04:00
}
2014-10-20 16:55:07 -04:00
m_LuaState = nullptr ;
2014-02-09 12:39:22 -05:00
m_Ref = LUA_REFNIL ;
2013-08-07 08:26:18 -04:00
}