A bunch of ScriptManager changes, still a lot more I want to do (and a bunch of cleanup), but committing what I have for now

* Made LoadFile check for execution errors as well as syntax errors (in 2006 FF execution errors were ignored)
 * Added client console command lua_dostring_ui that runs the given command arguments as Lua code in UIScriptManager's Lua environment
 * Loaded all default Lua libraries, but removed unsafe functions or made them safer (still some that need to be made safer)
 * Made Lua's print output to the console
 * Made Lua's require search the mod dir
 * UIScriptManager loads file ui/init.lua on client dll Init
This commit is contained in:
squeek 2013-11-03 08:21:12 +00:00
parent df82d96409
commit c4061fb5f7
4 changed files with 156 additions and 45 deletions

View file

@ -13,6 +13,8 @@ int CFF_CL_DLL_Interface::Init( CreateInterfaceFn appSystemFactory, CreateInterf
// start the Lua VM
g_UIScriptManager.Init();
g_GameScriptManager.Init();
g_UIScriptManager.LoadFile( "ui/init.lua" );
// Test steam API
uint32 appId = steamapicontext->SteamUtils()->GetAppID();

View file

@ -15,11 +15,6 @@ class CFF_SV_DLL_Interface : public CServerGameDLL
virtual void DLLShutdown( void );
// --> Extended functions
// --> Not yet implemented in CServerGameDLL but required
//virtual void GetPlayerLimits( int& minplayers, int& maxplayers, int &defaultMaxPlayers ) const;
//virtual void LevelInit_ParseAllEntities( const char *pMapEntities );
// <--
/*
public:
virtual bool DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory,

View file

@ -12,6 +12,7 @@
// engine
#include "filesystem.h"
#include "tier1/strtools.h"
// dexter note 10/29/2013 these are definitely still needed for lua/luabind
#undef MINMAX_H
@ -37,6 +38,28 @@
ConVar sv_mapluasuffix( "sv_mapluasuffix", "0", FCVAR_NOTIFY, "Have a custom lua file (game mode) loaded when the map loads. If this suffix string is set, maps\\mapname__suffix__.lua (if it exists) is used instead of maps\\mapname.lua. To reset this cvar, make it 0.");
ConVar sv_luaglobalscript( "sv_globalluascript", "0", FCVAR_NOTIFY, "Load a custom lua file globally after map scripts. Will overwrite map script. Will be loaded from maps\\globalscripts. To disable, set to 0.");
// redirect Lua's print function to the console
static int print(lua_State *L)
{
int n=lua_gettop(L);
int i;
for (i=1; i<=n; i++)
{
if (i==0) Msg("[Lua] ");
if (i>1) Msg("\t");
if (lua_isstring(L,i))
Msg("%s",lua_tostring(L,i));
else if (lua_isnil(L,i))
Msg("%s","nil");
else if (lua_isboolean(L,i))
Msg("%s",lua_toboolean(L,i) ? "true" : "false");
else
Msg("%s:%p",luaL_typename(L,i),lua_topointer(L,i));
}
Msg("\n");
return 0;
}
/////////////////////////////////////////////////////////////////////////////
using namespace luabind;
@ -98,12 +121,38 @@ void CFF_SH_ScriptManager::Init()
Msg("[SCRIPT:%s] Unable to initialize Lua VM.\n", LUA_CURRENT_CONTEXT);
return;
}
// load all libraries
luaL_openlibs(L);
// make the standard libraries safe
MakeSafe();
// overwrite Lua's print with our own
lua_register(L,"print",print);
// set package.path to the mod search dirs, so that we can use require
char szModSearchPaths[4096] = {0};
// FF TODO: This is set to ignore pack files; might need to add support for them (for custom mods packaged as .vpks)
filesystem->GetSearchPath( "MOD", false, szModSearchPaths, sizeof(szModSearchPaths) );
char szLuaSearchPaths[4096] = {0};
for ( char *szSearchPath = strtok( szModSearchPaths, ";" ); szSearchPath; szSearchPath = strtok( NULL, ";" ) )
{
char fullpath[MAX_PATH];
Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", szSearchPath, "/?.lua;" );
Q_FixSlashes( fullpath );
V_FixDoubleSlashes( fullpath );
// load base libraries
luaopen_base(L);
luaopen_table(L);
luaopen_string(L);
luaopen_math(L);
Q_strncat( szLuaSearchPaths, fullpath, sizeof(szLuaSearchPaths) );
}
// set package.path; this is equivelent to the Lua code: _G.package["path"] = szLuaSearchPaths
lua_pushstring(L, "package");
lua_gettable(L, LUA_GLOBALSINDEX); // get _G.package
lua_pushstring(L, "path");
lua_pushstring(L, szLuaSearchPaths);
lua_settable(L, -3); // -3 is the package table
lua_pop(L, 1); // pop _G.package
// initialize luabind
luabind::open(L);
@ -114,6 +163,48 @@ void CFF_SH_ScriptManager::Init()
Msg("[SCRIPT:%s] Lua VM initialization successful.\n", LUA_CURRENT_CONTEXT);
}
void LUAUTIL_RemoveKeysFromTable( lua_State *L, const char *pszTableName, const char** ppszKeys )
{
lua_getglobal(L, pszTableName);
if (lua_type(L, -1) == LUA_TTABLE)
{
for( int i=0; ppszKeys[i] != NULL; ++i )
{
lua_pushstring(L, ppszKeys[i]);
lua_pushnil(L);
lua_settable(L, -3);
}
}
lua_pop(L, 1);
}
/** Get rid of or alter any unsafe Lua functions
*/
void CFF_SH_ScriptManager::MakeSafe()
{
// os.*
const char* ppszUnsafeOSFunctions[] = { "execute", "exit", "getenv", "remove", "rename", "setlocale", NULL };
LUAUTIL_RemoveKeysFromTable( L, LUA_OSLIBNAME, ppszUnsafeOSFunctions );
// package.*
const char* ppszUnsafePackageFunctions[] = { "loadlib", NULL };
LUAUTIL_RemoveKeysFromTable( L, LUA_LOADLIBNAME, ppszUnsafePackageFunctions );
// require can load .dll/.so, need to disable the loaders that search for them
// the third index checks package.cpath and loads .so/.dll files
// the fourth index is an all-in-one loader that can load .so/.dll files
lua_getglobal(L, LUA_LOADLIBNAME);
lua_pushstring(L, "loaders");
lua_gettable(L, -2);
lua_pushnil(L);
lua_rawseti(L, -2, 4); // _G.package.loaders[4] = nil
lua_pushnil(L);
lua_rawseti(L, -2, 3); // _G.package.loaders[3] = nil
lua_pop(L, 2); // pop _G.package.loaders and _G.package
// FF TODO: restrict io library, maybe disable it completely
}
void CFF_SH_ScriptManager::LevelInit(const char* szMapName)
{
const char* default_luafile = "maps/default.lua";
@ -129,7 +220,7 @@ void CFF_SH_ScriptManager::LevelInit(const char* szMapName)
// load lua files
BeginScriptLoad();
LoadFile(L, "maps/includes/base.lua");
LoadFile("maps/includes/base.lua");
char filename[256] = {0};
char globalscript_filename[256] = {0};
@ -224,18 +315,18 @@ void CFF_SH_ScriptManager::LevelInit(const char* szMapName)
//}
//////////////////////////////////////////////////////////////////////////
if(filesystem->FileExists(filename))
m_ScriptExists = LoadFile(L, filename);
m_ScriptExists = LoadFile(filename);
else
{
Msg("[SCRIPT] File %s not found! Loaded fallback lua %s\n", filename, default_luafile);
m_ScriptExists = LoadFile(L, default_luafile);
m_ScriptExists = LoadFile(default_luafile);
}
// force loading global script in another call :/
if( sv_luaglobalscript.GetString()[0] != '0' && globalscript_filename[0] )
{
//BeginScriptLoad();
LoadFile(L, globalscript_filename);
LoadFile(globalscript_filename);
//EndScriptLoad();
}
@ -286,24 +377,17 @@ void CFF_SH_ScriptManager::EndScriptLoad()
/////////////////////////////////////////////////////////////////////////////
bool CFF_SH_ScriptManager::LoadFile( lua_State *L, const char *filename)
bool CFF_SH_ScriptManager::LoadFile(const char *filename)
{
//FF_TODO: VPROF_BUDGET( "CFF_SH_ScriptManager::LoadFile", VPROF_BUDGETGROUP_FF_LUA );
// don't allow scripters to sneak in scripts after the initial load
if(!m_isLoading)
{
Warning("[SCRIPT] Loading of scripts after initial map load is not allowed.\n");
return false;
}
// open the file
Msg("[SCRIPT] Loading Lua File: %s\n", filename);
Msg("[SCRIPT:%s] Loading Lua File: %s\n", LUA_CURRENT_CONTEXT, filename);
FileHandle_t hFile = filesystem->Open(filename, "rb", "MOD");
if (!hFile)
{
Warning("[SCRIPT] %s either does not exist or could not be opened.\n", filename);
Warning("[SCRIPT:%s] %s either does not exist or could not be opened.\n", LUA_CURRENT_CONTEXT, filename);
return false;
}
@ -324,32 +408,61 @@ bool CFF_SH_ScriptManager::LoadFile( lua_State *L, const char *filename)
int errorCode = luaL_loadbuffer(L, buffer, fileSize, filename);
// check if load was successful
if(errorCode)
if (errorCode != 0)
{
if(errorCode == LUA_ERRSYNTAX )
{
// syntax error, lookup description for the error
const char *error = lua_tostring(L, -1);
if (error)
{
Warning("Error loading %s: %s\n", filename, error);
lua_pop( L, 1 );
}
else
Warning("Unknown Syntax Error loading %s\n", filename);
}
else
{
Msg("Unknown Error loading %s\n", filename);
}
const char *error = lua_tostring(L, -1);
Warning( "[SCRIPT:%s] Error loading %s: %s\n", LUA_CURRENT_CONTEXT, filename, error );
lua_pop( L, 1 );
return false;
}
// execute script. script at top scrop gets exectued
lua_pcall(L, 0, 0, 0);
Msg( "[SCRIPT] Successfully loaded %s\n", filename );
errorCode = lua_pcall(L, 0, 0, 0);
// check if execution was successful
if (errorCode != 0)
{
const char *error = lua_tostring(L, -1);
Warning( "[SCRIPT:%s] Error loading %s: %s\n", LUA_CURRENT_CONTEXT, filename, error );
lua_pop( L, 1 );
return false;
}
Msg( "[SCRIPT:%s] Successfully loaded %s\n", LUA_CURRENT_CONTEXT, filename );
// cleanup
MemFreeScratch();
return true;
}
}
int luasrc_dostring (lua_State *L, const char *string) {
int iError = luaL_dostring(L, string);
if (iError != 0) {
Warning( "%s\n", lua_tostring(L, -1) );
lua_pop(L, 1);
}
return iError;
}
#ifdef CLIENT_DLL
CON_COMMAND( lua_dostring_ui, "Run a client-side Lua string in the UI environment" )
{
if ( args.ArgC() == 1 )
{
Msg( "Usage: lua_dostring <string>\n" );
return;
}
lua_State *L = g_UIScriptManager.GetLuaState();
int status = luasrc_dostring( L, args.ArgS() );
if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */
lua_getglobal(L, "print");
lua_insert(L, 1);
if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
Warning("%s", lua_pushfstring(L,
"error calling " LUA_QL("print") " (%s)",
lua_tostring(L, -1)));
}
lua_settop(L, 0); /* clear stack */
}
#endif

View file

@ -36,7 +36,8 @@ public:
public:
// inserts the lua file into the script environment
bool LoadFile(lua_State*, const char* filePath);
bool LoadFile(const char* filePath);
void MakeSafe();
public:
// initializes the script VM