1

This is for a multiplayer game where NPC scripts will be loaded and cached as function chunks on a single lua_State, and then each time a player interacts with an NPC, a new lua_thread is created, a cached function is fetched from a global lua_table and pushed onto the thread's stack, and then the thread is resumed (the npc script yields several times before finishing).

I am trying to change the environment of the lua thread using the C API, so that the function that has been loaded/run will not be able to change global variables / have them changed by other functions running on a different thread.

I achieved desired results by changing the functions environment, by adding this code to the start of my script. However I would like to do this using the C API side, so that I don't have to paste this code into every single script.

local newgt = {}
setmetatable(newgt, {__index = _G })
local _ENV = newgt

Example code + desired / current output

npcA.lua

x = 10
print(x)
y = 60

npcB.lua

print(x)
y = 25
player:someYieldingFunction()
print(y)

I expect that npcA print

10

and that npcB print

nil
25

The output currently is (this is not what I want)

if npcA runs first npcB would print

10
25

if npcB runs first and then yields, then npcA runs, and then npcB resumes. npcB would print

10
60

which is problematic

newbane2
  • 130
  • 5
  • 2
    A thread doesn't have `_ENV` variable. A compiled chunk (a Lua function value) does have it, could be set by [lua_setupvalue](https://www.lua.org/manual/5.3/manual.html#lua_setupvalue) with `n=1`. – Egor Skriptunoff Jul 14 '19 at 20:03
  • @EgorSkriptunoff would I be able to set a different environment for the same chunk running on different threads? – newbane2 Jul 14 '19 at 21:34
  • 1
    Yes. You should create separate function (with `lua_load`) for each thread and then set the environment for this function using `lua_setupvalue` – Egor Skriptunoff Jul 15 '19 at 06:17
  • @EgorSkriptunoff yes but no; the _ENV upvalue isn't always the first one or they may not even be one at all. – DarkWiiPlayer Jul 15 '19 at 12:08
  • 1
    To be honest, you're trying to fix something that's not a problem. Globals are meant to be global; if you don't want that, use locals instead. – DarkWiiPlayer Jul 15 '19 at 12:10
  • @DarkWiiPlayer https://www.lua.org/source/5.3/lapi.c.html#lua_load and http://www.lua.org/manual/5.3/manual.html#2.2 – Aki Jul 15 '19 at 13:18
  • 1
    @Green Again, yes but no. This only aplies to main chunks, i. e. chunks loaded directly from source code. It doesn't apply to functions though. The only safe way is to use the environment argument of `load` and `loadfile`, everything else is a hack and will most likely break things at some point. – DarkWiiPlayer Jul 15 '19 at 13:27
  • 1
    @newbane2 - *According to remark from DarkWiiPlayer, I rephrased my comment:* You should create a separate function for each thread by compiling the same Lua code with `lua_load`; and then set thread-specific environment for this function using `lua_setupvalue`. Compiling from Lua code guarantees that functions will have only one upvalue. – Egor Skriptunoff Jul 15 '19 at 18:13
  • @EgorSkriptunoff I can't find any examples of lua_load being used directly, are you referring to lua_loadfile() instead? Wouldn't doing so defeat the purpose of caching the function? – newbane2 Jul 15 '19 at 19:57
  • 1
    They are different closures (because have different upvalues). No way to cache. You can use `lua_loadfile()` instead of `lua_load()`. – Egor Skriptunoff Jul 15 '19 at 20:33
  • @DarkWiiPlayer Could you share an example for *the _ENV upvalue isn't always the first one or they may not even be one at all* or source on that? – Aki Jul 17 '19 at 14:14
  • @Green According to the spec, no, as it's not defined, so one implementation could make _ENV the *last* upvalue of every function and it would still be valid Lua. That being said, in PUC Lua, the upvalues are numbered in the order they appear, so if you access (read OR write) any upvalue before accessing a global, that upvalue will come before _ENV. – DarkWiiPlayer Jul 17 '19 at 15:31
  • @DarkWiiPlayer I think I understand your concerns now. However, Lua reference states that functions loaded with `lua_load` will have env in first upvalue. You mentioned that, but Lua reference also clearly states that first upvalue of function loaded with either `loadfile` or `load` will be set to value of `env` parameter or global environment. If your concern was that loaded function might expect environment in arbitrary upvalue then using `load` or `loadfile` is unsafe and it is as unsafe as `lua_load` with `lua_setupvalue`. Only truly safe way is to set `_ENV` and then define function. – Aki Jul 17 '19 at 18:14
  • `load` and `loadfile` are safe for text (aka source code), but if you load binary data, it depends on whether that was dumped from a *main* block (aka. originally loaded as text), in which case it is also safe, or from a function, in which case it isn't. – DarkWiiPlayer Jul 17 '19 at 18:51

0 Answers0