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.
|
2020-02-19 22:08:45 +00:00
|
|
|
// Copyright (C) 2012-2020 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"
|
|
|
|
#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
|
|
|
#include "p_slopes.h" // for P_SlopeById
|
2020-09-09 15:09:08 +00:00
|
|
|
#include "p_polyobj.h" // polyobj_t, PolyObjects
|
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"
|
2020-10-31 20:36:15 +00:00
|
|
|
#include "g_state.h"
|
2014-08-04 03:49:33 +00:00
|
|
|
|
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
|
2020-09-08 17:08:08 +00:00
|
|
|
LUA_PolyObjLib, // polyobj_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
|
|
|
|
}
|
|
|
|
|
2020-05-30 18:24:33 +00:00
|
|
|
#define LEVELS1 12 // size of the first part of the stack
|
|
|
|
#define LEVELS2 10 // size of the second part of the stack
|
|
|
|
|
|
|
|
// Error handler used with pcall() when loading scripts or calling hooks
|
|
|
|
// Takes a string with the original error message,
|
|
|
|
// appends the traceback to it, and return the result
|
|
|
|
int LUA_GetErrorMessage(lua_State *L)
|
|
|
|
{
|
|
|
|
int level = 1;
|
|
|
|
int firstpart = 1; // still before eventual `...'
|
|
|
|
lua_Debug ar;
|
|
|
|
|
|
|
|
lua_pushliteral(L, "\nstack traceback:");
|
|
|
|
while (lua_getstack(L, level++, &ar))
|
|
|
|
{
|
|
|
|
if (level > LEVELS1 && firstpart)
|
|
|
|
{
|
|
|
|
// no more than `LEVELS2' more levels?
|
|
|
|
if (!lua_getstack(L, level + LEVELS2, &ar))
|
|
|
|
level--; // keep going
|
|
|
|
else
|
|
|
|
{
|
|
|
|
lua_pushliteral(L, "\n ..."); // too many levels
|
|
|
|
while (lua_getstack(L, level + LEVELS2, &ar)) // find last levels
|
|
|
|
level++;
|
|
|
|
}
|
|
|
|
firstpart = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
lua_pushliteral(L, "\n ");
|
|
|
|
lua_getinfo(L, "Snl", &ar);
|
|
|
|
lua_pushfstring(L, "%s:", ar.short_src);
|
|
|
|
if (ar.currentline > 0)
|
|
|
|
lua_pushfstring(L, "%d:", ar.currentline);
|
|
|
|
if (*ar.namewhat != '\0') // is there a name?
|
|
|
|
lua_pushfstring(L, " in function " LUA_QS, ar.name);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (*ar.what == 'm') // main?
|
|
|
|
lua_pushfstring(L, " in main chunk");
|
|
|
|
else if (*ar.what == 'C' || *ar.what == 't')
|
|
|
|
lua_pushliteral(L, " ?"); // C function or tail call
|
|
|
|
else
|
|
|
|
lua_pushfstring(L, " in function <%s:%d>",
|
|
|
|
ar.short_src, ar.linedefined);
|
|
|
|
}
|
|
|
|
lua_concat(L, lua_gettop(L));
|
|
|
|
}
|
|
|
|
lua_concat(L, lua_gettop(L));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
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;
|
2020-03-15 15:23:15 +00:00
|
|
|
} else if (fastcmp(word,"udmf")) {
|
|
|
|
lua_pushboolean(L, udmf);
|
|
|
|
return 1;
|
2019-12-23 21:49:23 +00:00
|
|
|
} 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;
|
2020-02-09 23:53:30 +00:00
|
|
|
} else if (fastcmp(word,"stoppedclock")) {
|
|
|
|
lua_pushboolean(L, stoppedclock);
|
|
|
|
return 1;
|
2019-12-23 21:49:23 +00:00
|
|
|
} 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")) {
|
Introducing Marathon Run. (I was going to call it Marathon Mode, but NiGHTS Mode being right next to it on the menu looked terrible.)
Basically a dedicated Record Attack-like experience for speedrunning the game as a continuous chunk rather than ILs. Has several quality of life features.
Benefits include:
* An unambiguous real-time bar across the bottom of the screen, always displaying the current time, ticking up until you reach the ending.
* Disable the console (pausing is still allowed, but the timer will still increment).
* Automatically skip intermissions as if you're holding down the spin button.
* Show centiseconds on HUD automatically, like record attack.
* "Live Event Backups" - a category of run fit for major events like GDQ, where recovery from crashes or chokes makes for better entertainment. Essentially a modified SP savefile, down to using the same basic functions, but has its own filename and tweaked internal layout.
* "spmarathon_start" MainCfg block parameter and "marathonnext" mapheader parameter, allowing for a customised flow (makes this fit for purpose for an eventual SUGOI port).
* Disabling inter-level custom cutscenes by default with a menu option to toggle this (won't show up if the mod doesn't *have* any custom cutscenes), although either way ending cutscenes (vanilla or custom) remain intact since is time is called before them.
* Won't show up if you have a mod that consists of only one level (determined by spmarathon_start's nextlevel; this won't trip if you manually set its marathonnext).
* Unconditional gratitude on the evaluation screen, instead of a negging "Try again..." if you didn't get all the emeralds (which you may not have been aiming for).
* Gorgeous new menu (no new assets required, unless you wanna give it a header later).
Changes which were required for the above but affect other areas of the game include:
* "useBlackRock" MainCFG block parameter, which can be used to disable the presence of the Black Rock or Egg Rock in both the Evaluation screen and the Marathon Run menu (for total conversions with different stories).
* Disabling Continues in NiGHTS mode, to match the most common singleplayer experience post 2.2.4's release (is reverted if useContinues is set to true).
* Hiding the exitmove "powerup" outside of multiplayer. (Okay, this isn't really related, I just saw this bug in action a lot while doing test runs and got annoyed enough to fix it here.)
* The ability to use V_DrawPromptBack (in hardcode only at the moment, but) to draw in terms of pixels rather than rows of text, by providing negative instead of positive inputs).
* A refactoring of redundant game saves smattered across the ending, credits, and evaluation - in addition to saving the game slightly earlier.
* Minor m_menu.c touchups and refactorings here and there.
Built using feedback from the official server's #speedruns channel, among other places.
2020-05-14 22:10:00 +00:00
|
|
|
lua_pushboolean(L, (gamecomplete != 0));
|
|
|
|
return 1;
|
|
|
|
} else if (fastcmp(word,"marathonmode")) {
|
|
|
|
lua_pushinteger(L, marathonmode);
|
2019-12-23 21:49:23 +00:00
|
|
|
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;
|
Introducing Marathon Run. (I was going to call it Marathon Mode, but NiGHTS Mode being right next to it on the menu looked terrible.)
Basically a dedicated Record Attack-like experience for speedrunning the game as a continuous chunk rather than ILs. Has several quality of life features.
Benefits include:
* An unambiguous real-time bar across the bottom of the screen, always displaying the current time, ticking up until you reach the ending.
* Disable the console (pausing is still allowed, but the timer will still increment).
* Automatically skip intermissions as if you're holding down the spin button.
* Show centiseconds on HUD automatically, like record attack.
* "Live Event Backups" - a category of run fit for major events like GDQ, where recovery from crashes or chokes makes for better entertainment. Essentially a modified SP savefile, down to using the same basic functions, but has its own filename and tweaked internal layout.
* "spmarathon_start" MainCfg block parameter and "marathonnext" mapheader parameter, allowing for a customised flow (makes this fit for purpose for an eventual SUGOI port).
* Disabling inter-level custom cutscenes by default with a menu option to toggle this (won't show up if the mod doesn't *have* any custom cutscenes), although either way ending cutscenes (vanilla or custom) remain intact since is time is called before them.
* Won't show up if you have a mod that consists of only one level (determined by spmarathon_start's nextlevel; this won't trip if you manually set its marathonnext).
* Unconditional gratitude on the evaluation screen, instead of a negging "Try again..." if you didn't get all the emeralds (which you may not have been aiming for).
* Gorgeous new menu (no new assets required, unless you wanna give it a header later).
Changes which were required for the above but affect other areas of the game include:
* "useBlackRock" MainCFG block parameter, which can be used to disable the presence of the Black Rock or Egg Rock in both the Evaluation screen and the Marathon Run menu (for total conversions with different stories).
* Disabling Continues in NiGHTS mode, to match the most common singleplayer experience post 2.2.4's release (is reverted if useContinues is set to true).
* Hiding the exitmove "powerup" outside of multiplayer. (Okay, this isn't really related, I just saw this bug in action a lot while doing test runs and got annoyed enough to fix it here.)
* The ability to use V_DrawPromptBack (in hardcode only at the moment, but) to draw in terms of pixels rather than rows of text, by providing negative instead of positive inputs).
* A refactoring of redundant game saves smattered across the ending, credits, and evaluation - in addition to saving the game slightly earlier.
* Minor m_menu.c touchups and refactorings here and there.
Built using feedback from the official server's #speedruns channel, among other places.
2020-05-14 22:10:00 +00:00
|
|
|
} else if (fastcmp(word,"spmarathon_start")) {
|
|
|
|
lua_pushinteger(L, spmarathon_start);
|
|
|
|
return 1;
|
2019-12-23 21:49:23 +00:00
|
|
|
} 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;
|
2019-12-23 22:08:57 +00:00
|
|
|
} else if (fastcmp(word,"sstimer")) {
|
|
|
|
lua_pushinteger(L, sstimer);
|
|
|
|
return 1;
|
2019-12-23 21:49:23 +00:00
|
|
|
} 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;
|
2020-06-27 13:30:23 +00:00
|
|
|
} else if (fastcmp(word,"isserver")) {
|
|
|
|
lua_pushboolean(L, server);
|
|
|
|
return 1;
|
|
|
|
} else if (fastcmp(word,"isdedicatedserver")) {
|
|
|
|
lua_pushboolean(L, dedicated);
|
|
|
|
return 1;
|
2019-12-23 21:49:23 +00:00
|
|
|
// 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,"emeralds")) {
|
|
|
|
lua_pushinteger(L, emeralds);
|
|
|
|
return 1;
|
|
|
|
} else if (fastcmp(word,"gravity")) {
|
|
|
|
lua_pushinteger(L, gravity);
|
|
|
|
return 1;
|
2020-07-11 05:39:46 +00:00
|
|
|
} else if (fastcmp(word,"VERSION")) {
|
|
|
|
lua_pushinteger(L, VERSION);
|
|
|
|
return 1;
|
|
|
|
} else if (fastcmp(word,"SUBVERSION")) {
|
|
|
|
lua_pushinteger(L, SUBVERSION);
|
|
|
|
return 1;
|
2019-12-23 21:49:23 +00:00
|
|
|
} else if (fastcmp(word,"VERSIONSTRING")) {
|
|
|
|
lua_pushstring(L, VERSIONSTRING);
|
|
|
|
return 1;
|
|
|
|
} else if (fastcmp(word, "token")) {
|
|
|
|
lua_pushinteger(L, token);
|
|
|
|
return 1;
|
2020-10-31 20:36:15 +00:00
|
|
|
} else if (fastcmp(word, "gamestate")) {
|
|
|
|
lua_pushinteger(L, gamestate);
|
|
|
|
return 1;
|
2019-12-23 21:49:23 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See the above.
|
|
|
|
int LUA_CheckGlobals(lua_State *L, const char *word)
|
|
|
|
{
|
2019-12-28 22:50:14 +00:00
|
|
|
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);
|
|
|
|
|
2020-10-03 14:31:04 +00:00
|
|
|
// make LREG_METATABLES table for all registered metatables
|
|
|
|
lua_newtable(L);
|
|
|
|
lua_setfield(L, LUA_REGISTRYINDEX, LREG_METATABLES);
|
|
|
|
|
2014-03-15 16:59:03 +00:00
|
|
|
// 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)
|
2020-05-29 15:35:07 +00:00
|
|
|
INT32 lua_lumploading = 0;
|
2017-04-25 20:45:53 +00:00
|
|
|
|
2014-03-15 16:59:03 +00:00
|
|
|
// Load a script from a MYFILE
|
2020-05-29 15:35:07 +00:00
|
|
|
static inline void LUA_LoadFile(MYFILE *f, char *name, boolean noresults)
|
2014-03-15 16:59:03 +00:00
|
|
|
{
|
2020-06-01 13:43:14 +00:00
|
|
|
int errorhandlerindex;
|
|
|
|
|
2014-03-15 16:59:03 +00:00
|
|
|
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
|
|
|
|
2020-05-29 15:35:07 +00:00
|
|
|
lua_lumploading++; // turn on loading flag
|
2018-02-09 22:43:08 +00:00
|
|
|
|
2020-05-30 18:24:33 +00:00
|
|
|
lua_pushcfunction(gL, LUA_GetErrorMessage);
|
2020-06-01 13:43:14 +00:00
|
|
|
errorhandlerindex = lua_gettop(gL);
|
2020-06-01 13:28:56 +00:00
|
|
|
if (luaL_loadbuffer(gL, f->data, f->size, va("@%s",name)) || lua_pcall(gL, 0, noresults ? 0 : LUA_MULTRET, lua_gettop(gL) - 1)) {
|
2014-03-15 16:59:03 +00:00
|
|
|
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1));
|
|
|
|
lua_pop(gL,1);
|
|
|
|
}
|
|
|
|
lua_gc(gL, LUA_GCCOLLECT, 0);
|
2020-06-01 13:43:14 +00:00
|
|
|
lua_remove(gL, errorhandlerindex);
|
2018-02-09 22:43:08 +00:00
|
|
|
|
2020-05-29 15:35:07 +00:00
|
|
|
lua_lumploading--; // turn off again
|
2014-03-15 16:59:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Load a script from a lump
|
2020-05-29 15:35:07 +00:00
|
|
|
void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults)
|
2014-03-15 16:59:03 +00:00
|
|
|
{
|
|
|
|
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];
|
2020-04-20 21:40:41 +00:00
|
|
|
len += 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name
|
2018-10-10 13:51:34 +00:00
|
|
|
name = malloc(len+1);
|
2020-04-20 21:40:41 +00:00
|
|
|
sprintf(name, "%s|%s", wadfiles[wad]->filename, lump_p->fullname);
|
2018-10-10 13:51:34 +00:00
|
|
|
name[len] = '\0';
|
2014-03-15 16:59:03 +00:00
|
|
|
}
|
|
|
|
|
2020-05-29 15:35:07 +00:00
|
|
|
LUA_LoadFile(&f, name, noresults); // 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;
|
|
|
|
}
|
|
|
|
|
2020-02-02 23:19:19 +00:00
|
|
|
/*
|
|
|
|
LUA_PushUserdata but no userdata is created.
|
|
|
|
You can't invalidate it therefore.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void LUA_PushLightUserdata (lua_State *L, void *data, const char *meta)
|
|
|
|
{
|
|
|
|
if (data)
|
|
|
|
{
|
|
|
|
lua_pushlightuserdata(L, data);
|
|
|
|
luaL_getmetatable(L, meta);
|
|
|
|
/*
|
|
|
|
The metatable is the last value on the stack, so this
|
|
|
|
applies it to the second value, which is the userdata.
|
|
|
|
*/
|
|
|
|
lua_setmetatable(L, -2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
}
|
|
|
|
|
2014-03-15 16:59:03 +00:00
|
|
|
// 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(§ors[i]);
|
2019-09-25 19:27:41 +00:00
|
|
|
LUA_InvalidateUserdata(§ors[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]);
|
2020-09-09 15:09:08 +00:00
|
|
|
for (i = 0; i < (size_t)numPolyObjects; i++)
|
2020-09-09 20:15:02 +00:00
|
|
|
{
|
2020-09-09 15:09:08 +00:00
|
|
|
LUA_InvalidateUserdata(&PolyObjects[i]);
|
2020-09-09 20:15:02 +00:00
|
|
|
LUA_InvalidateUserdata(&PolyObjects[i].vertices);
|
|
|
|
LUA_InvalidateUserdata(&PolyObjects[i].lines);
|
|
|
|
}
|
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,
|
2020-04-28 21:11:28 +00:00
|
|
|
ARCH_TRUE,
|
|
|
|
ARCH_FALSE,
|
2020-04-28 21:12:02 +00:00
|
|
|
ARCH_INT8,
|
|
|
|
ARCH_INT16,
|
|
|
|
ARCH_INT32,
|
2020-04-28 20:19:44 +00:00
|
|
|
ARCH_SMALLSTRING,
|
|
|
|
ARCH_LARGESTRING,
|
2014-03-15 16:59:03 +00:00
|
|
|
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,
|
2020-09-09 15:09:08 +00:00
|
|
|
ARCH_POLYOBJ,
|
2018-10-21 14:00:07 +00:00
|
|
|
ARCH_SLOPE,
|
2014-08-04 03:49:33 +00:00
|
|
|
ARCH_MAPHEADER,
|
2020-02-15 08:18:41 +00:00
|
|
|
ARCH_SKINCOLOR,
|
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},
|
2020-09-09 15:09:08 +00:00
|
|
|
{META_POLYOBJ, ARCH_POLYOBJ},
|
2018-10-21 14:00:07 +00:00
|
|
|
{META_SLOPE, ARCH_SLOPE},
|
2014-08-04 03:49:33 +00:00
|
|
|
{META_MAPHEADER, ARCH_MAPHEADER},
|
2020-02-15 08:18:41 +00:00
|
|
|
{META_SKINCOLOR, ARCH_SKINCOLOR},
|
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:
|
2020-04-28 21:11:28 +00:00
|
|
|
WRITEUINT8(save_p, lua_toboolean(gL, myindex) ? ARCH_TRUE : ARCH_FALSE);
|
2014-03-15 16:59:03 +00:00
|
|
|
break;
|
|
|
|
case LUA_TNUMBER:
|
|
|
|
{
|
|
|
|
lua_Integer number = lua_tointeger(gL, myindex);
|
2020-04-28 21:12:02 +00:00
|
|
|
if (number >= INT8_MIN && number <= INT8_MAX)
|
|
|
|
{
|
|
|
|
WRITEUINT8(save_p, ARCH_INT8);
|
|
|
|
WRITESINT8(save_p, number);
|
|
|
|
}
|
|
|
|
else if (number >= INT16_MIN && number <= INT16_MAX)
|
|
|
|
{
|
|
|
|
WRITEUINT8(save_p, ARCH_INT16);
|
|
|
|
WRITEINT16(save_p, number);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
WRITEUINT8(save_p, ARCH_INT32);
|
|
|
|
WRITEFIXED(save_p, number);
|
|
|
|
}
|
2014-03-15 16:59:03 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LUA_TSTRING:
|
2018-08-05 21:02:20 +00:00
|
|
|
{
|
2020-04-28 20:19:44 +00:00
|
|
|
UINT32 len = (UINT32)lua_objlen(gL, myindex); // get length of string, including embedded zeros
|
2018-08-05 21:02:20 +00:00
|
|
|
const char *s = lua_tostring(gL, myindex);
|
2020-04-28 20:19:44 +00:00
|
|
|
UINT32 i = 0;
|
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
|
2020-04-28 20:19:44 +00:00
|
|
|
if (len < 255)
|
|
|
|
{
|
|
|
|
WRITEUINT8(save_p, ARCH_SMALLSTRING);
|
|
|
|
WRITEUINT8(save_p, len); // save size of string
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
WRITEUINT8(save_p, ARCH_LARGESTRING);
|
|
|
|
WRITEUINT32(save_p, len); // save size of string
|
|
|
|
}
|
2018-08-05 21:02:20 +00:00
|
|
|
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)
|
2020-11-08 16:33:49 +00:00
|
|
|
{
|
2014-03-15 16:59:03 +00:00
|
|
|
t++;
|
|
|
|
|
2020-11-08 16:33:49 +00:00
|
|
|
if (t == 0)
|
|
|
|
{
|
|
|
|
CONS_Alert(CONS_ERROR, "Too many tables to archive!\n");
|
|
|
|
WRITEUINT8(save_p, ARCH_NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-15 16:59:03 +00:00
|
|
|
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 {
|
2020-04-24 21:43:23 +00:00
|
|
|
UINT16 i = P_GetFFloorID(rover);
|
|
|
|
if (i == UINT16_MAX) // invalid ID
|
2016-07-10 16:58:54 +00:00
|
|
|
WRITEUINT8(save_p, ARCH_NULL);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
WRITEUINT8(save_p, ARCH_FFLOOR);
|
|
|
|
WRITEUINT16(save_p, rover->target - sectors);
|
|
|
|
WRITEUINT16(save_p, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2020-09-09 15:09:08 +00:00
|
|
|
case ARCH_POLYOBJ:
|
|
|
|
{
|
|
|
|
polyobj_t *polyobj = *((polyobj_t **)lua_touserdata(gL, myindex));
|
|
|
|
if (!polyobj)
|
|
|
|
WRITEUINT8(save_p, ARCH_NULL);
|
|
|
|
else {
|
|
|
|
WRITEUINT8(save_p, ARCH_POLYOBJ);
|
|
|
|
WRITEUINT16(save_p, polyobj-PolyObjects);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-10-21 14:00:07 +00:00
|
|
|
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;
|
|
|
|
}
|
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;
|
|
|
|
}
|
2020-02-15 08:18:41 +00:00
|
|
|
|
|
|
|
case ARCH_SKINCOLOR:
|
|
|
|
{
|
|
|
|
skincolor_t *info = *((skincolor_t **)lua_touserdata(gL, myindex));
|
|
|
|
WRITEUINT8(save_p, ARCH_SKINCOLOR);
|
|
|
|
WRITEUINT16(save_p, info - skincolors);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
WRITEUINT8(save_p, ARCH_TEND);
|
2020-10-03 14:31:04 +00:00
|
|
|
|
|
|
|
// Write metatable ID
|
|
|
|
if (lua_getmetatable(gL, -1))
|
|
|
|
{
|
|
|
|
// registry.metatables[metatable]
|
|
|
|
lua_getfield(gL, LUA_REGISTRYINDEX, LREG_METATABLES);
|
|
|
|
lua_pushvalue(gL, -2);
|
|
|
|
lua_gettable(gL, -2);
|
|
|
|
WRITEUINT16(save_p, lua_isnil(gL, -1) ? 0 : lua_tointeger(gL, -1));
|
|
|
|
lua_pop(gL, 3);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
WRITEUINT16(save_p, 0);
|
|
|
|
|
|
|
|
lua_pop(gL, 1);
|
2014-03-15 16:59:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT8 UnArchiveValue(int TABLESINDEX)
|
|
|
|
{
|
|
|
|
UINT8 type = READUINT8(save_p);
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case ARCH_NULL:
|
|
|
|
lua_pushnil(gL);
|
|
|
|
break;
|
2020-04-28 21:11:28 +00:00
|
|
|
case ARCH_TRUE:
|
|
|
|
lua_pushboolean(gL, true);
|
|
|
|
break;
|
|
|
|
case ARCH_FALSE:
|
|
|
|
lua_pushboolean(gL, false);
|
2014-03-15 16:59:03 +00:00
|
|
|
break;
|
2020-04-28 21:12:02 +00:00
|
|
|
case ARCH_INT8:
|
|
|
|
lua_pushinteger(gL, READSINT8(save_p));
|
|
|
|
break;
|
|
|
|
case ARCH_INT16:
|
|
|
|
lua_pushinteger(gL, READINT16(save_p));
|
2014-03-15 16:59:03 +00:00
|
|
|
break;
|
2020-04-28 21:12:02 +00:00
|
|
|
case ARCH_INT32:
|
2014-03-15 16:59:03 +00:00
|
|
|
lua_pushinteger(gL, READFIXED(save_p));
|
|
|
|
break;
|
2020-04-28 20:19:44 +00:00
|
|
|
case ARCH_SMALLSTRING:
|
|
|
|
case ARCH_LARGESTRING:
|
2014-03-15 16:59:03 +00:00
|
|
|
{
|
2020-04-28 20:19:44 +00:00
|
|
|
UINT32 len;
|
2018-08-05 21:02:20 +00:00
|
|
|
char *value;
|
2020-04-28 20:19:44 +00:00
|
|
|
UINT32 i = 0;
|
|
|
|
|
2018-08-05 21:02:20 +00:00
|
|
|
// 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
|
2020-04-28 20:19:44 +00:00
|
|
|
if (type == ARCH_SMALLSTRING)
|
|
|
|
len = READUINT8(save_p); // length of string, including embedded zeros
|
|
|
|
else
|
|
|
|
len = READUINT32(save_p); // length of string, including embedded zeros
|
2018-08-05 21:02:20 +00:00
|
|
|
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, §ors[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 = §ors[READUINT16(save_p)];
|
|
|
|
UINT16 id = READUINT16(save_p);
|
|
|
|
ffloor_t *rover = P_GetFFloorByID(sector, id);
|
|
|
|
if (rover)
|
|
|
|
LUA_PushUserdata(gL, rover, META_FFLOOR);
|
|
|
|
break;
|
|
|
|
}
|
2020-09-09 15:09:08 +00:00
|
|
|
case ARCH_POLYOBJ:
|
|
|
|
LUA_PushUserdata(gL, &PolyObjects[READUINT16(save_p)], META_POLYOBJ);
|
|
|
|
break;
|
2018-10-21 14:00:07 +00:00
|
|
|
case ARCH_SLOPE:
|
|
|
|
LUA_PushUserdata(gL, P_SlopeById(READUINT16(save_p)), META_SLOPE);
|
|
|
|
break;
|
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;
|
2020-02-15 08:18:41 +00:00
|
|
|
case ARCH_SKINCOLOR:
|
|
|
|
LUA_PushUserdata(gL, &skincolors[READUINT16(save_p)], META_SKINCOLOR);
|
|
|
|
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;
|
2020-10-03 14:31:04 +00:00
|
|
|
UINT16 metatableid;
|
2014-03-15 16:59:03 +00:00
|
|
|
|
|
|
|
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
|
|
|
}
|
2020-10-03 14:31:04 +00:00
|
|
|
|
|
|
|
metatableid = READUINT16(save_p);
|
|
|
|
if (metatableid)
|
|
|
|
{
|
|
|
|
// setmetatable(table, registry.metatables[metatableid])
|
|
|
|
lua_getfield(gL, LUA_REGISTRYINDEX, LREG_METATABLES);
|
|
|
|
lua_rawgeti(gL, -1, metatableid);
|
|
|
|
if (lua_isnil(gL, -1))
|
|
|
|
I_Error("Unknown metatable ID %d\n", metatableid);
|
|
|
|
lua_setmetatable(gL, -3);
|
|
|
|
lua_pop(gL, 1);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|