Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lua: properly implement require from pack #87

Merged
merged 4 commits into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion doc/PACKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,11 @@ The following interfaces are provided:

### global ScriptHost

* `bool :LoadScript(luafilename)`: load and execute a lua script
* `bool :LoadScript(luafilename)`: load and execute a lua script from absolute filename inside pack
* `require` can be used instead (since PopTracker 0.21.0)
* `require` behaves mostly like Lua require since 0.25.6
* `"foo.baz"` will try `/scripts/foo/baz.lua`, `/scripts/foo/baz/init.lua`, `/foo/baz.lua` and `/foo/baz/init.lua`
* `...` contains mod name for relative require since 0.25.6
* `bool :AddMemoryWatch(name,addr,len,callback,interal)`: add a memory watch for auto-tracking, see [AUTOTRACKING.md](AUTOTRACKING.md)
* `bool :RemoveMemoryWatch(name)`: remove memory watch by name, available since 0.11.0
* `bool :AddWatchForCode(name,code,callback)`: callback(code) will be called whenever an item changed state that canProvide(code). Only available in PopTracker, since 0.11.0, will return a reference (name) to the watch since 0.18.2. Use "*" to trigger for all codes since 0.25.5.
Expand Down Expand Up @@ -135,6 +138,7 @@ a table representing an enum with the following constants: \
### other globals

* `DEBUG` set to true to get more error or debug output
* `require` function, see [ScriptHost:LoadScript](#global-scripthost)


### type LuaItem
Expand Down
13 changes: 10 additions & 3 deletions src/core/scripthost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ bool ScriptHost::LoadScript(const std::string& file)
fprintf(stderr, "File not found!\n");
return false;
}

const char* buf = script.c_str();
size_t len = script.length();
if (len>=3 && memcmp(buf, "\xEF\xBB\xBF", 3) == 0) {
Expand All @@ -168,7 +168,14 @@ bool ScriptHost::LoadScript(const std::string& file)
len -= 3;
}
if (luaL_loadbufferx(_L, buf, len, file.c_str(), "t") == LUA_OK) {
if (lua_pcall(_L, 0, 1, 0) == LUA_OK) {
std::string modname = file;
if (strncasecmp(modname.c_str(), "scripts/", 8) == 0)
modname = modname.substr(8);
if (modname.length() > 4 && strcasecmp(modname.c_str() + modname.length() - 4, ".lua") == 0)
modname = modname.substr(0, modname.length() - 4);
std::replace(modname.begin(), modname.end(), '/', '.');
lua_pushstring(_L, modname.c_str());
if (lua_pcall(_L, 1, 1, 0) == LUA_OK) {
// if it was executed successfully, pop everything from stack
lua_pop(_L, lua_gettop(_L)); // TODO: lua_settop(L, 0); ?
} else {
Expand All @@ -183,7 +190,7 @@ bool ScriptHost::LoadScript(const std::string& file)
lua_pop(_L, 1); // TODO: verify this is correct
return false;
}

return true;
}

Expand Down
39 changes: 22 additions & 17 deletions src/luasandbox/require.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
static inline int luasandbox_require(lua_State *L)
{
// get filename being required
const char* file = luaL_checkstring(L, -1);
const char* name = luaL_checkstring(L, -1);

// check cache
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
int t = lua_getfield(L, -1, file);
int t = lua_getfield(L, -1, name);
if (t != LUA_TNONE && t != LUA_TNIL) {
lua_remove(L, -2); // remove _LOADED from stack
return 1;
Expand All @@ -34,42 +34,47 @@ static inline int luasandbox_require(lua_State *L)
return 0;
}

std::string filename = name;
std::replace(filename.begin(), filename.end(), '.', '/');

// load file to string
std::string* pscript = new std::string();
std::string pscript;
// TODO: add support for package.path
bool ok = false;
for (const std::string prefix : {"scripts/", ""}) {
for (const std::string& folder : {std::string(""), std::string(file)+"/"}) {
for (const std::string suffix : {"", ".lua"}) {
ok = pack->ReadFile(prefix + folder + file + suffix, *pscript);
if (ok) break;
for (const std::string& part : {filename, filename+"/init"}) {
for (const std::string suffix : {".LUA", ".lua"}) {
ok = pack->ReadFile(prefix + part + suffix, pscript);
if (ok) {
filename = prefix + part + suffix;
break;
}
}
if (ok) break;
if (ok)
break;
}
if (ok) break;
if (ok)
break;
}
if (!ok) {
delete pscript;
lua_pop(L, 2); // pop Tracker and _LOADED
luaL_error(L, "No such module: %s", file);
luaL_error(L, "No such module: %s", name);
return 0;
}
lua_pop(L, 1); // pop Tracker

if (luaL_loadbufferx(L, pscript->c_str(), pscript->length(), file, "t") == LUA_OK) {
if (lua_pcall(L, 0, 1, 0) == LUA_OK) {
delete pscript;
if (luaL_loadbufferx(L, pscript.c_str(), pscript.length(), filename.c_str(), "t") == LUA_OK) {
lua_pushstring(L, name);
if (lua_pcall(L, 1, 1, 0) == LUA_OK) {
lua_pushvalue(L, -1); // duplicate result
lua_setfield(L, -3, file);
lua_setfield(L, -3, name);
lua_remove(L, -2); // remove _LOADED from stack
return 1;
} else {
delete pscript;
lua_remove(L, -2); // remove _LOADED from stack
lua_error(L);
}
} else {
delete pscript;
lua_remove(L, -2); // remove _LOADED from stack
lua_error(L);
}
Expand Down