Added player-local context and /luaclear command

master
prestidigitator 2014-07-04 16:59:31 -07:00
parent d1ac137bc0
commit 63d0d602c7
3 changed files with 119 additions and 42 deletions

66
PlayerEnv.lua Normal file
View File

@ -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;

View File

@ -36,7 +36,9 @@ Required Minetest Version: (tested in 0.4.9)
Dependencies: (none)
Commands: /lua <luaStatement>
Commands:
* /lua <luaStatement>
* /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
-----------------------

View File

@ -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
});