2014-03-15 16:59:03 +00:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
2016-05-18 00:42:11 +00:00
// Copyright (C) 2012-2016 by John "JTE" Muniz.
2019-12-06 18:49:42 +00:00
// Copyright (C) 2012-2019 by Sonic Team Junior.
2014-03-15 16:59:03 +00:00
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file lua_script.c
/// \brief Lua scripting basics
# include "doomdef.h"
# ifdef HAVE_BLUA
# include "fastcmp.h"
# include "dehacked.h"
# include "z_zone.h"
# include "w_wad.h"
# include "p_setup.h"
# include "r_state.h"
2019-12-23 21:49:23 +00:00
# include "r_sky.h"
2014-03-15 16:59:03 +00:00
# include "g_game.h"
2019-12-23 21:49:23 +00:00
# include "f_finale.h"
2014-03-15 16:59:03 +00:00
# include "byteptr.h"
# include "p_saveg.h"
# include "p_local.h"
2018-10-21 14:00:07 +00:00
# ifdef ESLOPE
# include "p_slopes.h" // for P_SlopeById
# endif
2014-03-15 16:59:03 +00:00
# ifdef LUA_ALLOW_BYTECODE
# include "d_netfil.h" // for LUA_DumpFile
# endif
# include "lua_script.h"
# include "lua_libs.h"
# include "lua_hook.h"
2014-08-04 03:49:33 +00:00
# include "doomstat.h"
2014-03-15 16:59:03 +00:00
lua_State * gL = NULL ;
// List of internal libraries to load from SRB2
static lua_CFunction liblist [ ] = {
LUA_EnumLib , // global metatable for enums
LUA_SOCLib , // A_Action functions, freeslot
LUA_BaseLib , // string concatination by +, CONS_Printf, p_local.h stuff (P_InstaThrust, P_Move), etc.
LUA_MathLib , // fixed_t and angle_t math functions
LUA_HookLib , // hookAdd and hook-calling functions
LUA_ConsoleLib , // console command/variable functions and structs
LUA_InfoLib , // info.h stuff: mobjinfo_t, mobjinfo[], state_t, states[]
LUA_MobjLib , // mobj_t, mapthing_t
LUA_PlayerLib , // player_t
LUA_SkinLib , // skin_t, skins[]
LUA_ThinkerLib , // thinker_t
LUA_MapLib , // line_t, side_t, sector_t, subsector_t
2016-10-27 17:10:30 +00:00
LUA_BlockmapLib , // blockmap stuff
2014-03-15 16:59:03 +00:00
LUA_HudLib , // HUD stuff
NULL
} ;
// Lua asks for memory using this.
static void * LUA_Alloc ( void * ud , void * ptr , size_t osize , size_t nsize )
{
2014-08-05 23:59:40 +00:00
( void ) ud ;
2014-03-15 16:59:03 +00:00
if ( nsize = = 0 ) {
2014-08-05 23:59:40 +00:00
if ( osize ! = 0 )
Z_Free ( ptr ) ;
2014-03-15 16:59:03 +00:00
return NULL ;
} else
2014-08-05 23:59:40 +00:00
return Z_Realloc ( ptr , nsize , PU_LUA , NULL ) ;
2014-03-15 16:59:03 +00:00
}
// Panic function Lua calls when there's an unprotected error.
// This function cannot return. Lua would kill the application anyway if it did.
FUNCNORETURN static int LUA_Panic ( lua_State * L )
{
CONS_Alert ( CONS_ERROR , " LUA PANIC! %s \n " , lua_tostring ( L , - 1 ) ) ;
I_Error ( " An unfortunate Lua processing error occurred in the exe itself. This is not a scripting error on your part. " ) ;
# ifndef __GNUC__
return - 1 ;
# endif
}
2019-12-23 21:49:23 +00:00
// Moved here from lib_getenum.
int LUA_PushGlobals ( lua_State * L , const char * word )
2019-12-23 21:33:39 +00:00
{
2019-12-23 21:49:23 +00:00
if ( fastcmp ( word , " gamemap " ) ) {
lua_pushinteger ( L , gamemap ) ;
return 1 ;
} else if ( fastcmp ( word , " maptol " ) ) {
lua_pushinteger ( L , maptol ) ;
return 1 ;
} else if ( fastcmp ( word , " ultimatemode " ) ) {
lua_pushboolean ( L , ultimatemode ! = 0 ) ;
return 1 ;
} else if ( fastcmp ( word , " mariomode " ) ) {
lua_pushboolean ( L , mariomode ! = 0 ) ;
return 1 ;
} else if ( fastcmp ( word , " twodlevel " ) ) {
lua_pushboolean ( L , twodlevel ! = 0 ) ;
return 1 ;
} else if ( fastcmp ( word , " circuitmap " ) ) {
lua_pushboolean ( L , circuitmap ) ;
return 1 ;
} else if ( fastcmp ( word , " netgame " ) ) {
lua_pushboolean ( L , netgame ) ;
return 1 ;
} else if ( fastcmp ( word , " multiplayer " ) ) {
lua_pushboolean ( L , multiplayer ) ;
return 1 ;
} else if ( fastcmp ( word , " modeattacking " ) ) {
lua_pushboolean ( L , modeattacking ) ;
return 1 ;
} else if ( fastcmp ( word , " splitscreen " ) ) {
lua_pushboolean ( L , splitscreen ) ;
return 1 ;
} else if ( fastcmp ( word , " gamecomplete " ) ) {
lua_pushboolean ( L , gamecomplete ) ;
return 1 ;
} else if ( fastcmp ( word , " devparm " ) ) {
lua_pushboolean ( L , devparm ) ;
return 1 ;
} else if ( fastcmp ( word , " modifiedgame " ) ) {
lua_pushboolean ( L , modifiedgame & & ! savemoddata ) ;
return 1 ;
} else if ( fastcmp ( word , " menuactive " ) ) {
lua_pushboolean ( L , menuactive ) ;
return 1 ;
} else if ( fastcmp ( word , " paused " ) ) {
lua_pushboolean ( L , paused ) ;
return 1 ;
} else if ( fastcmp ( word , " bluescore " ) ) {
lua_pushinteger ( L , bluescore ) ;
return 1 ;
} else if ( fastcmp ( word , " redscore " ) ) {
lua_pushinteger ( L , redscore ) ;
return 1 ;
} else if ( fastcmp ( word , " timelimit " ) ) {
lua_pushinteger ( L , cv_timelimit . value ) ;
return 1 ;
} else if ( fastcmp ( word , " pointlimit " ) ) {
lua_pushinteger ( L , cv_pointlimit . value ) ;
return 1 ;
// begin map vars
} else if ( fastcmp ( word , " spstage_start " ) ) {
lua_pushinteger ( L , spstage_start ) ;
return 1 ;
} else if ( fastcmp ( word , " sstage_start " ) ) {
lua_pushinteger ( L , sstage_start ) ;
return 1 ;
} else if ( fastcmp ( word , " sstage_end " ) ) {
lua_pushinteger ( L , sstage_end ) ;
return 1 ;
} else if ( fastcmp ( word , " smpstage_start " ) ) {
lua_pushinteger ( L , smpstage_start ) ;
return 1 ;
} else if ( fastcmp ( word , " smpstage_end " ) ) {
lua_pushinteger ( L , smpstage_end ) ;
return 1 ;
} else if ( fastcmp ( word , " titlemap " ) ) {
lua_pushinteger ( L , titlemap ) ;
return 1 ;
} else if ( fastcmp ( word , " titlemapinaction " ) ) {
lua_pushboolean ( L , ( titlemapinaction ! = TITLEMAP_OFF ) ) ;
return 1 ;
} else if ( fastcmp ( word , " bootmap " ) ) {
lua_pushinteger ( L , bootmap ) ;
return 1 ;
} else if ( fastcmp ( word , " tutorialmap " ) ) {
lua_pushinteger ( L , tutorialmap ) ;
return 1 ;
} else if ( fastcmp ( word , " tutorialmode " ) ) {
lua_pushboolean ( L , tutorialmode ) ;
return 1 ;
// end map vars
// begin CTF colors
} else if ( fastcmp ( word , " skincolor_redteam " ) ) {
lua_pushinteger ( L , skincolor_redteam ) ;
return 1 ;
} else if ( fastcmp ( word , " skincolor_blueteam " ) ) {
lua_pushinteger ( L , skincolor_blueteam ) ;
return 1 ;
} else if ( fastcmp ( word , " skincolor_redring " ) ) {
lua_pushinteger ( L , skincolor_redring ) ;
return 1 ;
} else if ( fastcmp ( word , " skincolor_bluering " ) ) {
lua_pushinteger ( L , skincolor_bluering ) ;
return 1 ;
// end CTF colors
// begin timers
} else if ( fastcmp ( word , " invulntics " ) ) {
lua_pushinteger ( L , invulntics ) ;
return 1 ;
} else if ( fastcmp ( word , " sneakertics " ) ) {
lua_pushinteger ( L , sneakertics ) ;
return 1 ;
} else if ( fastcmp ( word , " flashingtics " ) ) {
lua_pushinteger ( L , flashingtics ) ;
return 1 ;
} else if ( fastcmp ( word , " tailsflytics " ) ) {
lua_pushinteger ( L , tailsflytics ) ;
return 1 ;
} else if ( fastcmp ( word , " underwatertics " ) ) {
lua_pushinteger ( L , underwatertics ) ;
return 1 ;
} else if ( fastcmp ( word , " spacetimetics " ) ) {
lua_pushinteger ( L , spacetimetics ) ;
return 1 ;
} else if ( fastcmp ( word , " extralifetics " ) ) {
lua_pushinteger ( L , extralifetics ) ;
return 1 ;
} else if ( fastcmp ( word , " nightslinktics " ) ) {
lua_pushinteger ( L , nightslinktics ) ;
return 1 ;
} else if ( fastcmp ( word , " gameovertics " ) ) {
lua_pushinteger ( L , gameovertics ) ;
return 1 ;
} else if ( fastcmp ( word , " ammoremovaltics " ) ) {
lua_pushinteger ( L , ammoremovaltics ) ;
return 1 ;
// end timers
} else if ( fastcmp ( word , " gametype " ) ) {
lua_pushinteger ( L , gametype ) ;
return 1 ;
} else if ( fastcmp ( word , " gametyperules " ) ) {
lua_pushinteger ( L , gametyperules ) ;
return 1 ;
} else if ( fastcmp ( word , " leveltime " ) ) {
lua_pushinteger ( L , leveltime ) ;
return 1 ;
} else if ( fastcmp ( word , " curWeather " ) ) {
lua_pushinteger ( L , curWeather ) ;
return 1 ;
} else if ( fastcmp ( word , " globalweather " ) ) {
lua_pushinteger ( L , globalweather ) ;
return 1 ;
} else if ( fastcmp ( word , " levelskynum " ) ) {
lua_pushinteger ( L , levelskynum ) ;
return 1 ;
} else if ( fastcmp ( word , " globallevelskynum " ) ) {
lua_pushinteger ( L , globallevelskynum ) ;
return 1 ;
} else if ( fastcmp ( word , " mapmusname " ) ) {
lua_pushstring ( L , mapmusname ) ;
return 1 ;
} else if ( fastcmp ( word , " mapmusflags " ) ) {
lua_pushinteger ( L , mapmusflags ) ;
return 1 ;
} else if ( fastcmp ( word , " mapmusposition " ) ) {
lua_pushinteger ( L , mapmusposition ) ;
return 1 ;
// local player variables, by popular request
} else if ( fastcmp ( word , " consoleplayer " ) ) { // player controlling console (aka local player 1)
if ( consoleplayer < 0 | | ! playeringame [ consoleplayer ] )
return 0 ;
LUA_PushUserdata ( L , & players [ consoleplayer ] , META_PLAYER ) ;
return 1 ;
} else if ( fastcmp ( word , " displayplayer " ) ) { // player visible on screen (aka display player 1)
if ( displayplayer < 0 | | ! playeringame [ displayplayer ] )
return 0 ;
LUA_PushUserdata ( L , & players [ displayplayer ] , META_PLAYER ) ;
return 1 ;
} else if ( fastcmp ( word , " secondarydisplayplayer " ) ) { // local/display player 2, for splitscreen
if ( ! splitscreen | | secondarydisplayplayer < 0 | | ! playeringame [ secondarydisplayplayer ] )
return 0 ;
LUA_PushUserdata ( L , & players [ secondarydisplayplayer ] , META_PLAYER ) ;
return 1 ;
// end local player variables
} else if ( fastcmp ( word , " server " ) ) {
if ( ( ! multiplayer | | ! netgame ) & & ! playeringame [ serverplayer ] )
return 0 ;
LUA_PushUserdata ( L , & players [ serverplayer ] , META_PLAYER ) ;
return 1 ;
} else if ( fastcmp ( word , " admin " ) ) { // BACKWARDS COMPATIBILITY HACK: This was replaced with IsPlayerAdmin(), but some 2.1 Lua scripts still use the admin variable. It now points to the first admin player in the array.
LUA_Deprecated ( L , " admin " , " IsPlayerAdmin(player) " ) ;
if ( ! playeringame [ adminplayers [ 0 ] ] | | IsPlayerAdmin ( serverplayer ) )
return 0 ;
LUA_PushUserdata ( L , & players [ adminplayers [ 0 ] ] , META_PLAYER ) ;
return 1 ;
} else if ( fastcmp ( word , " emeralds " ) ) {
lua_pushinteger ( L , emeralds ) ;
return 1 ;
} else if ( fastcmp ( word , " gravity " ) ) {
lua_pushinteger ( L , gravity ) ;
return 1 ;
} else if ( fastcmp ( word , " VERSIONSTRING " ) ) {
lua_pushstring ( L , VERSIONSTRING ) ;
return 1 ;
} else if ( fastcmp ( word , " token " ) ) {
lua_pushinteger ( L , token ) ;
return 1 ;
}
return 0 ;
}
// See the above.
int LUA_CheckGlobals ( lua_State * L , const char * word )
{
if ( fastcmp ( word , " redscore " ) )
2019-12-23 21:33:39 +00:00
redscore = ( UINT32 ) luaL_checkinteger ( L , 2 ) ;
2019-12-23 21:49:23 +00:00
else if ( fastcmp ( word , " bluescore " ) )
2019-12-23 21:33:39 +00:00
bluescore = ( UINT32 ) luaL_checkinteger ( L , 2 ) ;
else
2019-12-23 21:49:23 +00:00
return 0 ;
2019-12-23 21:33:39 +00:00
// Global variable set, so return and don't error.
2019-12-23 21:49:23 +00:00
return 1 ;
2019-12-23 21:33:39 +00:00
}
2014-03-15 16:59:03 +00:00
// This function decides which global variables you are allowed to set.
2019-12-23 21:33:39 +00:00
static int setglobals ( lua_State * L )
2014-03-15 16:59:03 +00:00
{
const char * csname ;
char * name ;
lua_remove ( L , 1 ) ; // we're not gonna be using _G
csname = lua_tostring ( L , 1 ) ;
// make an uppercase copy of the name
name = Z_StrDup ( csname ) ;
strupr ( name ) ;
if ( fastncmp ( name , " A_ " , 2 ) & & lua_isfunction ( L , 2 ) )
{
// Accept new A_Action functions
// Add the action to Lua actions refrence table
lua_getfield ( L , LUA_REGISTRYINDEX , LREG_ACTIONS ) ;
lua_pushstring ( L , name ) ; // "A_ACTION"
lua_pushvalue ( L , 2 ) ; // function
lua_rawset ( L , - 3 ) ; // rawset doesn't trigger this metatable again.
// otherwise we would've used setfield, obviously.
Z_Free ( name ) ;
return 0 ;
}
2019-12-23 21:49:23 +00:00
if ( LUA_CheckGlobals ( L , csname ) )
2019-12-19 06:14:34 +00:00
return 0 ;
2014-03-15 16:59:03 +00:00
Z_Free ( name ) ;
return luaL_error ( L , " Implicit global " LUA_QS " prevented. Create a local variable instead. " , csname ) ;
}
// Clear and create a new Lua state, laddo!
// There's SCRIPTIN to be had!
static void LUA_ClearState ( void )
{
lua_State * L ;
int i ;
// close previous state
if ( gL )
lua_close ( gL ) ;
gL = NULL ;
CONS_Printf ( M_GetText ( " Pardon me while I initialize the Lua scripting interface... \n " ) ) ;
// allocate state
L = lua_newstate ( LUA_Alloc , NULL ) ;
lua_atpanic ( L , LUA_Panic ) ;
// open base libraries
luaL_openlibs ( L ) ;
lua_pop ( L , - 1 ) ;
// make LREG_VALID table for all pushed userdata cache.
lua_newtable ( L ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , LREG_VALID ) ;
// open srb2 libraries
for ( i = 0 ; liblist [ i ] ; i + + ) {
lua_pushcfunction ( L , liblist [ i ] ) ;
lua_call ( L , 0 , 0 ) ;
}
// lock the global namespace
lua_getmetatable ( L , LUA_GLOBALSINDEX ) ;
2019-12-23 21:33:39 +00:00
lua_pushcfunction ( L , setglobals ) ;
2014-03-15 16:59:03 +00:00
lua_setfield ( L , - 2 , " __newindex " ) ;
lua_newtable ( L ) ;
lua_setfield ( L , - 2 , " __metatable " ) ;
lua_pop ( L , 1 ) ;
// lua state is ready!
gL = L ;
}
# ifdef _DEBUG
void LUA_ClearExtVars ( void )
{
if ( ! gL )
return ;
lua_newtable ( gL ) ;
lua_setfield ( gL , LUA_REGISTRYINDEX , LREG_EXTVARS ) ;
}
# endif
2017-04-25 20:45:53 +00:00
// Use this variable to prevent certain functions from running
// if they were not called on lump load
// (i.e. they were called in hooks or coroutines etc)
boolean lua_lumploading = false ;
2014-03-15 16:59:03 +00:00
// Load a script from a MYFILE
static inline void LUA_LoadFile ( MYFILE * f , char * name )
{
if ( ! name )
name = wadfiles [ f - > wad ] - > filename ;
CONS_Printf ( " Loading Lua script from %s \n " , name ) ;
if ( ! gL ) // Lua needs to be initialized
LUA_ClearState ( ) ;
lua_pushinteger ( gL , f - > wad ) ;
lua_setfield ( gL , LUA_REGISTRYINDEX , " WAD " ) ;
2018-02-09 22:43:08 +00:00
lua_lumploading = true ; // turn on loading flag
2014-03-15 16:59:03 +00:00
if ( luaL_loadbuffer ( gL , f - > data , f - > size , va ( " @%s " , name ) ) | | lua_pcall ( gL , 0 , 0 , 0 ) ) {
CONS_Alert ( CONS_WARNING , " %s \n " , lua_tostring ( gL , - 1 ) ) ;
lua_pop ( gL , 1 ) ;
}
lua_gc ( gL , LUA_GCCOLLECT , 0 ) ;
2018-02-09 22:43:08 +00:00
lua_lumploading = false ; // turn off again
2014-03-15 16:59:03 +00:00
}
// Load a script from a lump
void LUA_LoadLump ( UINT16 wad , UINT16 lump )
{
MYFILE f ;
char * name ;
2018-10-08 17:50:17 +00:00
size_t len ;
2014-03-15 16:59:03 +00:00
f . wad = wad ;
f . size = W_LumpLengthPwad ( wad , lump ) ;
2014-08-05 23:59:40 +00:00
f . data = Z_Malloc ( f . size , PU_LUA , NULL ) ;
2014-03-15 16:59:03 +00:00
W_ReadLumpPwad ( wad , lump , f . data ) ;
f . curpos = f . data ;
2018-10-10 13:51:34 +00:00
len = strlen ( wadfiles [ wad ] - > filename ) ; // length of file name
2018-02-09 22:43:08 +00:00
if ( wadfiles [ wad ] - > type = = RET_LUA )
{
2018-10-10 13:51:34 +00:00
name = malloc ( len + 1 ) ;
2018-02-09 22:43:08 +00:00
strcpy ( name , wadfiles [ wad ] - > filename ) ;
}
else // If it's not a .lua file, copy the lump name in too.
{
lumpinfo_t * lump_p = & wadfiles [ wad ] - > lumpinfo [ lump ] ;
2018-10-10 13:51:34 +00:00
len + = 1 + strlen ( lump_p - > name2 ) ; // length of file name, '|', and lump name
name = malloc ( len + 1 ) ;
2018-02-09 22:43:08 +00:00
sprintf ( name , " %s|%s " , wadfiles [ wad ] - > filename , lump_p - > name2 ) ;
2018-10-10 13:51:34 +00:00
name [ len ] = ' \0 ' ;
2014-03-15 16:59:03 +00:00
}
2017-04-25 20:45:53 +00:00
LUA_LoadFile ( & f , name ) ; // actually load file!
2014-03-15 16:59:03 +00:00
free ( name ) ;
Z_Free ( f . data ) ;
}
# ifdef LUA_ALLOW_BYTECODE
// must match lua_Writer
static int dumpWriter ( lua_State * L , const void * p , size_t sz , void * ud )
{
FILE * handle = ( FILE * ) ud ;
I_Assert ( handle ! = NULL ) ;
( void ) L ;
if ( ! sz ) return 0 ; // nothing to write? can't fail that! :D
return ( fwrite ( p , 1 , sz , handle ) ! = sz ) ; // if fwrite != sz, we've failed.
}
// Compile a script by name and dump it back to disk.
void LUA_DumpFile ( const char * filename )
{
FILE * handle ;
char filenamebuf [ MAX_WADPATH ] ;
if ( ! gL ) // Lua needs to be initialized
LUA_ClearState ( false ) ;
// find the file the SRB2 way
strncpy ( filenamebuf , filename , MAX_WADPATH ) ;
filenamebuf [ MAX_WADPATH - 1 ] = ' \0 ' ;
filename = filenamebuf ;
if ( ( handle = fopen ( filename , " rb " ) ) = = NULL )
{
// If we failed to load the file with the path as specified by
// the user, strip the directories and search for the file.
nameonly ( filenamebuf ) ;
// If findfile finds the file, the full path will be returned
// in filenamebuf == filename.
if ( findfile ( filenamebuf , NULL , true ) )
{
if ( ( handle = fopen ( filename , " rb " ) ) = = NULL )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Can't open %s \n " ) , filename ) ;
return ;
}
}
else
{
CONS_Alert ( CONS_ERROR , M_GetText ( " File %s not found. \n " ) , filename ) ;
return ;
}
}
fclose ( handle ) ;
// pass the path we found to Lua
// luaL_loadfile will open and read the file in as a Lua function
if ( luaL_loadfile ( gL , filename ) ) {
CONS_Alert ( CONS_ERROR , " %s \n " , lua_tostring ( gL , - 1 ) ) ;
lua_pop ( gL , 1 ) ;
return ;
}
// dump it back to disk
if ( ( handle = fopen ( filename , " wb " ) ) = = NULL )
CONS_Alert ( CONS_ERROR , M_GetText ( " Can't write to %s \n " ) , filename ) ;
if ( lua_dump ( gL , dumpWriter , handle ) )
CONS_Printf ( " Failed while writing %s to disk... Sorry! \n " , filename ) ;
else
CONS_Printf ( " Successfully compiled %s into bytecode. \n " , filename ) ;
fclose ( handle ) ;
lua_pop ( gL , 1 ) ; // function is still on stack after lua_dump
lua_gc ( gL , LUA_GCCOLLECT , 0 ) ;
return ;
}
# endif
fixed_t LUA_EvalMath ( const char * word )
{
lua_State * L = NULL ;
char buf [ 1024 ] , * b ;
const char * p ;
fixed_t res = 0 ;
// make a new state so SOC can't interefere with scripts
// allocate state
L = lua_newstate ( LUA_Alloc , NULL ) ;
lua_atpanic ( L , LUA_Panic ) ;
// open only enum lib
lua_pushcfunction ( L , LUA_EnumLib ) ;
lua_pushboolean ( L , true ) ;
lua_call ( L , 1 , 0 ) ;
// change ^ into ^^ for Lua.
strcpy ( buf , " return " ) ;
b = buf + strlen ( buf ) ;
for ( p = word ; * p & & b < & buf [ 1022 ] ; p + + )
{
* b + + = * p ;
if ( * p = = ' ^ ' )
* b + + = ' ^ ' ;
}
* b = ' \0 ' ;
// eval string.
lua_pop ( L , - 1 ) ;
if ( luaL_dostring ( L , buf ) )
{
p = lua_tostring ( L , - 1 ) ;
while ( * p + + ! = ' : ' & & * p ) ;
p + = 3 ; // "1: "
2014-08-05 23:59:40 +00:00
CONS_Alert ( CONS_WARNING , " %s \n " , p ) ;
2014-03-15 16:59:03 +00:00
}
else
res = lua_tointeger ( L , - 1 ) ;
// clean up and return.
lua_close ( L ) ;
return res ;
}
// Takes a pointer, any pointer, and a metatable name
// Creates a userdata for that pointer with the given metatable
// Pushes it to the stack and stores it in the registry.
void LUA_PushUserdata ( lua_State * L , void * data , const char * meta )
{
void * * userdata ;
if ( ! data ) { // push a NULL
lua_pushnil ( L ) ;
return ;
}
lua_getfield ( L , LUA_REGISTRYINDEX , LREG_VALID ) ;
I_Assert ( lua_istable ( L , - 1 ) ) ;
lua_pushlightuserdata ( L , data ) ;
lua_rawget ( L , - 2 ) ;
if ( lua_isnil ( L , - 1 ) ) { // no userdata? deary me, we'll have to make one.
lua_pop ( L , 1 ) ; // pop the nil
// create the userdata
userdata = lua_newuserdata ( L , sizeof ( void * ) ) ;
* userdata = data ;
luaL_getmetatable ( L , meta ) ;
lua_setmetatable ( L , - 2 ) ;
// Set it in the registry so we can find it again
lua_pushlightuserdata ( L , data ) ; // k (store the userdata via the data's pointer)
lua_pushvalue ( L , - 2 ) ; // v (copy of the userdata)
lua_rawset ( L , - 4 ) ;
// stack is left with the userdata on top, as if getting it had originally succeeded.
}
lua_remove ( L , - 2 ) ; // remove LREG_VALID
}
// When userdata is freed, use this function to remove it from Lua.
void LUA_InvalidateUserdata ( void * data )
{
void * * userdata ;
if ( ! gL )
return ;
// fetch the userdata
lua_getfield ( gL , LUA_REGISTRYINDEX , LREG_VALID ) ;
I_Assert ( lua_istable ( gL , - 1 ) ) ;
lua_pushlightuserdata ( gL , data ) ;
lua_rawget ( gL , - 2 ) ;
if ( lua_isnil ( gL , - 1 ) ) { // not found, not in lua
lua_pop ( gL , 2 ) ; // pop nil and LREG_VALID
return ;
}
// nullify any additional data
lua_getfield ( gL , LUA_REGISTRYINDEX , LREG_EXTVARS ) ;
I_Assert ( lua_istable ( gL , - 1 ) ) ;
lua_pushlightuserdata ( gL , data ) ;
lua_pushnil ( gL ) ;
lua_rawset ( gL , - 3 ) ;
lua_pop ( gL , 1 ) ;
// invalidate the userdata
userdata = lua_touserdata ( gL , - 1 ) ;
* userdata = NULL ;
lua_pop ( gL , 1 ) ;
// remove it from the registry
lua_pushlightuserdata ( gL , data ) ;
lua_pushnil ( gL ) ;
lua_rawset ( gL , - 3 ) ;
lua_pop ( gL , 1 ) ; // pop LREG_VALID
}
// Invalidate level data arrays
void LUA_InvalidateLevel ( void )
{
thinker_t * th ;
size_t i ;
2016-07-10 16:35:05 +00:00
ffloor_t * rover = NULL ;
2014-03-15 16:59:03 +00:00
if ( ! gL )
return ;
2019-04-21 10:58:22 +00:00
for ( i = 0 ; i < NUM_THINKERLISTS ; i + + )
for ( th = thlist [ i ] . next ; th & & th ! = & thlist [ i ] ; th = th - > next )
LUA_InvalidateUserdata ( th ) ;
2014-03-15 16:59:03 +00:00
LUA_InvalidateMapthings ( ) ;
for ( i = 0 ; i < numsubsectors ; i + + )
LUA_InvalidateUserdata ( & subsectors [ i ] ) ;
for ( i = 0 ; i < numsectors ; i + + )
2016-07-10 16:35:05 +00:00
{
2014-03-15 16:59:03 +00:00
LUA_InvalidateUserdata ( & sectors [ i ] ) ;
2019-09-25 19:27:41 +00:00
LUA_InvalidateUserdata ( & sectors [ i ] . lines ) ;
2016-07-10 16:35:05 +00:00
if ( sectors [ i ] . ffloors )
{
for ( rover = sectors [ i ] . ffloors ; rover ; rover = rover - > next )
LUA_InvalidateUserdata ( rover ) ;
}
}
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < numlines ; i + + )
{
LUA_InvalidateUserdata ( & lines [ i ] ) ;
LUA_InvalidateUserdata ( lines [ i ] . sidenum ) ;
}
for ( i = 0 ; i < numsides ; i + + )
LUA_InvalidateUserdata ( & sides [ i ] ) ;
for ( i = 0 ; i < numvertexes ; i + + )
LUA_InvalidateUserdata ( & vertexes [ i ] ) ;
2016-11-24 21:11:44 +00:00
# ifdef HAVE_LUA_SEGS
2016-07-08 19:43:02 +00:00
for ( i = 0 ; i < numsegs ; i + + )
LUA_InvalidateUserdata ( & segs [ i ] ) ;
for ( i = 0 ; i < numnodes ; i + + )
{
LUA_InvalidateUserdata ( & nodes [ i ] ) ;
LUA_InvalidateUserdata ( nodes [ i ] . bbox ) ;
LUA_InvalidateUserdata ( nodes [ i ] . children ) ;
}
2016-11-24 21:11:44 +00:00
# endif
2014-03-15 16:59:03 +00:00
}
void LUA_InvalidateMapthings ( void )
{
size_t i ;
if ( ! gL )
return ;
for ( i = 0 ; i < nummapthings ; i + + )
LUA_InvalidateUserdata ( & mapthings [ i ] ) ;
}
void LUA_InvalidatePlayer ( player_t * player )
{
if ( ! gL )
return ;
LUA_InvalidateUserdata ( player ) ;
LUA_InvalidateUserdata ( player - > powers ) ;
LUA_InvalidateUserdata ( & player - > cmd ) ;
}
enum
{
ARCH_NULL = 0 ,
ARCH_BOOLEAN ,
ARCH_SIGNED ,
ARCH_STRING ,
ARCH_TABLE ,
ARCH_MOBJINFO ,
ARCH_STATE ,
ARCH_MOBJ ,
ARCH_PLAYER ,
ARCH_MAPTHING ,
ARCH_VERTEX ,
ARCH_LINE ,
ARCH_SIDE ,
ARCH_SUBSECTOR ,
ARCH_SECTOR ,
2016-11-24 21:11:44 +00:00
# ifdef HAVE_LUA_SEGS
2016-05-25 16:15:44 +00:00
ARCH_SEG ,
2016-07-08 19:05:54 +00:00
ARCH_NODE ,
2016-11-24 21:11:44 +00:00
# endif
2016-07-10 16:58:54 +00:00
ARCH_FFLOOR ,
2018-10-21 14:00:07 +00:00
# ifdef ESLOPE
ARCH_SLOPE ,
# endif
2014-08-04 03:49:33 +00:00
ARCH_MAPHEADER ,
2014-03-15 16:59:03 +00:00
ARCH_TEND = 0xFF ,
} ;
static const struct {
const char * meta ;
UINT8 arch ;
} meta2arch [ ] = {
{ META_MOBJINFO , ARCH_MOBJINFO } ,
{ META_STATE , ARCH_STATE } ,
{ META_MOBJ , ARCH_MOBJ } ,
{ META_PLAYER , ARCH_PLAYER } ,
{ META_MAPTHING , ARCH_MAPTHING } ,
{ META_VERTEX , ARCH_VERTEX } ,
{ META_LINE , ARCH_LINE } ,
{ META_SIDE , ARCH_SIDE } ,
{ META_SUBSECTOR , ARCH_SUBSECTOR } ,
{ META_SECTOR , ARCH_SECTOR } ,
2016-11-24 21:11:44 +00:00
# ifdef HAVE_LUA_SEGS
2016-05-25 16:15:44 +00:00
{ META_SEG , ARCH_SEG } ,
2016-07-08 19:05:54 +00:00
{ META_NODE , ARCH_NODE } ,
2016-11-24 21:11:44 +00:00
# endif
2016-07-10 16:58:54 +00:00
{ META_FFLOOR , ARCH_FFLOOR } ,
2018-10-21 14:00:07 +00:00
# ifdef ESLOPE
{ META_SLOPE , ARCH_SLOPE } ,
# endif
2014-08-04 03:49:33 +00:00
{ META_MAPHEADER , ARCH_MAPHEADER } ,
2014-03-15 16:59:03 +00:00
{ NULL , ARCH_NULL }
} ;
2018-03-09 15:34:09 +00:00
static UINT8 GetUserdataArchType ( int index )
2014-03-15 16:59:03 +00:00
{
UINT8 i ;
2018-03-09 15:34:09 +00:00
lua_getmetatable ( gL , index ) ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; meta2arch [ i ] . meta ; i + + )
{
luaL_getmetatable ( gL , meta2arch [ i ] . meta ) ;
if ( lua_rawequal ( gL , - 1 , - 2 ) )
{
lua_pop ( gL , 2 ) ;
return meta2arch [ i ] . arch ;
}
lua_pop ( gL , 1 ) ;
}
lua_pop ( gL , 1 ) ;
return ARCH_NULL ;
}
static UINT8 ArchiveValue ( int TABLESINDEX , int myindex )
{
if ( myindex < 0 )
myindex = lua_gettop ( gL ) + 1 + myindex ;
switch ( lua_type ( gL , myindex ) )
{
case LUA_TNONE :
case LUA_TNIL :
WRITEUINT8 ( save_p , ARCH_NULL ) ;
break ;
// This might be a problem. D:
case LUA_TLIGHTUSERDATA :
case LUA_TTHREAD :
case LUA_TFUNCTION :
WRITEUINT8 ( save_p , ARCH_NULL ) ;
return 2 ;
case LUA_TBOOLEAN :
WRITEUINT8 ( save_p , ARCH_BOOLEAN ) ;
WRITEUINT8 ( save_p , lua_toboolean ( gL , myindex ) ) ;
break ;
case LUA_TNUMBER :
{
lua_Integer number = lua_tointeger ( gL , myindex ) ;
2015-05-21 03:54:04 +00:00
WRITEUINT8 ( save_p , ARCH_SIGNED ) ;
WRITEFIXED ( save_p , number ) ;
2014-03-15 16:59:03 +00:00
break ;
}
case LUA_TSTRING :
2018-08-05 21:02:20 +00:00
{
UINT16 len = ( UINT16 ) lua_objlen ( gL , myindex ) ; // get length of string, including embedded zeros
const char * s = lua_tostring ( gL , myindex ) ;
UINT16 i = 0 ;
2014-03-15 16:59:03 +00:00
WRITEUINT8 ( save_p , ARCH_STRING ) ;
2018-08-05 21:02:20 +00:00
// if you're wondering why we're writing a string to save_p this way,
// it turns out that Lua can have embedded zeros ('\0') in the strings,
// so we can't use WRITESTRING as that cuts off when it finds a '\0'.
// Saving the size of the string also allows us to get the size of the string on the other end,
// fixing the awful crashes previously encountered for reading strings longer than 1024
// (yes I know that's kind of a stupid thing to care about, but it'd be evil to trim or ignore them?)
// -- Monster Iestyn 05/08/18
WRITEUINT16 ( save_p , len ) ; // save size of string
while ( i < len )
WRITECHAR ( save_p , s [ i + + ] ) ; // write chars individually, including the embedded zeros
2014-03-15 16:59:03 +00:00
break ;
2018-08-05 21:02:20 +00:00
}
2014-03-15 16:59:03 +00:00
case LUA_TTABLE :
{
boolean found = false ;
INT32 i ;
UINT16 t = ( UINT16 ) lua_objlen ( gL , TABLESINDEX ) ;
for ( i = 1 ; i < = t & & ! found ; i + + )
{
lua_rawgeti ( gL , TABLESINDEX , i ) ;
if ( lua_rawequal ( gL , myindex , - 1 ) )
{
t = i ;
found = true ;
}
lua_pop ( gL , 1 ) ;
}
if ( ! found )
t + + ;
WRITEUINT8 ( save_p , ARCH_TABLE ) ;
WRITEUINT16 ( save_p , t ) ;
if ( ! found )
{
lua_pushvalue ( gL , myindex ) ;
lua_rawseti ( gL , TABLESINDEX , t ) ;
return 1 ;
}
break ;
}
case LUA_TUSERDATA :
2018-03-09 15:34:09 +00:00
switch ( GetUserdataArchType ( myindex ) )
2014-03-15 16:59:03 +00:00
{
case ARCH_MOBJINFO :
{
mobjinfo_t * info = * ( ( mobjinfo_t * * ) lua_touserdata ( gL , myindex ) ) ;
WRITEUINT8 ( save_p , ARCH_MOBJINFO ) ;
2017-07-02 15:50:11 +00:00
WRITEUINT16 ( save_p , info - mobjinfo ) ;
2014-03-15 16:59:03 +00:00
break ;
}
case ARCH_STATE :
{
state_t * state = * ( ( state_t * * ) lua_touserdata ( gL , myindex ) ) ;
WRITEUINT8 ( save_p , ARCH_STATE ) ;
2017-07-02 15:50:11 +00:00
WRITEUINT16 ( save_p , state - states ) ;
2014-03-15 16:59:03 +00:00
break ;
}
case ARCH_MOBJ :
{
mobj_t * mobj = * ( ( mobj_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! mobj )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
WRITEUINT8 ( save_p , ARCH_MOBJ ) ;
WRITEUINT32 ( save_p , mobj - > mobjnum ) ;
}
break ;
}
case ARCH_PLAYER :
{
player_t * player = * ( ( player_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! player )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
WRITEUINT8 ( save_p , ARCH_PLAYER ) ;
WRITEUINT8 ( save_p , player - players ) ;
}
break ;
}
case ARCH_MAPTHING :
{
mapthing_t * mapthing = * ( ( mapthing_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! mapthing )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
WRITEUINT8 ( save_p , ARCH_MAPTHING ) ;
WRITEUINT16 ( save_p , mapthing - mapthings ) ;
}
break ;
}
case ARCH_VERTEX :
{
vertex_t * vertex = * ( ( vertex_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! vertex )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
WRITEUINT8 ( save_p , ARCH_VERTEX ) ;
WRITEUINT16 ( save_p , vertex - vertexes ) ;
}
break ;
}
case ARCH_LINE :
{
line_t * line = * ( ( line_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! line )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
WRITEUINT8 ( save_p , ARCH_LINE ) ;
WRITEUINT16 ( save_p , line - lines ) ;
}
break ;
}
case ARCH_SIDE :
{
side_t * side = * ( ( side_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! side )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
WRITEUINT8 ( save_p , ARCH_SIDE ) ;
WRITEUINT16 ( save_p , side - sides ) ;
}
break ;
}
case ARCH_SUBSECTOR :
{
subsector_t * subsector = * ( ( subsector_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! subsector )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
WRITEUINT8 ( save_p , ARCH_SUBSECTOR ) ;
WRITEUINT16 ( save_p , subsector - subsectors ) ;
}
break ;
}
case ARCH_SECTOR :
{
sector_t * sector = * ( ( sector_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! sector )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
WRITEUINT8 ( save_p , ARCH_SECTOR ) ;
WRITEUINT16 ( save_p , sector - sectors ) ;
}
break ;
}
2016-11-24 21:11:44 +00:00
# ifdef HAVE_LUA_SEGS
2016-05-25 16:15:44 +00:00
case ARCH_SEG :
{
seg_t * seg = * ( ( seg_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! seg )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
WRITEUINT8 ( save_p , ARCH_SEG ) ;
WRITEUINT16 ( save_p , seg - segs ) ;
}
break ;
}
2016-07-08 19:05:54 +00:00
case ARCH_NODE :
{
node_t * node = * ( ( node_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! node )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
WRITEUINT8 ( save_p , ARCH_NODE ) ;
WRITEUINT16 ( save_p , node - nodes ) ;
}
break ;
}
2016-11-24 21:11:44 +00:00
# endif
2016-07-10 16:58:54 +00:00
case ARCH_FFLOOR :
{
ffloor_t * rover = * ( ( ffloor_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! rover )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
2016-10-25 15:15:17 +00:00
ffloor_t * r2 ;
2016-07-10 16:58:54 +00:00
UINT16 i = 0 ;
// search for id
2016-10-25 15:15:17 +00:00
for ( r2 = rover - > target - > ffloors ; r2 ; r2 = r2 - > next )
2016-07-10 16:58:54 +00:00
{
if ( r2 = = rover )
break ;
i + + ;
}
if ( ! r2 )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else
{
WRITEUINT8 ( save_p , ARCH_FFLOOR ) ;
WRITEUINT16 ( save_p , rover - > target - sectors ) ;
WRITEUINT16 ( save_p , i ) ;
}
}
break ;
}
2018-10-21 14:00:07 +00:00
# ifdef ESLOPE
case ARCH_SLOPE :
{
pslope_t * slope = * ( ( pslope_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! slope )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
WRITEUINT8 ( save_p , ARCH_SLOPE ) ;
WRITEUINT16 ( save_p , slope - > id ) ;
}
break ;
}
# endif
2014-08-04 03:49:33 +00:00
case ARCH_MAPHEADER :
{
mapheader_t * header = * ( ( mapheader_t * * ) lua_touserdata ( gL , myindex ) ) ;
if ( ! header )
WRITEUINT8 ( save_p , ARCH_NULL ) ;
else {
WRITEUINT8 ( save_p , ARCH_MAPHEADER ) ;
WRITEUINT16 ( save_p , header - * mapheaderinfo ) ;
}
break ;
}
2014-03-15 16:59:03 +00:00
default :
WRITEUINT8 ( save_p , ARCH_NULL ) ;
return 2 ;
}
break ;
}
return 0 ;
}
static void ArchiveExtVars ( void * pointer , const char * ptype )
{
int TABLESINDEX ;
UINT16 i ;
if ( ! gL ) {
if ( fastcmp ( ptype , " player " ) ) // players must always be included, even if no vars
WRITEUINT16 ( save_p , 0 ) ;
return ;
}
TABLESINDEX = lua_gettop ( gL ) ;
lua_getfield ( gL , LUA_REGISTRYINDEX , LREG_EXTVARS ) ;
I_Assert ( lua_istable ( gL , - 1 ) ) ;
lua_pushlightuserdata ( gL , pointer ) ;
lua_rawget ( gL , - 2 ) ;
lua_remove ( gL , - 2 ) ; // pop LREG_EXTVARS
if ( ! lua_istable ( gL , - 1 ) )
{ // no extra values table
lua_pop ( gL , 1 ) ;
if ( fastcmp ( ptype , " player " ) ) // players must always be included, even if no vars
WRITEUINT16 ( save_p , 0 ) ;
return ;
}
lua_pushnil ( gL ) ;
for ( i = 0 ; lua_next ( gL , - 2 ) ; i + + )
lua_pop ( gL , 1 ) ;
2017-12-15 21:02:34 +00:00
// skip anything that has an empty table and isn't a player.
if ( i = = 0 )
2017-12-15 15:31:27 +00:00
{
2017-12-15 21:02:34 +00:00
if ( fastcmp ( ptype , " player " ) ) // always include players even if they have no extra variables
WRITEUINT16 ( save_p , 0 ) ;
2017-12-15 15:31:27 +00:00
lua_pop ( gL , 1 ) ;
2014-03-15 16:59:03 +00:00
return ;
2017-12-15 15:31:27 +00:00
}
2017-12-15 21:02:34 +00:00
2014-03-15 16:59:03 +00:00
if ( fastcmp ( ptype , " mobj " ) ) // mobjs must write their mobjnum as a header
WRITEUINT32 ( save_p , ( ( mobj_t * ) pointer ) - > mobjnum ) ;
WRITEUINT16 ( save_p , i ) ;
lua_pushnil ( gL ) ;
while ( lua_next ( gL , - 2 ) )
{
I_Assert ( lua_type ( gL , - 2 ) = = LUA_TSTRING ) ;
WRITESTRING ( save_p , lua_tostring ( gL , - 2 ) ) ;
if ( ArchiveValue ( TABLESINDEX , - 1 ) = = 2 )
CONS_Alert ( CONS_ERROR , " Type of value for %s entry '%s' (%s) could not be archived! \n " , ptype , lua_tostring ( gL , - 2 ) , luaL_typename ( gL , - 1 ) ) ;
lua_pop ( gL , 1 ) ;
}
lua_pop ( gL , 1 ) ;
}
static int NetArchive ( lua_State * L )
{
int TABLESINDEX = lua_upvalueindex ( 1 ) ;
int i , n = lua_gettop ( L ) ;
2015-09-03 17:13:55 +00:00
for ( i = 1 ; i < = n ; i + + )
2014-03-15 16:59:03 +00:00
ArchiveValue ( TABLESINDEX , i ) ;
return n ;
}
static void ArchiveTables ( void )
{
int TABLESINDEX ;
UINT16 i , n ;
UINT8 e ;
if ( ! gL )
return ;
TABLESINDEX = lua_gettop ( gL ) ;
n = ( UINT16 ) lua_objlen ( gL , TABLESINDEX ) ;
for ( i = 1 ; i < = n ; i + + )
{
lua_rawgeti ( gL , TABLESINDEX , i ) ;
lua_pushnil ( gL ) ;
while ( lua_next ( gL , - 2 ) )
{
2018-01-20 21:18:16 +00:00
// Write key
e = ArchiveValue ( TABLESINDEX , - 2 ) ; // key should be either a number or a string, ArchiveValue can handle this.
if ( e = = 2 ) // invalid key type (function, thread, lightuserdata, or anything we don't recognise)
{
lua_pushvalue ( gL , - 2 ) ;
CONS_Alert ( CONS_ERROR , " Index '%s' (%s) of table %d could not be archived! \n " , lua_tostring ( gL , - 1 ) , luaL_typename ( gL , - 1 ) , i ) ;
lua_pop ( gL , 1 ) ;
}
// Write value
2014-03-15 16:59:03 +00:00
e = ArchiveValue ( TABLESINDEX , - 1 ) ;
if ( e = = 1 )
n + + ; // the table contained a new table we'll have to archive. :(
2018-01-20 21:18:16 +00:00
else if ( e = = 2 ) // invalid value type
2014-03-15 16:59:03 +00:00
{
lua_pushvalue ( gL , - 2 ) ;
CONS_Alert ( CONS_ERROR , " Type of value for table %d entry '%s' (%s) could not be archived! \n " , i , lua_tostring ( gL , - 1 ) , luaL_typename ( gL , - 1 ) ) ;
lua_pop ( gL , 1 ) ;
}
2018-03-09 15:34:09 +00:00
2014-03-15 16:59:03 +00:00
lua_pop ( gL , 1 ) ;
}
lua_pop ( gL , 1 ) ;
WRITEUINT8 ( save_p , ARCH_TEND ) ;
}
}
static UINT8 UnArchiveValue ( int TABLESINDEX )
{
UINT8 type = READUINT8 ( save_p ) ;
switch ( type )
{
case ARCH_NULL :
lua_pushnil ( gL ) ;
break ;
case ARCH_BOOLEAN :
lua_pushboolean ( gL , READUINT8 ( save_p ) ) ;
break ;
case ARCH_SIGNED :
lua_pushinteger ( gL , READFIXED ( save_p ) ) ;
break ;
case ARCH_STRING :
{
2018-08-05 21:02:20 +00:00
UINT16 len = READUINT16 ( save_p ) ; // length of string, including embedded zeros
char * value ;
UINT16 i = 0 ;
// See my comments in the ArchiveValue function;
// it's much the same for reading strings as writing them!
// (i.e. we can't use READSTRING either)
// -- Monster Iestyn 05/08/18
value = malloc ( len ) ; // make temp buffer of size len
// now read the actual string
while ( i < len )
value [ i + + ] = READCHAR ( save_p ) ; // read chars individually, including the embedded zeros
lua_pushlstring ( gL , value , len ) ; // push the string (note: this function supports embedded zeros)
free ( value ) ; // free the buffer
2014-03-15 16:59:03 +00:00
break ;
}
case ARCH_TABLE :
{
UINT16 tid = READUINT16 ( save_p ) ;
lua_rawgeti ( gL , TABLESINDEX , tid ) ;
if ( lua_isnil ( gL , - 1 ) )
{
lua_pop ( gL , 1 ) ;
lua_newtable ( gL ) ;
lua_pushvalue ( gL , - 1 ) ;
lua_rawseti ( gL , TABLESINDEX , tid ) ;
return 2 ;
}
break ;
}
case ARCH_MOBJINFO :
LUA_PushUserdata ( gL , & mobjinfo [ READUINT16 ( save_p ) ] , META_MOBJINFO ) ;
break ;
case ARCH_STATE :
LUA_PushUserdata ( gL , & states [ READUINT16 ( save_p ) ] , META_STATE ) ;
break ;
case ARCH_MOBJ :
LUA_PushUserdata ( gL , P_FindNewPosition ( READUINT32 ( save_p ) ) , META_MOBJ ) ;
break ;
case ARCH_PLAYER :
LUA_PushUserdata ( gL , & players [ READUINT8 ( save_p ) ] , META_PLAYER ) ;
break ;
case ARCH_MAPTHING :
LUA_PushUserdata ( gL , & mapthings [ READUINT16 ( save_p ) ] , META_MAPTHING ) ;
break ;
case ARCH_VERTEX :
LUA_PushUserdata ( gL , & vertexes [ READUINT16 ( save_p ) ] , META_VERTEX ) ;
break ;
case ARCH_LINE :
LUA_PushUserdata ( gL , & lines [ READUINT16 ( save_p ) ] , META_LINE ) ;
break ;
case ARCH_SIDE :
LUA_PushUserdata ( gL , & sides [ READUINT16 ( save_p ) ] , META_SIDE ) ;
break ;
case ARCH_SUBSECTOR :
LUA_PushUserdata ( gL , & subsectors [ READUINT16 ( save_p ) ] , META_SUBSECTOR ) ;
break ;
case ARCH_SECTOR :
LUA_PushUserdata ( gL , & sectors [ READUINT16 ( save_p ) ] , META_SECTOR ) ;
break ;
2016-11-24 21:11:44 +00:00
# ifdef HAVE_LUA_SEGS
2016-05-25 16:15:44 +00:00
case ARCH_SEG :
LUA_PushUserdata ( gL , & segs [ READUINT16 ( save_p ) ] , META_SEG ) ;
break ;
2016-07-08 19:05:54 +00:00
case ARCH_NODE :
LUA_PushUserdata ( gL , & nodes [ READUINT16 ( save_p ) ] , META_NODE ) ;
break ;
2016-11-24 21:11:44 +00:00
# endif
2016-07-10 16:58:54 +00:00
case ARCH_FFLOOR :
{
sector_t * sector = & sectors [ READUINT16 ( save_p ) ] ;
UINT16 id = READUINT16 ( save_p ) ;
ffloor_t * rover = P_GetFFloorByID ( sector , id ) ;
if ( rover )
LUA_PushUserdata ( gL , rover , META_FFLOOR ) ;
break ;
}
2018-10-21 14:00:07 +00:00
# ifdef ESLOPE
case ARCH_SLOPE :
LUA_PushUserdata ( gL , P_SlopeById ( READUINT16 ( save_p ) ) , META_SLOPE ) ;
break ;
# endif
2014-08-04 03:49:33 +00:00
case ARCH_MAPHEADER :
2018-10-21 14:15:54 +00:00
LUA_PushUserdata ( gL , mapheaderinfo [ READUINT16 ( save_p ) ] , META_MAPHEADER ) ;
2014-08-04 03:49:33 +00:00
break ;
2014-03-15 16:59:03 +00:00
case ARCH_TEND :
return 1 ;
}
return 0 ;
}
static void UnArchiveExtVars ( void * pointer )
{
int TABLESINDEX ;
UINT16 field_count = READUINT16 ( save_p ) ;
UINT16 i ;
char field [ 1024 ] ;
if ( field_count = = 0 )
return ;
I_Assert ( gL ! = NULL ) ;
TABLESINDEX = lua_gettop ( gL ) ;
lua_createtable ( gL , 0 , field_count ) ; // pointer's ext vars subtable
for ( i = 0 ; i < field_count ; i + + )
{
READSTRING ( save_p , field ) ;
UnArchiveValue ( TABLESINDEX ) ;
lua_setfield ( gL , - 2 , field ) ;
}
lua_getfield ( gL , LUA_REGISTRYINDEX , LREG_EXTVARS ) ;
I_Assert ( lua_istable ( gL , - 1 ) ) ;
lua_pushlightuserdata ( gL , pointer ) ;
lua_pushvalue ( gL , - 3 ) ; // pointer's ext vars subtable
lua_rawset ( gL , - 3 ) ;
lua_pop ( gL , 2 ) ; // pop LREG_EXTVARS and pointer's subtable
}
static int NetUnArchive ( lua_State * L )
{
int TABLESINDEX = lua_upvalueindex ( 1 ) ;
int i , n = lua_gettop ( L ) ;
2015-09-03 17:13:55 +00:00
for ( i = 1 ; i < = n ; i + + )
2014-03-15 16:59:03 +00:00
UnArchiveValue ( TABLESINDEX ) ;
return n ;
}
static void UnArchiveTables ( void )
{
int TABLESINDEX ;
UINT16 i , n ;
if ( ! gL )
return ;
TABLESINDEX = lua_gettop ( gL ) ;
n = ( UINT16 ) lua_objlen ( gL , TABLESINDEX ) ;
for ( i = 1 ; i < = n ; i + + )
{
lua_rawgeti ( gL , TABLESINDEX , i ) ;
while ( true )
{
2018-01-20 21:18:16 +00:00
if ( UnArchiveValue ( TABLESINDEX ) = = 1 ) // read key
2014-03-15 16:59:03 +00:00
break ;
2018-01-20 21:18:16 +00:00
if ( UnArchiveValue ( TABLESINDEX ) = = 2 ) // read value
2014-03-15 16:59:03 +00:00
n + + ;
2018-01-20 21:18:16 +00:00
if ( lua_isnil ( gL , - 2 ) ) // if key is nil (if a function etc was accidentally saved)
{
CONS_Alert ( CONS_ERROR , " A nil key in table %d was found! (Invalid key type or corrupted save?) \n " , i ) ;
lua_pop ( gL , 2 ) ; // pop key and value instead of setting them in the table, to prevent Lua panic errors
}
else
lua_rawset ( gL , - 3 ) ;
2014-03-15 16:59:03 +00:00
}
lua_pop ( gL , 1 ) ;
}
}
2015-06-10 17:42:45 +00:00
void LUA_Step ( void )
{
if ( ! gL )
return ;
lua_settop ( gL , 0 ) ;
lua_gc ( gL , LUA_GCSTEP , 1 ) ;
}
2014-03-15 16:59:03 +00:00
void LUA_Archive ( void )
{
INT32 i ;
thinker_t * th ;
if ( gL )
lua_newtable ( gL ) ; // tables to be archived.
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
2019-10-15 09:49:14 +00:00
if ( ! playeringame [ i ] & & i > 0 ) // dedicated servers...
2014-03-15 16:59:03 +00:00
continue ;
// all players in game will be archived, even if they just add a 0.
ArchiveExtVars ( & players [ i ] , " player " ) ;
}
2019-07-12 23:42:03 +00:00
for ( th = thlist [ THINK_MOBJ ] . next ; th ! = & thlist [ THINK_MOBJ ] ; th = th - > next )
{
if ( th - > function . acp1 = = ( actionf_p1 ) P_RemoveThinkerDelayed )
continue ;
// archive function will determine when to skip mobjs,
// and write mobjnum in otherwise.
ArchiveExtVars ( th , " mobj " ) ;
}
2019-04-21 10:58:22 +00:00
2014-03-15 16:59:03 +00:00
WRITEUINT32 ( save_p , UINT32_MAX ) ; // end of mobjs marker, replaces mobjnum.
2016-03-03 22:07:05 +00:00
LUAh_NetArchiveHook ( NetArchive ) ; // call the NetArchive hook in archive mode
2014-03-15 16:59:03 +00:00
ArchiveTables ( ) ;
if ( gL )
lua_pop ( gL , 1 ) ; // pop tables
}
void LUA_UnArchive ( void )
{
UINT32 mobjnum ;
INT32 i ;
thinker_t * th ;
if ( gL )
lua_newtable ( gL ) ; // tables to be read
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
2019-10-15 09:49:14 +00:00
if ( ! playeringame [ i ] & & i > 0 ) // dedicated servers...
2014-03-15 16:59:03 +00:00
continue ;
UnArchiveExtVars ( & players [ i ] ) ;
}
do {
mobjnum = READUINT32 ( save_p ) ; // read a mobjnum
2019-07-12 23:42:03 +00:00
for ( th = thlist [ THINK_MOBJ ] . next ; th ! = & thlist [ THINK_MOBJ ] ; th = th - > next )
{
if ( th - > function . acp1 = = ( actionf_p1 ) P_RemoveThinkerDelayed )
continue ;
if ( ( ( mobj_t * ) th ) - > mobjnum ! = mobjnum ) // find matching mobj
continue ;
UnArchiveExtVars ( th ) ; // apply variables
}
2014-03-15 16:59:03 +00:00
} while ( mobjnum ! = UINT32_MAX ) ; // repeat until end of mobjs marker.
2016-03-03 22:07:05 +00:00
LUAh_NetArchiveHook ( NetUnArchive ) ; // call the NetArchive hook in unarchive mode
2014-03-15 16:59:03 +00:00
UnArchiveTables ( ) ;
if ( gL )
lua_pop ( gL , 1 ) ; // pop tables
}
// For mobj_t, player_t, etc. to take custom variables.
int Lua_optoption ( lua_State * L , int narg ,
const char * def , const char * const lst [ ] )
{
const char * name = ( def ) ? luaL_optstring ( L , narg , def ) : luaL_checkstring ( L , narg ) ;
int i ;
for ( i = 0 ; lst [ i ] ; i + + )
if ( fastcmp ( lst [ i ] , name ) )
return i ;
return - 1 ;
}
# endif // HAVE_BLUA