Skip to content

Commit

Permalink
Some fixes to require and loadlib
Browse files Browse the repository at this point in the history
  • Loading branch information
Dregu committed Sep 11, 2024
1 parent 471fb65 commit 03e04fc
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 7 deletions.
107 changes: 100 additions & 7 deletions src/game_api/script/lua_require.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,30 @@ void register_custom_require(sol::state& lua)
lua.add_package_loader(custom_loader);

lua["__require"] = lua["require"];
lua["__loadlib"] = lua["package"]["loadlib"];

/// Custom implementation to trick Lua into allowing to `require 'lib.module'` more than once given it was called from a different source
lua["require"] = custom_require;

lua["package"]["loadlib"] = custom_loadlib;
}
sol::object custom_require(std::string path)
{
static sol::state& lua = get_lua_vm();

if (path == "io" || path == "os" || path == "math" || path == "string" || path == "table" || path == "coroutine" || path == "package")
return lua[path];

// Turn module into a real path
{
if (path.ends_with(".lua"))
if (path.ends_with(".lua") || path.ends_with(".dll"))
{
path = path.substr(0, path.size() - 4);
}
std::replace(path.begin(), path.end(), '.', '/');
std::replace(path.begin(), path.end(), ':', '.');
}

static sol::state& lua = get_lua_vm();

// Could be preloaded by some unsafe script, which can only be fetched by unsafe scripts
auto backend = LuaBackend::get_calling_backend();
const bool unsafe = backend->get_unsafe();
Expand Down Expand Up @@ -174,25 +180,112 @@ return info.short_src, info.source

return std::move(res).value_or(sol::nil);
}
sol::object custom_loadlib(std::string path, std::string func)
{
static sol::state& lua = get_lua_vm();
auto backend = LuaBackend::get_calling_backend();
const bool unsafe = backend->get_unsafe();

// Walk up the stack until we find an _ENV that is not global, then grab the source from that stack index
auto [short_source, source] = []() -> std::pair<std::string_view, std::string_view>
{
return lua.safe_script(R"(
-- Not available in Lua 5.2+
local getfenv = getfenv or function(f)
f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
local name, val
local up = 0
repeat
up = up + 1
name, val = debug.getupvalue(f, up)
until name == '_ENV' or name == nil
return val
end
local env
local up = 1
repeat
up = up + 1
env = getfenv(up)
until env ~= _G and env ~= nil
local info = debug.getinfo(up)
return info.short_src, info.source
)");
}();

if (short_source.starts_with("[string"))
{
source = backend->get_root();
}
else
{
auto last_slash = source.find_last_of("/\\");
if (last_slash != std::string::npos)
{
source = source.substr(0, last_slash);
}
if (source.starts_with('@'))
{
source = source.substr(1);
}
}

namespace fs = std::filesystem;

auto loadlib_if_exists = [&](fs::path _path, std::string _func) -> std::optional<sol::object>
{
if (unsafe && std::filesystem::exists(_path))
return lua["__loadlib"](_path.string(), _func);
return std::nullopt;
};

/// Valid unsafe options:
// path/to/calling/script/path/to/lib.dll
// path/to/calling/mod/path/to/lib.dll
// path/to/lib.dll

auto res = loadlib_if_exists(std::filesystem::path(source) / path, func);

if (!res && source != backend->get_root())
{
res = loadlib_if_exists(std::filesystem::path(backend->get_root()) / path, func);
}
if (!res)
{
res = loadlib_if_exists(std::filesystem::path(path), func);
}

return std::move(res).value_or(sol::nil);
}
int custom_loader(lua_State* L)
{
std::string path = sol::stack::get<std::string>(L, 1);
std::replace(path.begin(), path.end(), '.', '/');
std::replace(path.begin(), path.end(), ':', '.');
auto backend = LuaBackend::get_calling_backend();

auto try_load = [&](std::string& _path, std::string_view ext)
auto try_load = [&](std::string _path, std::string_view ext)
{
if (ext == ".dll")
{
auto root = std::string(backend->get_root());
auto module = _path.substr(root.size() + 1);
auto func = "luaopen_" + module;
std::replace(func.begin(), func.end(), '/', '_');
std::replace(func.begin(), func.end(), '.', '_');
sol::stack::push(L, backend->lua["__loadlib"](_path, func));
return true;
}
_path += ext;
const auto res = luaL_loadfilex(L, _path.c_str(), "bt");
const auto res = luaL_loadfile(L, _path.c_str());
if (res == LUA_OK)
{
backend->lua.push();
// The first up-value is always the _ENV of the chunk
// The first up-value is always the _ENV of the chunk
lua_setupvalue(L, -2, 1);
return true;
}
_path = _path.substr(0, _path.size() - 1);
return false;
};

Expand Down
1 change: 1 addition & 0 deletions src/game_api/script/lua_require.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ class state;
// This implementation makes the loaded chunk inherit the env from the loading chunk
void register_custom_require(sol::state& lua);
sol::object custom_require(std::string path);
sol::object custom_loadlib(std::string path, std::string func);
int custom_loader(struct lua_State* L);

0 comments on commit 03e04fc

Please sign in to comment.