2013-08-04 23:11:25 +02: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 17:14:46 +00:00
#include "lua/src/lualib.h"
2013-08-04 23:11:25 +02:00
}
2014-03-25 11:15:05 -06:00
#undef TOLUA_TEMPLATE_BIND
2013-12-08 18:24:56 +01:00
#include "tolua++/include/tolua++.h"
2013-08-04 23:11:25 +02:00
#include "Bindings.h"
#include "ManualBindings.h"
2014-03-02 11:12:29 +02:00
#include "DeprecatedBindings.h"
2015-12-23 11:08:39 +01:00
#include "LuaJson.h"
2014-10-19 11:46:38 +02:00
#include "../Entities/Entity.h"
#include "../BlockEntities/BlockEntity.h"
2013-08-04 23:11:25 +02:00
2016-03-02 11:34:39 +01:00
2015-05-09 09:25:09 +02:00
// fwd: "SQLite/lsqlite3.c"
2013-08-04 23:11:25 +02:00
extern "C"
{
2013-12-27 11:51:08 +01:00
int luaopen_lsqlite3 ( lua_State * L );
2013-08-04 23:11:25 +02:00
}
2015-05-09 09:25:09 +02:00
// fwd: "LuaExpat/lxplib.c":
2013-08-04 23:11:25 +02:00
extern "C"
{
int luaopen_lxp ( lua_State * L );
}
2013-08-15 13:05:24 +02:00
const cLuaState :: cRet cLuaState :: Return = {};
2016-08-16 13:02:08 +02:00
const cLuaState :: cNil cLuaState :: Nil = {};
2013-08-08 14:08:21 +02:00
2017-01-09 15:56:16 +01: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 11:34:39 +01:00
2013-08-08 14:08:21 +02:00
2015-09-28 21:30:31 +02: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 11:34:39 +01:00
////////////////////////////////////////////////////////////////////////////////
2016-07-06 20:52:04 +02:00
// cLuaState::cTrackedRef:
2016-03-02 11:34:39 +01:00
2016-07-06 20:52:04 +02:00
cLuaState :: cTrackedRef :: cTrackedRef ( void ) :
2016-06-12 16:53:24 +02:00
m_CS ( nullptr )
{
}
2016-07-06 20:52:04 +02:00
bool cLuaState :: cTrackedRef :: RefStack ( cLuaState & a_LuaState , int a_StackPos )
2016-03-02 11:34:39 +01:00
{
// Clear any previous callback:
Clear ();
// Add self to LuaState's callback-tracking:
2016-06-12 16:53:24 +02:00
auto canonState = a_LuaState . QueryCanonLuaState ();
2016-07-06 20:52:04 +02:00
canonState -> TrackRef ( * this );
2016-03-02 11:34:39 +01:00
// Store the new callback:
2016-06-12 16:53:24 +02:00
m_CS = & ( canonState -> m_CS );
m_Ref . RefStack ( * canonState , a_StackPos );
2016-03-02 11:34:39 +01:00
return true ;
}
2016-07-06 20:52:04 +02:00
void cLuaState :: cTrackedRef :: Clear ( void )
2016-03-02 11:34:39 +01:00
{
2016-07-06 20:52:04 +02:00
// Free the reference:
2016-03-02 11:34:39 +01:00
lua_State * luaState = nullptr ;
{
2017-01-13 10:31:05 +01:00
auto cs = m_CS . load ();
2016-06-12 16:53:24 +02:00
if ( cs != nullptr )
2016-03-02 11:34:39 +01:00
{
2016-06-12 16:53:24 +02:00
cCSLock Lock ( * cs );
if ( ! m_Ref . IsValid ())
{
return ;
}
luaState = m_Ref . GetLuaState ();
m_Ref . UnRef ();
2016-03-02 11:34:39 +01:00
}
}
2016-07-06 20:52:04 +02:00
m_CS = nullptr ;
2016-03-02 11:34:39 +01:00
// Remove from LuaState's callback-tracking:
2016-06-12 16:53:24 +02:00
if ( luaState == nullptr )
{
return ;
}
2016-07-06 20:52:04 +02:00
cLuaState ( luaState ). UntrackRef ( * this );
2016-03-02 11:34:39 +01:00
}
2016-07-06 20:52:04 +02:00
bool cLuaState :: cTrackedRef :: IsValid ( void )
2016-03-02 10:12:43 +01:00
{
2017-01-13 10:31:05 +01:00
auto cs = m_CS . load ();
2016-06-12 16:53:24 +02:00
if ( cs == nullptr )
{
return false ;
}
cCSLock lock ( * cs );
2016-03-02 10:12:43 +01:00
return m_Ref . IsValid ();
}
2016-07-06 20:52:04 +02:00
bool cLuaState :: cTrackedRef :: IsSameLuaState ( cLuaState & a_LuaState )
2016-06-10 21:30:07 +02:00
{
2017-01-13 10:31:05 +01:00
auto cs = m_CS . load ();
2016-06-12 16:53:24 +02:00
if ( cs == nullptr )
{
return false ;
}
cCSLock lock ( * cs );
2016-06-10 21:30:07 +02: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 20:52:04 +02:00
void cLuaState :: cTrackedRef :: Invalidate ( void )
2016-03-02 11:34:39 +01:00
{
2017-01-13 10:31:05 +01:00
auto cs = m_CS . load ();
2016-06-12 16:53:24 +02:00
if ( cs == nullptr )
{
// Already invalid
return ;
}
cCSLock Lock ( * cs );
2016-03-02 11:34:39 +01:00
if ( ! m_Ref . IsValid ())
{
2016-06-12 16:53:24 +02:00
LOGD ( "%s: Inconsistent callback at %p, has a CS but an invalid Ref. This should not happen" ,
2016-03-02 11:34:39 +01:00
__FUNCTION__ , reinterpret_cast < void *> ( this )
);
return ;
}
m_Ref . UnRef ();
2016-07-06 20:52:04 +02: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 16:26:31 +02: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 20:52:04 +02: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 11:34:39 +01:00
}
2016-08-14 16:26:31 +02: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 11:55:12 +02: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 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-08-04 23:11:25 +02:00
// cLuaState:
cLuaState :: cLuaState ( const AString & a_SubsystemName ) :
2014-10-20 21:55:07 +01:00
m_LuaState ( nullptr ),
2013-08-06 08:01:00 +02:00
m_IsOwned ( false ),
2013-08-06 08:59:54 +02:00
m_SubsystemName ( a_SubsystemName ),
m_NumCurrentFunctionArgs ( - 1 )
2013-08-04 23:11:25 +02:00
{
}
2013-08-06 08:01:00 +02:00
cLuaState :: cLuaState ( lua_State * a_AttachState ) :
m_LuaState ( a_AttachState ),
m_IsOwned ( false ),
2013-08-06 08:59:54 +02:00
m_SubsystemName ( "<attached>" ),
m_NumCurrentFunctionArgs ( - 1 )
2013-08-06 08:01:00 +02:00
{
}
2013-08-04 23:11:25 +02:00
cLuaState ::~ cLuaState ()
{
if ( IsValid ())
{
2013-08-07 14:26:18 +02:00
if ( m_IsOwned )
{
Close ();
}
else
{
Detach ();
}
2013-08-04 23:11:25 +02:00
}
}
void cLuaState :: Create ( void )
{
2014-10-20 21:55:07 +01:00
if ( m_LuaState != nullptr )
2013-08-04 23:11:25 +02:00
{
LOGWARNING ( "%s: Trying to create an already-existing LuaState, ignoring." , __FUNCTION__ );
return ;
}
m_LuaState = lua_open ();
luaL_openlibs ( m_LuaState );
2014-03-12 13:05:28 +01:00
m_IsOwned = true ;
2015-09-28 21:30:31 +02:00
cLuaStateTracker :: Add ( * this );
2017-01-09 15:56:16 +01:00
cCanonLuaStates :: Add ( * this );
2014-03-12 13:05:28 +01:00
}
void cLuaState :: RegisterAPILibs ( void )
{
2016-08-11 21:04:29 +02:00
auto top = lua_gettop ( m_LuaState );
2013-08-04 23:11:25 +02:00
tolua_AllToLua_open ( m_LuaState );
2016-08-11 21:04:29 +02:00
ASSERT ( top == lua_gettop ( m_LuaState ));
2015-05-13 11:30:57 +02:00
cManualBindings :: Bind ( m_LuaState );
2016-08-11 21:04:29 +02:00
ASSERT ( top == lua_gettop ( m_LuaState ));
2014-03-02 11:12:29 +02:00
DeprecatedBindings :: Bind ( m_LuaState );
2016-08-11 21:04:29 +02:00
ASSERT ( top == lua_gettop ( m_LuaState ));
2015-12-23 11:08:39 +01:00
cLuaJson :: Bind ( * this );
2016-08-11 21:04:29 +02:00
ASSERT ( top == lua_gettop ( m_LuaState ));
2013-08-04 23:11:25 +02:00
luaopen_lsqlite3 ( m_LuaState );
2016-08-11 21:04:29 +02: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 23:11:25 +02:00
luaopen_lxp ( m_LuaState );
2016-08-11 21:04:29 +02: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 23:11:25 +02:00
}
void cLuaState :: Close ( void )
{
2014-10-20 21:55:07 +01:00
if ( m_LuaState == nullptr )
2013-08-04 23:11:25 +02:00
{
LOGWARNING ( "%s: Trying to close an invalid LuaState, ignoring." , __FUNCTION__ );
return ;
}
2013-08-06 08:01:00 +02:00
if ( ! m_IsOwned )
{
LOGWARNING (
"%s: Detected mis-use, calling Close() on an attached state (0x%p). Detaching instead." ,
2015-09-24 16:04:44 +02:00
__FUNCTION__ , static_cast < void *> ( m_LuaState )
2013-08-06 08:01:00 +02:00
);
Detach ();
return ;
}
2016-03-02 11:34:39 +01:00
2016-07-06 20:52:04 +02:00
// Invalidate all tracked refs:
2016-03-02 11:34:39 +01:00
{
2016-07-06 20:52:04 +02:00
cCSLock Lock ( m_CSTrackedRefs );
for ( auto & r : m_TrackedRefs )
2016-03-02 11:34:39 +01:00
{
2016-07-06 20:52:04 +02:00
r -> Invalidate ();
2016-03-02 11:34:39 +01:00
}
2017-01-13 10:31:05 +01:00
m_TrackedRefs . clear ();
2016-03-02 11:34:39 +01:00
}
2017-01-09 15:56:16 +01:00
cCanonLuaStates :: Remove ( * this );
2015-09-28 21:30:31 +02:00
cLuaStateTracker :: Del ( * this );
2013-08-04 23:11:25 +02:00
lua_close ( m_LuaState );
2014-10-20 21:55:07 +01:00
m_LuaState = nullptr ;
2013-08-06 08:01:00 +02:00
m_IsOwned = false ;
}
void cLuaState :: Attach ( lua_State * a_State )
{
2014-10-20 21:55:07 +01:00
if ( m_LuaState != nullptr )
2013-08-06 08:01:00 +02:00
{
2015-09-24 16:04:44 +02:00
LOGINFO ( "%s: Already contains a LuaState (0x%p), will be closed / detached." , __FUNCTION__ , static_cast < void *> ( m_LuaState ));
2013-08-06 08:01:00 +02:00
if ( m_IsOwned )
{
Close ();
}
else
{
Detach ();
}
}
m_LuaState = a_State ;
m_IsOwned = false ;
}
void cLuaState :: Detach ( void )
{
2014-10-20 21:55:07 +01:00
if ( m_LuaState == nullptr )
2013-08-06 08:01:00 +02: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 16:04:44 +02:00
__FUNCTION__ , static_cast < void *> ( m_LuaState )
2013-08-06 08:01:00 +02:00
);
Close ();
return ;
}
2014-10-20 21:55:07 +01:00
m_LuaState = nullptr ;
2013-08-04 23:11:25 +02:00
}
2014-02-17 23:12:46 +01:00
void cLuaState :: AddPackagePath ( const AString & a_PathVariable , const AString & a_Path )
{
2016-11-10 16:46:31 +01:00
ASSERT_LUA_STACK_BALANCE ( m_LuaState );
2014-02-17 23:12:46 +01: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 23:45:45 +02:00
2014-02-17 23:12:46 +01:00
// Append the new path:
AString NewPackagePath ( PackagePath , len );
NewPackagePath . append ( LUA_PATHSEP );
NewPackagePath . append ( a_Path );
2016-02-05 23:45:45 +02:00
2014-02-17 23:12:46 +01: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 15:21:20 +02:00
bool cLuaState :: LoadFile ( const AString & a_FileName , bool a_LogWarnings )
2013-08-04 23:11:25 +02:00
{
ASSERT ( IsValid ());
2016-11-10 16:46:31 +01:00
ASSERT_LUA_STACK_BALANCE ( m_LuaState );
2015-11-11 10:32:42 +01:00
2013-08-04 23:11:25 +02:00
// Load the file:
int s = luaL_loadfile ( m_LuaState , a_FileName . c_str ());
2015-06-17 15:21:20 +02:00
if ( s != 0 )
2013-08-04 23:11:25 +02:00
{
2015-06-17 15:21:20 +02: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 23:11:25 +02:00
return false ;
}
// Execute the globals:
2016-11-10 16:46:31 +01:00
s = lua_pcall ( m_LuaState , 0 , 0 , 0 );
2015-06-17 15:21:20 +02:00
if ( s != 0 )
2013-08-04 23:11:25 +02:00
{
2015-06-17 15:21:20 +02: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 23:11:25 +02:00
return false ;
}
2015-11-11 10:32:42 +01:00
return true ;
}
bool cLuaState :: LoadString ( const AString & a_StringToLoad , const AString & a_FileName , bool a_LogWarnings )
{
ASSERT ( IsValid ());
2016-11-10 16:46:31 +01:00
ASSERT_LUA_STACK_BALANCE ( m_LuaState );
2015-11-11 10:32:42 +01: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 23:11:25 +02:00
return true ;
}
2013-08-08 16:02:07 +02:00
bool cLuaState :: HasFunction ( const char * a_FunctionName )
{
if ( ! IsValid ())
{
// This happens if cPlugin::Initialize() fails with an error
return false ;
}
2016-11-10 16:46:31 +01:00
ASSERT_LUA_STACK_BALANCE ( m_LuaState );
2013-08-08 16:02:07 +02: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 23:11:25 +02:00
{
2013-08-06 08:59:54 +02:00
ASSERT ( m_NumCurrentFunctionArgs == - 1 ); // If not, there's already something pushed onto the stack
2013-08-04 23:11:25 +02:00
if ( ! IsValid ())
{
// This happens if cPlugin::Initialize() fails with an error
return false ;
}
2016-02-05 23:45:45 +02:00
2014-01-11 23:10:40 +01:00
// Push the error handler for lua_pcall()
lua_pushcfunction ( m_LuaState , & ReportFnCallErrors );
2016-02-05 23:45:45 +02:00
2013-08-04 23:11:25 +02:00
lua_getglobal ( m_LuaState , a_FunctionName );
if ( ! lua_isfunction ( m_LuaState , - 1 ))
{
2013-08-08 16:02:07 +02:00
LOGWARNING ( "Error in %s: Could not find function %s()" , m_SubsystemName . c_str (), a_FunctionName );
2014-01-21 22:59:08 +01:00
lua_pop ( m_LuaState , 2 );
2013-08-04 23:11:25 +02:00
return false ;
}
2013-08-06 08:59:54 +02:00
m_CurrentFunctionName . assign ( a_FunctionName );
m_NumCurrentFunctionArgs = 0 ;
2013-08-04 23:11:25 +02:00
return true ;
}
2016-06-12 18:24:01 +02:00
bool cLuaState :: PushFunction ( const cRef & a_FnRef )
2013-08-04 23:11:25 +02:00
{
2013-08-06 08:59:54 +02:00
ASSERT ( IsValid ());
ASSERT ( m_NumCurrentFunctionArgs == - 1 ); // If not, there's already something pushed onto the stack
2016-02-05 23:45:45 +02:00
2014-01-11 23:10:40 +01:00
// Push the error handler for lua_pcall()
lua_pushcfunction ( m_LuaState , & ReportFnCallErrors );
2016-02-05 23:45:45 +02:00
2016-06-12 18:24:01 +02:00
lua_rawgeti ( m_LuaState , LUA_REGISTRYINDEX , static_cast < int > ( a_FnRef )); // same as lua_getref()
2013-08-04 23:11:25 +02:00
if ( ! lua_isfunction ( m_LuaState , - 1 ))
{
2014-01-21 22:59:08 +01:00
lua_pop ( m_LuaState , 2 );
2013-08-04 23:11:25 +02:00
return false ;
}
2013-08-06 08:59:54 +02:00
m_CurrentFunctionName = "<callback>" ;
m_NumCurrentFunctionArgs = 0 ;
2013-08-04 23:11:25 +02:00
return true ;
}
2016-07-06 20:52:04 +02:00
bool cLuaState :: PushFunction ( const cRef & a_TableRef , const char * a_FnName )
2013-08-07 14:26:18 +02:00
{
ASSERT ( IsValid ());
ASSERT ( m_NumCurrentFunctionArgs == - 1 ); // If not, there's already something pushed onto the stack
2016-02-05 23:45:45 +02:00
2016-07-06 20:52:04 +02:00
if ( ! a_TableRef . IsValid ())
{
return false ;
}
2014-01-11 23:10:40 +01:00
// Push the error handler for lua_pcall()
lua_pushcfunction ( m_LuaState , & ReportFnCallErrors );
2016-02-05 23:45:45 +02:00
2016-07-06 20:52:04 +02:00
// Get the function from the table:
lua_rawgeti ( m_LuaState , LUA_REGISTRYINDEX , static_cast < int > ( a_TableRef ));
2013-08-07 14:26:18 +02:00
if ( ! lua_istable ( m_LuaState , - 1 ))
{
// Not a table, bail out
2014-01-21 22:59:08 +01:00
lua_pop ( m_LuaState , 2 );
2013-08-07 14:26:18 +02:00
return false ;
}
2016-07-06 20:52:04 +02:00
lua_getfield ( m_LuaState , - 1 , a_FnName );
2013-08-07 14:26:18 +02:00
if ( lua_isnil ( m_LuaState , - 1 ) || ! lua_isfunction ( m_LuaState , - 1 ))
{
// Not a valid function, bail out
2014-01-29 22:56:23 +01:00
lua_pop ( m_LuaState , 3 );
2013-08-07 14:26:18 +02:00
return false ;
}
2016-02-05 23:45:45 +02:00
2014-01-29 22:56:23 +01:00
// Pop the table off the stack:
lua_remove ( m_LuaState , - 2 );
2016-02-05 23:45:45 +02:00
2016-07-06 20:52:04 +02:00
Printf ( m_CurrentFunctionName , "<table-callback %s>" , a_FnName );
2013-08-07 14:26:18 +02:00
m_NumCurrentFunctionArgs = 0 ;
return true ;
}
2013-08-08 14:08:21 +02:00
void cLuaState :: Push ( const AString & a_String )
{
ASSERT ( IsValid ());
2014-01-07 15:55:23 +01:00
lua_pushlstring ( m_LuaState , a_String . data (), a_String . size ());
2013-08-08 14:08:21 +02:00
}
2016-08-16 11:55:12 +02: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 14:08:21 +02:00
void cLuaState :: Push ( const AStringVector & a_Vector )
2013-08-04 23:11:25 +02:00
{
2013-08-06 08:59:54 +02:00
ASSERT ( IsValid ());
2015-07-29 09:04:03 -06:00
lua_createtable ( m_LuaState , static_cast < int > ( a_Vector . size ()), 0 );
2013-08-04 23:11:25 +02: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 08:59:54 +02:00
}
2014-06-03 19:29:04 +02:00
void cLuaState :: Push ( const cCraftingGrid * a_Grid )
2013-08-06 08:59:54 +02:00
{
ASSERT ( IsValid ());
2015-07-29 09:04:03 -06:00
tolua_pushusertype ( m_LuaState , reinterpret_cast < void *> ( const_cast < cCraftingGrid *> ( a_Grid )), "cCraftingGrid" );
2013-08-06 08:59:54 +02:00
}
2014-06-03 19:29:04 +02:00
void cLuaState :: Push ( const cCraftingRecipe * a_Recipe )
2013-08-06 08:59:54 +02:00
{
ASSERT ( IsValid ());
2015-07-29 09:04:03 -06:00
tolua_pushusertype ( m_LuaState , reinterpret_cast < void *> ( const_cast < cCraftingRecipe *> ( a_Recipe )), "cCraftingRecipe" );
2013-08-06 08:59:54 +02:00
}
2014-06-03 19:29:04 +02:00
void cLuaState :: Push ( const char * a_Value )
2013-08-06 08:59:54 +02:00
{
ASSERT ( IsValid ());
2014-06-03 19:29:04 +02:00
tolua_pushstring ( m_LuaState , a_Value );
2013-08-06 08:59:54 +02:00
}
2014-06-03 19:29:04 +02:00
void cLuaState :: Push ( const cItems & a_Items )
2013-08-06 08:59:54 +02:00
{
ASSERT ( IsValid ());
2015-07-29 09:04:03 -06:00
tolua_pushusertype ( m_LuaState , reinterpret_cast < void *> ( const_cast < cItems *> ( & a_Items )), "cItems" );
2016-08-16 13:02:08 +02:00
}
void cLuaState :: Push ( const cNil & a_Nil )
{
ASSERT ( IsValid ());
lua_pushnil ( m_LuaState );
2013-08-06 08:59:54 +02:00
}
2014-06-03 19:29:04 +02:00
void cLuaState :: Push ( const cPlayer * a_Player )
2013-08-06 08:59:54 +02:00
{
ASSERT ( IsValid ());
2015-07-29 09:04:03 -06:00
tolua_pushusertype ( m_LuaState , reinterpret_cast < void *> ( const_cast < cPlayer *> ( a_Player )), "cPlayer" );
2013-08-06 08:59:54 +02:00
}
2015-06-17 15:21:20 +02: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 19:29:04 +02:00
void cLuaState :: Push ( const HTTPRequest * a_Request )
2013-08-06 08:59:54 +02:00
{
ASSERT ( IsValid ());
2015-07-29 09:04:03 -06:00
tolua_pushusertype ( m_LuaState , reinterpret_cast < void *> ( const_cast < HTTPRequest *> ( a_Request )), "HTTPRequest" );
2013-08-06 08:59:54 +02:00
}
2014-06-03 19:29:04 +02:00
void cLuaState :: Push ( const HTTPTemplateRequest * a_Request )
2013-08-06 08:59:54 +02:00
{
ASSERT ( IsValid ());
2015-07-29 09:04:03 -06:00
tolua_pushusertype ( m_LuaState , reinterpret_cast < void *> ( const_cast < HTTPTemplateRequest *> ( a_Request )), "HTTPTemplateRequest" );
2013-08-06 08:59:54 +02:00
}
2014-06-03 19:29:04 +02:00
void cLuaState :: Push ( const Vector3d & a_Vector )
2013-08-08 14:08:21 +02:00
{
ASSERT ( IsValid ());
2015-07-29 09:04:03 -06:00
tolua_pushusertype ( m_LuaState , reinterpret_cast < void *> ( const_cast < Vector3d *> ( & a_Vector )), "Vector3<double>" );
2014-08-22 10:33:15 +02:00
}
void cLuaState :: Push ( const Vector3d * a_Vector )
{
ASSERT ( IsValid ());
2015-07-29 09:04:03 -06:00
tolua_pushusertype ( m_LuaState , reinterpret_cast < void *> ( const_cast < Vector3d *> ( a_Vector )), "Vector3<double>" );
2014-08-22 10:33:15 +02:00
}
void cLuaState :: Push ( const Vector3i & a_Vector )
{
ASSERT ( IsValid ());
2015-07-29 09:04:03 -06:00
tolua_pushusertype ( m_LuaState , reinterpret_cast < void *> ( const_cast < Vector3i *> ( & a_Vector )), "Vector3<int>" );
2014-08-22 10:33:15 +02:00
}
void cLuaState :: Push ( const Vector3i * a_Vector )
{
ASSERT ( IsValid ());
2015-07-29 09:04:03 -06:00
tolua_pushusertype ( m_LuaState , reinterpret_cast < void *> ( const_cast < Vector3i *> ( a_Vector )), "Vector3<int>" );
2013-08-08 14:08:21 +02:00
}
2014-06-03 19:29:04 +02:00
void cLuaState :: Push ( bool a_Value )
2013-08-06 08:59:54 +02:00
{
ASSERT ( IsValid ());
2014-06-03 19:29:04 +02:00
tolua_pushboolean ( m_LuaState , a_Value ? 1 : 0 );
2013-08-06 08:59:54 +02:00
}
2014-06-03 19:29:04 +02:00
void cLuaState :: Push ( cEntity * a_Entity )
{
ASSERT ( IsValid ());
2014-10-20 17:32:09 +02:00
if ( a_Entity == nullptr )
{
lua_pushnil ( m_LuaState );
}
2014-10-19 12:49:54 +02:00
else
{
2014-10-21 12:43:06 +02: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 16:06:16 +02:00
tolua_pushusertype ( m_LuaState , a_Entity , a_Entity -> GetClass ());
2014-10-21 12:43:06 +02: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 12:49:54 +02:00
}
2014-06-03 19:29:04 +02:00
}
2015-01-30 21:24:02 +01:00
void cLuaState :: Push ( cLuaServerHandle * a_ServerHandle )
{
ASSERT ( IsValid ());
tolua_pushusertype ( m_LuaState , a_ServerHandle , "cServerHandle" );
}
2015-01-28 15:14:05 +01:00
void cLuaState :: Push ( cLuaTCPLink * a_TCPLink )
{
ASSERT ( IsValid ());
tolua_pushusertype ( m_LuaState , a_TCPLink , "cTCPLink" );
}
2015-02-20 14:28:05 +01:00
void cLuaState :: Push ( cLuaUDPEndpoint * a_UDPEndpoint )
{
ASSERT ( IsValid ());
tolua_pushusertype ( m_LuaState , a_UDPEndpoint , "cUDPEndpoint" );
}
2014-06-03 19:29:04 +02:00
void cLuaState :: Push ( double a_Value )
2013-08-08 16:02:07 +02:00
{
ASSERT ( IsValid ());
2014-06-03 19:29:04 +02:00
tolua_pushnumber ( m_LuaState , a_Value );
2013-08-08 16:02:07 +02:00
}
2014-06-03 19:29:04 +02:00
void cLuaState :: Push ( int a_Value )
2013-08-08 16:30:02 +02:00
{
ASSERT ( IsValid ());
2014-06-03 19:29:04 +02:00
tolua_pushnumber ( m_LuaState , a_Value );
2013-08-08 16:30:02 +02:00
}
2015-09-26 22:54:18 +02:00
void cLuaState :: Push ( long a_Value )
{
ASSERT ( IsValid ());
tolua_pushnumber ( m_LuaState , static_cast < lua_Number > ( a_Value ));
}
2015-07-29 09:04:03 -06:00
void cLuaState :: Push ( UInt32 a_Value )
{
ASSERT ( IsValid ());
tolua_pushnumber ( m_LuaState , a_Value );
}
2015-01-11 21:12:26 +00:00
void cLuaState :: Push ( std :: chrono :: milliseconds a_Value )
{
ASSERT ( IsValid ());
2015-01-18 11:02:17 +01:00
tolua_pushnumber ( m_LuaState , static_cast < lua_Number > ( a_Value . count ()));
2015-01-11 21:12:26 +00:00
}
2013-08-09 14:58:43 +02:00
2013-08-11 14:57:07 +02:00
2016-06-10 21:30:07 +02:00
void cLuaState :: Pop ( int a_NumValuesToPop )
{
ASSERT ( IsValid ());
lua_pop ( m_LuaState , a_NumValuesToPop );
}
2015-05-19 15:25:18 +01:00
bool cLuaState :: GetStackValue ( int a_StackPos , AString & a_Value )
2015-04-23 19:41:01 +02: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 15:25:18 +01:00
return true ;
2015-04-23 19:41:01 +02:00
}
2015-05-19 15:25:18 +01:00
return false ;
2015-04-23 19:41:01 +02:00
}
2016-08-16 11:55:12 +02: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 15:25:18 +01:00
bool cLuaState :: GetStackValue ( int a_StackPos , bool & a_ReturnedVal )
2013-08-08 14:08:21 +02:00
{
a_ReturnedVal = ( tolua_toboolean ( m_LuaState , a_StackPos , a_ReturnedVal ? 1 : 0 ) > 0 );
2015-05-19 15:25:18 +01:00
return true ;
2013-08-08 14:08:21 +02:00
}
2016-03-02 10:12:43 +01: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 20:49:59 +02:00
{
if ( a_Callback == nullptr )
{
a_Callback = cpp14 :: make_unique < cCallback > ();
}
return a_Callback -> RefStack ( * this , a_StackPos );
}
2016-08-14 16:26:31 +02:00
bool cLuaState :: GetStackValue ( int a_StackPos , cOptionalCallback & a_Callback )
2016-03-02 10:12:43 +01:00
{
2016-08-14 16:26:31 +02:00
return a_Callback . RefStack ( * this , a_StackPos );
2016-03-02 10:12:43 +01:00
}
2016-08-14 16:26:31 +02:00
bool cLuaState :: GetStackValue ( int a_StackPos , cOptionalCallbackPtr & a_Callback )
2016-07-06 20:52:04 +02:00
{
2016-08-14 16:26:31 +02:00
if ( a_Callback == nullptr )
{
a_Callback = cpp14 :: make_unique < cOptionalCallback > ();
}
return a_Callback -> RefStack ( * this , a_StackPos );
2016-07-06 20:52:04 +02:00
}
2016-08-14 16:26:31 +02:00
bool cLuaState :: GetStackValue ( int a_StackPos , cCallbackSharedPtr & a_Callback )
2016-07-06 20:52:04 +02:00
{
2016-08-14 16:26:31 +02:00
if ( a_Callback == nullptr )
2016-07-06 20:52:04 +02:00
{
2016-08-14 16:26:31 +02:00
a_Callback = std :: make_shared < cCallback > ();
2016-07-06 20:52:04 +02:00
}
2016-08-14 16:26:31 +02:00
return a_Callback -> RefStack ( * this , a_StackPos );
2016-07-06 20:52:04 +02:00
}
2015-05-19 15:25:18 +01:00
bool cLuaState :: GetStackValue ( int a_StackPos , cPluginManager :: CommandResult & a_Result )
2015-05-10 23:11:30 +02: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 15:25:18 +01:00
return true ;
2015-05-10 23:11:30 +02:00
}
2015-05-19 15:25:18 +01:00
return false ;
2015-05-10 23:11:30 +02:00
}
2015-05-19 15:25:18 +01:00
bool cLuaState :: GetStackValue ( int a_StackPos , cRef & a_Ref )
2013-08-08 14:08:21 +02:00
{
2015-04-23 19:41:01 +02:00
a_Ref . RefStack ( * this , a_StackPos );
2015-05-19 15:25:18 +01:00
return true ;
2013-08-08 14:08:21 +02:00
}
2016-08-14 16:26:31 +02: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 20:52:04 +02: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 15:25:18 +01:00
bool cLuaState :: GetStackValue ( int a_StackPos , double & a_ReturnedVal )
2013-08-08 14:08:21 +02:00
{
if ( lua_isnumber ( m_LuaState , a_StackPos ))
{
2015-04-23 19:41:01 +02:00
a_ReturnedVal = tolua_tonumber ( m_LuaState , a_StackPos , a_ReturnedVal );
2015-05-19 15:25:18 +01:00
return true ;
2013-08-09 14:58:43 +02:00
}
2015-05-19 15:25:18 +01:00
return false ;
2013-08-09 14:58:43 +02:00
}
2015-06-17 15:21:20 +02:00
bool cLuaState :: GetStackValue ( int a_StackPos , eBlockFace & a_ReturnedVal )
2015-05-08 23:32:02 +01:00
{
2015-06-17 15:21:20 +02:00
if ( ! lua_isnumber ( m_LuaState , a_StackPos ))
2015-05-08 23:32:02 +01:00
{
2015-06-17 15:21:20 +02:00
return false ;
2015-05-08 23:32:02 +01:00
}
2015-06-17 15:21:20 +02: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 23:32:02 +01:00
}
2015-05-19 15:25:18 +01:00
bool cLuaState :: GetStackValue ( int a_StackPos , eWeather & a_ReturnedVal )
2013-08-09 14:58:43 +02:00
{
2015-04-23 19:41:01 +02:00
if ( ! lua_isnumber ( m_LuaState , a_StackPos ))
2013-08-09 14:58:43 +02:00
{
2015-05-19 15:25:18 +01:00
return false ;
2013-08-08 14:08:21 +02:00
}
2015-04-23 19:41:01 +02: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 15:25:18 +01:00
return true ;
2014-07-03 17:49:21 +02:00
}
2015-06-17 15:21:20 +02: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 15:37:41 +02: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 15:21:20 +02:00
lua_pushvalue ( m_LuaState , - 1 ); // Copy the stack value into the "working area"
2015-06-20 15:37:41 +02:00
auto path = StringSplit ( a_Name , "." );
2015-06-17 15:21:20 +02: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 10:32:42 +01:00
if ( lua_isnil ( m_LuaState , - 1 ))
{
lua_pop ( m_LuaState , 1 );
return cStackValue ();
}
2015-07-21 23:40:13 +01:00
return cStackValue ( * this );
2015-06-17 15:21:20 +02:00
}
2015-11-11 10:32:42 +01: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 22:41:17 +01:00
return cStackValue ( * this );
2015-11-11 10:32:42 +01:00
}
2013-08-06 08:59:54 +02:00
bool cLuaState :: CallFunction ( int a_NumResults )
2013-08-04 23:11:25 +02:00
{
2013-08-06 08:59:54 +02:00
ASSERT ( m_NumCurrentFunctionArgs >= 0 ); // A function must be pushed to stack first
2014-01-11 23:10:40 +01: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 23:45:45 +02:00
2014-02-11 08:52:14 +01: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 23:45:45 +02:00
2014-02-11 08:52:14 +01:00
// Call the function:
int s = lua_pcall ( m_LuaState , NumArgs , a_NumResults , - NumArgs - 2 );
2014-01-11 23:10:40 +01:00
if ( s != 0 )
2013-08-04 23:11:25 +02:00
{
2014-01-11 23:10:40 +01:00
// The error has already been printed together with the stacktrace
2014-02-11 08:52:14 +01:00
LOGWARNING ( "Error in %s calling function %s()" , m_SubsystemName . c_str (), CurrentFunctionName . c_str ());
2016-11-10 16:46:31 +01:00
// Remove the error handler and error message from the stack:
ASSERT ( lua_gettop ( m_LuaState ) == 2 );
lua_pop ( m_LuaState , 2 );
2013-08-04 23:11:25 +02:00
return false ;
}
2016-02-05 23:45:45 +02:00
2014-01-21 22:59:08 +01:00
// Remove the error handler from the stack:
lua_remove ( m_LuaState , - a_NumResults - 1 );
2013-08-04 23:11:25 +02:00
return true ;
}
2013-11-22 16:49:38 +01:00
bool cLuaState :: CheckParamUserTable ( int a_StartParam , const char * a_UserTable , int a_EndParam )
{
ASSERT ( IsValid ());
2016-02-05 23:45:45 +02:00
2013-11-22 16:49:38 +01:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 23:45:45 +02:00
2013-11-22 16:49:38 +01: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 21:55:07 +01:00
AString ErrMsg = Printf ( "#ferror in function '%s'." , ( entry . name != nullptr ) ? entry . name : "?" );
2013-11-22 16:49:38 +01:00
tolua_error ( m_LuaState , ErrMsg . c_str (), & tolua_err );
return false ;
} // for i - Param
2016-02-05 23:45:45 +02:00
2013-11-22 16:49:38 +01:00
// All params checked ok
return true ;
}
2013-08-07 14:26:18 +02:00
bool cLuaState :: CheckParamUserType ( int a_StartParam , const char * a_UserType , int a_EndParam )
{
ASSERT ( IsValid ());
2016-02-05 23:45:45 +02:00
2013-08-07 14:26:18 +02:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 23:45:45 +02:00
2013-08-07 14:26:18 +02: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 21:55:07 +01:00
AString ErrMsg = Printf ( "#ferror in function '%s'." , ( entry . name != nullptr ) ? entry . name : "?" );
2013-08-07 14:26:18 +02:00
tolua_error ( m_LuaState , ErrMsg . c_str (), & tolua_err );
return false ;
} // for i - Param
2016-02-05 23:45:45 +02:00
2013-08-07 14:26:18 +02:00
// All params checked ok
return true ;
}
bool cLuaState :: CheckParamTable ( int a_StartParam , int a_EndParam )
{
ASSERT ( IsValid ());
2016-02-05 23:45:45 +02:00
2013-08-07 14:26:18 +02:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 23:45:45 +02:00
2013-08-07 14:26:18 +02: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 21:55:07 +01:00
AString ErrMsg = Printf ( "#ferror in function '%s'." , ( entry . name != nullptr ) ? entry . name : "?" );
2015-05-16 16:19:18 +02:00
BreakIntoDebugger ( m_LuaState );
2013-08-07 14:26:18 +02:00
tolua_error ( m_LuaState , ErrMsg . c_str (), & tolua_err );
return false ;
} // for i - Param
2016-02-05 23:45:45 +02:00
2013-08-07 14:26:18 +02:00
// All params checked ok
return true ;
}
bool cLuaState :: CheckParamNumber ( int a_StartParam , int a_EndParam )
{
ASSERT ( IsValid ());
2016-02-05 23:45:45 +02:00
2013-08-07 14:26:18 +02:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 23:45:45 +02:00
2013-08-07 14:26:18 +02: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 21:55:07 +01:00
AString ErrMsg = Printf ( "#ferror in function '%s'." , ( entry . name != nullptr ) ? entry . name : "?" );
2013-08-07 14:26:18 +02:00
tolua_error ( m_LuaState , ErrMsg . c_str (), & tolua_err );
return false ;
} // for i - Param
2016-02-05 23:45:45 +02:00
2013-08-07 14:26:18 +02:00
// All params checked ok
return true ;
}
2015-12-16 15:04:47 +01:00
bool cLuaState :: CheckParamBool ( int a_StartParam , int a_EndParam )
{
ASSERT ( IsValid ());
2016-02-05 23:45:45 +02:00
2015-12-16 15:04:47 +01:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 23:45:45 +02:00
2015-12-16 15:04:47 +01: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 23:45:45 +02:00
2015-12-16 15:04:47 +01:00
// All params checked ok
return true ;
}
2013-11-22 20:11:24 +01:00
bool cLuaState :: CheckParamString ( int a_StartParam , int a_EndParam )
{
ASSERT ( IsValid ());
2016-02-05 23:45:45 +02:00
2013-11-22 20:11:24 +01:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 23:45:45 +02:00
2013-11-22 20:11:24 +01:00
tolua_Error tolua_err ;
for ( int i = a_StartParam ; i <= a_EndParam ; i ++ )
{
2015-09-28 14:53:50 +02:00
if ( lua_isstring ( m_LuaState , i ))
2013-11-22 20:11:24 +01: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 14:53:50 +02:00
tolua_err . array = 0 ;
tolua_err . type = "string" ;
tolua_err . index = i ;
2014-10-20 21:55:07 +01:00
AString ErrMsg = Printf ( "#ferror in function '%s'." , ( entry . name != nullptr ) ? entry . name : "?" );
2013-11-22 20:11:24 +01:00
tolua_error ( m_LuaState , ErrMsg . c_str (), & tolua_err );
return false ;
} // for i - Param
2016-02-05 23:45:45 +02:00
2013-11-22 20:11:24 +01:00
// All params checked ok
return true ;
}
2014-01-19 23:45:26 +01:00
bool cLuaState :: CheckParamFunction ( int a_StartParam , int a_EndParam )
{
ASSERT ( IsValid ());
2016-02-05 23:45:45 +02:00
2014-01-19 23:45:26 +01:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 23:45:45 +02:00
2014-01-19 23:45:26 +01: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 15:03:35 +01:00
luaL_error ( m_LuaState , "Error in function '%s' parameter #%d. Function expected, got %s" ,
2014-10-20 21:55:07 +01:00
( entry . name != nullptr ) ? entry . name : "?" , i , GetTypeText ( i ). c_str ()
2014-02-11 15:03:35 +01:00
);
return false ;
} // for i - Param
2016-02-05 23:45:45 +02:00
2014-02-11 15:03:35 +01:00
// All params checked ok
return true ;
}
bool cLuaState :: CheckParamFunctionOrNil ( int a_StartParam , int a_EndParam )
{
ASSERT ( IsValid ());
2016-02-05 23:45:45 +02:00
2014-02-11 15:03:35 +01:00
if ( a_EndParam < 0 )
{
a_EndParam = a_StartParam ;
}
2016-02-05 23:45:45 +02:00
2014-02-11 15:03:35 +01: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 21:55:07 +01:00
( entry . name != nullptr ) ? entry . name : "?" , i , GetTypeText ( i ). c_str ()
2014-01-19 23:45:26 +01:00
);
return false ;
} // for i - Param
2016-02-05 23:45:45 +02:00
2014-01-19 23:45:26 +01:00
// All params checked ok
return true ;
}
2013-08-07 14:26:18 +02:00
bool cLuaState :: CheckParamEnd ( int a_Param )
{
tolua_Error tolua_err ;
2015-05-08 23:32:02 +01:00
if ( tolua_isnoobj ( m_LuaState , a_Param , & tolua_err ) == 1 )
2013-08-07 14:26:18 +02: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 21:55:07 +01:00
AString ErrMsg = Printf ( "#ferror in function '%s': Too many arguments." , ( entry . name != nullptr ) ? entry . name : "?" );
2013-08-07 14:26:18 +02:00
tolua_error ( m_LuaState , ErrMsg . c_str (), & tolua_err );
return false ;
}
2015-03-21 17:17:26 +00:00
bool cLuaState :: IsParamUserType ( int a_Param , AString a_UserType )
{
ASSERT ( IsValid ());
2016-02-05 23:45:45 +02:00
2015-03-21 17:17:26 +00:00
tolua_Error tolua_err ;
2015-05-08 23:32:02 +01:00
return ( tolua_isusertype ( m_LuaState , a_Param , a_UserType . c_str (), 0 , & tolua_err ) == 1 );
2015-03-21 17:17:26 +00:00
}
bool cLuaState :: IsParamNumber ( int a_Param )
{
ASSERT ( IsValid ());
2016-02-05 23:45:45 +02:00
2015-03-21 17:17:26 +00:00
tolua_Error tolua_err ;
2015-05-08 23:32:02 +01:00
return ( tolua_isnumber ( m_LuaState , a_Param , 0 , & tolua_err ) == 1 );
2015-03-21 17:17:26 +00:00
}
2013-08-04 23:11:25 +02: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 23:45:45 +02:00
2013-08-04 23:11:25 +02:00
LOGWARNING ( "LUA: %d - %s" , a_Status , lua_tostring ( a_LuaState , - 1 ));
lua_pop ( a_LuaState , 1 );
return true ;
}
2013-08-07 14:26:18 +02:00
2014-03-19 22:55:47 +01:00
void cLuaState :: LogStackTrace ( int a_StartingDepth )
2014-01-11 22:51:10 +01:00
{
2014-03-19 22:55:47 +01:00
LogStackTrace ( m_LuaState , a_StartingDepth );
2014-01-11 22:51:10 +01:00
}
2014-03-19 22:55:47 +01:00
void cLuaState :: LogStackTrace ( lua_State * a_LuaState , int a_StartingDepth )
2013-08-21 20:06:37 +02:00
{
LOGWARNING ( "Stack trace:" );
lua_Debug entry ;
2014-03-19 22:55:47 +01:00
int depth = a_StartingDepth ;
2014-01-11 22:51:10 +01:00
while ( lua_getstack ( a_LuaState , depth , & entry ))
2013-08-21 20:06:37 +02:00
{
2014-02-04 22:24:03 +01:00
lua_getinfo ( a_LuaState , "Sln" , & entry );
2014-01-11 22:51:10 +01:00
LOGWARNING ( " %s(%d): %s" , entry . short_src , entry . currentline , entry . name ? entry . name : "(no name)" );
2013-08-21 20:06:37 +02:00
depth ++ ;
}
LOGWARNING ( "Stack trace end" );
}
AString cLuaState :: GetTypeText ( int a_StackPos )
{
2014-01-21 22:59:08 +01: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 23:45:45 +02:00
2014-01-21 22:59:08 +01:00
// Store the stack position before any changes
int OldTop = lua_gettop ( m_LuaState );
2016-02-05 23:45:45 +02:00
2014-01-21 22:59:08 +01: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 23:02:18 +02:00
lua_settop ( m_LuaState , OldTop );
2014-01-21 22:59:08 +01:00
return - 1 ;
}
// Copy the function parameters to the target state
if ( CopyStackFrom ( a_SrcLuaState , a_SrcParamStart , a_SrcParamEnd ) < 0 )
2013-08-21 20:06:37 +02:00
{
2014-01-21 22:59:08 +01:00
// Something went wrong, fix the stack and exit
2015-05-07 23:02:18 +02:00
lua_settop ( m_LuaState , OldTop );
2014-02-04 09:18:32 +01:00
m_NumCurrentFunctionArgs = - 1 ;
m_CurrentFunctionName . clear ();
2014-01-21 22:59:08 +01:00
return - 1 ;
}
2016-02-05 23:45:45 +02:00
2014-01-21 22:59:08 +01:00
// Call the function, with an error handler:
2014-02-04 10:29:10 +01:00
int s = lua_pcall ( m_LuaState , a_SrcParamEnd - a_SrcParamStart + 1 , LUA_MULTRET , OldTop + 1 );
2014-01-21 22:59:08 +01:00
if ( ReportErrors ( s ))
{
LOGWARN ( "Error while calling function '%s' in '%s'" , a_FunctionName . c_str (), m_SubsystemName . c_str ());
2015-05-07 23:02:18 +02:00
// Reset the stack:
lua_settop ( m_LuaState , OldTop );
2016-02-05 23:45:45 +02:00
2014-02-04 09:18:32 +01:00
// Reset the internal checking mechanisms:
m_NumCurrentFunctionArgs = - 1 ;
m_CurrentFunctionName . clear ();
2016-02-05 23:45:45 +02:00
2014-02-04 10:29:10 +01: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 20:06:37 +02:00
}
2016-02-05 23:45:45 +02:00
2014-01-21 22:59:08 +01:00
// Reset the internal checking mechanisms:
m_NumCurrentFunctionArgs = - 1 ;
2014-02-04 09:18:32 +01:00
m_CurrentFunctionName . clear ();
2016-02-05 23:45:45 +02:00
2014-01-21 22:59:08 +01:00
// Remove the error handler from the stack:
lua_remove ( m_LuaState , OldTop + 1 );
2016-02-05 23:45:45 +02:00
2014-01-21 22:59:08 +01:00
// Return the number of return values:
return lua_gettop ( m_LuaState ) - OldTop ;
}
2016-05-31 01:01:55 +02:00
int cLuaState :: CopyStackFrom ( cLuaState & a_SrcLuaState , int a_SrcStart , int a_SrcEnd , int a_NumAllowedNestingLevels )
2014-01-21 22:59:08 +01: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-31 01:01:55 +02:00
if ( ! CopySingleValueFrom ( a_SrcLuaState , i , a_NumAllowedNestingLevels ))
2014-01-21 22:59:08 +01:00
{
2016-05-31 01:01:55 +02: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 13:12:34 +02:00
ASSERT ( lua_gettop ( a_SrcLuaState ) == srcTop + 3 );
ASSERT ( lua_gettop ( m_LuaState ) == dstTop + 1 );
2016-05-31 01:01:55 +02: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 13:12:34 +02:00
ASSERT ( lua_gettop ( a_SrcLuaState ) == srcTop );
ASSERT ( lua_gettop ( m_LuaState ) == dstTop );
2016-05-31 01:01:55 +02:00
return false ;
}
2016-08-02 13:12:34 +02:00
ASSERT ( lua_gettop ( a_SrcLuaState ) == srcTop + 3 );
ASSERT ( lua_gettop ( m_LuaState ) == dstTop + 2 );
2016-05-31 01:01:55 +02: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 13:12:34 +02:00
ASSERT ( lua_gettop ( a_SrcLuaState ) == srcTop );
ASSERT ( lua_gettop ( m_LuaState ) == dstTop );
2016-05-31 01:01:55 +02:00
return false ;
}
2016-08-02 13:12:34 +02:00
ASSERT ( lua_gettop ( a_SrcLuaState ) == srcTop + 3 );
ASSERT ( lua_gettop ( m_LuaState ) == dstTop + 3 );
2016-05-31 01:01:55 +02: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 13:12:34 +02:00
ASSERT ( lua_gettop ( a_SrcLuaState ) == srcTop + 2 );
ASSERT ( lua_gettop ( m_LuaState ) == dstTop + 1 );
2016-05-31 01:01:55 +02:00
}
lua_pop ( a_SrcLuaState , 1 ); // SRC: empty
2016-08-02 13:12:34 +02:00
ASSERT ( lua_gettop ( a_SrcLuaState ) == srcTop );
ASSERT ( lua_gettop ( m_LuaState ) == dstTop + 1 );
2016-05-31 01:01:55 +02: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 22:59:08 +01:00
{
2016-05-31 01:01:55 +02:00
LOGWARNING ( "%s: Unknown class in pos %d, cannot copy." , __FUNCTION__ , a_StackIdx );
return false ;
2014-01-21 22:59:08 +01:00
}
2016-05-31 01:01:55 +02: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 22:59:08 +01:00
{
2016-05-31 01:01:55 +02:00
LOGWARNING ( "%s: Failed to copy table in pos %d." , __FUNCTION__ , a_StackIdx );
return false ;
2014-01-21 22:59:08 +01:00
}
2016-05-31 01:01:55 +02: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 22:59:08 +01: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 21:55:07 +01:00
if ( s != nullptr )
2014-01-21 22:59:08 +01:00
{
a_String . assign ( s , len );
}
}
2016-06-10 21:30:07 +02:00
void cLuaState :: LogStackValues ( const char * a_Header )
2014-01-21 22:59:08 +01:00
{
2016-06-10 21:30:07 +02:00
LogStackValues ( m_LuaState , a_Header );
2014-01-21 22:59:08 +01:00
}
2016-06-10 21:30:07 +02:00
void cLuaState :: LogStackValues ( lua_State * a_LuaState , const char * a_Header )
2014-01-21 22:59:08 +01:00
{
2014-03-11 14:16:08 -07:00
// Format string consisting only of %s is used to appease the compiler
2016-11-10 16:46:31 +01:00
ASSERT_LUA_STACK_BALANCE ( a_LuaState , false );
2014-10-20 21:55:07 +01:00
LOG ( "%s" , ( a_Header != nullptr ) ? a_Header : "Lua C API Stack contents:" );
2014-02-10 22:44:56 +01:00
for ( int i = lua_gettop ( a_LuaState ); i > 0 ; i -- )
2014-01-21 22:59:08 +01: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 09:04:03 -06:00
case LUA_TNUMBER : Printf ( Value , "%f" , static_cast < double > ( lua_tonumber ( a_LuaState , i ))); break ;
2016-11-10 16:46:31 +01: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 23:12:46 +01:00
case LUA_TTABLE : Printf ( Value , "%p" , lua_topointer ( a_LuaState , i )); break ;
2016-11-10 16:46:31 +01: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 22:59:08 +01: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 20:06:37 +02:00
}
2017-01-09 15:56:16 +01:00
cLuaState * cLuaState :: QueryCanonLuaState ( void ) const
2016-06-10 21:30:07 +02:00
{
2017-01-09 15:56:16 +01:00
return cCanonLuaStates :: GetCanonState ( m_LuaState );
2016-06-10 21:30:07 +02:00
}
2016-08-23 13:20:43 +02: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" );
}
2014-01-11 23:10:40 +01:00
int cLuaState :: ReportFnCallErrors ( lua_State * a_LuaState )
{
LOGWARNING ( "LUA: %s" , lua_tostring ( a_LuaState , - 1 ));
2014-03-19 22:55:47 +01:00
LogStackTrace ( a_LuaState , 1 );
2015-05-16 16:19:18 +02:00
BreakIntoDebugger ( a_LuaState );
2014-01-11 23:10:40 +01:00
return 1 ; // We left the error message on the stack as the return value
}
2015-05-16 16:19:18 +02:00
int cLuaState :: BreakIntoDebugger ( lua_State * a_LuaState )
{
2016-11-10 16:46:31 +01:00
ASSERT_LUA_STACK_BALANCE ( a_LuaState );
2015-05-16 16:19:18 +02: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 16:46:31 +01:00
lua_pushvalue ( a_LuaState , - 2 ); // Copy the string that has been passed to us
2015-05-16 16:19:18 +02:00
LOGD ( "Calling BreakIntoDebugger()..." );
lua_call ( a_LuaState , 1 , 0 );
LOGD ( "Returned from BreakIntoDebugger()." );
return 0 ;
}
2016-07-06 20:52:04 +02:00
void cLuaState :: TrackRef ( cTrackedRef & a_Ref )
2016-03-02 11:34:39 +01:00
{
// Get the CanonLuaState global from Lua:
2016-06-10 21:30:07 +02:00
auto canonState = QueryCanonLuaState ();
if ( canonState == nullptr )
2016-03-02 11:34:39 +01:00
{
LOGWARNING ( "%s: Lua state %p has invalid CanonLuaState!" , __FUNCTION__ , reinterpret_cast < void *> ( m_LuaState ));
return ;
}
// Add the callback:
2016-07-06 20:52:04 +02:00
cCSLock Lock ( canonState -> m_CSTrackedRefs );
canonState -> m_TrackedRefs . push_back ( & a_Ref );
2016-03-02 11:34:39 +01:00
}
2016-07-06 20:52:04 +02:00
void cLuaState :: UntrackRef ( cTrackedRef & a_Ref )
2016-03-02 11:34:39 +01:00
{
// Get the CanonLuaState global from Lua:
2016-06-10 21:30:07 +02:00
auto canonState = QueryCanonLuaState ();
if ( canonState == nullptr )
2016-03-02 11:34:39 +01:00
{
LOGWARNING ( "%s: Lua state %p has invalid CanonLuaState!" , __FUNCTION__ , reinterpret_cast < void *> ( m_LuaState ));
return ;
}
2017-01-15 12:54:46 +01:00
// Remove the callback (note that another thread may have cleared the callbacks by closing the LuaState):
2016-07-06 20:52:04 +02:00
cCSLock Lock ( canonState -> m_CSTrackedRefs );
auto & trackedRefs = canonState -> m_TrackedRefs ;
2017-01-15 12:54:46 +01:00
for ( auto itr = trackedRefs . begin (), end = trackedRefs . end (); itr != end ; ++ itr )
{
if ( * itr == & a_Ref )
2016-03-02 11:34:39 +01:00
{
2017-01-15 12:54:46 +01:00
trackedRefs . erase ( itr );
break ;
2016-03-02 11:34:39 +01:00
}
2017-01-15 12:54:46 +01:00
}
2016-03-02 11:34:39 +01:00
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-08-07 14:26:18 +02:00
// cLuaState::cRef:
2014-02-09 18:39:22 +01:00
cLuaState :: cRef :: cRef ( void ) :
2014-10-20 21:55:07 +01:00
m_LuaState ( nullptr ),
2014-02-09 18:39:22 +01:00
m_Ref ( LUA_REFNIL )
{
}
2013-08-07 14:26:18 +02:00
cLuaState :: cRef :: cRef ( cLuaState & a_LuaState , int a_StackPos ) :
2014-10-20 21:55:07 +01:00
m_LuaState ( nullptr ),
2014-02-09 18:39:22 +01:00
m_Ref ( LUA_REFNIL )
2013-08-07 14:26:18 +02:00
{
2014-02-09 18:39:22 +01:00
RefStack ( a_LuaState , a_StackPos );
2013-08-07 14:26:18 +02:00
}
2015-01-30 21:24:02 +01: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 14:26:18 +02:00
cLuaState :: cRef ::~ cRef ()
{
2014-10-20 21:55:07 +01:00
if ( m_LuaState != nullptr )
2014-02-09 18:39:22 +01:00
{
UnRef ();
}
}
void cLuaState :: cRef :: RefStack ( cLuaState & a_LuaState , int a_StackPos )
{
ASSERT ( a_LuaState . IsValid ());
2014-10-20 21:55:07 +01:00
if ( m_LuaState != nullptr )
2014-02-09 18:39:22 +01:00
{
UnRef ();
}
2017-01-13 10:31:05 +01:00
ASSERT ( cCanonLuaStates :: GetCanonState ( a_LuaState ) -> m_CS . IsLockedByCurrentThread ());
2016-03-02 11:34:39 +01:00
m_LuaState = a_LuaState ;
2014-02-09 18:39:22 +01: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 14:26:18 +02:00
if ( IsValid ())
{
2017-01-13 10:31:05 +01:00
ASSERT ( cCanonLuaStates :: GetCanonState ( m_LuaState ) -> m_CS . IsLockedByCurrentThread ());
2016-03-02 11:34:39 +01:00
luaL_unref ( m_LuaState , LUA_REGISTRYINDEX , m_Ref );
2013-08-07 14:26:18 +02:00
}
2014-10-20 21:55:07 +01:00
m_LuaState = nullptr ;
2014-02-09 18:39:22 +01:00
m_Ref = LUA_REFNIL ;
2013-08-07 14:26:18 +02:00
}