Create the necessary API for /giveme and /give and implement those commands; also...
authorPerttu Ahola <celeron55@gmail.com>
Tue, 29 Nov 2011 19:30:22 +0000 (21:30 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Tue, 29 Nov 2011 19:30:22 +0000 (21:30 +0200)
data/builtin.lua
data/mods/default/init.lua
data/mods/experimental/init.lua
src/auth.cpp
src/auth.h
src/scriptapi.cpp

index 5f5d40f6790466054b6d3fdfc99ccc5ba0a3969c..dc41caf69b21a4ba0e971c14deded008042ab83e 100644 (file)
@@ -147,6 +147,155 @@ minetest.register_node("ignore", {
        air_equivalent = true,
 })
 
+--
+-- stackstring manipulation functions
+-- example stackstring: 'CraftItem "apple" 4'
+-- example item: {type="CraftItem", name="apple"}
+-- example item: {type="ToolItem", name="SteelPick", wear="23272"}
+--
+
+function stackstring_take_item(stackstring)
+       if stackstring == nil then
+               return '', nil
+       end
+       local stacktype = nil
+       stacktype = string.match(stackstring,
+                       '([%a%d]+Item[%a%d]*)')
+       if stacktype == "NodeItem" or stacktype == "CraftItem" then
+               local itemtype = nil
+               local itemname = nil
+               local itemcount = nil
+               itemtype, itemname, itemcount = string.match(stackstring,
+                               '([%a%d]+Item[%a%d]*) "([^"]*)" (%d+)')
+               itemcount = tonumber(itemcount)
+               if itemcount == 0 then
+                       return '', nil
+               elseif itemcount == 1 then
+                       return '', {type=itemtype, name=itemname}
+               else
+                       return itemtype.." \""..itemname.."\" "..(itemcount-1),
+                                       {type=itemtype, name=itemname}
+               end
+       elseif stacktype == "ToolItem" then
+               local itemtype = nil
+               local itemname = nil
+               local itemwear = nil
+               itemtype, itemname, itemwear = string.match(stackstring,
+                               '([%a%d]+Item[%a%d]*) "([^"]*)" (%d+)')
+               itemwear = tonumber(itemwear)
+               return '', {type=itemtype, name=itemname, wear=itemwear}
+       end
+end
+
+function stackstring_put_item(stackstring, item)
+       if item == nil then
+               return stackstring, false
+       end
+       stackstring = stackstring or ''
+       local stacktype = nil
+       stacktype = string.match(stackstring,
+                       '([%a%d]+Item[%a%d]*)')
+       stacktype = stacktype or ''
+       if stacktype ~= '' and stacktype ~= item.type then
+               return stackstring, false
+       end
+       if item.type == "NodeItem" or item.type == "CraftItem" then
+               local itemtype = nil
+               local itemname = nil
+               local itemcount = nil
+               itemtype, itemname, itemcount = string.match(stackstring,
+                               '([%a%d]+Item[%a%d]*) "([^"]*)" (%d+)')
+               itemtype = itemtype or item.type
+               itemname = itemname or item.name
+               if itemcount == nil then
+                       itemcount = 0
+               end
+               itemcount = itemcount + 1
+               return itemtype.." \""..itemname.."\" "..itemcount, true
+       elseif item.type == "ToolItem" then
+               if stacktype ~= nil then
+                       return stackstring, false
+               end
+               local itemtype = nil
+               local itemname = nil
+               local itemwear = nil
+               itemtype, itemname, itemwear = string.match(stackstring,
+                               '([%a%d]+Item[%a%d]*) "([^"]*)" (%d+)')
+               itemwear = tonumber(itemwear)
+               return itemtype.." \""..itemname.."\" "..itemwear, true
+       end
+       return stackstring, false
+end
+
+function stackstring_put_stackstring(stackstring, src)
+       while src ~= '' do
+               --print("src="..dump(src))
+               src, item = stackstring_take_item(src)
+               --print("src="..dump(src).." item="..dump(item))
+               local success
+               stackstring, success = stackstring_put_item(stackstring, item)
+               if not success then
+                       return stackstring, false
+               end
+       end
+       return stackstring, true
+end
+
+function test_stackstring()
+       local stack
+       local item
+       local success
+
+       stack, item = stackstring_take_item('NodeItem "TNT" 3')
+       assert(stack == 'NodeItem "TNT" 2')
+       assert(item.type == 'NodeItem')
+       assert(item.name == 'TNT')
+
+       stack, item = stackstring_take_item('CraftItem "with spaces" 2')
+       assert(stack == 'CraftItem "with spaces" 1')
+       assert(item.type == 'CraftItem')
+       assert(item.name == 'with spaces')
+
+       stack, item = stackstring_take_item('CraftItem "with spaces" 1')
+       assert(stack == '')
+       assert(item.type == 'CraftItem')
+       assert(item.name == 'with spaces')
+
+       stack, item = stackstring_take_item('CraftItem "s8df2kj3" 0')
+       assert(stack == '')
+       assert(item == nil)
+
+       stack, item = stackstring_take_item('ToolItem "With Spaces" 32487')
+       assert(stack == '')
+       assert(item.type == 'ToolItem')
+       assert(item.name == 'With Spaces')
+       assert(item.wear == 32487)
+
+       stack, success = stackstring_put_item('NodeItem "With Spaces" 40',
+                       {type='NodeItem', name='With Spaces'})
+       assert(stack == 'NodeItem "With Spaces" 41')
+       assert(success == true)
+
+       stack, success = stackstring_put_item('CraftItem "With Spaces" 40',
+                       {type='CraftItem', name='With Spaces'})
+       assert(stack == 'CraftItem "With Spaces" 41')
+       assert(success == true)
+
+       stack, success = stackstring_put_item('ToolItem "With Spaces" 32487',
+                       {type='ToolItem', name='With Spaces'})
+       assert(stack == 'ToolItem "With Spaces" 32487')
+       assert(success == false)
+
+       stack, success = stackstring_put_item('NodeItem "With Spaces" 40',
+                       {type='ToolItem', name='With Spaces'})
+       assert(stack == 'NodeItem "With Spaces" 40')
+       assert(success == false)
+       
+       assert(stackstring_put_stackstring('NodeItem "With Spaces" 2',
+                       'NodeItem "With Spaces" 1') == 'NodeItem "With Spaces" 3')
+end
+test_stackstring()
+
 --
 -- Callback registration
 --
index 395e4a64ce580ed011262bd374a3f6547d6ae6a6..f9c4c823165060e3e774342713543934f20abb5c 100644 (file)
@@ -26,6 +26,7 @@
 -- minetest.setting_getbool(name)
 -- minetest.chat_send_all(text)
 -- minetest.chat_send_player(name, text)
+-- minetest.get_player_privs(name)
 --
 -- Global objects:
 -- minetest.env - environment reference
@@ -52,6 +53,7 @@
 -- - add_rat(pos)
 -- - add_firefly(pos)
 -- - get_meta(pos) -- Get a NodeMetaRef at that position
+-- - get_player_by_name(name) -- Get an ObjectRef to a player
 --
 -- NodeMetaRef
 -- - get_type()
@@ -1322,6 +1324,65 @@ function on_punchnode(p, node)
 end
 minetest.register_on_punchnode(on_punchnode)
 
+minetest.register_on_chat_message(function(name, message)
+       print("default on_chat_message: name="..dump(name).." message="..dump(message))
+       local cmd = "/giveme"
+       if message:sub(0, #cmd) == cmd then
+               if not minetest.get_player_privs(name)["give"] then
+                       minetest.chat_send_player(name, "you don't have permission to give")
+                       return true -- Handled chat message
+               end
+               stackstring = string.match(message, cmd.." (.*)")
+               if stackstring == nil then
+                       minetest.chat_send_player(name, 'usage: '..cmd..' stackstring')
+                       return true -- Handled chat message
+               end
+               print(cmd..' invoked, stackstring="'..stackstring..'"')
+               player = minetest.env:get_player_by_name(name)
+               added, error_msg = player:add_to_inventory(stackstring)
+               if added then
+                       minetest.chat_send_player(name, '"'..stackstring
+                                       ..'" added to inventory.');
+               else
+                       minetest.chat_send_player(name, 'Could not give "'..stackstring
+                                       ..'": '..error_msg);
+               end
+               return true -- Handled chat message
+       end
+       local cmd = "/give"
+       if message:sub(0, #cmd) == cmd then
+               print("minetest.get_player_privs(name)="
+                               ..dump(minetest.get_player_privs(name)))
+               if not minetest.get_player_privs(name)["give"] then
+                       minetest.chat_send_player(name, "you don't have permission to give")
+                       return true -- Handled chat message
+               end
+               name2, stackstring = string.match(message, cmd.." ([%a%d_-]+) (.*)")
+               if name == nil or stackstring == nil then
+                       minetest.chat_send_player(name, 'usage: '..cmd..' name stackstring')
+                       return true -- Handled chat message
+               end
+               print(cmd..' invoked, name2="'..name2
+                               ..'" stackstring="'..stackstring..'"')
+               player = minetest.env:get_player_by_name(name2)
+               if player == nil then
+                       minetest.chat_send_player(name, name2..' is not a known player')
+                       return true -- Handled chat message
+               end
+               added, error_msg = player:add_to_inventory(stackstring)
+               if added then
+                       minetest.chat_send_player(name, '"'..stackstring
+                                       ..'" added to '..name2..'\'s inventory.');
+                       minetest.chat_send_player(name2, '"'..stackstring
+                                       ..'" added to inventory.');
+               else
+                       minetest.chat_send_player(name, 'Could not give "'..stackstring
+                                       ..'": '..error_msg);
+               end
+               return true -- Handled chat message
+       end
+end)
+
 --
 -- Done, print some random stuff
 --
index 45f16738f48a45e54b8af37b1402690adf7df4b6..fdfc8a780938bf41d03cb16889173ce5ddb02bb6 100644 (file)
@@ -41,148 +41,6 @@ minetest.register_on_placenode(function(pos, newnode, placer)
        end
 end)
 
-function stackstring_take_item(stackstring)
-       if stackstring == nil then
-               return '', nil
-       end
-       local stacktype = nil
-       stacktype = string.match(stackstring,
-                       '([%a%d]+Item[%a%d]*)')
-       if stacktype == "NodeItem" or stacktype == "CraftItem" then
-               local itemtype = nil
-               local itemname = nil
-               local itemcount = nil
-               itemtype, itemname, itemcount = string.match(stackstring,
-                               '([%a%d]+Item[%a%d]*) "([^"]*)" (%d+)')
-               itemcount = tonumber(itemcount)
-               if itemcount == 0 then
-                       return '', nil
-               elseif itemcount == 1 then
-                       return '', {type=itemtype, name=itemname}
-               else
-                       return itemtype.." \""..itemname.."\" "..(itemcount-1),
-                                       {type=itemtype, name=itemname}
-               end
-       elseif stacktype == "ToolItem" then
-               local itemtype = nil
-               local itemname = nil
-               local itemwear = nil
-               itemtype, itemname, itemwear = string.match(stackstring,
-                               '([%a%d]+Item[%a%d]*) "([^"]*)" (%d+)')
-               itemwear = tonumber(itemwear)
-               return '', {type=itemtype, name=itemname, wear=itemwear}
-       end
-end
-
-function stackstring_put_item(stackstring, item)
-       if item == nil then
-               return stackstring, false
-       end
-       stackstring = stackstring or ''
-       local stacktype = nil
-       stacktype = string.match(stackstring,
-                       '([%a%d]+Item[%a%d]*)')
-       stacktype = stacktype or ''
-       if stacktype ~= '' and stacktype ~= item.type then
-               return stackstring, false
-       end
-       if item.type == "NodeItem" or item.type == "CraftItem" then
-               local itemtype = nil
-               local itemname = nil
-               local itemcount = nil
-               itemtype, itemname, itemcount = string.match(stackstring,
-                               '([%a%d]+Item[%a%d]*) "([^"]*)" (%d+)')
-               itemtype = itemtype or item.type
-               itemname = itemname or item.name
-               if itemcount == nil then
-                       itemcount = 0
-               end
-               itemcount = itemcount + 1
-               return itemtype.." \""..itemname.."\" "..itemcount, true
-       elseif item.type == "ToolItem" then
-               if stacktype ~= nil then
-                       return stackstring, false
-               end
-               local itemtype = nil
-               local itemname = nil
-               local itemwear = nil
-               itemtype, itemname, itemwear = string.match(stackstring,
-                               '([%a%d]+Item[%a%d]*) "([^"]*)" (%d+)')
-               itemwear = tonumber(itemwear)
-               return itemtype.." \""..itemname.."\" "..itemwear, true
-       end
-       return stackstring, false
-end
-
-function stackstring_put_stackstring(stackstring, src)
-       while src ~= '' do
-               --print("src="..dump(src))
-               src, item = stackstring_take_item(src)
-               --print("src="..dump(src).." item="..dump(item))
-               local success
-               stackstring, success = stackstring_put_item(stackstring, item)
-               if not success then
-                       return stackstring, false
-               end
-       end
-       return stackstring, true
-end
-
-function test_stack()
-       local stack
-       local item
-       local success
-
-       stack, item = stackstring_take_item('NodeItem "TNT" 3')
-       assert(stack == 'NodeItem "TNT" 2')
-       assert(item.type == 'NodeItem')
-       assert(item.name == 'TNT')
-
-       stack, item = stackstring_take_item('CraftItem "with spaces" 2')
-       assert(stack == 'CraftItem "with spaces" 1')
-       assert(item.type == 'CraftItem')
-       assert(item.name == 'with spaces')
-
-       stack, item = stackstring_take_item('CraftItem "with spaces" 1')
-       assert(stack == '')
-       assert(item.type == 'CraftItem')
-       assert(item.name == 'with spaces')
-
-       stack, item = stackstring_take_item('CraftItem "s8df2kj3" 0')
-       assert(stack == '')
-       assert(item == nil)
-
-       stack, item = stackstring_take_item('ToolItem "With Spaces" 32487')
-       assert(stack == '')
-       assert(item.type == 'ToolItem')
-       assert(item.name == 'With Spaces')
-       assert(item.wear == 32487)
-
-       stack, success = stackstring_put_item('NodeItem "With Spaces" 40',
-                       {type='NodeItem', name='With Spaces'})
-       assert(stack == 'NodeItem "With Spaces" 41')
-       assert(success == true)
-
-       stack, success = stackstring_put_item('CraftItem "With Spaces" 40',
-                       {type='CraftItem', name='With Spaces'})
-       assert(stack == 'CraftItem "With Spaces" 41')
-       assert(success == true)
-
-       stack, success = stackstring_put_item('ToolItem "With Spaces" 32487',
-                       {type='ToolItem', name='With Spaces'})
-       assert(stack == 'ToolItem "With Spaces" 32487')
-       assert(success == false)
-
-       stack, success = stackstring_put_item('NodeItem "With Spaces" 40',
-                       {type='ToolItem', name='With Spaces'})
-       assert(stack == 'NodeItem "With Spaces" 40')
-       assert(success == false)
-       
-       assert(stackstring_put_stackstring('NodeItem "With Spaces" 2',
-                       'NodeItem "With Spaces" 1') == 'NodeItem "With Spaces" 3')
-end
-test_stack()
-
 minetest.register_abm({
        nodenames = {"luafurnace"},
        interval = 1.0,
@@ -426,7 +284,7 @@ print("setting max_users = " .. dump(minetest.setting_get("max_users")))
 print("setting asdf = " .. dump(minetest.setting_get("asdf")))
 
 minetest.register_on_chat_message(function(name, message)
-       print("on_chat_message: name="..dump(name).." message="..dump(message))
+       --[[print("on_chat_message: name="..dump(name).." message="..dump(message))
        local cmd = "/testcommand"
        if message:sub(0, #cmd) == cmd then
                print(cmd.." invoked")
@@ -437,7 +295,7 @@ minetest.register_on_chat_message(function(name, message)
                print("script-overridden help command")
                minetest.chat_send_all("script-overridden help command")
                return true
-       end
+       end]]
 end)
 
 -- Grow papyrus on TNT every 10 seconds
index dc740411b2051109597906195bb65ce6c789ff94..684391654ab62909484d43d4e12928c389877b0b 100644 (file)
@@ -25,6 +25,26 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "strfnd.h"
 #include "debug.h"
 
+std::set<std::string> privsToSet(u64 privs)
+{
+       std::set<std::string> s;
+       if(privs & PRIV_BUILD)
+               s.insert("build");
+       if(privs & PRIV_TELEPORT)
+               s.insert("teleport");
+       if(privs & PRIV_SETTIME)
+               s.insert("settime");
+       if(privs & PRIV_PRIVS)
+               s.insert("privs");
+       if(privs & PRIV_SHOUT)
+               s.insert("shout");
+       if(privs & PRIV_BAN)
+               s.insert("ban");
+       if(privs & PRIV_GIVE)
+               s.insert("give");
+       return s;
+}
+
 // Convert a privileges value into a human-readable string,
 // with each component separated by a comma.
 std::string privsToString(u64 privs)
@@ -42,6 +62,8 @@ std::string privsToString(u64 privs)
                os<<"shout,";
        if(privs & PRIV_BAN)
                os<<"ban,";
+       if(privs & PRIV_GIVE)
+               os<<"give,";
        if(os.tellp())
        {
                // Drop the trailing comma. (Why on earth can't
@@ -74,6 +96,8 @@ u64 stringToPrivs(std::string str)
                        privs |= PRIV_SHOUT;
                else if(s == "ban")
                        privs |= PRIV_BAN;
+               else if(s == "give")
+                       privs |= PRIV_GIVE;
                else
                        return PRIV_INVALID;
        }
index 5ea697a6acd1792230ac1e509c31719438e78187..9939632a9b1d031bbccdb48978a6b2c7ee3648b2 100644 (file)
@@ -20,10 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifndef AUTH_HEADER
 #define AUTH_HEADER
 
+#include <set>
 #include <string>
 #include <jthread.h>
 #include <jmutex.h>
-#include "common_irrlicht.h"
+#include "irrlichttypes.h"
 #include "exceptions.h"
 
 // Player privileges. These form a bitmask stored in the privs field
@@ -39,6 +40,7 @@ const u64 PRIV_SERVER = 16;          // Can manage the server (e.g. shutodwn
 const u64 PRIV_SHOUT = 32;           // Can broadcast chat messages to all
                                      // players
 const u64 PRIV_BAN = 64;             // Can ban players
+const u64 PRIV_GIVE = 128;             // Can give stuff
 
 // Default privileges - these can be overriden for new players using the
 // config option "default_privs" - however, this value still applies for
@@ -47,6 +49,8 @@ const u64 PRIV_DEFAULT = PRIV_BUILD|PRIV_SHOUT;
 const u64 PRIV_ALL = 0x7FFFFFFFFFFFFFFFULL;
 const u64 PRIV_INVALID = 0x8000000000000000ULL;
 
+std::set<std::string> privsToSet(u64 privs);
+
 // Convert a privileges value into a human-readable string,
 // with each component separated by a comma.
 std::string privsToString(u64 privs);
index 83efef670e62631568f124107796f831fc1fdaad..06cf38d1e370f2493f2abd5e23af8ba11f198648 100644 (file)
@@ -1023,6 +1023,30 @@ static int l_chat_send_player(lua_State *L)
        return 0;
 }
 
+// get_player_privs(name, text)
+static int l_get_player_privs(lua_State *L)
+{
+       const char *name = luaL_checkstring(L, 1);
+       // Get server from registry
+       lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server");
+       Server *server = (Server*)lua_touserdata(L, -1);
+       // Do it
+       lua_newtable(L);
+       int table = lua_gettop(L);
+       u64 privs_i = server->getPlayerAuthPrivs(name);
+       // Special case for the "name" setting (local player / server owner)
+       if(name == g_settings->get("name"))
+               privs_i = PRIV_ALL;
+       std::set<std::string> privs_s = privsToSet(privs_i);
+       for(std::set<std::string>::const_iterator
+                       i = privs_s.begin(); i != privs_s.end(); i++){
+               lua_pushboolean(L, true);
+               lua_setfield(L, table, i->c_str());
+       }
+       lua_pushvalue(L, table);
+       return 1;
+}
+
 static const struct luaL_Reg minetest_f [] = {
        {"register_nodedef_defaults", l_register_nodedef_defaults},
        {"register_entity", l_register_entity},
@@ -1035,6 +1059,7 @@ static const struct luaL_Reg minetest_f [] = {
        {"setting_getbool", l_setting_getbool},
        {"chat_send_all", l_chat_send_all},
        {"chat_send_player", l_chat_send_player},
+       {"get_player_privs", l_get_player_privs},
        {NULL, NULL}
 };
 
@@ -1502,236 +1527,6 @@ const luaL_reg NodeMetaRef::methods[] = {
        {0,0}
 };
 
-/*
-       EnvRef
-*/
-
-class EnvRef
-{
-private:
-       ServerEnvironment *m_env;
-
-       static const char className[];
-       static const luaL_reg methods[];
-
-       static EnvRef *checkobject(lua_State *L, int narg)
-       {
-               luaL_checktype(L, narg, LUA_TUSERDATA);
-               void *ud = luaL_checkudata(L, narg, className);
-               if(!ud) luaL_typerror(L, narg, className);
-               return *(EnvRef**)ud;  // unbox pointer
-       }
-       
-       // Exported functions
-
-       // EnvRef:add_node(pos, node)
-       // pos = {x=num, y=num, z=num}
-       static int l_add_node(lua_State *L)
-       {
-               //infostream<<"EnvRef::l_add_node()"<<std::endl;
-               EnvRef *o = checkobject(L, 1);
-               ServerEnvironment *env = o->m_env;
-               if(env == NULL) return 0;
-               // pos
-               v3s16 pos = readpos(L, 2);
-               // content
-               MapNode n = readnode(L, 3, env->getGameDef()->ndef());
-               // Do it
-               bool succeeded = env->getMap().addNodeWithEvent(pos, n);
-               lua_pushboolean(L, succeeded);
-               return 1;
-       }
-
-       // EnvRef:remove_node(pos)
-       // pos = {x=num, y=num, z=num}
-       static int l_remove_node(lua_State *L)
-       {
-               //infostream<<"EnvRef::l_remove_node()"<<std::endl;
-               EnvRef *o = checkobject(L, 1);
-               ServerEnvironment *env = o->m_env;
-               if(env == NULL) return 0;
-               // pos
-               v3s16 pos = readpos(L, 2);
-               // Do it
-               bool succeeded = env->getMap().removeNodeWithEvent(pos);
-               lua_pushboolean(L, succeeded);
-               return 1;
-       }
-
-       // EnvRef:get_node(pos)
-       // pos = {x=num, y=num, z=num}
-       static int l_get_node(lua_State *L)
-       {
-               //infostream<<"EnvRef::l_get_node()"<<std::endl;
-               EnvRef *o = checkobject(L, 1);
-               ServerEnvironment *env = o->m_env;
-               if(env == NULL) return 0;
-               // pos
-               v3s16 pos = readpos(L, 2);
-               // Do it
-               MapNode n = env->getMap().getNodeNoEx(pos);
-               // Return node
-               pushnode(L, n, env->getGameDef()->ndef());
-               return 1;
-       }
-
-       // EnvRef:add_luaentity(pos, entityname)
-       // pos = {x=num, y=num, z=num}
-       static int l_add_luaentity(lua_State *L)
-       {
-               //infostream<<"EnvRef::l_add_luaentity()"<<std::endl;
-               EnvRef *o = checkobject(L, 1);
-               ServerEnvironment *env = o->m_env;
-               if(env == NULL) return 0;
-               // pos
-               v3f pos = readFloatPos(L, 2);
-               // content
-               const char *name = lua_tostring(L, 3);
-               // Do it
-               ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, "");
-               env->addActiveObject(obj);
-               return 0;
-       }
-
-       // EnvRef:add_item(pos, inventorystring)
-       // pos = {x=num, y=num, z=num}
-       static int l_add_item(lua_State *L)
-       {
-               infostream<<"EnvRef::l_add_item()"<<std::endl;
-               EnvRef *o = checkobject(L, 1);
-               ServerEnvironment *env = o->m_env;
-               if(env == NULL) return 0;
-               // pos
-               v3f pos = readFloatPos(L, 2);
-               // inventorystring
-               const char *inventorystring = lua_tostring(L, 3);
-               // Do it
-               ServerActiveObject *obj = new ItemSAO(env, pos, inventorystring);
-               env->addActiveObject(obj);
-               return 0;
-       }
-
-       // EnvRef:add_rat(pos)
-       // pos = {x=num, y=num, z=num}
-       static int l_add_rat(lua_State *L)
-       {
-               infostream<<"EnvRef::l_add_rat()"<<std::endl;
-               EnvRef *o = checkobject(L, 1);
-               ServerEnvironment *env = o->m_env;
-               if(env == NULL) return 0;
-               // pos
-               v3f pos = readFloatPos(L, 2);
-               // Do it
-               ServerActiveObject *obj = new RatSAO(env, pos);
-               env->addActiveObject(obj);
-               return 0;
-       }
-
-       // EnvRef:add_firefly(pos)
-       // pos = {x=num, y=num, z=num}
-       static int l_add_firefly(lua_State *L)
-       {
-               infostream<<"EnvRef::l_add_firefly()"<<std::endl;
-               EnvRef *o = checkobject(L, 1);
-               ServerEnvironment *env = o->m_env;
-               if(env == NULL) return 0;
-               // pos
-               v3f pos = readFloatPos(L, 2);
-               // Do it
-               ServerActiveObject *obj = new FireflySAO(env, pos);
-               env->addActiveObject(obj);
-               return 0;
-       }
-
-       // EnvRef:get_meta(pos)
-       static int l_get_meta(lua_State *L)
-       {
-               //infostream<<"EnvRef::l_get_meta()"<<std::endl;
-               EnvRef *o = checkobject(L, 1);
-               ServerEnvironment *env = o->m_env;
-               if(env == NULL) return 0;
-               // Do it
-               v3s16 p = readpos(L, 2);
-               NodeMetaRef::create(L, p, env);
-               return 1;
-       }
-
-       static int gc_object(lua_State *L) {
-               EnvRef *o = *(EnvRef **)(lua_touserdata(L, 1));
-               delete o;
-               return 0;
-       }
-
-public:
-       EnvRef(ServerEnvironment *env):
-               m_env(env)
-       {
-               infostream<<"EnvRef created"<<std::endl;
-       }
-
-       ~EnvRef()
-       {
-               infostream<<"EnvRef destructing"<<std::endl;
-       }
-
-       // Creates an EnvRef and leaves it on top of stack
-       // Not callable from Lua; all references are created on the C side.
-       static void create(lua_State *L, ServerEnvironment *env)
-       {
-               EnvRef *o = new EnvRef(env);
-               //infostream<<"EnvRef::create: o="<<o<<std::endl;
-               *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
-               luaL_getmetatable(L, className);
-               lua_setmetatable(L, -2);
-       }
-
-       static void set_null(lua_State *L)
-       {
-               EnvRef *o = checkobject(L, -1);
-               o->m_env = NULL;
-       }
-       
-       static void Register(lua_State *L)
-       {
-               lua_newtable(L);
-               int methodtable = lua_gettop(L);
-               luaL_newmetatable(L, className);
-               int metatable = lua_gettop(L);
-
-               lua_pushliteral(L, "__metatable");
-               lua_pushvalue(L, methodtable);
-               lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
-
-               lua_pushliteral(L, "__index");
-               lua_pushvalue(L, methodtable);
-               lua_settable(L, metatable);
-
-               lua_pushliteral(L, "__gc");
-               lua_pushcfunction(L, gc_object);
-               lua_settable(L, metatable);
-
-               lua_pop(L, 1);  // drop metatable
-
-               luaL_openlib(L, 0, methods, 0);  // fill methodtable
-               lua_pop(L, 1);  // drop methodtable
-
-               // Cannot be created from Lua
-               //lua_register(L, className, create_object);
-       }
-};
-const char EnvRef::className[] = "EnvRef";
-const luaL_reg EnvRef::methods[] = {
-       method(EnvRef, add_node),
-       method(EnvRef, remove_node),
-       method(EnvRef, get_node),
-       method(EnvRef, add_luaentity),
-       method(EnvRef, add_item),
-       method(EnvRef, add_rat),
-       method(EnvRef, add_firefly),
-       method(EnvRef, get_meta),
-       {0,0}
-};
-
 /*
        ObjectRef
 */
@@ -1886,7 +1681,7 @@ private:
        }
        
        // add_to_inventory(self, itemstring)
-       // returns: true if item was added, false otherwise
+       // returns: true if item was added, (false, "reason") otherwise
        static int l_add_to_inventory(lua_State *L)
        {
                ObjectRef *ref = checkobject(L, 1);
@@ -1902,12 +1697,23 @@ private:
                ServerEnvironment *env = co->getEnv();
                assert(env);
                IGameDef *gamedef = env->getGameDef();
-               InventoryItem *item = InventoryItem::deSerialize(is, gamedef);
-               infostream<<"item="<<env<<std::endl;
-               bool fits = co->addToInventory(item);
-               // Return
-               lua_pushboolean(L, fits);
-               return 1;
+               try{
+                       InventoryItem *item = InventoryItem::deSerialize(is, gamedef);
+                       if(item->getCount() == 0)
+                               item->setCount(1);
+                       bool added = co->addToInventory(item);
+                       // Return
+                       lua_pushboolean(L, added);
+                       if(!added)
+                               lua_pushstring(L, "does not fit");
+                       return 2;
+               } catch(SerializationError &e){
+                       // Return
+                       lua_pushboolean(L, false);
+                       lua_pushstring(L, (std::string("Invalid item: ")
+                                       + e.what()).c_str());
+                       return 2;
+               }
        }
 
        // add_to_inventory_later(self, itemstring)
@@ -2093,6 +1899,256 @@ static void objectref_get_or_create(lua_State *L,
        }
 }
 
+/*
+       EnvRef
+*/
+
+class EnvRef
+{
+private:
+       ServerEnvironment *m_env;
+
+       static const char className[];
+       static const luaL_reg methods[];
+
+       static EnvRef *checkobject(lua_State *L, int narg)
+       {
+               luaL_checktype(L, narg, LUA_TUSERDATA);
+               void *ud = luaL_checkudata(L, narg, className);
+               if(!ud) luaL_typerror(L, narg, className);
+               return *(EnvRef**)ud;  // unbox pointer
+       }
+       
+       // Exported functions
+
+       // EnvRef:add_node(pos, node)
+       // pos = {x=num, y=num, z=num}
+       static int l_add_node(lua_State *L)
+       {
+               //infostream<<"EnvRef::l_add_node()"<<std::endl;
+               EnvRef *o = checkobject(L, 1);
+               ServerEnvironment *env = o->m_env;
+               if(env == NULL) return 0;
+               // pos
+               v3s16 pos = readpos(L, 2);
+               // content
+               MapNode n = readnode(L, 3, env->getGameDef()->ndef());
+               // Do it
+               bool succeeded = env->getMap().addNodeWithEvent(pos, n);
+               lua_pushboolean(L, succeeded);
+               return 1;
+       }
+
+       // EnvRef:remove_node(pos)
+       // pos = {x=num, y=num, z=num}
+       static int l_remove_node(lua_State *L)
+       {
+               //infostream<<"EnvRef::l_remove_node()"<<std::endl;
+               EnvRef *o = checkobject(L, 1);
+               ServerEnvironment *env = o->m_env;
+               if(env == NULL) return 0;
+               // pos
+               v3s16 pos = readpos(L, 2);
+               // Do it
+               bool succeeded = env->getMap().removeNodeWithEvent(pos);
+               lua_pushboolean(L, succeeded);
+               return 1;
+       }
+
+       // EnvRef:get_node(pos)
+       // pos = {x=num, y=num, z=num}
+       static int l_get_node(lua_State *L)
+       {
+               //infostream<<"EnvRef::l_get_node()"<<std::endl;
+               EnvRef *o = checkobject(L, 1);
+               ServerEnvironment *env = o->m_env;
+               if(env == NULL) return 0;
+               // pos
+               v3s16 pos = readpos(L, 2);
+               // Do it
+               MapNode n = env->getMap().getNodeNoEx(pos);
+               // Return node
+               pushnode(L, n, env->getGameDef()->ndef());
+               return 1;
+       }
+
+       // EnvRef:add_luaentity(pos, entityname)
+       // pos = {x=num, y=num, z=num}
+       static int l_add_luaentity(lua_State *L)
+       {
+               //infostream<<"EnvRef::l_add_luaentity()"<<std::endl;
+               EnvRef *o = checkobject(L, 1);
+               ServerEnvironment *env = o->m_env;
+               if(env == NULL) return 0;
+               // pos
+               v3f pos = readFloatPos(L, 2);
+               // content
+               const char *name = lua_tostring(L, 3);
+               // Do it
+               ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, "");
+               env->addActiveObject(obj);
+               return 0;
+       }
+
+       // EnvRef:add_item(pos, inventorystring)
+       // pos = {x=num, y=num, z=num}
+       static int l_add_item(lua_State *L)
+       {
+               infostream<<"EnvRef::l_add_item()"<<std::endl;
+               EnvRef *o = checkobject(L, 1);
+               ServerEnvironment *env = o->m_env;
+               if(env == NULL) return 0;
+               // pos
+               v3f pos = readFloatPos(L, 2);
+               // inventorystring
+               const char *inventorystring = lua_tostring(L, 3);
+               // Do it
+               ServerActiveObject *obj = new ItemSAO(env, pos, inventorystring);
+               env->addActiveObject(obj);
+               return 0;
+       }
+
+       // EnvRef:add_rat(pos)
+       // pos = {x=num, y=num, z=num}
+       static int l_add_rat(lua_State *L)
+       {
+               infostream<<"EnvRef::l_add_rat()"<<std::endl;
+               EnvRef *o = checkobject(L, 1);
+               ServerEnvironment *env = o->m_env;
+               if(env == NULL) return 0;
+               // pos
+               v3f pos = readFloatPos(L, 2);
+               // Do it
+               ServerActiveObject *obj = new RatSAO(env, pos);
+               env->addActiveObject(obj);
+               return 0;
+       }
+
+       // EnvRef:add_firefly(pos)
+       // pos = {x=num, y=num, z=num}
+       static int l_add_firefly(lua_State *L)
+       {
+               infostream<<"EnvRef::l_add_firefly()"<<std::endl;
+               EnvRef *o = checkobject(L, 1);
+               ServerEnvironment *env = o->m_env;
+               if(env == NULL) return 0;
+               // pos
+               v3f pos = readFloatPos(L, 2);
+               // Do it
+               ServerActiveObject *obj = new FireflySAO(env, pos);
+               env->addActiveObject(obj);
+               return 0;
+       }
+
+       // EnvRef:get_meta(pos)
+       static int l_get_meta(lua_State *L)
+       {
+               //infostream<<"EnvRef::l_get_meta()"<<std::endl;
+               EnvRef *o = checkobject(L, 1);
+               ServerEnvironment *env = o->m_env;
+               if(env == NULL) return 0;
+               // Do it
+               v3s16 p = readpos(L, 2);
+               NodeMetaRef::create(L, p, env);
+               return 1;
+       }
+
+       // EnvRef:get_player_by_name(name)
+       static int l_get_player_by_name(lua_State *L)
+       {
+               EnvRef *o = checkobject(L, 1);
+               ServerEnvironment *env = o->m_env;
+               if(env == NULL) return 0;
+               // Do it
+               const char *name = lua_tostring(L, 2);
+               ServerRemotePlayer *player =
+                               static_cast<ServerRemotePlayer*>(env->getPlayer(name));
+               if(player == NULL){
+                       lua_pushnil(L);
+                       return 1;
+               }
+               // Put player on stack
+               objectref_get_or_create(L, player);
+               return 1;
+       }
+
+       static int gc_object(lua_State *L) {
+               EnvRef *o = *(EnvRef **)(lua_touserdata(L, 1));
+               delete o;
+               return 0;
+       }
+
+public:
+       EnvRef(ServerEnvironment *env):
+               m_env(env)
+       {
+               infostream<<"EnvRef created"<<std::endl;
+       }
+
+       ~EnvRef()
+       {
+               infostream<<"EnvRef destructing"<<std::endl;
+       }
+
+       // Creates an EnvRef and leaves it on top of stack
+       // Not callable from Lua; all references are created on the C side.
+       static void create(lua_State *L, ServerEnvironment *env)
+       {
+               EnvRef *o = new EnvRef(env);
+               //infostream<<"EnvRef::create: o="<<o<<std::endl;
+               *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+               luaL_getmetatable(L, className);
+               lua_setmetatable(L, -2);
+       }
+
+       static void set_null(lua_State *L)
+       {
+               EnvRef *o = checkobject(L, -1);
+               o->m_env = NULL;
+       }
+       
+       static void Register(lua_State *L)
+       {
+               lua_newtable(L);
+               int methodtable = lua_gettop(L);
+               luaL_newmetatable(L, className);
+               int metatable = lua_gettop(L);
+
+               lua_pushliteral(L, "__metatable");
+               lua_pushvalue(L, methodtable);
+               lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+               lua_pushliteral(L, "__index");
+               lua_pushvalue(L, methodtable);
+               lua_settable(L, metatable);
+
+               lua_pushliteral(L, "__gc");
+               lua_pushcfunction(L, gc_object);
+               lua_settable(L, metatable);
+
+               lua_pop(L, 1);  // drop metatable
+
+               luaL_openlib(L, 0, methods, 0);  // fill methodtable
+               lua_pop(L, 1);  // drop methodtable
+
+               // Cannot be created from Lua
+               //lua_register(L, className, create_object);
+       }
+};
+const char EnvRef::className[] = "EnvRef";
+const luaL_reg EnvRef::methods[] = {
+       method(EnvRef, add_node),
+       method(EnvRef, remove_node),
+       method(EnvRef, get_node),
+       method(EnvRef, add_luaentity),
+       method(EnvRef, add_item),
+       method(EnvRef, add_rat),
+       method(EnvRef, add_firefly),
+       method(EnvRef, get_meta),
+       method(EnvRef, get_player_by_name),
+       {0,0}
+};
+
 /*
        Main export function
 */