Skip to content

Commit a593163

Browse files
Reimplemented Lua packages in C++
Similar design to before, but in C++: use require, and package.loaded is enabled. Pure Lua modules must be placed correctly in either: <resource>/?.lua <resource>/?/init.lua Lua C Modules must be placed in: /server/mods/deathmatch/modules/?.dll To easily test or use modules for Windows: Download and install Lua for Windows. Copy contents of C:\Program Files (x86)\Lua\5.1\clibs into /server/mods/deathmatch/modules. Use e.g. require"lfs" To-do: * Port over to clientside (without C module support) * More detailed error messages for Pure lua packages * ACL support for C modules. * Extensive testing
1 parent 45e32c4 commit a593163

File tree

6 files changed

+231
-4
lines changed

6 files changed

+231
-4
lines changed

Server/mods/deathmatch/logic/lua/CLuaMain.cpp

Lines changed: 166 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ void CLuaMain::InitSecurity ( void )
126126
{
127127
lua_register ( m_luaVM, "dofile", CLuaUtilDefs::DisabledFunction );
128128
lua_register ( m_luaVM, "loadfile", CLuaUtilDefs::DisabledFunction );
129-
lua_register ( m_luaVM, "require", CLuaUtilDefs::DisabledFunction );
130129
lua_register ( m_luaVM, "loadlib", CLuaUtilDefs::DisabledFunction );
131130
lua_register ( m_luaVM, "getfenv", CLuaUtilDefs::DisabledFunction );
132131
lua_register ( m_luaVM, "newproxy", CLuaUtilDefs::DisabledFunction );
@@ -192,6 +191,9 @@ void CLuaMain::InitVM ( void )
192191
luaopen_debug ( m_luaVM );
193192
luaopen_utf8 ( m_luaVM );
194193

194+
// Initialize packages system
195+
InitPackageStorage(m_luaVM);
196+
195197
// Initialize security restrictions. Very important to prevent lua trojans and viruses!
196198
InitSecurity ();
197199

@@ -250,7 +252,7 @@ void CLuaMain::InstructionCountHook ( lua_State* luaVM, lua_Debug* pDebug )
250252
}
251253

252254

253-
bool CLuaMain::LoadScriptFromBuffer ( const char* cpInBuffer, unsigned int uiInSize, const char* szFileName )
255+
bool CLuaMain::LoadScriptFromBuffer(const char* cpInBuffer, unsigned int uiInSize, const char* szFileName, bool bClearReturnValues )
254256
{
255257
SString strNiceFilename = ConformResourcePath( szFileName );
256258

@@ -320,7 +322,7 @@ bool CLuaMain::LoadScriptFromBuffer ( const char* cpInBuffer, unsigned int uiInS
320322
g_pGame->GetScriptDebugging()->LogPCallError( m_luaVM, strRes, true );
321323
}
322324
// Cleanup any return values
323-
if ( lua_gettop ( m_luaVM ) > luaSavedTop )
325+
if ( bClearReturnValues && lua_gettop ( m_luaVM ) > luaSavedTop )
324326
lua_settop( m_luaVM, luaSavedTop );
325327
return true;
326328
}
@@ -686,3 +688,164 @@ int CLuaMain::OnUndump( const char* p, size_t n )
686688
}
687689
return 1;
688690
}
691+
692+
693+
///////////////////////////////////////////////////////////////
694+
//
695+
// CLuaMain::InitPackageStorage
696+
//
697+
// Create a table for storage of Lua packages - stored in the Lua VM
698+
//
699+
///////////////////////////////////////////////////////////////
700+
void CLuaMain::InitPackageStorage(lua_State* L)
701+
{
702+
lua_newtable(L); // stack: [tbl_new]
703+
lua_pushstring(L, "loaded"); // stack: [tbl_new,"loaded"]
704+
lua_newtable(L); // stack: [tbl_new,"loaded",tbl_new2]
705+
706+
// We push our default Lua libs are loaded packages
707+
std::vector<const char*> szDefaultLibs = { "math", "string", "table", "debug", "utf8" };
708+
for (auto it : szDefaultLibs)
709+
{
710+
lua_pushstring(L, it); // stack: [tbl_new,"loaded",tbl_new2,"math"]
711+
lua_getglobal(L, it); // stack: [tbl_new,"loaded",tbl_new2,"math",_G.math]
712+
lua_settable(L, -3); // stack: [tbl_new,"loaded",tbl_new2]
713+
}
714+
715+
// Finally, store our original table as package.loaded
716+
lua_settable(L, -3); // stack: [tbl_new]
717+
lua_setglobal(L, "package"); // stack: []
718+
}
719+
720+
///////////////////////////////////////////////////////////////
721+
//
722+
// CLuaMain::SetPackage
723+
//
724+
// Pop the top most value as a package
725+
//
726+
///////////////////////////////////////////////////////////////
727+
void CLuaMain::SetPackage(lua_State* L, SString &strName )
728+
{
729+
// We set varPkg, which is already on the stack, into package.loaded.moduleName = varPkg.
730+
// stack: [varPkg]
731+
int iPkg = luaL_ref(L, LUA_REGISTRYINDEX); // stack: []
732+
lua_getglobal(L, "package"); // stack: [tbl_package]
733+
if (lua_type(L, -1) == LUA_TNIL )
734+
InitPackageStorage(L);
735+
736+
lua_pushstring(L, "loaded"); // stack: [tbl_package,"loaded"]
737+
lua_rawget(L, -2); // stack: [tbl_package,tbl_loaded]
738+
if (lua_type(L, -1) == LUA_TNIL)
739+
InitPackageStorage(L);
740+
741+
742+
lua_pushstring(L, strName.c_str()); // stack: [tbl_package,tbl_loaded,"moduleName"]
743+
lua_rawgeti(L, LUA_REGISTRYINDEX, iPkg); // stack: [tbl_package,tbl_loaded,"moduleName",varPkg]
744+
lua_rawset(L, -3); // stack: [tbl_package,tbl_loaded]
745+
lua_pop(L, 2); // stack: []
746+
lua_rawgeti(L, LUA_REGISTRYINDEX, iPkg); // stack: [varPkg]
747+
748+
// Cleanup our used registry entry, REGISTRY[i] = nil.
749+
lua_pushnil(L); // stack: [varPkg,nil]
750+
lua_rawseti(L, LUA_REGISTRYINDEX, iPkg); // stack: [varPkg]
751+
}
752+
753+
///////////////////////////////////////////////////////////////
754+
//
755+
// CLuaMain::GetPackage
756+
//
757+
// Push the value of a package of name to the stack
758+
//
759+
///////////////////////////////////////////////////////////////
760+
void CLuaMain::GetPackage(lua_State* L, SString &strName)
761+
{
762+
lua_getglobal(L, "package"); // stack: [tbl_package]
763+
if (lua_type(L, -1) == LUA_TNIL )
764+
InitPackageStorage(m_luaVM);
765+
766+
lua_pushstring(L, "loaded"); // stack: [tbl_package,"loaded"]
767+
if (lua_type(L, -1) == LUA_TNIL)
768+
InitPackageStorage(m_luaVM);
769+
770+
lua_rawget(L, -2); // stack: [tbl_package,tbl_loaded]
771+
lua_pushstring(L, strName.c_str()); // stack: [tbl_package,tbl_loaded,"moduleName"]
772+
lua_rawget(L, -2); // stack: [tbl_package,varPkg]
773+
lua_remove(L, -2); // stack: [varPkg]
774+
}
775+
776+
///////////////////////////////////////////////////////////////
777+
//
778+
// CLuaMain::LoadLuaLib
779+
//
780+
// Load a Lua lib of a given name
781+
//
782+
///////////////////////////////////////////////////////////////
783+
bool CLuaMain::LoadLuaLib(lua_State *L, SString strName)
784+
{
785+
SString strPath = strName;
786+
// Name format shouldn't include slashes. Subdirs are dots.
787+
ReplaceOccurrencesInString(strPath, "\\", "");
788+
ReplaceOccurrencesInString(strPath, "/", "");
789+
ReplaceOccurrencesInString(strPath, ".", "/");
790+
791+
SString strResPath = m_pResource->IsResourceZip() ? m_pResource->GetResourceCacheDirectoryPath() : m_pResource->GetResourceDirectoryPath();
792+
793+
std::vector < char > buffer;
794+
// Try <resource>/?.lua
795+
SString strFilePath = PathJoin(strResPath, strPath + ".lua");
796+
if (FileExists(strFilePath))
797+
FileLoad(strFilePath, buffer);
798+
else
799+
{
800+
// Try <resource>/?/init.lua
801+
strFilePath = PathJoin(strResPath, strPath, "init.lua");
802+
if (FileExists(strFilePath))
803+
FileLoad(strFilePath, buffer);
804+
}
805+
806+
if (buffer.size() > 0)
807+
{
808+
int luaSavedTop = lua_gettop(L);
809+
LoadScriptFromBuffer(&buffer.at(0), buffer.size(), strFilePath.c_str(), false);
810+
// Only keep the first return value
811+
if (lua_gettop(L) > luaSavedTop)
812+
lua_settop(L, luaSavedTop+1);
813+
814+
SetPackage(L, strName); // Store our package into package.loaded
815+
GetPackage(L, strName); // Grab it back as a return value.
816+
return true;
817+
}
818+
819+
return false;
820+
}
821+
822+
///////////////////////////////////////////////////////////////
823+
//
824+
// CLuaMain::LoadClib
825+
//
826+
// Load a C lib of a given name
827+
//
828+
///////////////////////////////////////////////////////////////
829+
bool CLuaMain::LoadClib(lua_State* L, SString strName)
830+
{
831+
SString strPath = strName;
832+
// Name format shouldn't include slashes. Subdirs are dots.
833+
ReplaceOccurrencesInString(strPath, "\\", "");
834+
ReplaceOccurrencesInString(strPath, "/", "");
835+
ReplaceOccurrencesInString(strPath, ".", "/");
836+
837+
strPath = PathJoin(g_pServerInterface->GetModManager()->GetServerPath(), SERVER_BIN_PATH_MOD, "modules", strPath);
838+
839+
#ifdef WIN32
840+
strPath += ".dll";
841+
#else
842+
strPath += ".so";
843+
#endif
844+
845+
luaL_loader_C(L, strName.c_str(), strPath.c_str());
846+
if (lua_type(L, -1) == LUA_TNIL)
847+
return false;
848+
849+
SetPackage(L, strName);
850+
return true;
851+
}

Server/mods/deathmatch/logic/lua/CLuaMain.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class CLuaMain //: public CClient
5959

6060
~CLuaMain ( void );
6161

62-
bool LoadScriptFromBuffer ( const char* cpBuffer, unsigned int uiSize, const char* szFileName );
62+
bool LoadScriptFromBuffer ( const char* cpBuffer, unsigned int uiSize, const char* szFileName, bool bClearReturnValues = true );
6363
bool LoadScript ( const char* szLUAScript );
6464
void UnloadScript ( void );
6565

@@ -120,6 +120,12 @@ class CLuaMain //: public CClient
120120
static int LuaLoadBuffer ( lua_State *L, const char *buff, size_t sz, const char *name );
121121
static int OnUndump ( const char* p, size_t n );
122122

123+
void InitPackageStorage ( lua_State* L ); // Create a psuedo package.loaded table
124+
void GetPackage ( lua_State *L, SString &strName ); // Push the package value to the top of the stack
125+
void SetPackage ( lua_State *L, SString &strName ); // Set the package to the value at the top of the stack
126+
bool LoadLuaLib ( lua_State *L, SString strName ); // Load a lua library of a given name
127+
bool LoadClib ( lua_State *L, SString strName ); // Load a C Lib of a given name
128+
123129
private:
124130
void InitSecurity ( void );
125131
void InitClasses ( lua_State* luaVM );

Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ void CLuaUtilDefs::LoadFunctions ( void )
3838
CLuaCFunctions::AddFunction ( "pregFind", PregFind );
3939
CLuaCFunctions::AddFunction ( "pregReplace", PregReplace );
4040
CLuaCFunctions::AddFunction ( "pregMatch", PregMatch );
41+
42+
// Package functions
43+
CLuaCFunctions::AddFunction ( "require", Require );
4144
}
4245

4346
int CLuaUtilDefs::DisabledFunction ( lua_State* luaVM )
@@ -559,3 +562,38 @@ int CLuaUtilDefs::PregMatch ( lua_State* luaVM )
559562
lua_pushboolean ( luaVM, false );
560563
return 1;
561564
}
565+
566+
int CLuaUtilDefs::Require(lua_State* luaVM)
567+
{
568+
#ifndef MTA_CLIENT
569+
// table require ( string name )
570+
SString strMod;
571+
572+
CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM);
573+
CScriptArgReader argStream(luaVM);
574+
argStream.ReadString(strMod);
575+
576+
if (!argStream.HasErrors())
577+
{
578+
// Check if package exists already, if so load it
579+
// stack: ["moduleName"]
580+
pLuaMain->GetPackage(luaVM, strMod); // stack: ["moduleName",pkgModule/nil]
581+
if (lua_type(luaVM, -1) != LUA_TNIL)
582+
return 1;
583+
lua_pop(luaVM,1); // stack: ["moduleName"]
584+
585+
// Check whether the appropriate pure lua module exists
586+
if (pLuaMain->LoadLuaLib(luaVM, strMod)) // stack: ["moduleName",pkgLuaMod/nil]
587+
return 1;
588+
589+
// Check if a C library exists
590+
if (pLuaMain->LoadClib(luaVM, strMod)) // stack: ["moduleName",fncModule/nil]
591+
return 1;
592+
lua_pop(luaVM,2); // stack: []
593+
}
594+
else
595+
m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage());
596+
#endif
597+
lua_pushboolean(luaVM, false);
598+
return 1;
599+
}

Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,7 @@ class CLuaUtilDefs : public CLuaDefs
4545
LUA_DECLARE ( PregFind );
4646
LUA_DECLARE ( PregReplace );
4747
LUA_DECLARE ( PregMatch );
48+
49+
// Package functions
50+
LUA_DECLARE ( Require );
4851
};

vendor/lua/src/lauxlib.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,8 @@ LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
172172
#endif
173173

174174

175+
/////////////////////////////
176+
// MTA EXTENSIONS
177+
/////////////////////////////
178+
LUALIB_API int luaL_loader_C(lua_State *L, const char* name, const char* filename);
179+

vendor/lua/src/loadlib.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,3 +664,15 @@ LUALIB_API int luaopen_package (lua_State *L) {
664664
return 1; /* return 'package' table */
665665
}
666666

667+
668+
/////////////////////////////
669+
// MTA EXTENSIONS
670+
/////////////////////////////
671+
672+
// API to direct load a C library.
673+
LUA_API int luaL_loader_C(lua_State *L, const char* name, const char* filename) {
674+
const char* funcname = mkfuncname(L, name);
675+
if (ll_loadfunc(L, filename, funcname) != 0)
676+
loaderror(L, filename);
677+
return 1; /* library loaded successfully */
678+
}

0 commit comments

Comments
 (0)