From 63d0d602c794a531b94b0e7bf87337ef96ce6ba5 Mon Sep 17 00:00:00 2001 From: prestidigitator Date: Fri, 4 Jul 2014 16:59:31 -0700 Subject: [PATCH] Added player-local context and /luaclear command --- PlayerEnv.lua | 66 +++++++++++++++++++++++++++++++++++++++++ README.txt | 14 ++++++++- init.lua | 81 +++++++++++++++++++++++++-------------------------- 3 files changed, 119 insertions(+), 42 deletions(-) create mode 100644 PlayerEnv.lua diff --git a/PlayerEnv.lua b/PlayerEnv.lua new file mode 100644 index 0000000..21169ca --- /dev/null +++ b/PlayerEnv.lua @@ -0,0 +1,66 @@ +local PrintablePos = +{ + __tostring = function(pos) + return "(" .. pos.x .. ", " .. pos.y .. ", " .. pos.z .. ")"; + end +}; +function PrintablePos:new(pos) + return setmetatable(pos, self); +end + +local PlayerEnv = +{ + playerContexts = setmetatable({}, { __mode = "k" }), -- weak keys + + playerFuncs = + { + me = function(player) + return player; + end, + + myname = function(player) + return player:get_player_name(); + end, + + here = function(player) + return PrintablePos:new(player:getpos()); + end, + + print = function(player) + return function(...) + local str = ""; + for _, arg in ipairs({...}) do + str = str .. tostring(arg); + end + minetest.chat_send_player(player:get_player_name(), str, false); + end + end + }; +}; + +function PlayerEnv:new(player) + local env = {}; + self.playerContexts[env] = player; + setmetatable(env, self); + return env; +end +setmetatable(PlayerEnv, { __call = PlayerEnv.new }); + +function PlayerEnv.__index(env, key) + local playerFunc = PlayerEnv.playerFuncs[key]; + if playerFunc then + local player = PlayerEnv.playerContexts[env]; + return playerFunc(player); + else + return _G[key]; + end +end + +function PlayerEnv.__newindex(env, key, value) + if PlayerEnv.playerFuncs[key] then + error("cannot set special '"..key.."' player variable"); + end + rawset(env, key, value); +end + +return PlayerEnv; diff --git a/README.txt b/README.txt index e42ca2c..5528001 100644 --- a/README.txt +++ b/README.txt @@ -36,7 +36,9 @@ Required Minetest Version: (tested in 0.4.9) Dependencies: (none) -Commands: /lua +Commands: + * /lua + * /luaclear Privileges: lua @@ -64,6 +66,16 @@ Try the following commands: /lua print(myname); /lua print(here); +Version 1.2 + +* Release 2014-07-04 +* Keeps "global" variables set by commands in a player-local context. +* Prevents setting of special variables (e.g. me, myname, here, print). +* Added /luaclear command to clear the player-local context. + +To set true globals visible to mods and other players, use "_G.var = ...". Can +also be used to get any globals hidden by specials and player-local variables. + Copyright and Licensing ----------------------- diff --git a/init.lua b/init.lua index f95a043..73f7a66 100644 --- a/init.lua +++ b/init.lua @@ -1,16 +1,30 @@ -local function copyTable(t) - if type(t) ~= "table" then return t; end - local tc = {}; - for k, v in pairs(t) do - tc[k] = v; - end - return tc; -end +local MOD_NAME = minetest.get_current_modname(); +local MOD_PATH = minetest.get_modpath(MOD_NAME); -local function posToStr(pos) - return "(" .. pos.x .. ", " ..pos.y.. ", " .. pos.z .. ")"; +local PlayerEnv = dofile(MOD_PATH.."/PlayerEnv.lua"); + +local playerEnvs = {}; +minetest.register_on_leaveplayer( + function(player) + playerEnvs[player:get_player_name()] = nil; + end); + +local function runLuaCmd(playerName, paramStr) + local cmdFunc, errMsg = loadstring(paramStr, "/lua command"); + if not cmdFunc then + error(errMsg); + end + + local playerEnv = playerEnvs[playerName]; + if not playerEnv then + local player = minetest.get_player_by_name(playerName); + playerEnv = PlayerEnv:new(player); + playerEnvs[playerName] = playerEnv; + end + + setfenv(cmdFunc, playerEnv); + cmdFunc(); end -local posMeta = { __tostring = posToStr }; minetest.register_privilege( "lua", @@ -26,37 +40,22 @@ minetest.register_chatcommand( description = "Executes a lua statement (chunk), for debugging.", privs = { lua = true }, func = - function(name, param) - local cmdFunc, success, errMsg; - - cmdFunc, errMsg = loadstring(param, "/lua command"); - if not cmdFunc then - minetest.chat_send_player(name, "ERROR: "..errMsg); - return; - end - - local player = minetest.get_player_by_name(name); - local pos = player:getpos(); - setmetatable(pos, posMeta); - - local env = copyTable(getfenv(0)); - env.print = - function(...) - str = ""; - for _, arg in ipairs({...}) do - str = str .. tostring(arg); - end - minetest.chat_send_player(name, str, false); - end; - env.myname = name; - env.me = player; - env.here = pos; - - setfenv(cmdFunc, env); - - success, errMsg = pcall(cmdFunc); + function(playerName, paramStr) + local success, errMsg = pcall(runLuaCmd, playerName, paramStr); if not success then - minetest.chat_send_player(name, "ERROR: "..errMsg); + minetest.chat_send_player(playerName, "ERROR: "..errMsg); end end }); + +minetest.register_chatcommand( + "luaclear", + { + params = "", + description = "Clears all variables in your /lua player context", + privs = { lua = true }, + func = + function(playerName, paramStr) + playerEnvs[playerName] = nil; + end + });