diff --git a/Client/mods/deathmatch/logic/lua/CLuaMain.cpp b/Client/mods/deathmatch/logic/lua/CLuaMain.cpp
index 4f97dc44ba4..03412bd808b 100644
--- a/Client/mods/deathmatch/logic/lua/CLuaMain.cpp
+++ b/Client/mods/deathmatch/logic/lua/CLuaMain.cpp
@@ -38,6 +38,8 @@ CLuaMain::CLuaMain(CLuaManager* pLuaManager, CResource* pResourceOwner, bool bEn
m_pResource = pResourceOwner;
+ m_iPackageLoadedRef = -1;
+
m_bEnableOOP = bEnableOOP;
CClientPerfStatLuaMemory::GetSingleton()->OnLuaMainCreate(this);
@@ -158,6 +160,9 @@ void CLuaMain::InitVM()
luaopen_utf8(m_luaVM);
luaopen_os(m_luaVM);
+ // Initialize packages system
+ InitPackageStorage(m_luaVM);
+
// Initialize security restrictions. Very important to prevent lua trojans and viruses!
InitSecurity();
@@ -212,7 +217,8 @@ void CLuaMain::InstructionCountHook(lua_State* luaVM, lua_Debug* pDebug)
}
}
-bool CLuaMain::LoadScriptFromBuffer(const char* cpInBuffer, unsigned int uiInSize, const char* szFileName)
+
+bool CLuaMain::LoadScriptFromBuffer(const char* cpInBuffer, unsigned int uiInSize, const char* szFileName, bool bClearReturnValues)
{
SString strNiceFilename = ConformResourcePath(szFileName);
@@ -276,7 +282,7 @@ bool CLuaMain::LoadScriptFromBuffer(const char* cpInBuffer, unsigned int uiInSiz
g_pClientGame->GetScriptDebugging()->LogPCallError(m_luaVM, strRes, true);
}
// Cleanup any return values
- if (lua_gettop(m_luaVM) > luaSavedTop)
+ if (bClearReturnValues && lua_gettop(m_luaVM) > luaSavedTop)
lua_settop(m_luaVM, luaSavedTop);
return true;
}
diff --git a/Client/mods/deathmatch/logic/lua/CLuaMain.h b/Client/mods/deathmatch/logic/lua/CLuaMain.h
index 778564ddbeb..09d3b887437 100644
--- a/Client/mods/deathmatch/logic/lua/CLuaMain.h
+++ b/Client/mods/deathmatch/logic/lua/CLuaMain.h
@@ -40,7 +40,7 @@ class CLuaMain //: public CClient
CLuaMain(class CLuaManager* pLuaManager, CResource* pResourceOwner, bool bEnableOOP);
~CLuaMain();
- bool LoadScriptFromBuffer(const char* cpBuffer, unsigned int uiSize, const char* szFileName);
+ bool LoadScriptFromBuffer(const char* cpBuffer, unsigned int uiSize, const char* szFileName, bool bClearReturnValues = true);
bool LoadScript(const char* szLUAScript);
void UnloadScript();
@@ -79,6 +79,11 @@ class CLuaMain //: public CClient
bool IsOOPEnabled() { return m_bEnableOOP; }
+ void InitPackageStorage(lua_State* L); // Create a psuedo package.loaded table
+ void GetPackage(lua_State* L, SString& strName); // Push the package value to the top of the stack
+ void SetPackage(lua_State* L, SString& strName); // Set the package to the value at the top of the stack
+ bool LoadLuaLib(lua_State* L, SString strName, SString& strError, bool& bEmpty); // Load a lua library of a given name
+
private:
void InitSecurity();
@@ -88,6 +93,7 @@ class CLuaMain //: public CClient
lua_State* m_luaVM;
CLuaTimerManager* m_pLuaTimerManager;
+ int m_iPackageLoadedRef;
bool m_bBeingDeleted; // prevent it being deleted twice
diff --git a/Server/mods/deathmatch/logic/CAccessControlListManager.cpp b/Server/mods/deathmatch/logic/CAccessControlListManager.cpp
index c5b89d43bb8..07ebaeca690 100644
--- a/Server/mods/deathmatch/logic/CAccessControlListManager.cpp
+++ b/Server/mods/deathmatch/logic/CAccessControlListManager.cpp
@@ -141,6 +141,10 @@ bool CAccessControlListManager::Load()
{
pRight = pACL->AddRight(&szRightName[8], CAccessControlListRight::RIGHT_TYPE_GENERAL, bAccess);
}
+ else if (StringBeginsWith(szRightName, "module."))
+ {
+ pRight = pACL->AddRight(&szRightName[7], CAccessControlListRight::RIGHT_TYPE_MODULE, bAccess);
+ }
else
continue;
@@ -536,6 +540,11 @@ const char* CAccessControlListManager::ExtractRightName(const char* szRightName,
eType = CAccessControlListRight::RIGHT_TYPE_GENERAL;
return szRightName + 8;
}
+ else if ( StringBeginsWith( szRightName, "module." ) )
+ {
+ eType = CAccessControlListRight::RIGHT_TYPE_MODULE;
+ return szRightName + 7;
+ }
// Failed
return NULL;
diff --git a/Server/mods/deathmatch/logic/CAccessControlListRight.h b/Server/mods/deathmatch/logic/CAccessControlListRight.h
index 77754eef201..d74186a1046 100644
--- a/Server/mods/deathmatch/logic/CAccessControlListRight.h
+++ b/Server/mods/deathmatch/logic/CAccessControlListRight.h
@@ -23,7 +23,8 @@ class CAccessControlListRight
RIGHT_TYPE_COMMAND,
RIGHT_TYPE_FUNCTION,
RIGHT_TYPE_RESOURCE,
- RIGHT_TYPE_GENERAL
+ RIGHT_TYPE_GENERAL,
+ RIGHT_TYPE_MODULE
};
public:
diff --git a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp
index 5080411940a..e14a24ba62a 100644
--- a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp
+++ b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp
@@ -239,6 +239,7 @@ ADD_ENUM(CAccessControlListRight::RIGHT_TYPE_COMMAND, "command")
ADD_ENUM(CAccessControlListRight::RIGHT_TYPE_FUNCTION, "function")
ADD_ENUM(CAccessControlListRight::RIGHT_TYPE_RESOURCE, "resource")
ADD_ENUM(CAccessControlListRight::RIGHT_TYPE_GENERAL, "general")
+ADD_ENUM(CAccessControlListRight::RIGHT_TYPE_MODULE, "module")
IMPLEMENT_ENUM_END("right-type")
IMPLEMENT_ENUM_BEGIN(CElement::EElementType)
diff --git a/Server/mods/deathmatch/logic/lua/CLuaMain.cpp b/Server/mods/deathmatch/logic/lua/CLuaMain.cpp
index fa83d6b46d8..68c4cbddcf9 100644
--- a/Server/mods/deathmatch/logic/lua/CLuaMain.cpp
+++ b/Server/mods/deathmatch/logic/lua/CLuaMain.cpp
@@ -123,7 +123,6 @@ void CLuaMain::InitSecurity()
lua_register(m_luaVM, "dofile", CLuaUtilDefs::DisabledFunction);
lua_register(m_luaVM, "loadfile", CLuaUtilDefs::DisabledFunction);
- lua_register(m_luaVM, "require", CLuaUtilDefs::DisabledFunction);
lua_register(m_luaVM, "loadlib", CLuaUtilDefs::DisabledFunction);
lua_register(m_luaVM, "getfenv", CLuaUtilDefs::DisabledFunction);
lua_register(m_luaVM, "newproxy", CLuaUtilDefs::DisabledFunction);
@@ -187,6 +186,9 @@ void CLuaMain::InitVM()
luaopen_utf8(m_luaVM);
luaopen_os(m_luaVM);
+ // Initialize packages system
+ InitPackageStorage(m_luaVM);
+
// Initialize security restrictions. Very important to prevent lua trojans and viruses!
InitSecurity();
@@ -241,7 +243,7 @@ void CLuaMain::InstructionCountHook(lua_State* luaVM, lua_Debug* pDebug)
}
}
-bool CLuaMain::LoadScriptFromBuffer(const char* cpInBuffer, unsigned int uiInSize, const char* szFileName)
+bool CLuaMain::LoadScriptFromBuffer(const char* cpInBuffer, unsigned int uiInSize, const char* szFileName, bool bClearReturnValues)
{
SString strNiceFilename = ConformResourcePath(szFileName);
@@ -311,7 +313,7 @@ bool CLuaMain::LoadScriptFromBuffer(const char* cpInBuffer, unsigned int uiInSiz
g_pGame->GetScriptDebugging()->LogPCallError(m_luaVM, strRes, true);
}
// Cleanup any return values
- if (lua_gettop(m_luaVM) > luaSavedTop)
+ if (bClearReturnValues && lua_gettop(m_luaVM) > luaSavedTop)
lua_settop(m_luaVM, luaSavedTop);
return true;
}
@@ -661,3 +663,53 @@ int CLuaMain::OnUndump(const char* p, size_t n)
}
return 1;
}
+
+///////////////////////////////////////////////////////////////
+//
+// CLuaMain::LoadClib
+//
+// Load a C lib of a given name
+//
+///////////////////////////////////////////////////////////////
+bool CLuaMain::LoadClib(lua_State* L, SString strName, SString& strError)
+{
+ SString strPath = strName;
+ // Name format shouldn't include slashes. Subdirs are dots.
+ ReplaceOccurrencesInString(strPath, "\\", "");
+ ReplaceOccurrencesInString(strPath, "/", "");
+ ReplaceOccurrencesInString(strPath, ".", "/");
+
+ strPath = PathJoin(g_pServerInterface->GetModManager()->GetServerPath(), SERVER_BIN_PATH_MOD, "modules", strPath);
+
+#ifdef WIN32
+ strPath += ".dll";
+#else
+ strPath += ".so";
+#endif
+
+ if (!FileExists(strPath))
+ {
+ strError += "#3: " + ConformPath(strPath, "modules/");
+ return false;
+ }
+
+ // Check ACL permissions
+ if (!g_pGame->GetACLManager()->CanObjectUseRight(m_pResource->GetName().c_str(), CAccessControlListGroupObject::OBJECT_TYPE_RESOURCE, "*",
+ CAccessControlListRight::RIGHT_TYPE_MODULE, false) &&
+ !g_pGame->GetACLManager()->CanObjectUseRight(m_pResource->GetName().c_str(), CAccessControlListGroupObject::OBJECT_TYPE_RESOURCE, strName.c_str(),
+ CAccessControlListRight::RIGHT_TYPE_MODULE, false))
+ {
+ strError = "could not load module '" + strName + "' from file " + ConformPath(strPath, "modules/") + ":\n\t ACL access denied. Grant \"module." +
+ strName + "\" right to resource " + m_pResource->GetName();
+ return false;
+ }
+
+ if (!luaL_loader_C(L, strName.c_str(), strPath.c_str()) || lua_type(L, -1) == LUA_TNIL)
+ {
+ strError = "failed to load module '" + strName + "' from file " + ConformPath(strPath, "modules/");
+ return false;
+ }
+
+ SetPackage(L, strName);
+ return true;
+}
diff --git a/Server/mods/deathmatch/logic/lua/CLuaMain.h b/Server/mods/deathmatch/logic/lua/CLuaMain.h
index d8b4dcc3d72..79666ad840a 100644
--- a/Server/mods/deathmatch/logic/lua/CLuaMain.h
+++ b/Server/mods/deathmatch/logic/lua/CLuaMain.h
@@ -46,7 +46,7 @@ class CLuaMain //: public CClient
~CLuaMain();
- bool LoadScriptFromBuffer(const char* cpBuffer, unsigned int uiSize, const char* szFileName);
+ bool LoadScriptFromBuffer(const char* cpBuffer, unsigned int uiSize, const char* szFileName, bool bClearReturnValues = true);
bool LoadScript(const char* szLUAScript);
void UnloadScript();
@@ -109,6 +109,12 @@ class CLuaMain //: public CClient
static int LuaLoadBuffer(lua_State* L, const char* buff, size_t sz, const char* name);
static int OnUndump(const char* p, size_t n);
+ void InitPackageStorage(lua_State* L); // Create a psuedo package.loaded table
+ void GetPackage(lua_State* L, SString& strName); // Push the package value to the top of the stack
+ void SetPackage(lua_State* L, SString& strName); // Set the package to the value at the top of the stack
+ bool LoadLuaLib(lua_State* L, SString strName, SString& strError, bool& bEmpty); // Load a lua library of a given name
+ bool LoadClib(lua_State* L, SString strName, SString& strError); // Load a C Lib of a given name
+
private:
void InitSecurity();
void InitClasses(lua_State* luaVM);
@@ -123,6 +129,7 @@ class CLuaMain //: public CClient
lua_State* m_luaVM;
CLuaTimerManager* m_pLuaTimerManager;
+ int m_iPackageLoadedRef;
class CResource* m_pResource;
class CResourceFile* m_pResourceFile;
diff --git a/Server/mods/deathmatch/mtaserver.conf b/Server/mods/deathmatch/mtaserver.conf
index 2d8cc2ad260..abf0831f5c4 100644
--- a/Server/mods/deathmatch/mtaserver.conf
+++ b/Server/mods/deathmatch/mtaserver.conf
@@ -1,294 +1,294 @@
-
-
-
- Default MTA Server
-
-
-
-
-
-
- auto
-
-
-
- 22003
-
-
- 32
-
-
- 22005
-
-
-
-
-
- 5
-
-
- 20
-
-
-
-
-
- none
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
-
-
- 1
-
-
- 0
-
-
-
-
-
- medium
-
-
-
- 100
-
- 1500
-
- 500
-
- 400
-
- 400
-
- 100
-
- 100
-
-
- 1
-
-
- 0
-
-
- 150
-
-
- 0
-
-
- server-id.keys
-
-
- logs/server.log
-
-
- logs/server_auth.log
-
-
- logs/db.log
-
-
-
-
-
- acl.xml
-
-
- logs/scripts.log
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 36
-
-
- 0
-
-
- 1
-
-
- 4
-
-
-
-
-
- backups
-
-
- 3
-
-
- 5
-
-
- 1
-
-
- 1
-
-
- Admin
-
-
- 1
-
-
- 127.0.0.1
-
-
- 1
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Default MTA Server
+
+
+
+
+
+
+ auto
+
+
+
+ 22003
+
+
+ 32
+
+
+ 22005
+
+
+
+
+
+ 5
+
+
+ 20
+
+
+
+
+
+ none
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+ 1
+
+
+ 0
+
+
+
+
+
+ medium
+
+
+
+ 100
+
+ 1500
+
+ 500
+
+ 400
+
+ 400
+
+ 100
+
+ 100
+
+
+ 1
+
+
+ 0
+
+
+ 150
+
+
+ 0
+
+
+ server-id.keys
+
+
+ logs/server.log
+
+
+ logs/server_auth.log
+
+
+ logs/db.log
+
+
+
+
+
+ acl.xml
+
+
+ logs/scripts.log
+
+
+ 0
+
+
+ 0
+
+
+ 1
+
+
+ 36
+
+
+ 0
+
+
+ 1
+
+
+ 4
+
+
+
+
+
+ backups
+
+
+ 3
+
+
+ 5
+
+
+ 1
+
+
+ 1
+
+
+ Admin
+
+
+ 1
+
+
+ 127.0.0.1
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Shared/mods/deathmatch/logic/lua/CLuaMain.Shared.cpp b/Shared/mods/deathmatch/logic/lua/CLuaMain.Shared.cpp
new file mode 100644
index 00000000000..455d2a624ec
--- /dev/null
+++ b/Shared/mods/deathmatch/logic/lua/CLuaMain.Shared.cpp
@@ -0,0 +1,155 @@
+/*****************************************************************************
+ *
+ * PROJECT: Multi Theft Auto
+ * LICENSE: See LICENSE in the top level directory
+ * FILE: Shared/mods/logic/lua/CLuaMain.Shared.cpp
+ *
+ *****************************************************************************/
+
+#include "StdInc.h"
+
+///////////////////////////////////////////////////////////////
+//
+// CLuaMain::InitPackageStorage
+//
+// Create a table for storage of Lua packages - stored in the Lua VM
+//
+///////////////////////////////////////////////////////////////
+void CLuaMain::InitPackageStorage(lua_State* L)
+{
+ lua_newtable(L); // stack: [tbl_new]
+ lua_pushstring(L, "loaded"); // stack: [tbl_new,"loaded"]
+ lua_newtable(L); // stack: [tbl_new,"loaded",tbl_new2]
+
+ // We push our default Lua libs are loaded packages
+ std::vector szDefaultLibs = {"math", "string", "table", "debug", "utf8"};
+ for (auto it : szDefaultLibs)
+ {
+ lua_pushstring(L, it); // stack: [tbl_new,"loaded",tbl_new2,"math"]
+ lua_getglobal(L, it); // stack: [tbl_new,"loaded",tbl_new2,"math",_G.math]
+ lua_settable(L, -3); // stack: [tbl_new,"loaded",tbl_new2]
+ }
+
+ // Keep our package.loaded table safely in registry
+ m_iPackageLoadedRef = luaL_ref(L, LUA_REGISTRYINDEX); // stack: [tbl_new,"loaded"]
+ lua_rawgeti(L, LUA_REGISTRYINDEX, m_iPackageLoadedRef); // stack: [tbl_new,"loaded",tbl_new2]
+
+ // Finally, store our original table as global package.loaded
+ lua_settable(L, -3); // stack: [tbl_new]
+ lua_setglobal(L, "package"); // stack: []
+}
+
+///////////////////////////////////////////////////////////////
+//
+// CLuaMain::SetPackage
+//
+// Set the top most value as a package (No pop)
+//
+///////////////////////////////////////////////////////////////
+void CLuaMain::SetPackage(lua_State* L, SString& strName)
+{
+ if (m_iPackageLoadedRef < 0)
+ return;
+ // We set varPkg, which is already on the stack, into package.loaded.moduleName = varPkg.
+ // stack: [varPkg]
+ int iPkg = luaL_ref(L, LUA_REGISTRYINDEX); // stack: []
+ lua_rawgeti(L, LUA_REGISTRYINDEX, m_iPackageLoadedRef); // [tbl_loaded]
+
+ lua_pushstring(L, strName.c_str()); // stack: [tbl_loaded,"moduleName"]
+ lua_rawgeti(L, LUA_REGISTRYINDEX, iPkg); // stack: [tbl_loaded,"moduleName",varPkg]
+ lua_rawset(L, -3); // stack: [tbl_loaded]
+ lua_pop(L, 2); // stack: []
+ lua_rawgeti(L, LUA_REGISTRYINDEX, iPkg); // stack: [varPkg]
+
+ // Cleanup our used registry entry, i.e. REGISTRY[i] = nil.
+ lua_pushnil(L); // stack: [varPkg,nil]
+ lua_rawseti(L, LUA_REGISTRYINDEX, iPkg); // stack: [varPkg]
+}
+
+///////////////////////////////////////////////////////////////
+//
+// CLuaMain::GetPackage
+//
+// Push the value of a package of name to the stack
+//
+///////////////////////////////////////////////////////////////
+void CLuaMain::GetPackage(lua_State* L, SString& strName)
+{
+ if (m_iPackageLoadedRef < 0)
+ return;
+
+ lua_rawgeti(L, LUA_REGISTRYINDEX, m_iPackageLoadedRef); // stack: [tbl_loaded]
+ lua_pushstring(L, strName.c_str()); // stack: [tbl_loaded,"moduleName"]
+ lua_rawget(L, -2); // stack: [tbl_loaded,varPkg]
+ lua_remove(L, -2); // stack: [varPkg]
+}
+
+///////////////////////////////////////////////////////////////
+//
+// CLuaMain::LoadLuaLib
+//
+// Load a Lua lib of a given name
+//
+///////////////////////////////////////////////////////////////
+bool CLuaMain::LoadLuaLib(lua_State* L, SString strName, SString& strError, bool& bEmpty)
+{
+ SString strPath = strName;
+ // Name format shouldn't include slashes. Subdirs are dots.
+ ReplaceOccurrencesInString(strPath, "\\", "");
+ ReplaceOccurrencesInString(strPath, "/", "");
+ ReplaceOccurrencesInString(strPath, ".", "/");
+
+#ifdef MTA_CLIENT
+ SString strResPath = m_pResource->GetResourceDirectoryPath(ACCESS_PUBLIC, "");
+#else
+ SString strResPath = m_pResource->IsResourceZip() ? m_pResource->GetResourceCacheDirectoryPath() : m_pResource->GetResourceDirectoryPath();
+#endif
+
+ std::vector buffer;
+ strError = "error loading module '" + strName + "' from locations:\n\t";
+ // Try /?.lua
+ SString strFilePath = PathJoin(strResPath, strPath + ".lua");
+ if (FileExists(strFilePath))
+ FileLoad(strFilePath, buffer);
+ else
+ {
+ // Don't use a format string for safety, so we construct the error by hand
+ strError += "#1: " + ConformPath(strFilePath, "resources/") + "\n\t";
+
+ // Try /?/init.lua
+ strFilePath = PathJoin(strResPath, strPath, "init.lua");
+ if (FileExists(strFilePath))
+ FileLoad(strFilePath, buffer);
+ else
+ {
+ strError += "#2: " + ConformPath(strFilePath, "resources/") + "\n\t";
+ return false;
+ }
+ }
+
+ if (buffer.size() > 0)
+ {
+ int luaSavedTop = lua_gettop(L);
+ LoadScriptFromBuffer(&buffer.at(0), buffer.size(), strFilePath.c_str(), false);
+ // Only keep the first return value
+ if (lua_gettop(L) > luaSavedTop)
+ lua_settop(L, luaSavedTop + 1);
+
+ if (lua_type(L, -1) == LUA_TNIL)
+ {
+ strError = "module '" + strName + "' in location " + ConformPath(strFilePath, "resources/") + " didn't return a value.";
+ bEmpty = true;
+ return false;
+ }
+
+ SetPackage(L, strName); // Store our package into package.loaded
+ return true;
+ }
+ else
+ {
+ strError = "could not load module '" + strName + "' from file " + ConformPath(strFilePath, "resources/") + ": File is empty.";
+ bEmpty = true;
+ }
+
+ return false;
+}
diff --git a/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.cpp b/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.cpp
index bc1b90618f9..b63da616bcc 100644
--- a/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.cpp
+++ b/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.cpp
@@ -46,6 +46,9 @@ void CLuaUtilDefs::LoadFunctions()
// Utility functions
{"gettok", GetTok},
{"tocolor", tocolor},
+
+ // Package functions
+ {"require", Require},
};
// Add functions
@@ -711,3 +714,47 @@ int CLuaUtilDefs::tocolor(lua_State* luaVM)
lua_pushnumber(luaVM, static_cast(ulColor));
return 1;
}
+
+int CLuaUtilDefs::Require(lua_State* luaVM)
+{
+ // table require ( string name )
+ SString strMod;
+
+ CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM);
+ CScriptArgReader argStream(luaVM);
+ argStream.ReadString(strMod);
+
+ if (!argStream.HasErrors())
+ {
+ SString strError = "";
+ bool bEmpty = false;
+ // Check if package exists already, if so load it
+ // stack: ["moduleName"]
+ pLuaMain->GetPackage(luaVM, strMod); // stack: ["moduleName",pkgModule/nil]
+ if (lua_type(luaVM, -1) != LUA_TNIL)
+ return 1;
+ lua_pop(luaVM, 1); // stack: ["moduleName"]
+
+ // Check whether the appropriate pure lua module exists
+ if (pLuaMain->LoadLuaLib(luaVM, strMod, strError, bEmpty)) // stack: ["moduleName",pkgLuaMod/nil]
+ return 1;
+
+ if (bEmpty)
+ {
+ m_pScriptDebugging->LogCustom(luaVM, strError);
+ lua_pushboolean(luaVM, false);
+ return 1;
+ }
+#ifndef MTA_CLIENT
+ // Check if a C library exists
+ if (pLuaMain->LoadClib(luaVM, strMod, strError)) // stack: ["moduleName",fncModule/nil]
+ return 1;
+#endif
+ m_pScriptDebugging->LogCustom(luaVM, strError);
+ }
+ else
+ m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage());
+
+ lua_pushboolean(luaVM, false);
+ return 1;
+}
diff --git a/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.h b/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.h
index 849011a94d4..a9c785394a4 100644
--- a/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.h
+++ b/Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.h
@@ -52,4 +52,7 @@ class CLuaUtilDefs : public CLuaDefs
// Utility functions
LUA_DECLARE(GetTok);
LUA_DECLARE(tocolor);
+
+ // Package functions
+ LUA_DECLARE(Require);
};
diff --git a/Shared/sdk/SharedUtil.Misc.h b/Shared/sdk/SharedUtil.Misc.h
index 6ec7c42bdc0..f1d1df55953 100644
--- a/Shared/sdk/SharedUtil.Misc.h
+++ b/Shared/sdk/SharedUtil.Misc.h
@@ -219,6 +219,8 @@ namespace SharedUtil
//
SString ConformResourcePath(const char* szRes, bool bConvertToUnixPathSep = false);
+ SString ConformPath(const char* szRes, SString szPath, bool bConvertToUnixPathSep = false);
+
//
// string stuff
//
diff --git a/Shared/sdk/SharedUtil.Misc.hpp b/Shared/sdk/SharedUtil.Misc.hpp
index 6b67f65cfab..cd11a4f7b27 100644
--- a/Shared/sdk/SharedUtil.Misc.hpp
+++ b/Shared/sdk/SharedUtil.Misc.hpp
@@ -1607,6 +1607,43 @@ SString SharedUtil::ConformResourcePath(const char* szRes, bool bConvertToUnixPa
return strText;
}
+SString SharedUtil::ConformPath(const char* szRes, SString szPath, bool bConvertToUnixPathSep)
+{
+ SString strText = szRes ? szRes : "";
+ char cPathSep;
+
+ // Handle which path sep char
+#ifdef WIN32
+ if (!bConvertToUnixPathSep)
+ {
+ cPathSep = '\\';
+ szPath = szPath.Replace("/", "\\");
+ strText = strText.Replace("/", "\\");
+ }
+ else
+#endif
+ {
+ cPathSep = '/';
+ szPath = szPath.Replace("\\", "/");
+ strText = strText.Replace("\\", "/");
+ }
+
+ // Remove up to first occurrence
+ int iPos = strText.find(szPath);
+ if (iPos >= 0)
+ return SString("%s%s", szPath.c_str(), strText.substr(iPos + szPath.length()).c_str());
+
+ if (strText.substr(0, 3) == "...")
+ {
+ // Remove up to first '/'
+ int iPos = strText.find(cPathSep);
+ if (iPos >= 0)
+ return SString("%s%s", szPath.c_str(), strText.substr(iPos + 1).c_str());
+ }
+
+ return strText;
+}
+
namespace SharedUtil
{
CArgMap::CArgMap(const SString& strArgSep, const SString& strPartsSep, const SString& strExtraDisallowedChars)
diff --git a/vendor/lua/src/lauxlib.h b/vendor/lua/src/lauxlib.h
index 34258235dbe..fad2c246835 100644
--- a/vendor/lua/src/lauxlib.h
+++ b/vendor/lua/src/lauxlib.h
@@ -172,3 +172,8 @@ LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
#endif
+/////////////////////////////
+// MTA EXTENSIONS
+/////////////////////////////
+LUALIB_API int luaL_loader_C(lua_State *L, const char* name, const char* filename);
+
diff --git a/vendor/lua/src/loadlib.c b/vendor/lua/src/loadlib.c
index 6158c5353df..7b4fd6880e6 100644
--- a/vendor/lua/src/loadlib.c
+++ b/vendor/lua/src/loadlib.c
@@ -664,3 +664,16 @@ LUALIB_API int luaopen_package (lua_State *L) {
return 1; /* return 'package' table */
}
+
+/////////////////////////////
+// MTA EXTENSIONS
+/////////////////////////////
+
+// API to direct load a C library.
+LUA_API int luaL_loader_C(lua_State* L, const char* name, const char* filename)
+{
+ const char* funcname = mkfuncname(L, name);
+ if (ll_loadfunc(L, filename, funcname) != 0)
+ return 0;
+ return 1; /* library loaded successfully */
+}
\ No newline at end of file