From 5e5f2a4914b20223ee6cd9c923584678af8c891b Mon Sep 17 00:00:00 2001 From: squeek Date: Mon, 4 Nov 2013 05:18:03 +0000 Subject: [PATCH] More ScriptManager stuff * Split the UIScriptManager and GameScriptManager into their own classes that inherit from ScriptManager so that they can have different implementations of Init, LevelInit, and stuff like that * Not many functionality changes, mostly cleanup --- mp/src/game/client/client_ff.vpc | 5 + mp/src/game/client/ff/ff_cl_dll_interface.cpp | 5 +- mp/src/game/client/ff/ff_cl_scriptman_ui.cpp | 95 +++++ mp/src/game/client/ff/ff_cl_scriptman_ui.h | 30 ++ mp/src/game/server/ff/ff_sv_dll_interface.cpp | 2 +- mp/src/game/shared/ff/ff_sh_luautil.cpp | 19 + mp/src/game/shared/ff/ff_sh_luautil.h | 9 + mp/src/game/shared/ff/ff_sh_scriptman.cpp | 352 ++++-------------- mp/src/game/shared/ff/ff_sh_scriptman.h | 63 ++-- .../game/shared/ff/ff_sh_scriptman_game.cpp | 200 ++++++++++ mp/src/game/shared/ff/ff_sh_scriptman_game.h | 46 +++ mp/src/game/shared/ff/ff_shared.vpc | 4 + 12 files changed, 515 insertions(+), 315 deletions(-) create mode 100644 mp/src/game/client/ff/ff_cl_scriptman_ui.cpp create mode 100644 mp/src/game/client/ff/ff_cl_scriptman_ui.h create mode 100644 mp/src/game/shared/ff/ff_sh_luautil.cpp create mode 100644 mp/src/game/shared/ff/ff_sh_luautil.h create mode 100644 mp/src/game/shared/ff/ff_sh_scriptman_game.cpp create mode 100644 mp/src/game/shared/ff/ff_sh_scriptman_game.h diff --git a/mp/src/game/client/client_ff.vpc b/mp/src/game/client/client_ff.vpc index 8df094e0..127ff508 100644 --- a/mp/src/game/client/client_ff.vpc +++ b/mp/src/game/client/client_ff.vpc @@ -35,6 +35,11 @@ $Project "Client (FF)" $File "ff\ff_cl_dll_interface.cpp" $File "ff\ff_cl_dll_interface.h" } + $Folder "Lua" + { + $File "ff\ff_cl_scriptman_ui.cpp" + $File "ff\ff_cl_scriptman_ui.h" + } $Folder "Player" { $File "ff\ff_cl_player.cpp" diff --git a/mp/src/game/client/ff/ff_cl_dll_interface.cpp b/mp/src/game/client/ff/ff_cl_dll_interface.cpp index 67b2d70d..d348c8f3 100644 --- a/mp/src/game/client/ff/ff_cl_dll_interface.cpp +++ b/mp/src/game/client/ff/ff_cl_dll_interface.cpp @@ -2,7 +2,8 @@ #include "ff_cl_dll_interface.h" #include "steam/steam_api.h" -#include "ff_sh_scriptman.h" +#include "ff_cl_scriptman_ui.h" +#include "ff_sh_scriptman_game.h" #define CLIENT_DLL_INTERFACE_VERSION "VClient017" @@ -13,8 +14,6 @@ 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(); diff --git a/mp/src/game/client/ff/ff_cl_scriptman_ui.cpp b/mp/src/game/client/ff/ff_cl_scriptman_ui.cpp new file mode 100644 index 00000000..f8c2837f --- /dev/null +++ b/mp/src/game/client/ff/ff_cl_scriptman_ui.cpp @@ -0,0 +1,95 @@ + +///////////////////////////////////////////////////////////////////////////// +// includes +#include "cbase.h" +#include "ff_cl_scriptman_ui.h" + +// engine +//#include "filesystem.h" + +// dexter note 10/29/2013 these are definitely still needed for lua/luabind +#undef MINMAX_H +#undef min +#undef max +// +// luabind +#include "lua.hpp" +#include "luabind/luabind.hpp" +//#include "luabind/object.hpp" +//#include "luabind/iterator_policy.hpp" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace luabind; + +CFF_CL_ScriptManager_UI g_UIScriptManager; + +///////////////////////////////////////////////////////////////////////////// +CFF_CL_ScriptManager_UI::CFF_CL_ScriptManager_UI() +{ +} + +///////////////////////////////////////////////////////////////////////////// +CFF_CL_ScriptManager_UI::~CFF_CL_ScriptManager_UI() +{ +} + + +///////////////////////////////////////////////////////////////////////////// +void CFF_CL_ScriptManager_UI::Shutdown() +{ + BaseClass::Shutdown(); +} + + +///////////////////////////////////////////////////////////////////////////// +bool CFF_CL_ScriptManager_UI::Init() +{ + bool bInitSuccessful = BaseClass::Init(); + + if (bInitSuccessful) + { + LoadFile( "ui/init.lua" ); + } + + return bInitSuccessful; +} + +void CFF_CL_ScriptManager_UI::LevelInit(const char* szMapName) +{ + +} + +///////////////////////////////////////////////////////////////////////////// +void CFF_CL_ScriptManager_UI::LevelShutdown() +{ + +} + + +CON_COMMAND( lua_dostring_ui, "Run a client-side Lua string in the UI environment" ) +{ + if ( args.ArgC() == 1 ) + { + Msg( "Usage: lua_dostring \n" ); + return; + } + + lua_State *L = g_UIScriptManager.GetLuaState(); + int status = luaL_dostring(L, args.ArgS()); + if (status != 0) { + Warning( "%s\n", lua_tostring(L, -1) ); + lua_pop(L, 1); + return; + } + 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 */ +} \ No newline at end of file diff --git a/mp/src/game/client/ff/ff_cl_scriptman_ui.h b/mp/src/game/client/ff/ff_cl_scriptman_ui.h new file mode 100644 index 00000000..c185dca1 --- /dev/null +++ b/mp/src/game/client/ff/ff_cl_scriptman_ui.h @@ -0,0 +1,30 @@ +#pragma once +#ifndef FF_CL_SCRIPTMAN_UI_H +#define FF_CL_SCRIPTMAN_UI_H + +#include "ff_sh_scriptman.h" + +class CFF_CL_ScriptManager_UI : public CFF_SH_ScriptManager +{ +public: + DECLARE_CLASS( CFF_CL_ScriptManager_UI, CFF_SH_ScriptManager ); + // 'structors + CFF_CL_ScriptManager_UI(); + ~CFF_CL_ScriptManager_UI(); + +protected: + virtual const char* GetMsgIdentifier() { return "UI"; }; + virtual const char* GetPackagePathRoot() { return "/ui/"; }; + +public: + virtual bool Init(); + virtual void Shutdown(); + + virtual void LevelInit(const char* szMapName); + virtual void LevelShutdown(); + +}; + +extern CFF_CL_ScriptManager_UI g_UIScriptManager; + +#endif \ No newline at end of file diff --git a/mp/src/game/server/ff/ff_sv_dll_interface.cpp b/mp/src/game/server/ff/ff_sv_dll_interface.cpp index 4f4e8a9f..ac44cbd5 100644 --- a/mp/src/game/server/ff/ff_sv_dll_interface.cpp +++ b/mp/src/game/server/ff/ff_sv_dll_interface.cpp @@ -1,7 +1,7 @@ #include "cbase.h" #include "ff_sv_dll_interface.h" -#include "ff_sh_scriptman.h" +#include "ff_sh_scriptman_game.h" bool CFF_SV_DLL_Interface::DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn fileSystemFactory, CGlobalVars *pGlobals) { diff --git a/mp/src/game/shared/ff/ff_sh_luautil.cpp b/mp/src/game/shared/ff/ff_sh_luautil.cpp new file mode 100644 index 00000000..6d0c05f9 --- /dev/null +++ b/mp/src/game/shared/ff/ff_sh_luautil.cpp @@ -0,0 +1,19 @@ +#include "cbase.h" +#include "ff_sh_luautil.h" + +#include "lua.hpp" + +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); +} \ No newline at end of file diff --git a/mp/src/game/shared/ff/ff_sh_luautil.h b/mp/src/game/shared/ff/ff_sh_luautil.h new file mode 100644 index 00000000..910d6c2d --- /dev/null +++ b/mp/src/game/shared/ff/ff_sh_luautil.h @@ -0,0 +1,9 @@ +#ifndef FF_SH_LUAUTIL_H +#define FF_SH_LUAUTIL_H +#pragma once + +struct lua_State; + +void LUAUTIL_RemoveKeysFromTable( lua_State *L, const char *pszTableName, const char** ppszKeys ); + +#endif \ No newline at end of file diff --git a/mp/src/game/shared/ff/ff_sh_scriptman.cpp b/mp/src/game/shared/ff/ff_sh_scriptman.cpp index 935e3a90..ebfc5680 100644 --- a/mp/src/game/shared/ff/ff_sh_scriptman.cpp +++ b/mp/src/game/shared/ff/ff_sh_scriptman.cpp @@ -9,6 +9,7 @@ //#include "ff_utils.h" //#include "ff_item_flag.h" //#include "triggers.h" +#include "ff_sh_luautil.h" // engine #include "filesystem.h" @@ -28,16 +29,6 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -#ifdef CLIENT_DLL -#define LUA_CURRENT_CONTEXT "Client" -#else -#define LUA_CURRENT_CONTEXT "Server" -#endif - -// custom game modes made so damn easy -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) { @@ -60,38 +51,22 @@ static int print(lua_State *L) return 0; } -///////////////////////////////////////////////////////////////////////////// using namespace luabind; -///////////////////////////////////////////////////////////////////////////// -// globals -CFF_SH_ScriptManager g_GameScriptManager; -#ifdef CLIENT_DLL -CFF_SH_ScriptManager g_UIScriptManager; -#endif - - -///////////////////////////////////////////////////////////////////////////// CFF_SH_ScriptManager::CFF_SH_ScriptManager() -: L(NULL) -, m_isLoading(false) -, m_scriptCRC(0) -, m_ScriptExists(false) { + L = NULL; } -///////////////////////////////////////////////////////////////////////////// CFF_SH_ScriptManager::~CFF_SH_ScriptManager() { Shutdown(); } - -///////////////////////////////////////////////////////////////////////////// +/** Close the Lua VM +*/ void CFF_SH_ScriptManager::Shutdown() { - m_ScriptExists = false; - // shtutdown VM if(L) { @@ -100,46 +75,58 @@ void CFF_SH_ScriptManager::Shutdown() } } - -///////////////////////////////////////////////////////////////////////////// -void CFF_SH_ScriptManager::Init() +/** Open the Lua VM + @returns True if successful, false if couldn't open Lua VM +*/ +bool CFF_SH_ScriptManager::Init() { // shutdown VM if already running - if(L) - { - lua_close(L); - L = NULL; - } + Shutdown(); // initialize VM - Msg("[SCRIPT:%s] Attempting to start the Lua VM...\n", LUA_CURRENT_CONTEXT); + LuaMsg("Attempting to start the Lua VM...\n"); L = lua_open(); // no need to continue if VM failed to initialize if(!L) { - Msg("[SCRIPT:%s] Unable to initialize Lua VM.\n", LUA_CURRENT_CONTEXT); - return; + LuaMsg("Unable to initialize Lua VM.\n"); + return false; } + // initialize all the FF specific stuff + SetupEnvironmentForFF(); + + // make the standard libraries safe + MakeEnvironmentSafe(); + + LuaMsg("Lua VM initialization successful.\n"); + return true; +} + +/** Loads the Lua libraries and sets all the variables needed for FF +*/ +void CFF_SH_ScriptManager::SetupEnvironmentForFF() +{ + Assert(L); + if (!L) 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) + // 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_snprintf( fullpath, sizeof( fullpath ), "%s%s%s", szSearchPath, GetPackagePathRoot(), "?.lua;" ); Q_FixSlashes( fullpath ); V_FixDoubleSlashes( fullpath ); @@ -156,32 +143,20 @@ void CFF_SH_ScriptManager::Init() // initialize luabind luabind::open(L); - + // initialize game-specific library //FF_TODO: CFFLuaLib::Init(L); - - 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 +/** Gets rid of or alter any unsafe Lua functions in the current environment */ -void CFF_SH_ScriptManager::MakeSafe() +void CFF_SH_ScriptManager::MakeEnvironmentSafe() { + Assert(L); + if (!L) return; + + // See: http://lua-users.org/wiki/SandBoxes for a general overview of the safety of Lua functions + // os.* const char* ppszUnsafeOSFunctions[] = { "execute", "exit", "getenv", "remove", "rename", "setlocale", NULL }; LUAUTIL_RemoveKeysFromTable( L, LUA_OSLIBNAME, ppszUnsafeOSFunctions ); @@ -195,7 +170,7 @@ void CFF_SH_ScriptManager::MakeSafe() // 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_gettable(L, -2); // get _G.package.loaders lua_pushnil(L); lua_rawseti(L, -2, 4); // _G.package.loaders[4] = nil lua_pushnil(L); @@ -203,191 +178,23 @@ void CFF_SH_ScriptManager::MakeSafe() lua_pop(L, 2); // pop _G.package.loaders and _G.package // FF TODO: restrict io library, maybe disable it completely + // FF TODO: somehow protect package.path? not sure if it's necessary, the "risk" would be allowing execution of .lua files in arbitrary locations } -void CFF_SH_ScriptManager::LevelInit(const char* szMapName) -{ - const char* default_luafile = "maps/default.lua"; - //FF_TODO: VPROF_BUDGET("CFF_SH_ScriptManager::LevelInit", VPROF_BUDGETGROUP_FF_LUA); - - if(!szMapName) - return; - - //FF_TODO: g_Disable_Timelimit = false; - - // setup VM - Init(); - - // load lua files - BeginScriptLoad(); - LoadFile("maps/includes/base.lua"); - - char filename[256] = {0}; - char globalscript_filename[256] = {0}; - // Even though LoadFile already checks to see if the file exists, we'll check now so at least the default map lua file is loaded. - // That way servers can keep their suffix set without worrying about every map having whatever game mode they always want to use. - if ( sv_mapluasuffix.GetString()[0] != '0' ) - { - Msg( "[SCRIPT] sv_mapluasuffix set to %s | finding maps\\%s__%s__.lua\n", sv_mapluasuffix.GetString(), szMapName, sv_mapluasuffix.GetString() ); -#ifdef CLIENT_DLL - if ( filesystem->FileExists( VarArgs( "maps/%s__%s__.lua", szMapName, sv_mapluasuffix.GetString() ) ) ) -#else - if ( filesystem->FileExists( UTIL_VarArgs( "maps/%s__%s__.lua", szMapName, sv_mapluasuffix.GetString() ) ) ) -#endif - { - Q_snprintf( filename, sizeof(filename), "maps/%s__%s__.lua", szMapName, sv_mapluasuffix.GetString() ); - Msg( "[SCRIPT] maps\\%s__%s__.lua found\n", szMapName, sv_mapluasuffix.GetString() ); - } - else - { - Msg( "[SCRIPT] maps\\%s__%s__.lua not found | reverting to maps\\%s.lua\n", szMapName, sv_mapluasuffix.GetString(), szMapName); - } - } - - // Load global include script, overwriting previously loaded stuff per map - if( sv_luaglobalscript.GetString()[0] != '0' ) - { - const char* scriptname = sv_luaglobalscript.GetString(); - Msg("[SCRIPT] sv_luaglobalscript set to %s | loading global script maps maps\\globalscripts\\%s.lua\n", scriptname, scriptname ); -#ifdef CLIENT_DLL - if( filesystem->FileExists( VarArgs( "maps/globalscripts/%s.lua", scriptname ) ) ) -#else - if( filesystem->FileExists( UTIL_VarArgs( "maps/globalscripts/%s.lua", scriptname ) ) ) -#endif - { - Q_snprintf( globalscript_filename, sizeof(globalscript_filename), "maps/globalscripts/%s.lua", scriptname ); - Msg("[SCRIPT] maps\\globalscripts\\%s.lua found\n", scriptname );\ - } - else - { - Msg("[SCRIPT] global script maps\\globalscripts\\%s.lua not found - nothing loaded post map lua.\n", scriptname ); - } - } - - if ( !filename[0] ) - Q_snprintf( filename, sizeof(filename), "maps/%s.lua", szMapName ); - ////////////////////////////////////////////////////////////////////////// - // Try a precache, rumor has it this will cause the engine to send the lua files to clients - // FF_TODO: dexter - disabled for now, doesnt work anyway right? - //if(PRECACHE_LUA_FILES) - //{ - // V_FixSlashes(filename); - // if(filesystem->FileExists(filename)) - // { - // Util_AddDownload(filename); - - // if(!engine->IsGenericPrecached(filename)) - // engine->PrecacheGeneric(filename, true); - // } - // else // - if no map lua is found, send default (for testing mainly) - // { - // // no check - this file should *always* be there - // Util_AddDownload(default_luafile); - // if(!engine->IsGenericPrecached(default_luafile)) - // engine->PrecacheGeneric(default_luafile, true); - // } - // - - // // if we have a globalscript, precache it as well - // if( sv_luaglobalscript.GetString()[0] != '0' && globalscript_filename[0] ) - // { - // V_FixSlashes(globalscript_filename); - // if(filesystem->FileExists(globalscript_filename)) - // { - // Util_AddDownload(globalscript_filename); - - // if(!engine->IsGenericPrecached(globalscript_filename)) - // engine->PrecacheGeneric(globalscript_filename, true); - // } - // } - - // ////////////////////////////////////////////////////////////////////////// - // /*char testfile[] = {"maps/ff_dm.txt"}; - // V_FixSlashes(testfile); - // if(filesystem->FileExists(testfile)) - // { - // Util_AddDownload(testfile); - - // if(!engine->IsGenericPrecached(testfile)) - // engine->PrecacheGeneric(testfile, true); - // }*/ - // ////////////////////////////////////////////////////////////////////////// - //} - ////////////////////////////////////////////////////////////////////////// - if(filesystem->FileExists(filename)) - m_ScriptExists = LoadFile(filename); - else - { - Msg("[SCRIPT] File %s not found! Loaded fallback lua %s\n", filename, 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(globalscript_filename); - //EndScriptLoad(); - } - - EndScriptLoad(); - - // spawn the helper entity - //FF_TODO: CFFEntitySystemHelper::Create(); -} - -///////////////////////////////////////////////////////////////////////////// -void CFF_SH_ScriptManager::LevelShutdown() -{ - // shutdown the VM - if(L) - { - lua_close(L); - L = NULL; - } -} - -///////////////////////////////////////////////////////////////////////////// -void CFF_SH_ScriptManager::OnScriptLoad(const char* szFileName, - const char* szFileContents) -{ - // ignore the message if we are not still in the "loading" phase - if(!m_isLoading) - return; - - // compute checksums of file contents - CRC32_ProcessBuffer(&m_scriptCRC, - szFileContents, - strlen(szFileContents)); -} - -///////////////////////////////////////////////////////////////////////////// -void CFF_SH_ScriptManager::BeginScriptLoad() -{ - CRC32_Init(&m_scriptCRC); - m_isLoading = true; -} - -///////////////////////////////////////////////////////////////////////////// -void CFF_SH_ScriptManager::EndScriptLoad() -{ - CRC32_Final(&m_scriptCRC); - m_isLoading = false; -} - - -///////////////////////////////////////////////////////////////////////////// +/** Loads a Lua file into the current environment relative to a "MOD" search path + @returns True if file successfully loaded, false if there were any errors (syntax or execution) +*/ bool CFF_SH_ScriptManager::LoadFile(const char *filename) { //FF_TODO: VPROF_BUDGET( "CFF_SH_ScriptManager::LoadFile", VPROF_BUDGETGROUP_FF_LUA ); // open the file - Msg("[SCRIPT:%s] Loading Lua File: %s\n", LUA_CURRENT_CONTEXT, filename); + LuaMsg("Loading Lua File: %s\n", filename); FileHandle_t hFile = filesystem->Open(filename, "rb", "MOD"); if (!hFile) { - Warning("[SCRIPT:%s] %s either does not exist or could not be opened.\n", LUA_CURRENT_CONTEXT, filename); + LuaWarning("%s either does not exist or could not be opened.\n", filename); return false; } @@ -411,7 +218,7 @@ bool CFF_SH_ScriptManager::LoadFile(const char *filename) if (errorCode != 0) { const char *error = lua_tostring(L, -1); - Warning( "[SCRIPT:%s] Error loading %s: %s\n", LUA_CURRENT_CONTEXT, filename, error ); + LuaWarning( "Error loading %s: %s\n", filename, error ); lua_pop( L, 1 ); return false; } @@ -423,46 +230,49 @@ bool CFF_SH_ScriptManager::LoadFile(const char *filename) if (errorCode != 0) { const char *error = lua_tostring(L, -1); - Warning( "[SCRIPT:%s] Error loading %s: %s\n", LUA_CURRENT_CONTEXT, filename, error ); + LuaWarning( "Error loading %s: %s\n", filename, error ); lua_pop( L, 1 ); return false; } - Msg( "[SCRIPT:%s] Successfully loaded %s\n", LUA_CURRENT_CONTEXT, filename ); + LuaMsg( "Successfully loaded %s\n", 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; -} - +// FF TODO: Better way to get this sort of string? #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 \n" ); - return; - } +#define LUA_MSG_DLL_CONTEXT "Client" +#else +#define LUA_MSG_DLL_CONTEXT "Server" +#endif - 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 */ +/** Wrapper for Msg that prefixes the string with info about where it's coming from in the format: [Lua.:] +*/ +void CFF_SH_ScriptManager::LuaMsg( const char *pszFormat, ... ) +{ + va_list argptr; + static char string[4096]; + + va_start (argptr, pszFormat); + Q_vsnprintf(string, sizeof(string), pszFormat, argptr); + va_end (argptr); + + Msg("[Lua.%s:%s] %s", GetMsgIdentifier(), LUA_MSG_DLL_CONTEXT, string ); } -#endif \ No newline at end of file + +/** Wrapper for Warning that prefixes the string with info about where it's coming from in the format: [Lua.:] +*/ +void CFF_SH_ScriptManager::LuaWarning( const char *pszFormat, ... ) +{ + va_list argptr; + static char string[4096]; + + va_start (argptr, pszFormat); + Q_vsnprintf(string, sizeof(string), pszFormat, argptr); + va_end (argptr); + + Warning("[Lua.%s:%s] %s", GetMsgIdentifier(), LUA_MSG_DLL_CONTEXT, string ); +} \ No newline at end of file diff --git a/mp/src/game/shared/ff/ff_sh_scriptman.h b/mp/src/game/shared/ff/ff_sh_scriptman.h index 98bfd5c2..b2d91698 100644 --- a/mp/src/game/shared/ff/ff_sh_scriptman.h +++ b/mp/src/game/shared/ff/ff_sh_scriptman.h @@ -1,8 +1,6 @@ - -// ff_sh_scriptman.h - #ifndef FF_SH_SCRIPTMAN_H #define FF_SH_SCRIPTMAN_H +#pragma once ///////////////////////////////////////////////////////////////////////////// // includes @@ -30,37 +28,36 @@ class CFFLuaSC; class CFF_SH_ScriptManager { public: + DECLARE_CLASS_NOBASE( CFF_SH_ScriptManager ); // 'structors CFF_SH_ScriptManager(); ~CFF_SH_ScriptManager(); -public: // inserts the lua file into the script environment - bool LoadFile(const char* filePath); - void MakeSafe(); + virtual bool LoadFile(const char* filePath); + // removes unsafe functions from the Lua environment or makes them safer + virtual void MakeEnvironmentSafe(); + virtual void SetupEnvironmentForFF(); -public: // initializes the script VM - void Init(); + virtual bool Init(); // closes the script VM - void Shutdown(); + virtual void Shutdown(); + + virtual void LevelInit(const char* szMapName) {}; + virtual void LevelShutdown() {}; - // loads the scripts for the level - void LevelInit(const char* szMapName); - // cleans up the scripts for the most recent level - void LevelShutdown(); + void LuaMsg( const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); + void LuaWarning( const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); -private: - // surround code that loads scripts to capture crc checksum - // of the scripts that are loaded - void BeginScriptLoad(); - void EndScriptLoad(); - -private: - // called when a script is loaded. internally computes the - // crc checksum of the file contents - void OnScriptLoad(const char* szFileName, const char* szFileContents); +protected: + virtual const char* GetMsgIdentifier() { return "Generic"; }; + virtual const char* GetPackagePathRoot() { return "/"; }; + + /// Called from LoadFile + virtual void OnScriptLoad(const char* szFileName, const char* szFileContents) {}; +/* public: // sets a global variable in the script environment static void SetVar( lua_State *L, const char *name, const char *value ); @@ -89,29 +86,15 @@ public: public: int RunPredicates( CBaseEntity *pObject, CBaseEntity *pEntity, const char *szFunctionName = NULL); bool RunPredicates_LUA( CBaseEntity *pObject, CFFLuaSC *pContext, const char *szFunctionName ); +*/ public: // returns the lua interpreter lua_State* GetLuaState() const { return L; } - bool ScriptExists( void ) const { return m_ScriptExists; } - // returns the crc checksum of the currently active script - CRC32_t GetScriptCRC() const { return m_scriptCRC; } - -private: - // private data - lua_State* L; // lua VM - bool m_ScriptExists; - bool m_isLoading; - CRC32_t m_scriptCRC; +protected: + lua_State* L; ///< Lua VM }; -///////////////////////////////////////////////////////////////////////////// -// global externs -extern CFF_SH_ScriptManager g_GameScriptManager; -#ifdef CLIENT_DLL -extern CFF_SH_ScriptManager g_UIScriptManager; -#endif - ///////////////////////////////////////////////////////////////////////////// #endif diff --git a/mp/src/game/shared/ff/ff_sh_scriptman_game.cpp b/mp/src/game/shared/ff/ff_sh_scriptman_game.cpp new file mode 100644 index 00000000..f41d6406 --- /dev/null +++ b/mp/src/game/shared/ff/ff_sh_scriptman_game.cpp @@ -0,0 +1,200 @@ + +///////////////////////////////////////////////////////////////////////////// +// includes +#include "cbase.h" +#include "ff_sh_scriptman_game.h" + +// engine +#include "filesystem.h" + +// dexter note 10/29/2013 these are definitely still needed for lua/luabind +#undef MINMAX_H +#undef min +#undef max +// +// luabind +#include "lua.hpp" +#include "luabind/luabind.hpp" +//#include "luabind/object.hpp" +//#include "luabind/iterator_policy.hpp" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// custom game modes made so damn easy +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."); + +using namespace luabind; + +CFF_SH_ScriptManager_Game g_GameScriptManager; + +///////////////////////////////////////////////////////////////////////////// +CFF_SH_ScriptManager_Game::CFF_SH_ScriptManager_Game() +{ + m_bLoading = m_bScriptLoaded = false; + m_ScriptCRC = NULL; +} + +///////////////////////////////////////////////////////////////////////////// +CFF_SH_ScriptManager_Game::~CFF_SH_ScriptManager_Game() +{ +} + + +///////////////////////////////////////////////////////////////////////////// +void CFF_SH_ScriptManager_Game::Shutdown() +{ + BaseClass::Shutdown(); +} + + +///////////////////////////////////////////////////////////////////////////// +bool CFF_SH_ScriptManager_Game::Init() +{ + bool bInitSuccessful = BaseClass::Init(); + + if (bInitSuccessful) + { + + } + + return bInitSuccessful; +} + +void CFF_SH_ScriptManager_Game::LevelInit(const char* szMapName) +{ + const char* default_luafile = "maps/default.lua"; + //FF_TODO: VPROF_BUDGET("CFF_SH_ScriptManager::LevelInit", VPROF_BUDGETGROUP_FF_LUA); + + if(!szMapName) + return; + + //FF_TODO: g_Disable_Timelimit = false; + + // setup VM + Init(); + + // load lua files + BeginScriptLoad(); + LoadFile("maps/includes/base.lua"); + + char filename[256] = {0}; + char globalscript_filename[256] = {0}; + // Even though LoadFile already checks to see if the file exists, we'll check now so at least the default map lua file is loaded. + // That way servers can keep their suffix set without worrying about every map having whatever game mode they always want to use. + if ( sv_mapluasuffix.GetString()[0] != '0' ) + { + Msg( "[SCRIPT] sv_mapluasuffix set to %s | finding maps\\%s__%s__.lua\n", sv_mapluasuffix.GetString(), szMapName, sv_mapluasuffix.GetString() ); + if ( filesystem->FileExists( UTIL_VarArgs( "maps/%s__%s__.lua", szMapName, sv_mapluasuffix.GetString() ) ) ) + { + Q_snprintf( filename, sizeof(filename), "maps/%s__%s__.lua", szMapName, sv_mapluasuffix.GetString() ); + Msg( "[SCRIPT] maps\\%s__%s__.lua found\n", szMapName, sv_mapluasuffix.GetString() ); + } + else + { + Msg( "[SCRIPT] maps\\%s__%s__.lua not found | reverting to maps\\%s.lua\n", szMapName, sv_mapluasuffix.GetString(), szMapName); + } + } + + // Load global include script, overwriting previously loaded stuff per map + if( sv_luaglobalscript.GetString()[0] != '0' ) + { + const char* scriptname = sv_luaglobalscript.GetString(); + Msg("[SCRIPT] sv_luaglobalscript set to %s | loading global script maps maps\\globalscripts\\%s.lua\n", scriptname, scriptname ); + if( filesystem->FileExists( UTIL_VarArgs( "maps/globalscripts/%s.lua", scriptname ) ) ) + { + Q_snprintf( globalscript_filename, sizeof(globalscript_filename), "maps/globalscripts/%s.lua", scriptname ); + Msg("[SCRIPT] maps\\globalscripts\\%s.lua found\n", scriptname );\ + } + else + { + Msg("[SCRIPT] global script maps\\globalscripts\\%s.lua not found - nothing loaded post map lua.\n", scriptname ); + } + } + + if ( !filename[0] ) + Q_snprintf( filename, sizeof(filename), "maps/%s.lua", szMapName ); + + if(filesystem->FileExists(filename)) + m_bScriptLoaded = LoadFile(filename); + else + { + Msg("[SCRIPT] File %s not found! Loaded fallback lua %s\n", filename, default_luafile); + m_bScriptLoaded = LoadFile(default_luafile); + } + + // force loading global script in another call :/ + if( sv_luaglobalscript.GetString()[0] != '0' && globalscript_filename[0] ) + { + LoadFile(globalscript_filename); + } + + EndScriptLoad(); + + // spawn the helper entity + //FF_TODO: CFFEntitySystemHelper::Create(); +} + +///////////////////////////////////////////////////////////////////////////// +void CFF_SH_ScriptManager_Game::LevelShutdown() +{ + // shutdown the VM + if(L) + { + lua_close(L); + L = NULL; + } +} + +void CFF_SH_ScriptManager_Game::OnScriptLoad(const char* szFileName, const char* szFileContents) +{ + // ignore the message if we are not still in the "loading" phase + if(!m_bLoading) + return; + + // compute checksums of file contents + CRC32_ProcessBuffer( &m_ScriptCRC, szFileContents, strlen(szFileContents) ); +} + +void CFF_SH_ScriptManager_Game::BeginScriptLoad() +{ + CRC32_Init(&m_ScriptCRC); + m_bLoading = true; +} + +void CFF_SH_ScriptManager_Game::EndScriptLoad() +{ + CRC32_Final(&m_ScriptCRC); + m_bLoading = false; +} + +#ifdef CLIENT_DLL +CON_COMMAND( lua_dostring_game_cl, "Run a client-side Lua string in the Game environment" ) +#else +CON_COMMAND( lua_dostring_game_sv, "Run a server-side Lua string in the Game environment" ) +#endif +{ + if ( args.ArgC() == 1 ) + { + Msg( "Usage: lua_dostring \n" ); + return; + } + + lua_State *L = g_GameScriptManager.GetLuaState(); + int status = luaL_dostring(L, args.ArgS()); + if (status != 0) { + Warning( "%s\n", lua_tostring(L, -1) ); + lua_pop(L, 1); + return; + } + 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 */ +} \ No newline at end of file diff --git a/mp/src/game/shared/ff/ff_sh_scriptman_game.h b/mp/src/game/shared/ff/ff_sh_scriptman_game.h new file mode 100644 index 00000000..60e38685 --- /dev/null +++ b/mp/src/game/shared/ff/ff_sh_scriptman_game.h @@ -0,0 +1,46 @@ +#pragma once +#ifndef FF_CL_SCRIPTMAN_GAME_H +#define FF_CL_SCRIPTMAN_GAME_H + +#include "ff_sh_scriptman.h" + +class CFF_SH_ScriptManager_Game : public CFF_SH_ScriptManager +{ +public: + DECLARE_CLASS( CFF_SH_ScriptManager_Game, CFF_SH_ScriptManager ); + // 'structors + CFF_SH_ScriptManager_Game(); + ~CFF_SH_ScriptManager_Game(); + +protected: + virtual const char* GetMsgIdentifier() { return "Game"; }; + virtual const char* GetPackagePathRoot() { return "/lua/"; }; + + virtual void OnScriptLoad(const char* szFileName, const char* szFileContents); + +public: + virtual bool Init(); + virtual void Shutdown(); + + virtual void LevelInit(const char* szMapName); + virtual void LevelShutdown(); + + bool ScriptLoaded( void ) const { return m_bScriptLoaded; } + CRC32_t GetScriptCRC() const { return m_ScriptCRC; } + +private: + // surround code that loads scripts to capture crc checksum + // of the scripts that are loaded + void BeginScriptLoad(); + void EndScriptLoad(); + +private: + bool m_bScriptLoaded; ///< Whether or not a script file has been loaded in the current Lua environment + bool m_bLoading; ///< True at the start of LevelInit, false at the end + CRC32_t m_ScriptCRC; ///< CRC of all scripts loaded while m_isLoading was true + +}; + +extern CFF_SH_ScriptManager_Game g_GameScriptManager; + +#endif \ No newline at end of file diff --git a/mp/src/game/shared/ff/ff_shared.vpc b/mp/src/game/shared/ff/ff_shared.vpc index 2622b189..7c7562c5 100644 --- a/mp/src/game/shared/ff/ff_shared.vpc +++ b/mp/src/game/shared/ff/ff_shared.vpc @@ -11,6 +11,10 @@ $Project { $File "$SRCDIR\game\shared\ff\ff_sh_scriptman.cpp" $File "$SRCDIR\game\shared\ff\ff_sh_scriptman.h" + $File "$SRCDIR\game\shared\ff\ff_sh_scriptman_game.cpp" + $File "$SRCDIR\game\shared\ff\ff_sh_scriptman_game.h" + $File "$SRCDIR\game\shared\ff\ff_sh_luautil.cpp" + $File "$SRCDIR\game\shared\ff\ff_sh_luautil.h" } $Folder "Movement" {