mirror of
https://github.com/fortressforever/fortressforever-2013.git
synced 2024-11-10 07:11:45 +00:00
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:
parent
df82d96409
commit
c4061fb5f7
4 changed files with 156 additions and 45 deletions
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue