CraftItem rework and Lua interface
authorKahrl <kahrl@gmx.net>
Tue, 29 Nov 2011 15:15:18 +0000 (17:15 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Tue, 29 Nov 2011 17:13:58 +0000 (19:13 +0200)
21 files changed:
data/mods/default/init.lua
src/CMakeLists.txt
src/client.cpp
src/client.h
src/clientserver.h
src/content_sao.cpp
src/content_sao.h
src/game.cpp
src/gamedef.h
src/inventory.cpp
src/inventory.h
src/player.cpp
src/player.h
src/scriptapi.cpp
src/scriptapi.h
src/server.cpp
src/server.h
src/servercommand.cpp
src/serverobject.h
src/utility.cpp
src/utility.h

index 58ac72242f28e999086a243f53897463b8df5319..a8e3b7ba781be784eb84f2048636f397c25f5b43 100644 (file)
@@ -10,6 +10,7 @@
 -- minetest.register_entity(name, prototype_table)
 -- minetest.register_tool(name, {lots of stuff})
 -- minetest.register_node(name, {lots of stuff})
+-- minetest.register_craftitem(name, {lots of stuff})
 -- minetest.register_craft({output=item, recipe={...})
 -- minetest.register_globalstep(func)
 -- minetest.register_on_placenode(func(pos, newnode, placer))
@@ -30,7 +31,9 @@
 --
 -- Global tables:
 -- minetest.registered_nodes
--- ^ List of registed node definitions, indexed by name
+-- ^ List of registered node definitions, indexed by name
+-- minetest.registered_craftitems
+-- ^ List of registered craft item definitions, indexed by name
 -- minetest.registered_entities
 -- ^ List of registered entity prototypes, indexed by name
 -- minetest.object_refs
@@ -44,6 +47,9 @@
 -- - remove_node(pos)
 -- - get_node(pos)
 -- - add_luaentity(pos, name)
+-- - add_item(pos, itemstring)
+-- - add_rat(pos)
+-- - add_firefly(pos)
 -- - get_meta(pos) -- Get a NodeMetaRef at that position
 --
 -- NodeMetaRef
@@ -73,6 +79,9 @@
 -- - setpos(pos); pos={x=num, y=num, z=num}
 -- - moveto(pos, continuous=false): interpolated move
 -- - add_to_inventory(itemstring): add an item to object inventory
+-- - add_to_inventory_later(itemstring): like above, but after callback returns (only allowed for craftitem callbacks)
+-- - get_hp(): returns number of hitpoints (2 * number of hearts)
+-- - set_hp(hp): set number of hitpoints (2 * number of hearts)
 -- - settexturemod(mod)
 -- - setsprite(p={x=0,y=0}, num_frames=1, framelength=0.2,
 -- -           select_horiz_by_yawpitch=false)
@@ -1159,6 +1168,118 @@ minetest.register_node("TNT", {
        },
 })
 
+--
+-- Crafting items
+--
+
+local craftitem_place_item = function(item, placer, pos)
+       --print("craftitem_place_item")
+       print("item: " .. dump(item))
+       print("placer: " .. dump(placer))
+       print("pos: " .. dump(pos))
+       minetest.env:add_item(pos, 'CraftItem "' .. item .. '" 1')
+       return true
+end
+
+local craftitem_eat = function(hp_change)
+       return function(item, user, pointed_thing)  -- closure
+               --print("craftitem_eat(" .. hp_change .. ")")
+               --print("item: " .. dump(item))
+               --print("user: " .. dump(user))
+               --print("pointed_thing: " .. dump(pointed_thing))
+               user:set_hp(user:get_hp() + hp_change)
+               return true
+       end
+end
+
+minetest.register_craftitem("Stick", {
+       image = "stick.png",
+       --furnace_burntime = ...,
+       on_place_on_ground = craftitem_place_item,
+})
+
+minetest.register_craftitem("paper", {
+       image = "paper.png",
+       on_place_on_ground = craftitem_place_item,
+})
+
+minetest.register_craftitem("book", {
+       image = "book.png",
+       on_place_on_ground = craftitem_place_item,
+})
+
+minetest.register_craftitem("lump_of_coal", {
+       image = "lump_of_coal.png",
+       furnace_burntime = 40;
+       on_place_on_ground = craftitem_place_item,
+})
+
+minetest.register_craftitem("lump_of_iron", {
+       image = "lump_of_iron.png",
+       cookresult_item = 'CraftItem "steel_ingot" 1',
+       on_place_on_ground = craftitem_place_item,
+})
+
+minetest.register_craftitem("lump_of_clay", {
+       image = "lump_of_clay.png",
+       cookresult_item = 'CraftItem "clay_brick" 1',
+       on_place_on_ground = craftitem_place_item,
+})
+
+minetest.register_craftitem("steel_ingot", {
+       image = "steel_ingot.png",
+       on_place_on_ground = craftitem_place_item,
+})
+
+minetest.register_craftitem("clay_brick", {
+       image = "clay_brick.png",
+       on_place_on_ground = craftitem_place_item,
+})
+
+minetest.register_craftitem("rat", {
+       image = "rat.png",
+       cookresult_item = 'CraftItem "cooked_rat" 1',
+       on_drop = function(item, dropper, pos)
+               minetest.env:add_rat(pos)
+               return true
+       end,
+})
+
+minetest.register_craftitem("cooked_rat", {
+       image = "cooked_rat.png",
+       cookresult_item = 'CraftItem "scorched_stuff" 1',
+       on_place_on_ground = craftitem_place_item,
+       on_use = craftitem_eat(6),
+})
+
+minetest.register_craftitem("scorched_stuff", {
+       image = "scorched_stuff.png",
+       on_place_on_ground = craftitem_place_item,
+})
+
+minetest.register_craftitem("firefly", {
+       image = "firefly.png",
+       on_drop = function(item, dropper, pos)
+               minetest.env:add_firefly(pos)
+               return true
+       end,
+})
+
+minetest.register_craftitem("apple", {
+       image = "apple.png",
+       on_place_on_ground = craftitem_place_item,
+       on_use = craftitem_eat(4),
+})
+
+minetest.register_craftitem("apple_iron", {
+       image = "apple_iron.png",
+       on_place_on_ground = craftitem_place_item,
+       on_use = craftitem_eat(8),
+})
+
+print(dump(minetest.registered_craftitems))
+
+
 --
 -- Some common functions
 --
@@ -1237,6 +1358,7 @@ function TNT:on_punch(hitter)
        if self.health <= 0 then
                self.object:remove()
                hitter:add_to_inventory("NodeItem TNT 1")
+               hitter:set_hp(hitter:get_hp() - 1)
        end
 end
 
@@ -1344,6 +1466,8 @@ function on_punchnode(p, node)
        if node.name == "TNT" then
                minetest.env:remove_node(p)
                minetest.env:add_luaentity(p, "TNT")
+               --minetest.env:add_luaentity(p, "testentity")
+               --minetest.env:add_firefly(p)
                nodeupdate(p)
        end
 end
@@ -1381,7 +1505,7 @@ minetest.register_on_chat_message(function(name, message)
 end)
 
 -- Grow papyrus on TNT every 10 seconds
---[[minetest.register_abm({
+minetest.register_abm({
        nodenames = {"TNT"},
        interval = 10.0,
        chance = 1,
@@ -1390,7 +1514,7 @@ end)
                pos.y = pos.y + 1
                minetest.env:add_node(pos, {name="papyrus"})
        end,
-})]]
+})
 
 -- Replace texts of alls signs with "foo" every 10 seconds
 --[[minetest.register_abm({
@@ -1404,6 +1528,79 @@ end)
        end,
 })]]
 
+--[[local ncpos = nil
+local ncq = 1
+local ncstuff = {
+    {2, 1, 0, 3}, {3, 0, 1, 2}, {4, -1, 0, 1}, {5, -1, 0, 1}, {6, 0, -1, 0},
+    {7, 0, -1, 0}, {8, 1, 0, 3}, {9, 1, 0, 3}, {10, 1, 0, 3}, {11, 0, 1, 2},
+    {12, 0, 1, 2}, {13, 0, 1, 2}, {14, -1, 0, 1}, {15, -1, 0, 1}, {16, -1, 0, 1},
+    {17, -1, 0, 1}, {18, 0, -1, 0}, {19, 0, -1, 0}, {20, 0, -1, 0}, {21, 0, -1, 0},
+    {22, 1, 0, 3}, {23, 1, 0, 3}, {24, 1, 0, 3}, {25, 1, 0, 3}, {10, 0, 1, 2}
+}
+local ncold = {}
+local nctime = nil
+
+minetest.register_abm({
+    nodenames = {"dirt_with_grass"},
+    interval = 100000.0,
+    chance = 1,
+    action = function(pos, node, active_object_count, active_object_count_wider)
+        if ncpos ~= nil then
+            return
+        end
+       
+        if pos.x % 16 ~= 8 or pos.z % 16 ~= 8 then
+            return
+        end
+       
+        pos.y = pos.y + 1
+        n = minetest.env:get_node(pos)
+        print(dump(n))
+        if n.name ~= "air" then
+            return
+        end
+
+        pos.y = pos.y + 2
+        ncpos = pos
+        nctime = os.clock()
+        minetest.env:add_node(ncpos, {name="nyancat"})
+    end
+})
+
+minetest.register_abm({
+    nodenames = {"nyancat"},
+    interval = 1.0,
+    chance = 1,
+    action = function(pos, node, active_object_count, active_object_count_wider)
+        if ncpos == nil then
+            return
+        end
+        if pos.x == ncpos.x and pos.y == ncpos.y and pos.z == ncpos.z then
+            clock = os.clock()
+            if clock - nctime < 0.1 then
+                return
+            end
+            nctime = clock
+           
+            s0 = ncstuff[ncq]
+            ncq = s0[1]
+            s1 = ncstuff[ncq]
+            p0 = pos
+            p1 = {x = p0.x + s0[2], y = p0.y, z = p0.z + s0[3]}
+            p2 = {x = p1.x + s1[2], y = p1.y, z = p1.z + s1[3]}
+            table.insert(ncold, 1, p0)
+            while #ncold >= 10 do
+                minetest.env:add_node(ncold[#ncold], {name="air"})
+                table.remove(ncold, #ncold)
+            end
+            minetest.env:add_node(p0, {name="nyancat_rainbow"})
+            minetest.env:add_node(p1, {name="nyancat", param1=s0[4]})
+            minetest.env:add_node(p2, {name="air"})
+            ncpos = p1
+        end
+    end,
+})--]]
+
 -- LuaNodeMetadata should support something like this
 --meta.setvar("somevariable", {x=0, y=0, z=0})
 --meta.getvar("somevariable") -> {x=0, y=0, z=0}
index a7805faeafadc4c5038c653286f0ea9f9bde0d4b..153faf7ac05a1bbf2103905cf45bca8757222148 100644 (file)
@@ -99,6 +99,7 @@ set(common_SRCS
        nameidmapping.cpp
        tooldef.cpp
        nodedef.cpp
+       craftitemdef.cpp
        luaentity_common.cpp
        scriptapi.cpp
        script.cpp
index 2326ff35db6735adb96e555f6bc37c18d2fa91d6..0f28087d4ca791c47db92a316d7ac3a73bf898d8 100644 (file)
@@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nodemetadata.h"
 #include "nodedef.h"
 #include "tooldef.h"
+#include "craftitemdef.h"
 #include <IFileSystem.h>
 
 /*
@@ -191,11 +192,13 @@ Client::Client(
                MapDrawControl &control,
                IWritableTextureSource *tsrc,
                IWritableToolDefManager *tooldef,
-               IWritableNodeDefManager *nodedef
+               IWritableNodeDefManager *nodedef,
+               IWritableCraftItemDefManager *craftitemdef
 ):
        m_tsrc(tsrc),
        m_tooldef(tooldef),
        m_nodedef(nodedef),
+       m_craftitemdef(craftitemdef),
        m_mesh_update_thread(this),
        m_env(
                new ClientMap(this, this, control,
@@ -207,6 +210,7 @@ Client::Client(
        m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
        m_device(device),
        m_server_ser_ver(SER_FMT_VER_INVALID),
+       m_playeritem(0),
        m_inventory_updated(false),
        m_time_of_day(0),
        m_map_seed(0),
@@ -215,7 +219,8 @@ Client::Client(
        m_texture_receive_progress(0),
        m_textures_received(false),
        m_tooldef_received(false),
-       m_nodedef_received(false)
+       m_nodedef_received(false),
+       m_craftitemdef_received(false)
 {
        m_packetcounter_timer = 0.0;
        //m_delete_unused_sectors_timer = 0.0;
@@ -1628,6 +1633,26 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                m_mesh_update_thread.setRun(true);
                m_mesh_update_thread.Start();
        }
+       else if(command == TOCLIENT_CRAFTITEMDEF)
+       {
+               infostream<<"Client: Received CraftItem definitions: packet size: "
+                               <<datasize<<std::endl;
+
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               m_craftitemdef_received = true;
+
+               // Stop threads while updating content definitions
+               m_mesh_update_thread.stop();
+
+               std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+               m_craftitemdef->deSerialize(tmp_is);
+               
+               // Resume threads
+               m_mesh_update_thread.setRun(true);
+               m_mesh_update_thread.Start();
+       }
        else
        {
                infostream<<"Client: Ignoring unknown command "
@@ -1641,93 +1666,41 @@ void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
        m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
 }
 
-void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
-               v3s16 nodepos_oversurface, u16 item)
+void Client::interact(u8 action, const PointedThing& pointed)
 {
        if(connectedAndInitialized() == false){
-               infostream<<"Client::groundAction() "
+               infostream<<"Client::interact() "
                                "cancelled (not connected)"
                                <<std::endl;
                return;
        }
-       
+
+       std::ostringstream os(std::ios_base::binary);
+
        /*
-               length: 17
                [0] u16 command
                [2] u8 action
-               [3] v3s16 nodepos_undersurface
-               [9] v3s16 nodepos_abovesurface
-               [15] u16 item
+               [3] u16 item
+               [5] u32 length of the next item
+               [9] serialized PointedThing
                actions:
-               0: start digging
-               1: place block
-               2: stop digging (all parameters ignored)
-               3: digging completed
+               0: start digging (from undersurface) or use
+               1: stop digging (all parameters ignored)
+               2: digging completed
+               3: place block or item (to abovesurface)
+               4: use item
        */
-       u8 datasize = 2 + 1 + 6 + 6 + 2;
-       SharedBuffer<u8> data(datasize);
-       writeU16(&data[0], TOSERVER_GROUND_ACTION);
-       writeU8(&data[2], action);
-       writeV3S16(&data[3], nodepos_undersurface);
-       writeV3S16(&data[9], nodepos_oversurface);
-       writeU16(&data[15], item);
-       Send(0, data, true);
-}
-
-void Client::clickActiveObject(u8 button, u16 id, u16 item_i)
-{
-       if(connectedAndInitialized() == false){
-               infostream<<"Client::clickActiveObject() "
-                               "cancelled (not connected)"
-                               <<std::endl;
-               return;
-       }
-
-       Player *player = m_env.getLocalPlayer();
-       if(player == NULL)
-               return;
-
-       ClientActiveObject *obj = m_env.getActiveObject(id);
-       if(obj){
-               if(button == 0){
-                       ToolItem *titem = NULL;
-                       std::string toolname = "";
+       writeU16(os, TOSERVER_INTERACT);
+       writeU8(os, action);
+       writeU16(os, getPlayerItem());
+       std::ostringstream tmp_os(std::ios::binary);
+       pointed.serialize(tmp_os);
+       os<<serializeLongString(tmp_os.str());
 
-                       InventoryList *mlist = player->inventory.getList("main");
-                       if(mlist != NULL)
-                       {
-                               InventoryItem *item = mlist->getItem(item_i);
-                               if(item && (std::string)item->getName() == "ToolItem")
-                               {
-                                       titem = (ToolItem*)item;
-                                       toolname = titem->getToolName();
-                               }
-                       }
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
 
-                       v3f playerpos = player->getPosition();
-                       v3f objpos = obj->getPosition();
-                       v3f dir = (objpos - playerpos).normalize();
-                       
-                       bool disable_send = obj->directReportPunch(toolname, dir);
-                       
-                       if(disable_send)
-                               return;
-               }
-       }
-       
-       /*
-               length: 7
-               [0] u16 command
-               [2] u8 button (0=left, 1=right)
-               [3] u16 id
-               [5] u16 item
-       */
-       u8 datasize = 2 + 1 + 6 + 2 + 2;
-       SharedBuffer<u8> data(datasize);
-       writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
-       writeU8(&data[2], button);
-       writeU16(&data[3], id);
-       writeU16(&data[5], item_i);
+       // Send as reliable
        Send(0, data, true);
 }
 
@@ -2036,9 +2009,12 @@ void Client::setPlayerControl(PlayerControl &control)
 
 void Client::selectPlayerItem(u16 item)
 {
+       //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
+       m_playeritem = item;
+       m_inventory_updated = true;
+
        LocalPlayer *player = m_env.getLocalPlayer();
        assert(player != NULL);
-
        player->wieldItem(item);
 
        sendPlayerItem(item);
@@ -2322,6 +2298,10 @@ ICraftDefManager* Client::getCraftDefManager()
        return NULL;
        //return m_craftdef;
 }
+ICraftItemDefManager* Client::getCraftItemDefManager()
+{
+       return m_craftitemdef;
+}
 ITextureSource* Client::getTextureSource()
 {
        return m_tsrc;
index e5368b17a0ff0d3d48da3d4f1aaaf62f33a49052..2f212dad8d92ce9cf74bd7378df0462896956763 100644 (file)
@@ -37,6 +37,7 @@ class IWritableTextureSource;
 class IWritableToolDefManager;
 class IWritableNodeDefManager;
 //class IWritableCraftDefManager;
+class IWritableCraftItemDefManager;
 
 class ClientNotReadyException : public BaseException
 {
@@ -165,7 +166,8 @@ public:
                        MapDrawControl &control,
                        IWritableTextureSource *tsrc,
                        IWritableToolDefManager *tooldef,
-                       IWritableNodeDefManager *nodedef
+                       IWritableNodeDefManager *nodedef,
+                       IWritableCraftItemDefManager *craftitemdef
        );
        
        ~Client();
@@ -203,9 +205,7 @@ public:
        // Pops out a packet from the packet queue
        //IncomingPacket getPacket();
 
-       void groundAction(u8 action, v3s16 nodepos_undersurface,
-                       v3s16 nodepos_oversurface, u16 item);
-       void clickActiveObject(u8 button, u16 id, u16 item_i);
+       void interact(u8 action, const PointedThing& pointed);
 
        void sendSignNodeText(v3s16 p, std::string text);
        void sendInventoryAction(InventoryAction *a);
@@ -234,6 +234,8 @@ public:
        void setPlayerControl(PlayerControl &control);
 
        void selectPlayerItem(u16 item);
+       u16 getPlayerItem() const
+       { return m_playeritem; }
 
        // Returns true if the inventory of the local player has been
        // updated from the server. If it is true, it is set to false.
@@ -321,6 +323,8 @@ public:
        { return m_tooldef_received; }
        bool nodedefReceived()
        { return m_nodedef_received; }
+       bool craftitemdefReceived()
+       { return m_craftitemdef_received; }
        
        float getRTT(void);
 
@@ -328,6 +332,7 @@ public:
        virtual IToolDefManager* getToolDefManager();
        virtual INodeDefManager* getNodeDefManager();
        virtual ICraftDefManager* getCraftDefManager();
+       virtual ICraftItemDefManager* getCraftItemDefManager();
        virtual ITextureSource* getTextureSource();
        virtual u16 allocateUnknownNodeId(const std::string &name);
 
@@ -356,13 +361,14 @@ private:
        IWritableTextureSource *m_tsrc;
        IWritableToolDefManager *m_tooldef;
        IWritableNodeDefManager *m_nodedef;
+       IWritableCraftItemDefManager *m_craftitemdef;
        MeshUpdateThread m_mesh_update_thread;
        ClientEnvironment m_env;
        con::Connection m_con;
        IrrlichtDevice *m_device;
        // Server serialization version
        u8 m_server_ser_ver;
-       // This is behind m_env_mutex.
+       u16 m_playeritem;
        bool m_inventory_updated;
        core::map<v3s16, bool> m_active_blocks;
        PacketCounter m_packetcounter;
@@ -383,6 +389,7 @@ private:
        bool m_textures_received;
        bool m_tooldef_received;
        bool m_nodedef_received;
+       bool m_craftitemdef_received;
        friend class FarMesh;
 };
 
index 148f99cc37f98121ccd21324bea6766c5fe60e09..ff9fc31f97b56cdb772fe1bbd147420eb8557beb 100644 (file)
@@ -31,6 +31,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
                Add TOCLIENT_TEXTURES
                Add TOCLIENT_TOOLDEF
                Add TOCLIENT_NODEDEF
+               Add TOCLIENT_CRAFTITEMDEF
+               Add TOSERVER_INTERACT
+               Obsolete TOSERVER_CLICK_ACTIVEOBJECT
+               Obsolete TOSERVER_GROUND_ACTION
 */
 
 #define PROTOCOL_VERSION 4
@@ -222,6 +226,13 @@ enum ToClientCommand
                u32 length of the next item
                serialized NodeDefManager
        */
+       
+       TOCLIENT_CRAFTITEMDEF = 0x3b,
+       /*
+               u16 command
+               u32 length of the next item
+               serialized CraftiItemDefManager
+       */
 };
 
 enum ToServerCommand
@@ -283,7 +294,7 @@ enum ToServerCommand
                [8] u16 i
        */
 
-       TOSERVER_CLICK_OBJECT = 0x27,
+       TOSERVER_CLICK_OBJECT = 0x27, // Obsolete
        /*
                length: 13
                [0] u16 command
@@ -293,7 +304,7 @@ enum ToServerCommand
                [11] u16 item
        */
 
-       TOSERVER_GROUND_ACTION = 0x28,
+       TOSERVER_GROUND_ACTION = 0x28, // Obsolete
        /*
                length: 17
                [0] u16 command
@@ -312,7 +323,7 @@ enum ToServerCommand
 
        // (oops, there is some gap here)
 
-       TOSERVER_SIGNTEXT = 0x30, // Old signs
+       TOSERVER_SIGNTEXT = 0x30, // Old signs, obsolete
        /*
                u16 command
                v3s16 blockpos
@@ -341,7 +352,7 @@ enum ToServerCommand
                textdata
        */
 
-       TOSERVER_CLICK_ACTIVEOBJECT = 0x34,
+       TOSERVER_CLICK_ACTIVEOBJECT = 0x34, // Obsolete
        /*
                length: 7
                [0] u16 command
@@ -377,6 +388,24 @@ enum ToServerCommand
        /*
                u16 TOSERVER_RESPAWN
        */
+
+       TOSERVER_INTERACT = 0x39,
+       /*
+               [0] u16 command
+               [2] u8 action
+               [3] u16 item
+               [5] u32 length of the next item
+               [9] serialized PointedThing
+               actions:
+               0: start digging (from undersurface) or use
+               1: stop digging (all parameters ignored)
+               2: digging completed
+               3: place block or item (to abovesurface)
+               4: use item
+
+               (Obsoletes TOSERVER_GROUND_ACTION and TOSERVER_CLICK_ACTIVEOBJECT.)
+       */
+       
 };
 
 inline SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time)
index b013069aaf673858c99945d63f2c3e616e7f0883..414d63f2d38ee03d8e1ac35afe5eaf24a7502d36 100644 (file)
@@ -251,24 +251,6 @@ void ItemSAO::punch(ServerActiveObject *puncher)
                delete item;
 }
 
-void ItemSAO::rightClick(ServerActiveObject *clicker)
-{
-       infostream<<__FUNCTION_NAME<<std::endl;
-       InventoryItem *item = createInventoryItem();
-       if(item == NULL)
-               return;
-       
-       bool to_be_deleted = item->use(m_env, clicker);
-
-       if(to_be_deleted)
-               m_removed = true;
-       else
-               // Reflect changes to the item here
-               m_inventorystring = item->getItemString();
-       
-       delete item; // Delete temporary item
-}
-
 /*
        RatSAO
 */
index c5e1471bc2c4666fa7d172f913c1e6145392f438..428998799f5089b23bc53b51720ca9ce242a124f 100644 (file)
@@ -51,7 +51,6 @@ public:
        std::string getStaticData();
        InventoryItem* createInventoryItem();
        void punch(ServerActiveObject *puncher);
-       void rightClick(ServerActiveObject *clicker);
        float getMinimumSavedMovement(){ return 0.1*BS; }
 private:
        std::string m_inventorystring;
index 5bbd92d55494a267651dfee22c60efe27cb4252b..abeceae0b443e88e4e5150932c81dd31b5d0fa31 100644 (file)
@@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "profiler.h"
 #include "mainmenumanager.h"
+#include "craftitemdef.h"
 #include "gettext.h"
 #include "log.h"
 #include "filesys.h"
@@ -87,8 +88,6 @@ Queue<InventoryAction*> inventory_action_queue;
 // This is a copy of the inventory that the client's environment has
 Inventory local_inventory;
 
-u16 g_selected_item = 0;
-
 /*
        Text input system
 */
@@ -159,7 +158,7 @@ private:
 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
                ITextureSource *tsrc,
                v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
-               Inventory *inventory, s32 halfheartcount)
+               Inventory *inventory, s32 halfheartcount, u16 playeritem)
 {
        InventoryList *mainlist = inventory->getList("main");
        if(mainlist == NULL)
@@ -190,7 +189,7 @@ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
                core::rect<s32> rect = imgrect + pos
                                + v2s32(padding+i*(imgsize+padding*2), padding);
                
-               if(g_selected_item == i)
+               if(playeritem == i)
                {
                        video::SColor c_outside(255,255,0,0);
                        //video::SColor c_outside(255,0,0,0);
@@ -288,16 +287,65 @@ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
        }
 }
 
+/*
+       Check if a node is pointable
+*/
+inline bool isPointableNode(const MapNode& n,
+               Client *client, bool liquids_pointable)
+{
+       const ContentFeatures &features = client->getNodeDefManager()->get(n);
+       return features.pointable ||
+               (liquids_pointable && features.isLiquid());
+}
+
 /*
        Find what the player is pointing at
 */
-void getPointedNode(Client *client, v3f player_position,
+PointedThing getPointedThing(Client *client, v3f player_position,
                v3f camera_direction, v3f camera_position,
-               bool &nodefound, core::line3d<f32> shootline,
-               v3s16 &nodepos, v3s16 &neighbourpos,
-               core::aabbox3d<f32> &nodehilightbox,
-               f32 d)
+               core::line3d<f32> shootline, f32 d,
+               bool liquids_pointable,
+               bool look_for_object,
+               core::aabbox3d<f32> &hilightbox,
+               bool &should_show_hilightbox,
+               ClientActiveObject *&selected_object)
 {
+       PointedThing result;
+
+       hilightbox = core::aabbox3d<f32>(0,0,0,0,0,0);
+       should_show_hilightbox = false;
+       selected_object = NULL;
+
+       // First try to find a pointed at active object
+       if(look_for_object)
+       {
+               selected_object = client->getSelectedActiveObject(d*BS,
+                               camera_position, shootline);
+       }
+       if(selected_object != NULL)
+       {
+               core::aabbox3d<f32> *selection_box
+                       = selected_object->getSelectionBox();
+               // Box should exist because object was returned in the
+               // first place
+               assert(selection_box);
+
+               v3f pos = selected_object->getPosition();
+
+               hilightbox = core::aabbox3d<f32>(
+                               selection_box->MinEdge + pos,
+                               selection_box->MaxEdge + pos
+               );
+
+               should_show_hilightbox = selected_object->doShowSelectionBox();
+
+               result.type = POINTEDTHING_OBJECT;
+               result.object_id = selected_object->getId();
+               return result;
+       }
+
+       // That didn't work, try to find a pointed at node
+
        f32 mindistance = BS * 1001;
        
        v3s16 pos_i = floatToInt(player_position, BS);
@@ -321,13 +369,13 @@ void getPointedNode(Client *client, v3f player_position,
                try
                {
                        n = client->getNode(v3s16(x,y,z));
-                       if(client->getNodeDefManager()->get(n).pointable == false)
-                               continue;
                }
                catch(InvalidPositionException &e)
                {
                        continue;
                }
+               if(!isPointableNode(n, client, liquids_pointable))
+                       continue;
 
                v3s16 np(x,y,z);
                v3f npf = intToFloat(np, BS);
@@ -402,11 +450,12 @@ void getPointedNode(Client *client, v3f player_position,
                                        continue;
                                if(!faceboxes[i].intersectsWithLine(shootline))
                                        continue;
-                               nodefound = true;
-                               nodepos = np;
-                               neighbourpos = np+facedirs[i];
+                               result.type = POINTEDTHING_NODE;
+                               result.node_undersurface = np;
+                               result.node_abovesurface = np+facedirs[i];
                                mindistance = distance;
-                               nodehilightbox = box;
+                               hilightbox = box;
+                               should_show_hilightbox = true;
                        }
                }
                else if(f.selection_box.type == NODEBOX_WALLMOUNTED)
@@ -458,11 +507,12 @@ void getPointedNode(Client *client, v3f player_position,
                        {
                                if(box.intersectsWithLine(shootline))
                                {
-                                       nodefound = true;
-                                       nodepos = np;
-                                       neighbourpos = np;
+                                       result.type = POINTEDTHING_NODE;
+                                       result.node_undersurface = np;
+                                       result.node_abovesurface = np;
                                        mindistance = distance;
-                                       nodehilightbox = box;
+                                       hilightbox = box;
+                                       should_show_hilightbox = true;
                                }
                        }
                }
@@ -498,25 +548,28 @@ void getPointedNode(Client *client, v3f player_position,
 
                                        if(facebox.intersectsWithLine(shootline))
                                        {
-                                               nodefound = true;
-                                               nodepos = np;
-                                               neighbourpos = np + dirs[i];
+                                               result.type = POINTEDTHING_NODE;
+                                               result.node_undersurface = np;
+                                               result.node_abovesurface = np + dirs[i];
                                                mindistance = distance;
 
-                                               //nodehilightbox = facebox;
+                                               //hilightbox = facebox;
 
                                                const float d = 0.502;
                                                core::aabbox3d<f32> nodebox
                                                                (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
-                                               v3f nodepos_f = intToFloat(nodepos, BS);
+                                               v3f nodepos_f = intToFloat(np, BS);
                                                nodebox.MinEdge += nodepos_f;
                                                nodebox.MaxEdge += nodepos_f;
-                                               nodehilightbox = nodebox;
+                                               hilightbox = nodebox;
+                                               should_show_hilightbox = true;
                                        }
                                } // if distance < mindistance
                        } // for dirs
                } // regular block
        } // for coords
+
+       return result;
 }
 
 void update_skybox(video::IVideoDriver* driver, ITextureSource *tsrc,
@@ -642,6 +695,8 @@ void the_game(
        IWritableToolDefManager *tooldef = createToolDefManager();
        // Create node definition manager
        IWritableNodeDefManager *nodedef = createNodeDefManager();
+       // Create CraftItem definition manager
+       IWritableCraftItemDefManager *craftitemdef = createCraftItemDefManager();
 
        // Add chat log output for errors to be shown in chat
        LogOutputBuffer chat_log_error_buf(LMT_ERROR);
@@ -670,7 +725,7 @@ void the_game(
        MapDrawControl draw_control;
 
        Client client(device, playername.c_str(), password, draw_control,
-                       tsrc, tooldef, nodedef);
+                       tsrc, tooldef, nodedef, craftitemdef);
        
        // Client acts as our GameDef
        IGameDef *gamedef = &client;
@@ -781,7 +836,8 @@ void the_game(
                        // End condition
                        if(client.texturesReceived() &&
                                        client.tooldefReceived() &&
-                                       client.nodedefReceived()){
+                                       client.nodedefReceived() &&
+                                       client.craftitemdefReceived()){
                                got_content = true;
                                break;
                        }
@@ -801,6 +857,8 @@ void the_game(
                        ss<<L" Tool definitions\n";
                        ss<<(client.nodedefReceived()?L"[X]":L"[  ]");
                        ss<<L" Node definitions\n";
+                       ss<<(client.craftitemdefReceived()?L"[X]":L"[  ]");
+                       ss<<L" Item definitions\n";
                        //ss<<(client.texturesReceived()?L"[X]":L"[  ]");
                        ss<<L"["<<(int)(client.textureReceiveProgress()*100+0.5)<<L"%] ";
                        ss<<L" Textures\n";
@@ -937,10 +995,11 @@ void the_game(
 
        core::list<float> frametime_log;
 
-       float nodig_delay_counter = 0.0;
+       float nodig_delay_timer = 0.0;
        float dig_time = 0.0;
        u16 dig_index = 0;
-       v3s16 nodepos_old(-32768,-32768,-32768);
+       PointedThing pointed_old;
+       bool digging = false;
        bool ldown_for_dig = false;
 
        float damage_flash_timer = 0;
@@ -1083,7 +1142,10 @@ void the_game(
 
                /* Run timers */
 
-               object_hit_delay_timer -= dtime;
+               if(nodig_delay_timer >= 0)
+                       nodig_delay_timer -= dtime;
+               if(object_hit_delay_timer >= 0)
+                       object_hit_delay_timer -= dtime;
 
                g_profiler->add("Elapsed time", dtime);
                g_profiler->avg("FPS", 1./dtime);
@@ -1228,7 +1290,7 @@ void the_game(
                        a->count = 0;
                        a->from_inv = "current_player";
                        a->from_list = "main";
-                       a->from_i = g_selected_item;
+                       a->from_i = client.getPlayerItem();
                        client.inventoryAction(a);
                }
                else if(input->wasKeyDown(getKeySetting("keymap_inventory")))
@@ -1369,6 +1431,7 @@ void the_game(
                }
 
                // Item selection with mouse wheel
+               u16 new_playeritem = client.getPlayerItem();
                {
                        s32 wheel = input->getMouseWheel();
                        u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
@@ -1376,17 +1439,17 @@ void the_game(
 
                        if(wheel < 0)
                        {
-                               if(g_selected_item < max_item)
-                                       g_selected_item++;
+                               if(new_playeritem < max_item)
+                                       new_playeritem++;
                                else
-                                       g_selected_item = 0;
+                                       new_playeritem = 0;
                        }
                        else if(wheel > 0)
                        {
-                               if(g_selected_item > 0)
-                                       g_selected_item--;
+                               if(new_playeritem > 0)
+                                       new_playeritem--;
                                else
-                                       g_selected_item = max_item;
+                                       new_playeritem = max_item;
                        }
                }
                
@@ -1398,10 +1461,10 @@ void the_game(
                        {
                                if(i < PLAYER_INVENTORY_SIZE && i < hotbar_itemcount)
                                {
-                                       g_selected_item = i;
+                                       new_playeritem = i;
 
                                        infostream<<"Selected item: "
-                                                       <<g_selected_item<<std::endl;
+                                                       <<new_playeritem<<std::endl;
                                }
                        }
                }
@@ -1623,116 +1686,124 @@ void the_game(
                //timer2.stop();
                //TimeTaker //timer3("//timer3");
 
+               /*
+                       For interaction purposes, get info about the held item
+                       - Is it a tool, and what is the toolname?
+                       - Is it a usable item?
+                       - Can it point to liquids?
+               */
+               std::string playeritem_toolname = "";
+               bool playeritem_usable = false;
+               bool playeritem_liquids_pointable = false;
+               {
+                       InventoryList *mlist = local_inventory.getList("main");
+                       if(mlist != NULL)
+                       {
+                               InventoryItem *item = mlist->getItem(client.getPlayerItem());
+                               if(item)
+                               {
+                                       if((std::string)item->getName() == "ToolItem")
+                                       {
+                                               ToolItem *titem = (ToolItem*)item;
+                                               playeritem_toolname = titem->getToolName();
+                                       }
+
+                                       playeritem_usable = item->isUsable();
+
+                                       playeritem_liquids_pointable =
+                                               item->areLiquidsPointable();
+                               }
+                       }
+               }
+
                /*
                        Calculate what block is the crosshair pointing to
                */
                
                //u32 t1 = device->getTimer()->getRealTime();
                
-               //f32 d = 4; // max. distance
                f32 d = 4; // max. distance
                core::line3d<f32> shootline(camera_position,
                                camera_position + camera_direction * BS * (d+1));
 
-               ClientActiveObject *selected_active_object
-                               = client.getSelectedActiveObject
-                                       (d*BS, camera_position, shootline);
-               
-               bool left_punch = false;
-               bool left_punch_muted = false;
-
-               if(selected_active_object != NULL && !ldown_for_dig)
-               {
-                       /* Clear possible cracking animation */
-                       if(nodepos_old != v3s16(-32768,-32768,-32768))
-                       {
-                               client.clearTempMod(nodepos_old);
-                               dig_time = 0.0;
-                               nodepos_old = v3s16(-32768,-32768,-32768);
-                       }
-
-                       //infostream<<"Client returned selected_active_object != NULL"<<std::endl;
-                       
-                       core::aabbox3d<f32> *selection_box
-                                       = selected_active_object->getSelectionBox();
-                       // Box should exist because object was returned in the
-                       // first place
-                       assert(selection_box);
+               core::aabbox3d<f32> hilightbox;
+               bool should_show_hilightbox = false;
+               ClientActiveObject *selected_object = NULL;
 
-                       v3f pos = selected_active_object->getPosition();
+               PointedThing pointed = getPointedThing(
+                               // input
+                               &client, player_position, camera_direction,
+                               camera_position, shootline, d,
+                               playeritem_liquids_pointable, !ldown_for_dig,
+                               // output
+                               hilightbox, should_show_hilightbox,
+                               selected_object);
 
-                       core::aabbox3d<f32> box_on_map(
-                                       selection_box->MinEdge + pos,
-                                       selection_box->MaxEdge + pos
-                       );
-                       
-                       if(selected_active_object->doShowSelectionBox())
-                               hilightboxes.push_back(box_on_map);
+               if(pointed != pointed_old)
+               {
+                       infostream<<"Pointing at "<<pointed.dump()<<std::endl;
+                       //dstream<<"Pointing at "<<pointed.dump()<<std::endl;
+               }
 
-                       //infotext = narrow_to_wide("A ClientActiveObject");
-                       infotext = narrow_to_wide(selected_active_object->infoText());
+               /*
+                       Visualize selection
+               */
+               if(should_show_hilightbox)
+                       hilightboxes.push_back(hilightbox);
 
-                       //if(input->getLeftClicked())
-                       if(input->getLeftState())
+               /*
+                       Stop digging when
+                       - releasing left mouse button
+                       - pointing away from node
+               */
+               if(digging)
+               {
+                       if(input->getLeftReleased())
                        {
-                               bool do_punch = false;
-                               bool do_punch_damage = false;
-                               if(object_hit_delay_timer <= 0.0){
-                                       do_punch = true;
-                                       do_punch_damage = true;
-                                       object_hit_delay_timer = object_hit_delay;
-                               }
-                               if(input->getLeftClicked()){
-                                       do_punch = true;
-                               }
-                               if(do_punch){
-                                       infostream<<"Left-clicked object"<<std::endl;
-                                       left_punch = true;
+                               infostream<<"Left button released"
+                                       <<" (stopped digging)"<<std::endl;
+                               digging = false;
+                       }
+                       else if(pointed != pointed_old)
+                       {
+                               if (pointed.type == POINTEDTHING_NODE
+                                       && pointed_old.type == POINTEDTHING_NODE
+                                       && pointed.node_undersurface == pointed_old.node_undersurface)
+                               {
+                                       // Still pointing to the same node,
+                                       // but a different face. Don't reset.
                                }
-                               if(do_punch_damage){
-                                       client.clickActiveObject(0,
-                                                       selected_active_object->getId(), g_selected_item);
+                               else
+                               {
+                                       infostream<<"Pointing away from node"
+                                               <<" (stopped digging)"<<std::endl;
+                                       digging = false;
                                }
                        }
-                       else if(input->getRightClicked())
+                       if(!digging)
                        {
-                               infostream<<"Right-clicked object"<<std::endl;
-                               client.clickActiveObject(1,
-                                               selected_active_object->getId(), g_selected_item);
+                               client.interact(1, pointed_old);
+                               client.clearTempMod(pointed_old.node_undersurface);
+                               dig_time = 0.0;
                        }
                }
-               else // selected_object == NULL
+               if(!digging && ldown_for_dig && !input->getLeftState())
                {
+                       ldown_for_dig = false;
+               }
 
-               /*
-                       Find out which node we are pointing at
-               */
-               
-               bool nodefound = false;
-               v3s16 nodepos;
-               v3s16 neighbourpos;
-               core::aabbox3d<f32> nodehilightbox;
-
-               getPointedNode(&client, player_position,
-                               camera_direction, camera_position,
-                               nodefound, shootline,
-                               nodepos, neighbourpos,
-                               nodehilightbox, d);
-       
-               if(!nodefound){
-                       if(nodepos_old != v3s16(-32768,-32768,-32768))
-                       {
-                               client.clearTempMod(nodepos_old);
-                               dig_time = 0.0;
-                               nodepos_old = v3s16(-32768,-32768,-32768);
-                               ldown_for_dig = false;
-                       }
-               } else {
-                       /*
-                               Visualize selection
-                       */
+               bool left_punch = false;
+               bool left_punch_muted = false;
 
-                       hilightboxes.push_back(nodehilightbox);
+               if(playeritem_usable && input->getLeftState())
+               {
+                       if(input->getLeftClicked())
+                               client.interact(4, pointed);
+               }
+               else if(pointed.type == POINTEDTHING_NODE)
+               {
+                       v3s16 nodepos = pointed.node_undersurface;
+                       v3s16 neighbourpos = pointed.node_abovesurface;
 
                        /*
                                Check information text of node
@@ -1744,139 +1815,94 @@ void the_game(
                                infotext = narrow_to_wide(meta->infoText());
                        }
                        
-                       //MapNode node = client.getNode(nodepos);
-
                        /*
                                Handle digging
                        */
                        
-                       if(input->getLeftReleased())
-                       {
-                               client.clearTempMod(nodepos);
-                               dig_time = 0.0;
-                               ldown_for_dig = false;
-                       }
                        
-                       if(nodig_delay_counter > 0.0)
+                       if(nodig_delay_timer <= 0.0 && input->getLeftState())
                        {
-                               nodig_delay_counter -= dtime;
-                       }
-                       else
-                       {
-                               if(nodepos != nodepos_old)
+                               if(!digging)
                                {
-                                       infostream<<"Pointing at ("<<nodepos.X<<","
-                                                       <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
+                                       infostream<<"Started digging"<<std::endl;
+                                       client.interact(0, pointed);
+                                       digging = true;
+                                       ldown_for_dig = true;
+                               }
+                               MapNode n = client.getNode(nodepos);
 
-                                       if(nodepos_old != v3s16(-32768,-32768,-32768))
-                                       {
-                                               client.clearTempMod(nodepos_old);
-                                               dig_time = 0.0;
-                                               nodepos_old = v3s16(-32768,-32768,-32768);
-                                       }
+                               // Get digging properties for material and tool
+                               content_t material = n.getContent();
+                               ToolDiggingProperties tp =
+                                               tooldef->getDiggingProperties(playeritem_toolname);
+                               DiggingProperties prop =
+                                               getDiggingProperties(material, &tp, nodedef);
+
+                               float dig_time_complete = 0.0;
+
+                               if(prop.diggable == false)
+                               {
+                                       /*infostream<<"Material "<<(int)material
+                                                       <<" not diggable with \""
+                                                       <<playeritem_toolname<<"\""<<std::endl;*/
+                                       // I guess nobody will wait for this long
+                                       dig_time_complete = 10000000.0;
+                               }
+                               else
+                               {
+                                       dig_time_complete = prop.time;
                                }
 
-                               if(input->getLeftClicked() ||
-                                               (input->getLeftState() && nodepos != nodepos_old))
+                               if(dig_time_complete >= 0.001)
                                {
-                                       infostream<<"Started digging"<<std::endl;
-                                       client.groundAction(0, nodepos, neighbourpos, g_selected_item);
+                                       dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
+                                                       * dig_time/dig_time_complete);
                                }
-                               if(input->getLeftClicked())
+                               // This is for torches
+                               else
                                {
-                                       client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
-                                       ldown_for_dig = true;
+                                       dig_index = CRACK_ANIMATION_LENGTH;
                                }
-                               if(input->getLeftState())
+
+                               if(dig_index < CRACK_ANIMATION_LENGTH)
                                {
-                                       MapNode n = client.getNode(nodepos);
-                               
-                                       // Get tool name. Default is "" = bare hands
-                                       std::string toolname = "";
-                                       InventoryList *mlist = local_inventory.getList("main");
-                                       if(mlist != NULL)
-                                       {
-                                               InventoryItem *item = mlist->getItem(g_selected_item);
-                                               if(item && (std::string)item->getName() == "ToolItem")
-                                               {
-                                                       ToolItem *titem = (ToolItem*)item;
-                                                       toolname = titem->getToolName();
-                                               }
-                                       }
+                                       //TimeTaker timer("client.setTempMod");
+                                       //infostream<<"dig_index="<<dig_index<<std::endl;
+                                       client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
+                               }
+                               else
+                               {
+                                       infostream<<"Digging completed"<<std::endl;
+                                       client.interact(2, pointed);
+                                       client.clearTempMod(nodepos);
+                                       client.removeNode(nodepos);
 
-                                       // Get digging properties for material and tool
-                                       content_t material = n.getContent();
-                                       ToolDiggingProperties tp =
-                                                       tooldef->getDiggingProperties(toolname);
-                                       DiggingProperties prop =
-                                                       getDiggingProperties(material, &tp, nodedef);
-                                       
-                                       float dig_time_complete = 0.0;
+                                       dig_time = 0;
+                                       digging = false;
 
-                                       if(prop.diggable == false)
-                                       {
-                                               /*infostream<<"Material "<<(int)material
-                                                               <<" not diggable with \""
-                                                               <<toolname<<"\""<<std::endl;*/
-                                               // I guess nobody will wait for this long
-                                               dig_time_complete = 10000000.0;
-                                       }
-                                       else
-                                       {
-                                               dig_time_complete = prop.time;
-                                       }
-                                       
-                                       if(dig_time_complete >= 0.001)
-                                       {
-                                               dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
-                                                               * dig_time/dig_time_complete);
-                                       }
-                                       // This is for torches
-                                       else
-                                       {
-                                               dig_index = CRACK_ANIMATION_LENGTH;
-                                       }
+                                       nodig_delay_timer = dig_time_complete
+                                                       / (float)CRACK_ANIMATION_LENGTH;
 
-                                       if(dig_index < CRACK_ANIMATION_LENGTH)
+                                       // We don't want a corresponding delay to
+                                       // very time consuming nodes
+                                       if(nodig_delay_timer > 0.5)
                                        {
-                                               //TimeTaker timer("client.setTempMod");
-                                               //infostream<<"dig_index="<<dig_index<<std::endl;
-                                               client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
+                                               nodig_delay_timer = 0.5;
                                        }
-                                       else
+                                       // We want a slight delay to very little
+                                       // time consuming nodes
+                                       float mindelay = 0.15;
+                                       if(nodig_delay_timer < mindelay)
                                        {
-                                               infostream<<"Digging completed"<<std::endl;
-                                               client.groundAction(3, nodepos, neighbourpos, g_selected_item);
-                                               client.clearTempMod(nodepos);
-                                               client.removeNode(nodepos);
-
-                                               dig_time = 0;
-
-                                               nodig_delay_counter = dig_time_complete
-                                                               / (float)CRACK_ANIMATION_LENGTH;
-
-                                               // We don't want a corresponding delay to
-                                               // very time consuming nodes
-                                               if(nodig_delay_counter > 0.5)
-                                               {
-                                                       nodig_delay_counter = 0.5;
-                                               }
-                                               // We want a slight delay to very little
-                                               // time consuming nodes
-                                               float mindelay = 0.15;
-                                               if(nodig_delay_counter < mindelay)
-                                               {
-                                                       nodig_delay_counter = mindelay;
-                                               }
+                                               nodig_delay_timer = mindelay;
                                        }
+                               }
 
-                                       dig_time += dtime;
+                               dig_time += dtime;
 
-                                       camera.setDigging(0);  // left click animation
-                               }
+                               camera.setDigging(0);  // left click animation
                        }
-                       
-                       
+
                        if(input->getRightClicked())
                        {
                                infostream<<"Ground right-clicked"<<std::endl;
@@ -1933,15 +1959,50 @@ void the_game(
                                // Otherwise report right click to server
                                else
                                {
-                                       client.groundAction(1, nodepos, neighbourpos, g_selected_item);
+                                       client.interact(3, pointed);
                                        camera.setDigging(1);  // right click animation
                                }
                        }
-                       
-                       nodepos_old = nodepos;
+               }
+               else if(pointed.type == POINTEDTHING_OBJECT)
+               {
+                       infotext = narrow_to_wide(selected_object->infoText());
+
+                       //if(input->getLeftClicked())
+                       if(input->getLeftState())
+                       {
+                               bool do_punch = false;
+                               bool do_punch_damage = false;
+                               if(object_hit_delay_timer <= 0.0){
+                                       do_punch = true;
+                                       do_punch_damage = true;
+                                       object_hit_delay_timer = object_hit_delay;
+                               }
+                               if(input->getLeftClicked()){
+                                       do_punch = true;
+                               }
+                               if(do_punch){
+                                       infostream<<"Left-clicked object"<<std::endl;
+                                       left_punch = true;
+                               }
+                               if(do_punch_damage){
+                                       // Report direct punch
+                                       v3f objpos = selected_object->getPosition();
+                                       v3f dir = (objpos - player_position).normalize();
+
+                                       bool disable_send = selected_object->directReportPunch(playeritem_toolname, dir);
+                                       if(!disable_send)
+                                               client.interact(0, pointed);
+                               }
+                       }
+                       else if(input->getRightClicked())
+                       {
+                               infostream<<"Right-clicked object"<<std::endl;
+                               client.interact(3, pointed);  // place
+                       }
                }
 
-               } // selected_object == NULL
+               pointed_old = pointed;
                
                if(left_punch || (input->getLeftClicked() && !left_punch_muted))
                {
@@ -1950,20 +2011,7 @@ void the_game(
 
                input->resetLeftClicked();
                input->resetRightClicked();
-               
-               if(input->getLeftReleased())
-               {
-                       infostream<<"Left button released (stopped digging)"
-                                       <<std::endl;
-                       client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
-                       ldown_for_dig = false;
-               }
-               if(input->getRightReleased())
-               {
-                       //inostream<<DTIME<<"Right released"<<std::endl;
-                       // Nothing here
-               }
-               
+
                input->resetLeftReleased();
                input->resetRightReleased();
                
@@ -2210,12 +2258,12 @@ void the_game(
                        Inventory
                */
                
-               static u16 old_selected_item = 65535;
-               if(client.getLocalInventoryUpdated()
-                               || g_selected_item != old_selected_item)
+               if(client.getPlayerItem() != new_playeritem)
+               {
+                       client.selectPlayerItem(new_playeritem);
+               }
+               if(client.getLocalInventoryUpdated())
                {
-                       client.selectPlayerItem(g_selected_item);
-                       old_selected_item = g_selected_item;
                        //infostream<<"Updating local inventory"<<std::endl;
                        client.getLocalInventory(local_inventory);
 
@@ -2223,7 +2271,7 @@ void the_game(
                        InventoryList *mlist = local_inventory.getList("main");
                        InventoryItem *item = NULL;
                        if(mlist != NULL)
-                               item = mlist->getItem(g_selected_item);
+                               item = mlist->getItem(client.getPlayerItem());
                        camera.wield(item, gamedef);
                }
                
@@ -2350,7 +2398,7 @@ void the_game(
                        draw_hotbar(driver, font, tsrc,
                                        v2s32(displaycenter.X, screensize.Y),
                                        hotbar_imagesize, hotbar_itemcount, &local_inventory,
-                                       client.getHP());
+                                       client.getHP(), client.getPlayerItem());
                }
 
                /*
index bca27a21a426de68ea073475b9267d77badc0ce6..c450568b7eda139fdafd414e42ed154ff16cf877 100644 (file)
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 class IToolDefManager;
 class INodeDefManager;
 class ICraftDefManager;
-//class IItemDefManager; //TODO
+class ICraftItemDefManager;
 // Mineral too?
 class ITextureSource;
 
@@ -42,7 +42,7 @@ public:
        virtual IToolDefManager* getToolDefManager()=0;
        virtual INodeDefManager* getNodeDefManager()=0;
        virtual ICraftDefManager* getCraftDefManager()=0;
-       //virtual IItemDefManager* getItemDefManager()=0;
+       virtual ICraftItemDefManager* getCraftItemDefManager()=0;
 
        // This is always thread-safe, but referencing the irrlicht texture
        // pointers in other threads than main thread will make things explode.
@@ -55,6 +55,7 @@ public:
        IToolDefManager* tdef(){return getToolDefManager();}
        INodeDefManager* ndef(){return getNodeDefManager();}
        ICraftDefManager* cdef(){return getCraftDefManager();}
+       ICraftItemDefManager* cidef(){return getCraftItemDefManager();}
        ITextureSource* tsrc(){return getTextureSource();}
 };
 
index 4e897d9ff343e26827cf96462c913453784465be..d276e61c9f7e84c08abeafcf38d99abd2a04c5de 100644 (file)
@@ -33,7 +33,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "nodedef.h"
 #include "tooldef.h"
+#include "craftitemdef.h"
 #include "gamedef.h"
+#include "scriptapi.h"
 #include "strfnd.h"
 
 /*
@@ -182,18 +184,52 @@ std::string InventoryItem::getItemString() {
        return os.str();
 }
 
-ServerActiveObject* InventoryItem::createSAO(ServerEnvironment *env, v3f pos)
+bool InventoryItem::dropOrPlace(ServerEnvironment *env,
+               ServerActiveObject *dropper,
+               v3f pos, bool place, s16 count)
 {
        /*
-               Create an ItemSAO
+               Ensure that the block is loaded so that the item
+               can properly be added to the static list too
+       */
+       v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
+       MapBlock *block = env->getMap().emergeBlock(blockpos, false);
+       if(block==NULL)
+       {
+               infostream<<"InventoryItem::dropOrPlace(): FAIL: block not found: "
+                               <<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z
+                               <<std::endl;
+               return false;
+       }
+
+       /*
+               Take specified number of items,
+               but limit to getDropCount().
        */
-       pos.Y -= BS*0.25; // let it drop a bit
-       // Randomize a bit
-       pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
-       pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
-       // Create object
-       ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
-       return obj;
+       s16 dropcount = getDropCount();
+       if(count < 0 || count > dropcount)
+               count = dropcount;
+       if(count < 0 || count > getCount());
+               count = getCount();
+       if(count > 0)
+       {
+               /*
+                       Create an ItemSAO
+               */
+               pos.Y -= BS*0.25; // let it drop a bit
+               // Randomize a bit
+               //pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
+               //pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
+               // Create object
+               ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
+               // Add the object to the environment
+               env->addActiveObject(obj);
+               infostream<<"Dropped item"<<std::endl;
+
+               setCount(getCount() - count);
+       }
+
+       return getCount() < 1; // delete the item?
 }
 
 /*
@@ -307,75 +343,150 @@ video::ITexture * ToolItem::getImageRaw() const
 #ifndef SERVER
 video::ITexture * CraftItem::getImage() const
 {
+       ICraftItemDefManager *cidef = m_gamedef->cidef();
        ITextureSource *tsrc = m_gamedef->tsrc();
-
-       std::string name = item_craft_get_image_name(m_subname, m_gamedef);
-
-       // Get such a texture
-       return tsrc->getTextureRaw(name);
+       std::string imagename = cidef->getImagename(m_subname);
+       return tsrc->getTextureRaw(imagename);
 }
 #endif
 
-ServerActiveObject* CraftItem::createSAO(ServerEnvironment *env, v3f pos)
+u16 CraftItem::getStackMax() const
 {
-       // Special cases
-       ServerActiveObject *obj = item_craft_create_object(m_subname, env, pos);
-       if(obj)
-               return obj;
-       // Default
-       return InventoryItem::createSAO(env, pos);
+       ICraftItemDefManager *cidef = m_gamedef->cidef();
+       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
+       if(def == NULL)
+               return InventoryItem::getStackMax();
+       return def->stack_max;
 }
 
-u16 CraftItem::getDropCount() const
+bool CraftItem::isUsable() const
 {
-       // Special cases
-       s16 dc = item_craft_get_drop_count(m_subname, m_gamedef);
-       if(dc != -1)
-               return dc;
-       // Default
-       return InventoryItem::getDropCount();
+       ICraftItemDefManager *cidef = m_gamedef->cidef();
+       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
+       return def != NULL && def->usable;
 }
 
 bool CraftItem::isCookable() const
 {
-       return item_craft_is_cookable(m_subname, m_gamedef);
+       ICraftItemDefManager *cidef = m_gamedef->cidef();
+       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
+       return def != NULL && def->cookresult_item != "";
 }
 
 InventoryItem *CraftItem::createCookResult() const
 {
-       return item_craft_create_cook_result(m_subname, m_gamedef);
+       ICraftItemDefManager *cidef = m_gamedef->cidef();
+       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
+       if(def == NULL)
+               return InventoryItem::createCookResult();
+       std::istringstream is(def->cookresult_item, std::ios::binary);
+       return InventoryItem::deSerialize(is, m_gamedef);
 }
 
 float CraftItem::getCookTime() const
 {
-       return 3.0;
+       ICraftItemDefManager *cidef = m_gamedef->cidef();
+       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
+       if (def == NULL)
+               return InventoryItem::getCookTime();
+       return def->furnace_cooktime;
 }
 
 float CraftItem::getBurnTime() const
 {
-       if(m_subname == "lump_of_coal")
-               return 40;
-       return -1;
+       ICraftItemDefManager *cidef = m_gamedef->cidef();
+       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
+       if (def == NULL)
+               return InventoryItem::getBurnTime();
+       return def->furnace_burntime;
+}
+
+s16 CraftItem::getDropCount() const
+{
+       // Special cases
+       ICraftItemDefManager *cidef = m_gamedef->cidef();
+       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
+       if(def != NULL && def->dropcount >= 0)
+               return def->dropcount;
+       // Default
+       return InventoryItem::getDropCount();
+}
+
+bool CraftItem::areLiquidsPointable() const
+{
+       ICraftItemDefManager *cidef = m_gamedef->cidef();
+       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
+       return def != NULL && def->liquids_pointable;
 }
 
-bool CraftItem::use(ServerEnvironment *env, ServerActiveObject *user)
+bool CraftItem::dropOrPlace(ServerEnvironment *env,
+               ServerActiveObject *dropper,
+               v3f pos, bool place, s16 count)
 {
-       if(!item_craft_is_eatable(m_subname, m_gamedef))
+       if(count == 0)
                return false;
-       
-       u16 result_count = getCount() - 1; // Eat one at a time
-       s16 hp_change = item_craft_eat_hp_change(m_subname, m_gamedef);
-       s16 hp = user->getHP();
-       hp += hp_change;
-       if(hp < 0)
-               hp = 0;
-       user->setHP(hp);
-       
-       if(result_count < 1)
-               return true;
-               
-       setCount(result_count);
-       return false;
+
+       bool callback_exists = false;
+       bool result = false;
+
+       if(place)
+       {
+               result = scriptapi_craftitem_on_place_on_ground(
+                               env->getLua(),
+                               m_subname.c_str(), dropper, pos,
+                               callback_exists);
+       }
+
+       // note: on_drop is fallback for on_place_on_ground
+
+       if(!callback_exists)
+       {
+               result = scriptapi_craftitem_on_drop(
+                               env->getLua(),
+                               m_subname.c_str(), dropper, pos,
+                               callback_exists);
+       }
+
+       if(callback_exists)
+       {
+               // If the callback returned true, drop one item
+               if(result)
+                       setCount(getCount() - 1);
+               return getCount() < 1;
+       }
+       else
+       {
+               // If neither on_place_on_ground (if place==true)
+               // nor on_drop exists, call the base implementation
+               return InventoryItem::dropOrPlace(env, dropper, pos, place, count);
+       }
+}
+
+bool CraftItem::use(ServerEnvironment *env,
+               ServerActiveObject *user,
+               const PointedThing& pointed)
+{
+       bool callback_exists = false;
+       bool result = false;
+
+       result = scriptapi_craftitem_on_use(
+                       env->getLua(),
+                       m_subname.c_str(), user, pointed,
+                       callback_exists);
+
+       if(callback_exists)
+       {
+               // If the callback returned true, drop one item
+               if(result)
+                       setCount(getCount() - 1);
+               return getCount() < 1;
+       }
+       else
+       {
+               // If neither on_place_on_ground (if place==true)
+               // nor on_drop exists, call the base implementation
+               return InventoryItem::use(env, user, pointed);
+       }
 }
 
 /*
@@ -1067,6 +1178,19 @@ IDropAction::IDropAction(std::istream &is)
 void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
                ServerEnvironment *env)
 {
+       if(c->current_player == NULL){
+               infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl;
+               return;
+       }
+
+       // Do NOT cast directly to ServerActiveObject*, it breaks
+       // because of multiple inheritance.
+       ServerActiveObject *dropper =
+               static_cast<ServerActiveObject*>(
+               static_cast<ServerRemotePlayer*>(
+                       c->current_player
+               ));
+
        Inventory *inv_from = mgr->getInventory(c, from_inv);
        
        if(!inv_from){
@@ -1086,7 +1210,8 @@ void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
                                <<", from_list=\""<<from_list<<"\""<<std::endl;
                return;
        }
-       if(list_from->getItem(from_i) == NULL)
+       InventoryItem *item = list_from->getItem(from_i);
+       if(item == NULL)
        {
                infostream<<"IDropAction::apply(): FAIL: source item not found: "
                                <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
@@ -1095,43 +1220,19 @@ void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
                return;
        }
 
-       v3f pos = c->current_player->getPosition();
+       v3f pos = dropper->getBasePosition();
        pos.Y += 0.5*BS;
-       v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
+
+       s16 count2 = count;
+       if(count2 == 0)
+               count2 = -1;
 
        /*
-               Ensure that the block is loaded so that the item
-               can properly be added to the static list too
+               Drop the item
        */
-       MapBlock *block = env->getMap().emergeBlock(blockpos, false);
-       if(block==NULL)
-       {
-               infostream<<"IDropAction::apply(): FAIL: block not found: "
-                               <<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z
-                               <<std::endl;
-               return;
-       }
-
-       // Take item from source list
-       if(count == 0)
-               count = list_from->getItem(from_i)->getDropCount();
-       InventoryItem *item1 = list_from->takeItem(from_i, count);
-
-       // Create an active object
-       ServerActiveObject *obj = item1->createSAO(env, pos);
-       if(obj == NULL)
-       {
-               infostream<<"IDropAction::apply(): item resulted in NULL object, "
-                       <<"not placing onto map"
-                       <<std::endl;
-       }
-       else
-       {
-               // Add the object to the environment
-               env->addActiveObject(obj);
-
-               infostream<<"Dropped object"<<std::endl;
-       }
+       bool remove = item->dropOrPlace(env, dropper, pos, false, count2);
+       if(remove)
+               list_from->deleteItem(from_i);
 
        mgr->inventoryModified(c, from_inv);
 
index 6253ea50e659a180a93b134fbe12da935f31768c..cee81a21e629f1cca77da69f36bc70437e96bacc 100644 (file)
@@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 class ServerActiveObject;
 class ServerEnvironment;
+class PointedThing;
 class ITextureSource;
 class IGameDef;
 
@@ -63,10 +64,6 @@ public:
        virtual std::string getText() { return ""; }
        // Returns the string used for inventory
        virtual std::string getItemString();
-       // Creates an object from the item, to be placed in the world.
-       virtual ServerActiveObject* createSAO(ServerEnvironment *env, v3f pos);
-       // Gets amount of items that dropping one SAO will decrement
-       virtual u16 getDropCount() const { return getCount(); }
 
        /*
                Quantity methods
@@ -88,13 +85,17 @@ public:
        void setCount(u16 count)
        { m_count = count; }
 
-       // This should return something else for stackable items
-       virtual u16 freeSpace() const
-       { return 0; }
+       u16 freeSpace() const
+       {
+               u16 max = getStackMax();
+               if(m_count > max)
+                       return 0;
+               return max - m_count;
+       }
 
        void add(u16 count)
        {
-               assert(m_count + count <= QUANTITY_ITEM_MAX_COUNT);
+               assert(m_count + count <= getStackMax());
                m_count += count;
        }
        void remove(u16 count)
@@ -107,6 +108,10 @@ public:
                Other properties
        */
 
+       // Maximum size of a stack
+       virtual u16 getStackMax() const {return 1;}
+       // Whether it can be used
+       virtual bool isUsable() const {return false;}
        // Whether it can be cooked
        virtual bool isCookable() const {return false;}
        // Result of cooking (can randomize)
@@ -115,12 +120,24 @@ public:
        virtual float getCookTime() const {return 3.0;}
        // Whether it can be burned (<0 = cannot be burned)
        virtual float getBurnTime() const {return -1;}
-       
+       // Gets amount of items that dropping one ItemSAO will decrement
+       // -1 means as many as possible
+       virtual s16 getDropCount() const { return -1; }
+       // Whether this item can point to liquids
+       virtual bool areLiquidsPointable() const { return false; }
+
+       // Creates an object from the item and places it in the world.
+       // If return value is true, item should be removed.
+       virtual bool dropOrPlace(ServerEnvironment *env,
+                       ServerActiveObject *dropper,
+                       v3f pos, bool place, s16 count);
+
        // Eat, press, activate, whatever.
-       // Called when item is right-clicked when lying on ground.
+       // Called when item is left-clicked while in hand.
        // If returns true, item shall be deleted.
        virtual bool use(ServerEnvironment *env,
-                       ServerActiveObject *user){return false;}
+                       ServerActiveObject *user,
+                       const PointedThing& pointed){return false;}
 
 protected:
        IGameDef *m_gamedef;
@@ -189,12 +206,11 @@ public:
                return true;
        }
 
-       u16 freeSpace() const
+       u16 getStackMax() const
        {
-               if(m_count > QUANTITY_ITEM_MAX_COUNT)
-                       return 0;
-               return QUANTITY_ITEM_MAX_COUNT - m_count;
+               return QUANTITY_ITEM_MAX_COUNT;
        }
+
        /*
                Other properties
        */
@@ -254,9 +270,6 @@ public:
                return os.str();
        }
 
-       ServerActiveObject* createSAO(ServerEnvironment *env, v3f pos);
-       u16 getDropCount() const;
-
        virtual bool addableTo(const InventoryItem *other) const
        {
                if(std::string(other->getName()) != "CraftItem")
@@ -284,24 +297,26 @@ public:
                return true;
        }
 
-       u16 freeSpace() const
-       {
-               if(m_count > QUANTITY_ITEM_MAX_COUNT)
-                       return 0;
-               return QUANTITY_ITEM_MAX_COUNT - m_count;
-       }
-
        /*
                Other properties
        */
 
+       u16 getStackMax() const;
+       bool isUsable() const;
        bool isCookable() const;
        InventoryItem *createCookResult() const;
        float getCookTime() const;
        float getBurnTime() const;
+       s16 getDropCount() const;
+       bool areLiquidsPointable() const;
+
+       bool dropOrPlace(ServerEnvironment *env,
+                       ServerActiveObject *dropper,
+                       v3f pos, bool place, s16 count);
+       bool use(ServerEnvironment *env,
+                       ServerActiveObject *user,
+                       const PointedThing& pointed);
 
-       bool use(ServerEnvironment *env, ServerActiveObject *user);
-       
        /*
                Special methods
        */
index ce2e269451e6de419c0df3d8b1706ef176adfd8b..353f44d8a788a6808b24345f674a04c154fef6ec 100644 (file)
@@ -183,7 +183,8 @@ ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env):
        Player(env->getGameDef()),
        ServerActiveObject(env, v3f(0,0,0)),
        m_last_good_position(0,0,0),
-       m_last_good_position_age(0)
+       m_last_good_position_age(0),
+       m_additional_items()
 {
 }
 ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env, v3f pos_, u16 peer_id_,
@@ -195,6 +196,10 @@ ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env, v3f pos_, u16 pee
        peer_id = peer_id_;
        updateName(name_);
 }
+ServerRemotePlayer::~ServerRemotePlayer()
+{
+       clearAddToInventoryLater();
+}
 
 /* ServerActiveObject interface */
 
@@ -247,9 +252,59 @@ bool ServerRemotePlayer::addToInventory(InventoryItem *item)
 
        return true;
 }
+void ServerRemotePlayer::addToInventoryLater(InventoryItem *item)
+{
+       infostream<<"Adding (later) "<<item->getName()<<" into "<<getName()
+                       <<"'s inventory"<<std::endl;
+       m_additional_items.push_back(item);
+}
+void ServerRemotePlayer::clearAddToInventoryLater()
+{
+       for (std::vector<InventoryItem*>::iterator
+                       i = m_additional_items.begin();
+                       i != m_additional_items.end(); i++)
+       {
+               delete *i;
+       }
+       m_additional_items.clear();
+}
+void ServerRemotePlayer::completeAddToInventoryLater(u16 preferred_index)
+{
+       InventoryList *ilist = inventory.getList("main");
+       if(ilist == NULL)
+       {
+               clearAddToInventoryLater();
+               return;
+       }
+       
+       // In creative mode, just delete the items
+       if(g_settings->getBool("creative_mode"))
+       {
+               clearAddToInventoryLater();
+               return;
+       }
+       
+       for (std::vector<InventoryItem*>::iterator
+                       i = m_additional_items.begin();
+                       i != m_additional_items.end(); i++)
+       {
+               InventoryItem *item = *i;
+               InventoryItem *leftover = item;
+               leftover = ilist->addItem(preferred_index, leftover);
+               leftover = ilist->addItem(leftover);
+               delete leftover;
+       }
+       m_additional_items.clear();
+}
 void ServerRemotePlayer::setHP(s16 hp_)
 {
        hp = hp_;
+
+       // FIXME: don't hardcode maximum HP, make configurable per object
+       if(hp < 0)
+               hp = 0;
+       else if(hp > 20)
+               hp = 20;
 }
 s16 ServerRemotePlayer::getHP()
 {
index 9476a9be3bb761c7a90da670b35013bb60947e5b..0f92ccc87afdca5aa829eb873e84293092866b96 100644 (file)
@@ -188,8 +188,7 @@ public:
        ServerRemotePlayer(ServerEnvironment *env, v3f pos_, u16 peer_id_,
                        const char *name_);
 
-       virtual ~ServerRemotePlayer()
-       {}
+       virtual ~ServerRemotePlayer();
 
        virtual bool isLocal() const
        { return false; }
@@ -230,11 +229,15 @@ public:
        virtual void damageWieldedItem(u16 amount);
        // If all fits, eats item and returns true. Otherwise returns false.
        virtual bool addToInventory(InventoryItem *item);
+       virtual void addToInventoryLater(InventoryItem *item);
+       void clearAddToInventoryLater();
+       void completeAddToInventoryLater(u16 preferred_index);
        virtual void setHP(s16 hp_);
        virtual s16 getHP();
        
        v3f m_last_good_position;
        float m_last_good_position_age;
+       std::vector<InventoryItem*> m_additional_items;
        
 private:
 };
index 3918bdfc36b31b94ee9cb800eb845c0705240ed2..83efef670e62631568f124107796f831fc1fdaad 100644 (file)
@@ -39,11 +39,13 @@ extern "C" {
 #include "tooldef.h"
 #include "nodedef.h"
 #include "craftdef.h"
+#include "craftitemdef.h"
 #include "main.h" // For g_settings
 #include "settings.h" // For accessing g_settings
 #include "nodemetadata.h"
 #include "mapblock.h" // For getNodeBlockPos
 #include "content_nodemeta.h"
+#include "utility.h"
 
 static void stackDump(lua_State *L, std::ostream &o)
 {
@@ -615,6 +617,55 @@ static int l_register_tool(lua_State *L)
        return 0; /* number of results */
 }
 
+// register_craftitem(name, {lots of stuff})
+static int l_register_craftitem(lua_State *L)
+{
+       const char *name = luaL_checkstring(L, 1);
+       infostream<<"register_craftitem: "<<name<<std::endl;
+       luaL_checktype(L, 2, LUA_TTABLE);
+       int table = 2;
+
+       // Get server from registry
+       lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server");
+       Server *server = (Server*)lua_touserdata(L, -1);
+       // And get the writable CraftItem definition manager from the server
+       IWritableCraftItemDefManager *craftitemdef =
+                       server->getWritableCraftItemDefManager();
+       
+       // Check if on_drop is defined
+       lua_getfield(L, table, "on_drop");
+       bool got_on_drop = !lua_isnil(L, -1);
+       lua_pop(L, 1);
+
+       // Check if on_use is defined
+       lua_getfield(L, table, "on_use");
+       bool got_on_use = !lua_isnil(L, -1);
+       lua_pop(L, 1);
+
+       CraftItemDefinition def;
+       
+       getstringfield(L, table, "image", def.imagename);
+       getstringfield(L, table, "cookresult_item", def.cookresult_item);
+       getfloatfield(L, table, "furnace_cooktime", def.furnace_cooktime);
+       getfloatfield(L, table, "furnace_burntime", def.furnace_burntime);
+       def.usable = getboolfield_default(L, table, "usable", got_on_use);
+       getboolfield(L, table, "liquids_pointable", def.liquids_pointable);
+       def.dropcount = getintfield_default(L, table, "dropcount", def.dropcount);
+       def.stack_max = getintfield_default(L, table, "stack_max", def.stack_max);
+
+       // If an on_drop callback is defined, force dropcount to 1
+       if (got_on_drop)
+               def.dropcount = 1;
+
+       // Register it
+       craftitemdef->registerCraftItem(name, def);
+
+       lua_pushvalue(L, table);
+       scriptapi_add_craftitem(L, name);
+
+       return 0; /* number of results */
+}
+
 // register_node(name, {lots of stuff})
 static int l_register_node(lua_State *L)
 {
@@ -976,6 +1027,7 @@ static const struct luaL_Reg minetest_f [] = {
        {"register_nodedef_defaults", l_register_nodedef_defaults},
        {"register_entity", l_register_entity},
        {"register_tool", l_register_tool},
+       {"register_craftitem", l_register_craftitem},
        {"register_node", l_register_node},
        {"register_craft", l_register_craft},
        {"register_abm", l_register_abm},
@@ -1541,6 +1593,56 @@ private:
                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)
        {
@@ -1623,6 +1725,9 @@ const luaL_reg EnvRef::methods[] = {
        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}
 };
@@ -1663,6 +1768,16 @@ private:
                return (LuaEntitySAO*)obj;
        }
        
+       static ServerRemotePlayer* getplayer(ObjectRef *ref)
+       {
+               ServerActiveObject *obj = getobject(ref);
+               if(obj == NULL)
+                       return NULL;
+               if(obj->getType() != ACTIVEOBJECT_TYPE_PLAYER)
+                       return NULL;
+               return static_cast<ServerRemotePlayer*>(obj);
+       }
+       
        // Exported functions
        
        // garbage collector
@@ -1795,6 +1910,64 @@ private:
                return 1;
        }
 
+       // add_to_inventory_later(self, itemstring)
+       // returns: nil
+       static int l_add_to_inventory_later(lua_State *L)
+       {
+               ObjectRef *ref = checkobject(L, 1);
+               luaL_checkstring(L, 2);
+               ServerActiveObject *co = getobject(ref);
+               if(co == NULL) return 0;
+               // itemstring
+               const char *itemstring = lua_tostring(L, 2);
+               infostream<<"ObjectRef::l_add_to_inventory_later(): id="<<co->getId()
+                               <<" itemstring=\""<<itemstring<<"\""<<std::endl;
+               // Do it
+               std::istringstream is(itemstring, std::ios::binary);
+               ServerEnvironment *env = co->getEnv();
+               assert(env);
+               IGameDef *gamedef = env->getGameDef();
+               InventoryItem *item = InventoryItem::deSerialize(is, gamedef);
+               infostream<<"item="<<env<<std::endl;
+               co->addToInventoryLater(item);
+               // Return
+               return 0;
+       }
+
+       // get_hp(self)
+       // returns: number of hitpoints (2 * number of hearts)
+       // 0 if not applicable to this type of object
+       static int l_get_hp(lua_State *L)
+       {
+               ObjectRef *ref = checkobject(L, 1);
+               ServerActiveObject *co = getobject(ref);
+               if(co == NULL) return 0;
+               int hp = co->getHP();
+               infostream<<"ObjectRef::l_get_hp(): id="<<co->getId()
+                               <<" hp="<<hp<<std::endl;
+               // Return
+               lua_pushnumber(L, hp);
+               return 1;
+       }
+
+       // set_hp(self, hp)
+       // hp = number of hitpoints (2 * number of hearts)
+       // returns: nil
+       static int l_set_hp(lua_State *L)
+       {
+               ObjectRef *ref = checkobject(L, 1);
+               luaL_checknumber(L, 2);
+               ServerActiveObject *co = getobject(ref);
+               if(co == NULL) return 0;
+               int hp = lua_tonumber(L, 2);
+               infostream<<"ObjectRef::l_set_hp(): id="<<co->getId()
+                               <<" hp="<<hp<<std::endl;
+               // Do it
+               co->setHP(hp);
+               // Return
+               return 0;
+       }
+
        // settexturemod(self, mod)
        static int l_settexturemod(lua_State *L)
        {
@@ -1901,6 +2074,9 @@ const luaL_reg ObjectRef::methods[] = {
        method(ObjectRef, setvelocity),
        method(ObjectRef, setacceleration),
        method(ObjectRef, add_to_inventory),
+       method(ObjectRef, add_to_inventory_later),
+       method(ObjectRef, get_hp),
+       method(ObjectRef, set_hp),
        method(ObjectRef, settexturemod),
        method(ObjectRef, setsprite),
        {0,0}
@@ -1951,6 +2127,8 @@ void scriptapi_export(lua_State *L, Server *server)
        lua_newtable(L);
        lua_setfield(L, -2, "registered_entities");
        lua_newtable(L);
+       lua_setfield(L, -2, "registered_craftitems");
+       lua_newtable(L);
        lua_setfield(L, -2, "registered_abms");
        
        lua_newtable(L);
@@ -2199,6 +2377,163 @@ bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player)
        return positioning_handled_by_some;
 }
 
+/*
+       craftitem
+*/
+
+static void pushPointedThing(lua_State *L, const PointedThing& pointed)
+{
+       lua_newtable(L);
+       if(pointed.type == POINTEDTHING_NODE)
+       {
+               lua_pushstring(L, "node");
+               lua_setfield(L, -2, "type");
+               pushpos(L, pointed.node_undersurface);
+               lua_setfield(L, -2, "under");
+               pushpos(L, pointed.node_abovesurface);
+               lua_setfield(L, -2, "above");
+       }
+       else if(pointed.type == POINTEDTHING_OBJECT)
+       {
+               lua_pushstring(L, "object");
+               lua_setfield(L, -2, "type");
+               objectref_get(L, pointed.object_id);
+               lua_setfield(L, -2, "ref");
+       }
+       else
+       {
+               lua_pushstring(L, "nothing");
+               lua_setfield(L, -2, "type");
+       }
+}
+
+void scriptapi_add_craftitem(lua_State *L, const char *name)
+{
+       StackUnroller stack_unroller(L);
+       assert(lua_gettop(L) > 0);
+
+       // Set minetest.registered_craftitems[name] = table on top of stack
+       lua_getglobal(L, "minetest");
+       lua_getfield(L, -1, "registered_craftitems");
+       luaL_checktype(L, -1, LUA_TTABLE);
+       lua_pushvalue(L, -3); // push another reference to the table to be registered
+       lua_setfield(L, -2, name); // set minetest.registered_craftitems[name]
+}
+
+static bool get_craftitem_callback(lua_State *L, const char *name,
+               const char *callbackname)
+{
+       // Get minetest.registered_craftitems[name][callbackname]
+       // If that is nil or on error, return false and stack is unchanged
+       // If that is a function, returns true and pushes the
+       // function onto the stack
+
+       lua_getglobal(L, "minetest");
+       lua_getfield(L, -1, "registered_craftitems");
+       lua_remove(L, -2);
+       luaL_checktype(L, -1, LUA_TTABLE);
+       lua_getfield(L, -1, name);
+       lua_remove(L, -2);
+       // Should be a table
+       if(lua_type(L, -1) != LUA_TTABLE)
+       {
+               errorstream<<"CraftItem name \""<<name<<"\" not defined"<<std::endl;
+               lua_pop(L, 1);
+               return false;
+       }
+       lua_getfield(L, -1, callbackname);
+       lua_remove(L, -2);
+       // Should be a function or nil
+       if(lua_type(L, -1) == LUA_TFUNCTION)
+       {
+               return true;
+       }
+       else if(lua_isnil(L, -1))
+       {
+               lua_pop(L, 1);
+               return false;
+       }
+       else
+       {
+               errorstream<<"CraftItem name \""<<name<<"\" callback \""
+                       <<callbackname<<" is not a function"<<std::endl;
+               lua_pop(L, 1);
+               return false;
+       }
+}
+
+bool scriptapi_craftitem_on_drop(lua_State *L, const char *name,
+               ServerActiveObject *dropper, v3f pos,
+               bool &callback_exists)
+{
+       realitycheck(L);
+       assert(lua_checkstack(L, 20));
+       //infostream<<"scriptapi_craftitem_on_drop"<<std::endl;
+       StackUnroller stack_unroller(L);
+
+       bool result = false;
+       callback_exists = get_craftitem_callback(L, name, "on_drop");
+       if(callback_exists)
+       {
+               // Call function
+               lua_pushstring(L, name);
+               objectref_get_or_create(L, dropper);
+               pushFloatPos(L, pos);
+               if(lua_pcall(L, 3, 1, 0))
+                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+               result = lua_toboolean(L, -1);
+       }
+       return result;
+}
+
+bool scriptapi_craftitem_on_place_on_ground(lua_State *L, const char *name,
+               ServerActiveObject *placer, v3f pos,
+               bool &callback_exists)
+{
+       realitycheck(L);
+       assert(lua_checkstack(L, 20));
+       //infostream<<"scriptapi_craftitem_on_place_on_ground"<<std::endl;
+       StackUnroller stack_unroller(L);
+
+       bool result = false;
+       callback_exists = get_craftitem_callback(L, name, "on_place_on_ground");
+       if(callback_exists)
+       {
+               // Call function
+               lua_pushstring(L, name);
+               objectref_get_or_create(L, placer);
+               pushFloatPos(L, pos);
+               if(lua_pcall(L, 3, 1, 0))
+                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+               result = lua_toboolean(L, -1);
+       }
+       return result;
+}
+
+bool scriptapi_craftitem_on_use(lua_State *L, const char *name,
+               ServerActiveObject *user, const PointedThing& pointed,
+               bool &callback_exists)
+{
+       realitycheck(L);
+       assert(lua_checkstack(L, 20));
+       //infostream<<"scriptapi_craftitem_on_use"<<std::endl;
+       StackUnroller stack_unroller(L);
+
+       bool result = false;
+       callback_exists = get_craftitem_callback(L, name, "on_use");
+       if(callback_exists)
+       {
+               // Call function
+               lua_pushstring(L, name);
+               objectref_get_or_create(L, user);
+               pushPointedThing(L, pointed);
+               if(lua_pcall(L, 3, 1, 0))
+                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+               result = lua_toboolean(L, -1);
+       }
+       return result;
+}
+
 /*
        environment
 */
index e6570e764f6171f1e2c9ebb5563310c3d7944343..fe9329d75bb4746d41cd38aee41ffb632f55f40f 100644 (file)
@@ -29,6 +29,7 @@ class ServerEnvironment;
 class ServerActiveObject;
 typedef struct lua_State lua_State;
 struct LuaEntityProperties;
+struct PointedThing;
 //class IGameDef;
 
 void scriptapi_export(lua_State *L, Server *server);
@@ -60,6 +61,18 @@ void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp);
 void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player);
 bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player);
 
+/* craftitem */
+void scriptapi_add_craftitem(lua_State *L, const char *name);
+bool scriptapi_craftitem_on_drop(lua_State *L, const char *name,
+               ServerActiveObject *dropper, v3f pos,
+               bool &callback_exists);
+bool scriptapi_craftitem_on_place_on_ground(lua_State *L, const char *name,
+               ServerActiveObject *placer, v3f pos,
+               bool &callback_exists);
+bool scriptapi_craftitem_on_use(lua_State *L, const char *name,
+               ServerActiveObject *user, const PointedThing& pointed,
+               bool &callback_exists);
+
 /* luaentity */
 // Returns true if succesfully added into Lua; false otherwise.
 bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name,
index 63172e955850a50e668b2761ec7fc0c8bfd4ff78..70638a0a6f79cede78c9fca003ff34184b5c6daa 100644 (file)
@@ -45,6 +45,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nodedef.h"
 #include "tooldef.h"
 #include "craftdef.h"
+#include "craftitemdef.h"
 #include "mapgen.h"
 #include "content_abm.h"
 
@@ -1033,6 +1034,7 @@ Server::Server(
        m_toolmgr(createToolDefManager()),
        m_nodedef(createNodeDefManager()),
        m_craftdef(createCraftDefManager()),
+       m_craftitemdef(createCraftItemDefManager()),
        m_thread(this),
        m_emergethread(this),
        m_time_counter(0),
@@ -1206,6 +1208,8 @@ Server::~Server()
 
        delete m_toolmgr;
        delete m_nodedef;
+       delete m_craftdef;
+       delete m_craftitemdef;
        
        // Deinitialize scripting
        infostream<<"Server: Deinitializing scripting"<<std::endl;
@@ -1396,7 +1400,8 @@ void Server::AsyncRunStep()
                {
                        RemoteClient *client = i.getNode()->getValue();
                        ServerRemotePlayer *player =
-                                       (ServerRemotePlayer*)m_env->getPlayer(client->peer_id);
+                                       static_cast<ServerRemotePlayer*>
+                                       (m_env->getPlayer(client->peer_id));
                        if(player==NULL)
                                continue;
                        player->m_last_good_position_age += dtime;
@@ -2240,6 +2245,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                // Send node definitions
                SendNodeDef(m_con, peer_id, m_nodedef);
                
+               // Send CraftItem definitions
+               SendCraftItemDef(m_con, peer_id, m_craftitemdef);
+               
                // Send textures
                SendTextures(peer_id);
                
@@ -2420,1190 +2428,1130 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        }
        else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
        {
-               if(datasize < 7)
-                       return;
+               infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
+               return;
+       }
+       else if(command == TOSERVER_GROUND_ACTION)
+       {
+               infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
+               return;
 
+       }
+       else if(command == TOSERVER_RELEASE)
+       {
+               infostream<<"Server: RELEASE not supported anymore"<<std::endl;
+               return;
+       }
+       else if(command == TOSERVER_SIGNTEXT)
+       {
+               infostream<<"Server: SIGNTEXT not supported anymore"
+                               <<std::endl;
+               return;
+       }
+       else if(command == TOSERVER_SIGNNODETEXT)
+       {
                if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
                        return;
-
                /*
-                       length: 7
-                       [0] u16 command
-                       [2] u8 button (0=left, 1=right)
-                       [3] u16 id
-                       [5] u16 item
+                       u16 command
+                       v3s16 p
+                       u16 textlen
+                       textdata
                */
-               u8 button = readU8(&data[2]);
-               u16 id = readS16(&data[3]);
-               u16 item_i = readU16(&data[5]);
-       
-               ServerActiveObject *obj = m_env->getActiveObject(id);
-
-               if(obj == NULL)
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+               u8 buf[6];
+               // Read stuff
+               is.read((char*)buf, 6);
+               v3s16 p = readV3S16(buf);
+               is.read((char*)buf, 2);
+               u16 textlen = readU16(buf);
+               std::string text;
+               for(u16 i=0; i<textlen; i++)
                {
-                       infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
-                                       <<std::endl;
-                       return;
+                       is.read((char*)buf, 1);
+                       text += (char)buf[0];
                }
 
-               // Skip if object has been removed
-               if(obj->m_removed)
+               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
+               if(!meta)
                        return;
-               
-               //TODO: Check that object is reasonably close
-       
-               // Get ServerRemotePlayer
-               ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
 
-               // Update wielded item
-               srp->wieldItem(item_i);
+               meta->setText(text);
                
-               // Left click, pick/punch
-               if(button == 0)
+               actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
+                               <<" at "<<PP(p)<<std::endl;
+                               
+               v3s16 blockpos = getNodeBlockPos(p);
+               MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
+               if(block)
                {
-                       actionstream<<player->getName()<<" punches object "
-                                       <<obj->getId()<<std::endl;
-                       
-                       // Do stuff
-                       obj->punch(srp);
-                       
-#if 0
+                       block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                       "sign node text");
+               }
+
+               setBlockNotSent(blockpos);
+       }
+       else if(command == TOSERVER_INVENTORY_ACTION)
+       {
+               /*// Ignore inventory changes if in creative mode
+               if(g_settings->getBool("creative_mode") == true)
+               {
+                       infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
+                                       <<std::endl;
+                       return;
+               }*/
+               // Strip command and create a stream
+               std::string datastring((char*)&data[2], datasize-2);
+               infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
+               std::istringstream is(datastring, std::ios_base::binary);
+               // Create an action
+               InventoryAction *a = InventoryAction::deSerialize(is);
+               if(a != NULL)
+               {
+                       // Create context
+                       InventoryContext c;
+                       c.current_player = player;
+
                        /*
-                               Try creating inventory item
+                               Handle craftresult specially if not in creative mode
                        */
-                       InventoryItem *item = obj->createPickedUpItem();
-                       
-                       if(item)
+                       bool disable_action = false;
+                       if(a->getType() == IACTION_MOVE
+                                       && g_settings->getBool("creative_mode") == false)
                        {
-                               InventoryList *ilist = player->inventory.getList("main");
-                               if(ilist != NULL)
+                               IMoveAction *ma = (IMoveAction*)a;
+                               if(ma->to_inv == "current_player" &&
+                                               ma->from_inv == "current_player")
                                {
-                                       actionstream<<player->getName()<<" picked up "
-                                                       <<item->getName()<<std::endl;
-                                       if(g_settings->getBool("creative_mode") == false)
+                                       InventoryList *rlist = player->inventory.getList("craftresult");
+                                       assert(rlist);
+                                       InventoryList *clist = player->inventory.getList("craft");
+                                       assert(clist);
+                                       InventoryList *mlist = player->inventory.getList("main");
+                                       assert(mlist);
+                                       /*
+                                               Craftresult is no longer preview if something
+                                               is moved into it
+                                       */
+                                       if(ma->to_list == "craftresult"
+                                                       && ma->from_list != "craftresult")
                                        {
-                                               // Skip if inventory has no free space
-                                               if(ilist->roomForItem(item) == false)
+                                               // If it currently is a preview, remove
+                                               // its contents
+                                               if(player->craftresult_is_preview)
                                                {
-                                                       infostream<<"Player inventory has no free space"<<std::endl;
-                                                       return;
+                                                       rlist->deleteItem(0);
                                                }
-
-                                               // Add to inventory and send inventory
-                                               ilist->addItem(item);
-                                               UpdateCrafting(player->peer_id);
-                                               SendInventory(player->peer_id);
+                                               player->craftresult_is_preview = false;
+                                       }
+                                       /*
+                                               Crafting takes place if this condition is true.
+                                       */
+                                       if(player->craftresult_is_preview &&
+                                                       ma->from_list == "craftresult")
+                                       {
+                                               player->craftresult_is_preview = false;
+                                               clist->decrementMaterials(1);
+                                               
+                                               /* Print out action */
+                                               InventoryList *list =
+                                                               player->inventory.getList("craftresult");
+                                               assert(list);
+                                               InventoryItem *item = list->getItem(0);
+                                               std::string itemname = "NULL";
+                                               if(item)
+                                                       itemname = item->getName();
+                                               actionstream<<player->getName()<<" crafts "
+                                                               <<itemname<<std::endl;
+                                       }
+                                       /*
+                                               If the craftresult is placed on itself, move it to
+                                               main inventory instead of doing the action
+                                       */
+                                       if(ma->to_list == "craftresult"
+                                                       && ma->from_list == "craftresult")
+                                       {
+                                               disable_action = true;
+                                               
+                                               InventoryItem *item1 = rlist->changeItem(0, NULL);
+                                               mlist->addItem(item1);
                                        }
-
-                                       // Remove object from environment
-                                       obj->m_removed = true;
                                }
-                       }
-                       else
-                       {
-                               /*
-                                       Item cannot be picked up. Punch it instead.
-                               */
-
-                               actionstream<<player->getName()<<" punches object "
-                                               <<obj->getId()<<std::endl;
-
-                               ToolItem *titem = NULL;
-                               std::string toolname = "";
-
-                               InventoryList *mlist = player->inventory.getList("main");
-                               if(mlist != NULL)
+                               // Disallow moving items if not allowed to build
+                               else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
                                {
-                                       InventoryItem *item = mlist->getItem(item_i);
-                                       if(item && (std::string)item->getName() == "ToolItem")
+                                       disable_action = true;
+                               }
+                               // if it's a locking chest, only allow the owner or server admins to move items
+                               else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
+                               {
+                                       Strfnd fn(ma->from_inv);
+                                       std::string id0 = fn.next(":");
+                                       if(id0 == "nodemeta")
+                                       {
+                                               v3s16 p;
+                                               p.X = stoi(fn.next(","));
+                                               p.Y = stoi(fn.next(","));
+                                               p.Z = stoi(fn.next(","));
+                                               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
+                                               if(meta->getOwner() != ""){
+                                                       if(meta->getOwner() != player->getName())
+                                                               disable_action = true;
+                                               }
+                                       }
+                               }
+                               else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
+                               {
+                                       Strfnd fn(ma->to_inv);
+                                       std::string id0 = fn.next(":");
+                                       if(id0 == "nodemeta")
                                        {
-                                               titem = (ToolItem*)item;
-                                               toolname = titem->getToolName();
+                                               v3s16 p;
+                                               p.X = stoi(fn.next(","));
+                                               p.Y = stoi(fn.next(","));
+                                               p.Z = stoi(fn.next(","));
+                                               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
+                                               if(meta->getOwner() != ""){
+                                                       if(meta->getOwner() != player->getName())
+                                                               disable_action = true;
+                                               }
                                        }
                                }
+                       }
 
-                               v3f playerpos = player->getPosition();
-                               v3f objpos = obj->getBasePosition();
-                               v3f dir = (objpos - playerpos).normalize();
-                               
-                               u16 wear = obj->punch(toolname, dir, player->getName());
-                               
-                               if(titem)
+                       if(a->getType() == IACTION_DROP)
+                       {
+                               IDropAction *da = (IDropAction*)a;
+                               // Disallow dropping items if not allowed to build
+                               if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
+                               {
+                                       disable_action = true;
+                               }
+                               // if it's a locking chest, only allow the owner or server admins to drop items
+                               else if (da->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
                                {
-                                       bool weared_out = titem->addWear(wear);
-                                       if(weared_out)
-                                               mlist->deleteItem(item_i);
-                                       SendInventory(player->peer_id);
+                                       Strfnd fn(da->from_inv);
+                                       std::string id0 = fn.next(":");
+                                       if(id0 == "nodemeta")
+                                       {
+                                               v3s16 p;
+                                               p.X = stoi(fn.next(","));
+                                               p.Y = stoi(fn.next(","));
+                                               p.Z = stoi(fn.next(","));
+                                               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
+                                               if(meta->getOwner() != ""){
+                                                       if(meta->getOwner() != player->getName())
+                                                               disable_action = true;
+                                               }
+                                       }
                                }
                        }
-#endif
+                       
+                       if(disable_action == false)
+                       {
+                               // Feed action to player inventory
+                               a->apply(&c, this, m_env);
+                       }
+
+                       // Eat the action
+                       delete a;
                }
-               // Right click, do something with object
-               if(button == 1)
+               else
                {
-                       actionstream<<player->getName()<<" right clicks object "
-                                       <<obj->getId()<<std::endl;
-
-                       // Do stuff
-                       obj->rightClick(srp);
+                       infostream<<"TOSERVER_INVENTORY_ACTION: "
+                                       <<"InventoryAction::deSerialize() returned NULL"
+                                       <<std::endl;
                }
-
-               /*
-                       Update player state to client
-               */
-               SendPlayerHP(player);
-               UpdateCrafting(player->peer_id);
-               SendInventory(player->peer_id);
        }
-       else if(command == TOSERVER_GROUND_ACTION)
+       else if(command == TOSERVER_CHAT_MESSAGE)
        {
-               if(datasize < 17)
-                       return;
-               /*
-                       length: 17
-                       [0] u16 command
-                       [2] u8 action
-                       [3] v3s16 nodepos_undersurface
-                       [9] v3s16 nodepos_abovesurface
-                       [15] u16 item
-                       actions:
-                       0: start digging
-                       1: place block
-                       2: stop digging (all parameters ignored)
-                       3: digging completed
-               */
-               u8 action = readU8(&data[2]);
-               v3s16 p_under;
-               p_under.X = readS16(&data[3]);
-               p_under.Y = readS16(&data[5]);
-               p_under.Z = readS16(&data[7]);
-               v3s16 p_over;
-               p_over.X = readS16(&data[9]);
-               p_over.Y = readS16(&data[11]);
-               p_over.Z = readS16(&data[13]);
-               u16 item_i = readU16(&data[15]);
-
-               ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
-
                /*
-                       Check that target is reasonably close
+                       u16 command
+                       u16 length
+                       wstring message
                */
-               if(action != 2) // action 2 has always position (0,0,0)
-               {
-                       v3f np_f = intToFloat(p_under, BS);
-                       float max_d = BS * 10; // Just some large enough value
-                       float d = srp->m_last_good_position.getDistanceFrom(np_f);
-                       if(d > max_d){
-                               actionstream<<"Player "<<player->getName()
-                                               <<" tried to access node from too far: "
-                                               <<"d="<<d<<", max_d="<<max_d
-                                               <<". ignoring."<<std::endl;
-                               // Re-send block to revert change on client-side
-                               RemoteClient *client = getClient(peer_id);
-                               v3s16 blockpos = getNodeBlockPos(p_under);
-                               client->SetBlockNotSent(blockpos);
-                               // Do nothing else
-                               return;
-                       }
+               u8 buf[6];
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+               
+               // Read stuff
+               is.read((char*)buf, 2);
+               u16 len = readU16(buf);
+               
+               std::wstring message;
+               for(u16 i=0; i<len; i++)
+               {
+                       is.read((char*)buf, 2);
+                       message += (wchar_t)readU16(buf);
                }
 
-               /*
-                       0: start digging
-               */
-               if(action == 0)
+               // Get player name of this client
+               std::wstring name = narrow_to_wide(player->getName());
+               
+               // Run script hook
+               bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
+                               wide_to_narrow(message));
+               // If script ate the message, don't proceed
+               if(ate)
+                       return;
+               
+               // Line to send to players
+               std::wstring line;
+               // Whether to send to the player that sent the line
+               bool send_to_sender = false;
+               // Whether to send to other players
+               bool send_to_others = false;
+               
+               // Local player gets all privileges regardless of
+               // what's set on their account.
+               u64 privs = getPlayerPrivs(player);
+
+               // Parse commands
+               if(message[0] == L'/')
                {
-                       /*
-                               NOTE: This can be used in the future to check if
-                               somebody is cheating, by checking the timing.
-                       */
-                       bool cannot_punch_node = false;
+                       size_t strip_size = 1;
+                       if (message[1] == L'#') // support old-style commans
+                               ++strip_size;
+                       message = message.substr(strip_size);
 
-                       MapNode n(CONTENT_IGNORE);
+                       WStrfnd f1(message);
+                       f1.next(L" "); // Skip over /#whatever
+                       std::wstring paramstring = f1.next(L"");
 
-                       try
+                       ServerCommandContext *ctx = new ServerCommandContext(
+                               str_split(message, L' '),
+                               paramstring,
+                               this,
+                               m_env,
+                               player,
+                               privs);
+
+                       std::wstring reply(processServerCommand(ctx));
+                       send_to_sender = ctx->flags & SEND_TO_SENDER;
+                       send_to_others = ctx->flags & SEND_TO_OTHERS;
+
+                       if (ctx->flags & SEND_NO_PREFIX)
+                               line += reply;
+                       else
+                               line += L"Server: " + reply;
+
+                       delete ctx;
+
+               }
+               else
+               {
+                       if(privs & PRIV_SHOUT)
                        {
-                               n = m_env->getMap().getNode(p_under);
+                               line += L"<";
+                               line += name;
+                               line += L"> ";
+                               line += message;
+                               send_to_others = true;
                        }
-                       catch(InvalidPositionException &e)
+                       else
                        {
-                               infostream<<"Server: Not punching: Node not found."
-                                               <<" Adding block to emerge queue."
-                                               <<std::endl;
-                               m_emerge_queue.addBlock(peer_id,
-                                               getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
-                               cannot_punch_node = true;
+                               line += L"Server: You are not allowed to shout";
+                               send_to_sender = true;
                        }
-
-                       if(cannot_punch_node)
-                               return;
+               }
+               
+               if(line != L"")
+               {
+                       if(send_to_others)
+                               actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
 
                        /*
-                               Run script hook
+                               Send the message to clients
                        */
-                       scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
+                       for(core::map<u16, RemoteClient*>::Iterator
+                               i = m_clients.getIterator();
+                               i.atEnd() == false; i++)
+                       {
+                               // Get client and check that it is valid
+                               RemoteClient *client = i.getNode()->getValue();
+                               assert(client->peer_id == i.getNode()->getKey());
+                               if(client->serialization_version == SER_FMT_VER_INVALID)
+                                       continue;
 
-               } // action == 0
+                               // Filter recipient
+                               bool sender_selected = (peer_id == client->peer_id);
+                               if(sender_selected == true && send_to_sender == false)
+                                       continue;
+                               if(sender_selected == false && send_to_others == false)
+                                       continue;
+
+                               SendChatMessage(client->peer_id, line);
+                       }
+               }
+       }
+       else if(command == TOSERVER_DAMAGE)
+       {
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+               u8 damage = readU8(is);
 
+               if(g_settings->getBool("enable_damage"))
+               {
+                       actionstream<<player->getName()<<" damaged by "
+                                       <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
+                                       <<std::endl;
+                               
+                       HandlePlayerHP(player, damage);
+               }
+               else
+               {
+                       SendPlayerHP(player);
+               }
+       }
+       else if(command == TOSERVER_PASSWORD)
+       {
                /*
-                       2: stop digging
+                       [0] u16 TOSERVER_PASSWORD
+                       [2] u8[28] old password
+                       [30] u8[28] new password
                */
-               else if(action == 2)
+
+               if(datasize != 2+PASSWORD_SIZE*2)
+                       return;
+               /*char password[PASSWORD_SIZE];
+               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+                       password[i] = data[2+i];
+               password[PASSWORD_SIZE-1] = 0;*/
+               std::string oldpwd;
+               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
                {
-#if 0
-                       RemoteClient *client = getClient(peer_id);
-                       JMutexAutoLock digmutex(client->m_dig_mutex);
-                       client->m_dig_tool_item = -1;
-#endif
+                       char c = data[2+i];
+                       if(c == 0)
+                               break;
+                       oldpwd += c;
+               }
+               std::string newpwd;
+               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+               {
+                       char c = data[2+PASSWORD_SIZE+i];
+                       if(c == 0)
+                               break;
+                       newpwd += c;
+               }
+
+               infostream<<"Server: Client requests a password change from "
+                               <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
+
+               std::string playername = player->getName();
+
+               if(m_authmanager.exists(playername) == false)
+               {
+                       infostream<<"Server: playername not found in authmanager"<<std::endl;
+                       // Wrong old password supplied!!
+                       SendChatMessage(peer_id, L"playername not found in authmanager");
+                       return;
+               }
+
+               std::string checkpwd = m_authmanager.getPassword(playername);
+
+               if(oldpwd != checkpwd)
+               {
+                       infostream<<"Server: invalid old password"<<std::endl;
+                       // Wrong old password supplied!!
+                       SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
+                       return;
                }
 
+               actionstream<<player->getName()<<" changes password"<<std::endl;
+
+               m_authmanager.setPassword(playername, newpwd);
+               
+               infostream<<"Server: password change successful for "<<playername
+                               <<std::endl;
+               SendChatMessage(peer_id, L"Password change successful");
+       }
+       else if(command == TOSERVER_PLAYERITEM)
+       {
+               if (datasize < 2+2)
+                       return;
+
+               u16 item = readU16(&data[2]);
+               player->wieldItem(item);
+               SendWieldedItem(player);
+       }
+       else if(command == TOSERVER_RESPAWN)
+       {
+               if(player->hp != 0)
+                       return;
+               
+               RespawnPlayer(player);
+               
+               actionstream<<player->getName()<<" respawns at "
+                               <<PP(player->getPosition()/BS)<<std::endl;
+       }
+       else if(command == TOSERVER_INTERACT)
+       {
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
                /*
-                       3: Digging completed
+                       [0] u16 command
+                       [2] u8 action
+                       [3] u16 item
+                       [5] u32 length of the next item
+                       [9] serialized PointedThing
+                       actions:
+                       0: start digging (from undersurface) or use
+                       1: stop digging (all parameters ignored)
+                       2: digging completed
+                       3: place block or item (to abovesurface)
+                       4: use item
                */
-               else if(action == 3)
+               u8 action = readU8(is);
+               u16 item_i = readU16(is);
+               std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+               PointedThing pointed;
+               pointed.deSerialize(tmp_is);
+
+               infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
+
+               ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
+               v3f player_pos = srp->m_last_good_position;
+
+               // Update wielded item
+               srp->wieldItem(item_i);
+
+               // Get pointed to node (undefined if not POINTEDTYPE_NODE)
+               v3s16 p_under = pointed.node_undersurface;
+               v3s16 p_above = pointed.node_abovesurface;
+
+               // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
+               ServerActiveObject *pointed_object = NULL;
+               if(pointed.type == POINTEDTHING_OBJECT)
                {
-                       // Mandatory parameter; actually used for nothing
-                       core::map<v3s16, MapBlock*> modified_blocks;
+                       pointed_object = m_env->getActiveObject(pointed.object_id);
+                       if(pointed_object == NULL)
+                       {
+                               infostream<<"TOSERVER_INTERACT: "
+                                       "pointed object is NULL"<<std::endl;
+                               return;
+                       }
 
-                       content_t material = CONTENT_IGNORE;
-                       u8 mineral = MINERAL_NONE;
+               }
 
-                       bool cannot_remove_node = false;
-                       
-                       MapNode n(CONTENT_IGNORE);
-                       try
+               /*
+                       Check that target is reasonably close
+                       (only when digging or placing things)
+               */
+               if(action == 0 || action == 2 || action == 3)
+               {
+                       v3f pointed_pos = player_pos;
+                       if(pointed.type == POINTEDTHING_NODE)
                        {
-                               n = m_env->getMap().getNode(p_under);
-                               // Get mineral
-                               mineral = n.getMineral(m_nodedef);
-                               // Get material at position
-                               material = n.getContent();
-                               // If not yet cancelled
-                               if(cannot_remove_node == false)
-                               {
-                                       // If it's not diggable, do nothing
-                                       if(m_nodedef->get(material).diggable == false)
-                                       {
-                                               infostream<<"Server: Not finishing digging: "
-                                                               <<"Node not diggable"
-                                                               <<std::endl;
-                                               cannot_remove_node = true;
-                                       }
-                               }
-                               // If not yet cancelled
-                               if(cannot_remove_node == false)
-                               {
-                                       // Get node metadata
-                                       NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
-                                       if(meta && meta->nodeRemovalDisabled() == true)
-                                       {
-                                               infostream<<"Server: Not finishing digging: "
-                                                               <<"Node metadata disables removal"
-                                                               <<std::endl;
-                                               cannot_remove_node = true;
-                                       }
-                               }
+                               pointed_pos = intToFloat(p_under, BS);
                        }
-                       catch(InvalidPositionException &e)
+                       else if(pointed.type == POINTEDTHING_OBJECT)
                        {
-                               infostream<<"Server: Not finishing digging: Node not found."
-                                               <<" Adding block to emerge queue."
-                                               <<std::endl;
-                               m_emerge_queue.addBlock(peer_id,
-                                               getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
-                               cannot_remove_node = true;
+                               pointed_pos = pointed_object->getBasePosition();
                        }
 
-                       // Make sure the player is allowed to do it
-                       if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
-                       {
-                               infostream<<"Player "<<player->getName()<<" cannot remove node"
-                                               <<" because privileges are "<<getPlayerPrivs(player)
-                                               <<std::endl;
-                               cannot_remove_node = true;
-                       }
-
-                       /*
-                               If node can't be removed, set block to be re-sent to
-                               client and quit.
-                       */
-                       if(cannot_remove_node)
-                       {
-                               infostream<<"Server: Not finishing digging."<<std::endl;
-
-                               // Client probably has wrong data.
-                               // Set block not sent, so that client will get
-                               // a valid one.
-                               infostream<<"Client "<<peer_id<<" tried to dig "
-                                               <<"node; but node cannot be removed."
-                                               <<" setting MapBlock not sent."<<std::endl;
+                       float d = player_pos.getDistanceFrom(pointed_pos);
+                       float max_d = BS * 10; // Just some large enough value
+                       if(d > max_d){
+                               actionstream<<"Player "<<player->getName()
+                                               <<" tried to access "<<pointed.dump()
+                                               <<" from too far: "
+                                               <<"d="<<d<<", max_d="<<max_d
+                                               <<". ignoring."<<std::endl;
+                               // Re-send block to revert change on client-side
                                RemoteClient *client = getClient(peer_id);
-                               v3s16 blockpos = getNodeBlockPos(p_under);
+                               v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
                                client->SetBlockNotSent(blockpos);
-                                       
-                               return;
-                       }
-                       
-                       actionstream<<player->getName()<<" digs "<<PP(p_under)
-                                       <<", gets material "<<(int)material<<", mineral "
-                                       <<(int)mineral<<std::endl;
-                       
-                       /*
-                               Send the removal to all close-by players.
-                               - If other player is close, send REMOVENODE
-                               - Otherwise set blocks not sent
-                       */
-                       core::list<u16> far_players;
-                       sendRemoveNode(p_under, peer_id, &far_players, 30);
-                       
-                       /*
-                               Update and send inventory
-                       */
-
-                       if(g_settings->getBool("creative_mode") == false)
-                       {
-                               /*
-                                       Wear out tool
-                               */
-                               InventoryList *mlist = player->inventory.getList("main");
-                               if(mlist != NULL)
-                               {
-                                       InventoryItem *item = mlist->getItem(item_i);
-                                       if(item && (std::string)item->getName() == "ToolItem")
-                                       {
-                                               ToolItem *titem = (ToolItem*)item;
-                                               std::string toolname = titem->getToolName();
-
-                                               // Get digging properties for material and tool
-                                               ToolDiggingProperties tp =
-                                                               m_toolmgr->getDiggingProperties(toolname);
-                                               DiggingProperties prop =
-                                                               getDiggingProperties(material, &tp, m_nodedef);
-
-                                               if(prop.diggable == false)
-                                               {
-                                                       infostream<<"Server: WARNING: Player digged"
-                                                                       <<" with impossible material + tool"
-                                                                       <<" combination"<<std::endl;
-                                               }
-                                               
-                                               bool weared_out = titem->addWear(prop.wear);
-
-                                               if(weared_out)
-                                               {
-                                                       mlist->deleteItem(item_i);
-                                               }
-                                       }
-                               }
-
-                               /*
-                                       Add dug item to inventory
-                               */
-
-                               InventoryItem *item = NULL;
-
-                               if(mineral != MINERAL_NONE)
-                                       item = getDiggedMineralItem(mineral, this);
-                               
-                               // If not mineral
-                               if(item == NULL)
-                               {
-                                       const std::string &dug_s = m_nodedef->get(material).dug_item;
-                                       if(dug_s != "")
-                                       {
-                                               std::istringstream is(dug_s, std::ios::binary);
-                                               item = InventoryItem::deSerialize(is, this);
-                                       }
-                               }
-                               
-                               if(item != NULL)
-                               {
-                                       // Add a item to inventory
-                                       player->inventory.addItem("main", item);
-
-                                       // Send inventory
-                                       UpdateCrafting(player->peer_id);
-                                       SendInventory(player->peer_id);
-                               }
-
-                               item = NULL;
-
-                               if(mineral != MINERAL_NONE)
-                                 item = getDiggedMineralItem(mineral, this);
-                       
-                               // If not mineral
-                               if(item == NULL)
-                               {
-                                       const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
-                                       s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
-                                       if(extra_dug_s != "" && extra_rarity != 0
-                                          && myrand() % extra_rarity == 0)
-                                       {
-                                               std::istringstream is(extra_dug_s, std::ios::binary);
-                                               item = InventoryItem::deSerialize(is, this);
-                                       }
-                               }
-                       
-                               if(item != NULL)
-                               {
-                                       // Add a item to inventory
-                                       player->inventory.addItem("main", item);
-
-                                       // Send inventory
-                                       UpdateCrafting(player->peer_id);
-                                       SendInventory(player->peer_id);
-                               }
-                       }
-
-                       /*
-                               Remove the node
-                               (this takes some time so it is done after the quick stuff)
-                       */
-                       {
-                               MapEditEventIgnorer ign(&m_ignore_map_edit_events);
-
-                               m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
-                       }
-                       /*
-                               Set blocks not sent to far players
-                       */
-                       for(core::list<u16>::Iterator
-                                       i = far_players.begin();
-                                       i != far_players.end(); i++)
-                       {
-                               u16 peer_id = *i;
-                               RemoteClient *client = getClient(peer_id);
-                               if(client==NULL)
-                                       continue;
-                               client->SetBlocksNotSent(modified_blocks);
-                       }
-
-                       /*
-                               Run script hook
-                       */
-                       scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
-               }
-               
-               /*
-                       1: place block
-               */
-               else if(action == 1)
-               {
-
-                       InventoryList *ilist = player->inventory.getList("main");
-                       if(ilist == NULL)
-                               return;
-
-                       // Get item
-                       InventoryItem *item = ilist->getItem(item_i);
-                       
-                       // If there is no item, it is not possible to add it anywhere
-                       if(item == NULL)
+                               // Do nothing else
                                return;
-                       
-                       /*
-                               Handle material items
-                       */
-                       if(std::string("MaterialItem") == item->getName())
-                       {
-                               try{
-                                       // Don't add a node if this is not a free space
-                                       MapNode n2 = m_env->getMap().getNode(p_over);
-                                       bool no_enough_privs =
-                                                       ((getPlayerPrivs(player) & PRIV_BUILD)==0);
-                                       if(no_enough_privs)
-                                               infostream<<"Player "<<player->getName()<<" cannot add node"
-                                                       <<" because privileges are "<<getPlayerPrivs(player)
-                                                       <<std::endl;
-
-                                       if(m_nodedef->get(n2).buildable_to == false
-                                               || no_enough_privs)
-                                       {
-                                               // Client probably has wrong data.
-                                               // Set block not sent, so that client will get
-                                               // a valid one.
-                                               infostream<<"Client "<<peer_id<<" tried to place"
-                                                               <<" node in invalid position; setting"
-                                                               <<" MapBlock not sent."<<std::endl;
-                                               RemoteClient *client = getClient(peer_id);
-                                               v3s16 blockpos = getNodeBlockPos(p_over);
-                                               client->SetBlockNotSent(blockpos);
-                                               return;
-                                       }
-                               }
-                               catch(InvalidPositionException &e)
-                               {
-                                       infostream<<"Server: Ignoring ADDNODE: Node not found"
-                                                       <<" Adding block to emerge queue."
-                                                       <<std::endl;
-                                       m_emerge_queue.addBlock(peer_id,
-                                                       getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
-                                       return;
-                               }
-
-                               // Reset build time counter
-                               getClient(peer_id)->m_time_from_building = 0.0;
-                               
-                               // Create node data
-                               MaterialItem *mitem = (MaterialItem*)item;
-                               MapNode n;
-                               n.setContent(mitem->getMaterial());
-
-                               actionstream<<player->getName()<<" places material "
-                                               <<(int)mitem->getMaterial()
-                                               <<" at "<<PP(p_under)<<std::endl;
-                       
-                               // Calculate direction for wall mounted stuff
-                               if(m_nodedef->get(n).wall_mounted)
-                                       n.param2 = packDir(p_under - p_over);
-
-                               // Calculate the direction for furnaces and chests and stuff
-                               if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
-                               {
-                                       v3f playerpos = player->getPosition();
-                                       v3f blockpos = intToFloat(p_over, BS) - playerpos;
-                                       blockpos = blockpos.normalize();
-                                       n.param1 = 0;
-                                       if (fabs(blockpos.X) > fabs(blockpos.Z)) {
-                                               if (blockpos.X < 0)
-                                                       n.param1 = 3;
-                                               else
-                                                       n.param1 = 1;
-                                       } else {
-                                               if (blockpos.Z < 0)
-                                                       n.param1 = 2;
-                                               else
-                                                       n.param1 = 0;
-                                       }
-                               }
-
-                               /*
-                                       Send to all close-by players
-                               */
-                               core::list<u16> far_players;
-                               sendAddNode(p_over, n, 0, &far_players, 30);
-                               
-                               /*
-                                       Handle inventory
-                               */
-                               InventoryList *ilist = player->inventory.getList("main");
-                               if(g_settings->getBool("creative_mode") == false && ilist)
-                               {
-                                       // Remove from inventory and send inventory
-                                       if(mitem->getCount() == 1)
-                                               ilist->deleteItem(item_i);
-                                       else
-                                               mitem->remove(1);
-                                       // Send inventory
-                                       UpdateCrafting(peer_id);
-                                       SendInventory(peer_id);
-                               }
-                               
-                               /*
-                                       Add node.
-
-                                       This takes some time so it is done after the quick stuff
-                               */
-                               core::map<v3s16, MapBlock*> modified_blocks;
-                               {
-                                       MapEditEventIgnorer ign(&m_ignore_map_edit_events);
-
-                                       std::string p_name = std::string(player->getName());
-                                       m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
-                               }
-                               /*
-                                       Set blocks not sent to far players
-                               */
-                               for(core::list<u16>::Iterator
-                                               i = far_players.begin();
-                                               i != far_players.end(); i++)
-                               {
-                                       u16 peer_id = *i;
-                                       RemoteClient *client = getClient(peer_id);
-                                       if(client==NULL)
-                                               continue;
-                                       client->SetBlocksNotSent(modified_blocks);
-                               }
-
-                               /*
-                                       Run script hook
-                               */
-                               scriptapi_environment_on_placenode(m_lua, p_over, n, srp);
-
-                               /*
-                                       Calculate special events
-                               */
-                               
-                               /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
-                               {
-                                       u32 count = 0;
-                                       for(s16 z=-1; z<=1; z++)
-                                       for(s16 y=-1; y<=1; y++)
-                                       for(s16 x=-1; x<=1; x++)
-                                       {
-                                               
-                                       }
-                               }*/
                        }
-                       /*
-                               Place other item (not a block)
-                       */
-                       else
+               }
+
+               /*
+                       Make sure the player is allowed to do it
+               */
+               bool build_priv = (getPlayerPrivs(player) & PRIV_BUILD) != 0;
+               if(!build_priv)
+               {
+                       infostream<<"Ignoring interaction from player "<<player->getName()
+                                       <<" because privileges are "<<getPlayerPrivs(player)
+                                       <<std::endl;
+                       // NOTE: no return; here, fall through
+               }
+
+               /*
+                       0: start digging or punch object
+               */
+               if(action == 0)
+               {
+                       if(pointed.type == POINTEDTHING_NODE)
                        {
-                               v3s16 blockpos = getNodeBlockPos(p_over);
-                               
                                /*
-                                       Check that the block is loaded so that the item
-                                       can properly be added to the static list too
+                                       NOTE: This can be used in the future to check if
+                                       somebody is cheating, by checking the timing.
                                */
-                               MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
-                               if(block==NULL)
+                               bool cannot_punch_node = !build_priv;
+
+                               MapNode n(CONTENT_IGNORE);
+
+                               try
                                {
-                                       infostream<<"Error while placing object: "
-                                                       "block not found"<<std::endl;
-                                       return;
+                                       n = m_env->getMap().getNode(p_under);
                                }
-
-                               /*
-                                       If in creative mode, item dropping is disabled unless
-                                       player has build privileges
-                               */
-                               if(g_settings->getBool("creative_mode") &&
-                                       (getPlayerPrivs(player) & PRIV_BUILD) == 0)
+                               catch(InvalidPositionException &e)
                                {
-                                       infostream<<"Not allowing player to drop item: "
-                                                       "creative mode and no build privs"<<std::endl;
-                                       return;
+                                       infostream<<"Server: Not punching: Node not found."
+                                                       <<" Adding block to emerge queue."
+                                                       <<std::endl;
+                                       m_emerge_queue.addBlock(peer_id,
+                                                       getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
+                                       cannot_punch_node = true;
                                }
 
-                               // Calculate a position for it
-                               v3f pos = intToFloat(p_over, BS);
-                               //pos.Y -= BS*0.45;
-                               /*pos.Y -= BS*0.25; // let it drop a bit
-                               // Randomize a bit
-                               pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
-                               pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
+                               if(cannot_punch_node)
+                                       return;
 
                                /*
-                                       Create the object
+                                       Run script hook
                                */
-                               ServerActiveObject *obj = item->createSAO(m_env, pos);
+                               scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
+                       }
+                       else if(pointed.type == POINTEDTHING_OBJECT)
+                       {
+                               if(!build_priv)
+                                       return;
 
-                               if(obj == NULL)
-                               {
-                                       infostream<<"WARNING: item resulted in NULL object, "
-                                                       <<"not placing onto map"
-                                                       <<std::endl;
-                               }
-                               else
-                               {
-                                       actionstream<<player->getName()<<" places "<<item->getName()
-                                                       <<" at "<<PP(p_over)<<std::endl;
-                               
-                                       // Add the object to the environment
-                                       m_env->addActiveObject(obj);
-                                       
-                                       infostream<<"Placed object"<<std::endl;
+                               // Skip if object has been removed
+                               if(pointed_object->m_removed)
+                                       return;
 
-                                       if(g_settings->getBool("creative_mode") == false)
-                                       {
-                                               // Delete the right amount of items from the slot
-                                               u16 dropcount = item->getDropCount();
-                                               
-                                               // Delete item if all gone
-                                               if(item->getCount() <= dropcount)
-                                               {
-                                                       if(item->getCount() < dropcount)
-                                                               infostream<<"WARNING: Server: dropped more items"
-                                                                               <<" than the slot contains"<<std::endl;
-                                                       
-                                                       InventoryList *ilist = player->inventory.getList("main");
-                                                       if(ilist)
-                                                               // Remove from inventory and send inventory
-                                                               ilist->deleteItem(item_i);
-                                               }
-                                               // Else decrement it
-                                               else
-                                                       item->remove(dropcount);
-                                               
-                                               // Send inventory
-                                               UpdateCrafting(peer_id);
-                                               SendInventory(peer_id);
-                                       }
-                               }
+                               actionstream<<player->getName()<<" punches object "
+                                       <<pointed.object_id<<std::endl;
+
+                               // Do stuff
+                               pointed_object->punch(srp);
                        }
 
-               } // action == 1
+               } // action == 0
 
                /*
-                       Catch invalid actions
+                       1: stop digging
                */
-               else
+               else if(action == 1)
                {
-                       infostream<<"WARNING: Server: Invalid action "
-                                       <<action<<std::endl;
-               }
-       }
-#if 0
-       else if(command == TOSERVER_RELEASE)
-       {
-               if(datasize < 3)
-                       return;
-               /*
-                       length: 3
-                       [0] u16 command
-                       [2] u8 button
-               */
-               infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
-       }
-#endif
-       else if(command == TOSERVER_SIGNTEXT)
-       {
-               infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
-                               <<std::endl;
-               return;
-       }
-       else if(command == TOSERVER_SIGNNODETEXT)
-       {
-               if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
-                       return;
+               } // action == 1
+
                /*
-                       u16 command
-                       v3s16 p
-                       u16 textlen
-                       textdata
+                       2: Digging completed
                */
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-               u8 buf[6];
-               // Read stuff
-               is.read((char*)buf, 6);
-               v3s16 p = readV3S16(buf);
-               is.read((char*)buf, 2);
-               u16 textlen = readU16(buf);
-               std::string text;
-               for(u16 i=0; i<textlen; i++)
+               else if(action == 2)
                {
-                       is.read((char*)buf, 1);
-                       text += (char)buf[0];
-               }
+                       // Only complete digging of nodes
+                       if(pointed.type != POINTEDTHING_NODE)
+                               return;
 
-               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
-               if(!meta)
-                       return;
+                       // Mandatory parameter; actually used for nothing
+                       core::map<v3s16, MapBlock*> modified_blocks;
 
-               meta->setText(text);
-               
-               actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
-                               <<" at "<<PP(p)<<std::endl;
-                               
-               v3s16 blockpos = getNodeBlockPos(p);
-               MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
-               if(block)
-               {
-                       block->raiseModified(MOD_STATE_WRITE_NEEDED,
-                                       "sign node text");
-               }
+                       content_t material = CONTENT_IGNORE;
+                       u8 mineral = MINERAL_NONE;
 
-               setBlockNotSent(blockpos);
-       }
-       else if(command == TOSERVER_INVENTORY_ACTION)
-       {
-               /*// Ignore inventory changes if in creative mode
-               if(g_settings->getBool("creative_mode") == true)
-               {
-                       infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
-                                       <<std::endl;
-                       return;
-               }*/
-               // Strip command and create a stream
-               std::string datastring((char*)&data[2], datasize-2);
-               infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
-               std::istringstream is(datastring, std::ios_base::binary);
-               // Create an action
-               InventoryAction *a = InventoryAction::deSerialize(is);
-               if(a != NULL)
-               {
-                       // Create context
-                       InventoryContext c;
-                       c.current_player = player;
+                       bool cannot_remove_node = !build_priv;
+                       
+                       MapNode n(CONTENT_IGNORE);
+                       try
+                       {
+                               n = m_env->getMap().getNode(p_under);
+                               // Get mineral
+                               mineral = n.getMineral(m_nodedef);
+                               // Get material at position
+                               material = n.getContent();
+                               // If not yet cancelled
+                               if(cannot_remove_node == false)
+                               {
+                                       // If it's not diggable, do nothing
+                                       if(m_nodedef->get(material).diggable == false)
+                                       {
+                                               infostream<<"Server: Not finishing digging: "
+                                                               <<"Node not diggable"
+                                                               <<std::endl;
+                                               cannot_remove_node = true;
+                                       }
+                               }
+                               // If not yet cancelled
+                               if(cannot_remove_node == false)
+                               {
+                                       // Get node metadata
+                                       NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
+                                       if(meta && meta->nodeRemovalDisabled() == true)
+                                       {
+                                               infostream<<"Server: Not finishing digging: "
+                                                               <<"Node metadata disables removal"
+                                                               <<std::endl;
+                                               cannot_remove_node = true;
+                                       }
+                               }
+                       }
+                       catch(InvalidPositionException &e)
+                       {
+                               infostream<<"Server: Not finishing digging: Node not found."
+                                               <<" Adding block to emerge queue."
+                                               <<std::endl;
+                               m_emerge_queue.addBlock(peer_id,
+                                               getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
+                               cannot_remove_node = true;
+                       }
 
                        /*
-                               Handle craftresult specially if not in creative mode
+                               If node can't be removed, set block to be re-sent to
+                               client and quit.
                        */
-                       bool disable_action = false;
-                       if(a->getType() == IACTION_MOVE
-                                       && g_settings->getBool("creative_mode") == false)
+                       if(cannot_remove_node)
                        {
-                               IMoveAction *ma = (IMoveAction*)a;
-                               if(ma->to_inv == "current_player" &&
-                                               ma->from_inv == "current_player")
+                               infostream<<"Server: Not finishing digging."<<std::endl;
+
+                               // Client probably has wrong data.
+                               // Set block not sent, so that client will get
+                               // a valid one.
+                               infostream<<"Client "<<peer_id<<" tried to dig "
+                                               <<"node; but node cannot be removed."
+                                               <<" setting MapBlock not sent."<<std::endl;
+                               RemoteClient *client = getClient(peer_id);
+                               v3s16 blockpos = getNodeBlockPos(p_under);
+                               client->SetBlockNotSent(blockpos);
+                                       
+                               return;
+                       }
+                       
+                       actionstream<<player->getName()<<" digs "<<PP(p_under)
+                                       <<", gets material "<<(int)material<<", mineral "
+                                       <<(int)mineral<<std::endl;
+                       
+                       /*
+                               Send the removal to all close-by players.
+                               - If other player is close, send REMOVENODE
+                               - Otherwise set blocks not sent
+                       */
+                       core::list<u16> far_players;
+                       sendRemoveNode(p_under, peer_id, &far_players, 30);
+                       
+                       /*
+                               Update and send inventory
+                       */
+
+                       if(g_settings->getBool("creative_mode") == false)
+                       {
+                               /*
+                                       Wear out tool
+                               */
+                               InventoryList *mlist = player->inventory.getList("main");
+                               if(mlist != NULL)
                                {
-                                       InventoryList *rlist = player->inventory.getList("craftresult");
-                                       assert(rlist);
-                                       InventoryList *clist = player->inventory.getList("craft");
-                                       assert(clist);
-                                       InventoryList *mlist = player->inventory.getList("main");
-                                       assert(mlist);
-                                       /*
-                                               Craftresult is no longer preview if something
-                                               is moved into it
-                                       */
-                                       if(ma->to_list == "craftresult"
-                                                       && ma->from_list != "craftresult")
+                                       InventoryItem *item = mlist->getItem(item_i);
+                                       if(item && (std::string)item->getName() == "ToolItem")
                                        {
-                                               // If it currently is a preview, remove
-                                               // its contents
-                                               if(player->craftresult_is_preview)
+                                               ToolItem *titem = (ToolItem*)item;
+                                               std::string toolname = titem->getToolName();
+
+                                               // Get digging properties for material and tool
+                                               ToolDiggingProperties tp =
+                                                               m_toolmgr->getDiggingProperties(toolname);
+                                               DiggingProperties prop =
+                                                               getDiggingProperties(material, &tp, m_nodedef);
+
+                                               if(prop.diggable == false)
                                                {
-                                                       rlist->deleteItem(0);
+                                                       infostream<<"Server: WARNING: Player digged"
+                                                                       <<" with impossible material + tool"
+                                                                       <<" combination"<<std::endl;
                                                }
-                                               player->craftresult_is_preview = false;
-                                       }
-                                       /*
-                                               Crafting takes place if this condition is true.
-                                       */
-                                       if(player->craftresult_is_preview &&
-                                                       ma->from_list == "craftresult")
-                                       {
-                                               player->craftresult_is_preview = false;
-                                               clist->decrementMaterials(1);
-                                               
-                                               /* Print out action */
-                                               InventoryList *list =
-                                                               player->inventory.getList("craftresult");
-                                               assert(list);
-                                               InventoryItem *item = list->getItem(0);
-                                               std::string itemname = "NULL";
-                                               if(item)
-                                                       itemname = item->getName();
-                                               actionstream<<player->getName()<<" crafts "
-                                                               <<itemname<<std::endl;
-                                       }
-                                       /*
-                                               If the craftresult is placed on itself, move it to
-                                               main inventory instead of doing the action
-                                       */
-                                       if(ma->to_list == "craftresult"
-                                                       && ma->from_list == "craftresult")
-                                       {
-                                               disable_action = true;
                                                
-                                               InventoryItem *item1 = rlist->changeItem(0, NULL);
-                                               mlist->addItem(item1);
-                                       }
-                               }
-                               // Disallow moving items if not allowed to build
-                               else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
-                               {
-                                       disable_action = true;
-                               }
-                               // if it's a locking chest, only allow the owner or server admins to move items
-                               else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
-                               {
-                                       Strfnd fn(ma->from_inv);
-                                       std::string id0 = fn.next(":");
-                                       if(id0 == "nodemeta")
-                                       {
-                                               v3s16 p;
-                                               p.X = stoi(fn.next(","));
-                                               p.Y = stoi(fn.next(","));
-                                               p.Z = stoi(fn.next(","));
-                                               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
-                                               if(meta->getOwner() != ""){
-                                                       if(meta->getOwner() != player->getName())
-                                                               disable_action = true;
+                                               bool weared_out = titem->addWear(prop.wear);
+
+                                               if(weared_out)
+                                               {
+                                                       mlist->deleteItem(item_i);
                                                }
                                        }
                                }
-                               else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
+
+                               /*
+                                       Add dug item to inventory
+                               */
+
+                               InventoryItem *item = NULL;
+
+                               if(mineral != MINERAL_NONE)
+                                       item = getDiggedMineralItem(mineral, this);
+                               
+                               // If not mineral
+                               if(item == NULL)
                                {
-                                       Strfnd fn(ma->to_inv);
-                                       std::string id0 = fn.next(":");
-                                       if(id0 == "nodemeta")
+                                       const std::string &dug_s = m_nodedef->get(material).dug_item;
+                                       if(dug_s != "")
                                        {
-                                               v3s16 p;
-                                               p.X = stoi(fn.next(","));
-                                               p.Y = stoi(fn.next(","));
-                                               p.Z = stoi(fn.next(","));
-                                               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
-                                               if(meta->getOwner() != ""){
-                                                       if(meta->getOwner() != player->getName())
-                                                               disable_action = true;
-                                               }
+                                               std::istringstream is(dug_s, std::ios::binary);
+                                               item = InventoryItem::deSerialize(is, this);
                                        }
                                }
-                       }
-
-                       if(a->getType() == IACTION_DROP)
-                       {
-                               IDropAction *da = (IDropAction*)a;
-                               // Disallow dropping items if not allowed to build
-                               if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
+                               
+                               if(item != NULL)
                                {
-                                       disable_action = true;
+                                       // Add a item to inventory
+                                       player->inventory.addItem("main", item);
                                }
-                               // if it's a locking chest, only allow the owner or server admins to drop items
-                               else if (da->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
+
+                               item = NULL;
+
+                               if(mineral != MINERAL_NONE)
+                                 item = getDiggedMineralItem(mineral, this);
+                       
+                               // If not mineral
+                               if(item == NULL)
                                {
-                                       Strfnd fn(da->from_inv);
-                                       std::string id0 = fn.next(":");
-                                       if(id0 == "nodemeta")
+                                       const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
+                                       s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
+                                       if(extra_dug_s != "" && extra_rarity != 0
+                                          && myrand() % extra_rarity == 0)
                                        {
-                                               v3s16 p;
-                                               p.X = stoi(fn.next(","));
-                                               p.Y = stoi(fn.next(","));
-                                               p.Z = stoi(fn.next(","));
-                                               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
-                                               if(meta->getOwner() != ""){
-                                                       if(meta->getOwner() != player->getName())
-                                                               disable_action = true;
-                                               }
+                                               std::istringstream is(extra_dug_s, std::ios::binary);
+                                               item = InventoryItem::deSerialize(is, this);
                                        }
                                }
-                       }
                        
-                       if(disable_action == false)
+                               if(item != NULL)
+                               {
+                                       // Add a item to inventory
+                                       player->inventory.addItem("main", item);
+                               }
+                       }
+
+                       /*
+                               Remove the node
+                               (this takes some time so it is done after the quick stuff)
+                       */
                        {
-                               // Feed action to player inventory
-                               a->apply(&c, this, m_env);
+                               MapEditEventIgnorer ign(&m_ignore_map_edit_events);
+
+                               m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
                        }
-                       else
+                       /*
+                               Set blocks not sent to far players
+                       */
+                       for(core::list<u16>::Iterator
+                                       i = far_players.begin();
+                                       i != far_players.end(); i++)
                        {
-                               // Send inventory
-                               UpdateCrafting(player->peer_id);
-                               SendInventory(player->peer_id);
+                               u16 peer_id = *i;
+                               RemoteClient *client = getClient(peer_id);
+                               if(client==NULL)
+                                       continue;
+                               client->SetBlocksNotSent(modified_blocks);
                        }
 
-                       // Eat the action
-                       delete a;
-               }
-               else
-               {
-                       infostream<<"TOSERVER_INVENTORY_ACTION: "
-                                       <<"InventoryAction::deSerialize() returned NULL"
-                                       <<std::endl;
-               }
-       }
-       else if(command == TOSERVER_CHAT_MESSAGE)
-       {
+                       /*
+                               Run script hook
+                       */
+                       scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
+               } // action == 2
+               
                /*
-                       u16 command
-                       u16 length
-                       wstring message
+                       3: place block or right-click object
                */
-               u8 buf[6];
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-               
-               // Read stuff
-               is.read((char*)buf, 2);
-               u16 len = readU16(buf);
-               
-               std::wstring message;
-               for(u16 i=0; i<len; i++)
+               else if(action == 3)
                {
-                       is.read((char*)buf, 2);
-                       message += (wchar_t)readU16(buf);
-               }
+                       if(pointed.type == POINTEDTHING_NODE)
+                       {
+                               InventoryList *ilist = player->inventory.getList("main");
+                               if(ilist == NULL)
+                                       return;
 
-               // Get player name of this client
-               std::wstring name = narrow_to_wide(player->getName());
-               
-               // Run script hook
-               bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
-                               wide_to_narrow(message));
-               // If script ate the message, don't proceed
-               if(ate)
-                       return;
-               
-               // Line to send to players
-               std::wstring line;
-               // Whether to send to the player that sent the line
-               bool send_to_sender = false;
-               // Whether to send to other players
-               bool send_to_others = false;
-               
-               // Local player gets all privileges regardless of
-               // what's set on their account.
-               u64 privs = getPlayerPrivs(player);
+                               // Get item
+                               InventoryItem *item = ilist->getItem(item_i);
+                               
+                               // If there is no item, it is not possible to add it anywhere
+                               if(item == NULL)
+                                       return;
+
+                               /*
+                                       Handle material items
+                               */
+                               if(std::string("MaterialItem") == item->getName())
+                               {
+                                       bool cannot_place_node = !build_priv;
+
+                                       try{
+                                               // Don't add a node if this is not a free space
+                                               MapNode n2 = m_env->getMap().getNode(p_above);
+                                               if(m_nodedef->get(n2).buildable_to == false)
+                                               {
+                                                       infostream<<"Client "<<peer_id<<" tried to place"
+                                                                       <<" node in invalid position."<<std::endl;
+                                                       cannot_place_node = true;
+                                               }
+                                       }
+                                       catch(InvalidPositionException &e)
+                                       {
+                                               infostream<<"Server: Ignoring ADDNODE: Node not found"
+                                                               <<" Adding block to emerge queue."
+                                                               <<std::endl;
+                                               m_emerge_queue.addBlock(peer_id,
+                                                               getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
+                                               cannot_place_node = true;
+                                       }
+
+                                       if(cannot_place_node)
+                                       {
+                                               // Client probably has wrong data.
+                                               // Set block not sent, so that client will get
+                                               // a valid one.
+                                               RemoteClient *client = getClient(peer_id);
+                                               v3s16 blockpos = getNodeBlockPos(p_above);
+                                               client->SetBlockNotSent(blockpos);
+                                               return;
+                                       }
+
+                                       // Reset build time counter
+                                       getClient(peer_id)->m_time_from_building = 0.0;
+                                       
+                                       // Create node data
+                                       MaterialItem *mitem = (MaterialItem*)item;
+                                       MapNode n;
+                                       n.setContent(mitem->getMaterial());
+
+                                       actionstream<<player->getName()<<" places material "
+                                                       <<(int)mitem->getMaterial()
+                                                       <<" at "<<PP(p_under)<<std::endl;
+                               
+                                       // Calculate direction for wall mounted stuff
+                                       if(m_nodedef->get(n).wall_mounted)
+                                               n.param2 = packDir(p_under - p_above);
+
+                                       // Calculate the direction for furnaces and chests and stuff
+                                       if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
+                                       {
+                                               v3f playerpos = player->getPosition();
+                                               v3f blockpos = intToFloat(p_above, BS) - playerpos;
+                                               blockpos = blockpos.normalize();
+                                               n.param1 = 0;
+                                               if (fabs(blockpos.X) > fabs(blockpos.Z)) {
+                                                       if (blockpos.X < 0)
+                                                               n.param1 = 3;
+                                                       else
+                                                               n.param1 = 1;
+                                               } else {
+                                                       if (blockpos.Z < 0)
+                                                               n.param1 = 2;
+                                                       else
+                                                               n.param1 = 0;
+                                               }
+                                       }
+
+                                       /*
+                                               Send to all close-by players
+                                       */
+                                       core::list<u16> far_players;
+                                       sendAddNode(p_above, n, 0, &far_players, 30);
+                                       
+                                       /*
+                                               Handle inventory
+                                       */
+                                       InventoryList *ilist = player->inventory.getList("main");
+                                       if(g_settings->getBool("creative_mode") == false && ilist)
+                                       {
+                                               // Remove from inventory and send inventory
+                                               if(mitem->getCount() == 1)
+                                                       ilist->deleteItem(item_i);
+                                               else
+                                                       mitem->remove(1);
+                                       }
+                                       
+                                       /*
+                                               Add node.
 
-               // Parse commands
-               if(message[0] == L'/')
-               {
-                       size_t strip_size = 1;
-                       if (message[1] == L'#') // support old-style commans
-                               ++strip_size;
-                       message = message.substr(strip_size);
+                                               This takes some time so it is done after the quick stuff
+                                       */
+                                       core::map<v3s16, MapBlock*> modified_blocks;
+                                       {
+                                               MapEditEventIgnorer ign(&m_ignore_map_edit_events);
 
-                       WStrfnd f1(message);
-                       f1.next(L" "); // Skip over /#whatever
-                       std::wstring paramstring = f1.next(L"");
+                                               std::string p_name = std::string(player->getName());
+                                               m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
+                                       }
+                                       /*
+                                               Set blocks not sent to far players
+                                       */
+                                       for(core::list<u16>::Iterator
+                                                       i = far_players.begin();
+                                                       i != far_players.end(); i++)
+                                       {
+                                               u16 peer_id = *i;
+                                               RemoteClient *client = getClient(peer_id);
+                                               if(client==NULL)
+                                                       continue;
+                                               client->SetBlocksNotSent(modified_blocks);
+                                       }
 
-                       ServerCommandContext *ctx = new ServerCommandContext(
-                               str_split(message, L' '),
-                               paramstring,
-                               this,
-                               m_env,
-                               player,
-                               privs);
+                                       /*
+                                               Run script hook
+                                       */
+                                       scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
 
-                       std::wstring reply(processServerCommand(ctx));
-                       send_to_sender = ctx->flags & SEND_TO_SENDER;
-                       send_to_others = ctx->flags & SEND_TO_OTHERS;
+                                       /*
+                                               Calculate special events
+                                       */
+                                       
+                                       /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
+                                       {
+                                               u32 count = 0;
+                                               for(s16 z=-1; z<=1; z++)
+                                               for(s16 y=-1; y<=1; y++)
+                                               for(s16 x=-1; x<=1; x++)
+                                               {
+                                                       
+                                               }
+                                       }*/
+                               }
+                               /*
+                                       Place other item (not a block)
+                               */
+                               else
+                               {
+                                       if(!build_priv)
+                                       {
+                                               infostream<<"Not allowing player to place item: "
+                                                               "no build privileges"<<std::endl;
+                                               return;
+                                       }
 
-                       if (ctx->flags & SEND_NO_PREFIX)
-                               line += reply;
-                       else
-                               line += L"Server: " + reply;
+                                       // Calculate a position for it
+                                       v3f pos = player_pos;
+                                       if(pointed.type == POINTEDTHING_NOTHING)
+                                       {
+                                               infostream<<"Not allowing player to place item: "
+                                                               "pointing to nothing"<<std::endl;
+                                               return;
+                                       }
+                                       else if(pointed.type == POINTEDTHING_NODE)
+                                       {
+                                               pos = intToFloat(p_above, BS);
+                                       }
+                                       else if(pointed.type == POINTEDTHING_OBJECT)
+                                       {
+                                               pos = pointed_object->getBasePosition();
 
-                       delete ctx;
+                                               // Randomize a bit
+                                               pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
+                                               pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
+                                       }
 
-               }
-               else
-               {
-                       if(privs & PRIV_SHOUT)
-                       {
-                               line += L"<";
-                               line += name;
-                               line += L"> ";
-                               line += message;
-                               send_to_others = true;
+                                       //pos.Y -= BS*0.45;
+                                       //pos.Y -= BS*0.25; // let it drop a bit
+
+                                       /*
+                                               Check that the block is loaded so that the item
+                                               can properly be added to the static list too
+                                       */
+                                       v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
+                                       MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
+                                       if(block==NULL)
+                                       {
+                                               infostream<<"Error while placing item: "
+                                                               "block not found"<<std::endl;
+                                               return;
+                                       }
+
+                                       actionstream<<player->getName()<<" places "<<item->getName()
+                                                       <<" at "<<PP(pos)<<std::endl;
+
+                                       /*
+                                               Place the item
+                                       */
+                                       bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
+                                       if(remove && g_settings->getBool("creative_mode") == false)
+                                       {
+                                               InventoryList *ilist = player->inventory.getList("main");
+                                               if(ilist)
+                                                       // Remove from inventory and send inventory
+                                                       ilist->deleteItem(item_i);
+                                       }
+                               }
                        }
-                       else
+                       else if(pointed.type == POINTEDTHING_OBJECT)
                        {
-                               line += L"Server: You are not allowed to shout";
-                               send_to_sender = true;
-                       }
-               }
-               
-               if(line != L"")
-               {
-                       if(send_to_others)
-                               actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
+                               // Right click object
 
-                       /*
-                               Send the message to clients
-                       */
-                       for(core::map<u16, RemoteClient*>::Iterator
-                               i = m_clients.getIterator();
-                               i.atEnd() == false; i++)
-                       {
-                               // Get client and check that it is valid
-                               RemoteClient *client = i.getNode()->getValue();
-                               assert(client->peer_id == i.getNode()->getKey());
-                               if(client->serialization_version == SER_FMT_VER_INVALID)
-                                       continue;
+                               if(!build_priv)
+                                       return;
 
-                               // Filter recipient
-                               bool sender_selected = (peer_id == client->peer_id);
-                               if(sender_selected == true && send_to_sender == false)
-                                       continue;
-                               if(sender_selected == false && send_to_others == false)
-                                       continue;
+                               // Skip if object has been removed
+                               if(pointed_object->m_removed)
+                                       return;
 
-                               SendChatMessage(client->peer_id, line);
+                               actionstream<<player->getName()<<" right-clicks object "
+                                       <<pointed.object_id<<std::endl;
+
+                               // Do stuff
+                               pointed_object->rightClick(srp);
                        }
-               }
-       }
-       else if(command == TOSERVER_DAMAGE)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-               u8 damage = readU8(is);
 
-               if(g_settings->getBool("enable_damage"))
-               {
-                       actionstream<<player->getName()<<" damaged by "
-                                       <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
-                                       <<std::endl;
-                               
-                       HandlePlayerHP(player, damage);
-               }
-               else
-               {
-                       SendPlayerHP(player);
-               }
-       }
-       else if(command == TOSERVER_PASSWORD)
-       {
+               } // action == 3
+
                /*
-                       [0] u16 TOSERVER_PASSWORD
-                       [2] u8[28] old password
-                       [30] u8[28] new password
+                       4: use
                */
-
-               if(datasize != 2+PASSWORD_SIZE*2)
-                       return;
-               /*char password[PASSWORD_SIZE];
-               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
-                       password[i] = data[2+i];
-               password[PASSWORD_SIZE-1] = 0;*/
-               std::string oldpwd;
-               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
-               {
-                       char c = data[2+i];
-                       if(c == 0)
-                               break;
-                       oldpwd += c;
-               }
-               std::string newpwd;
-               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+               else if(action == 4)
                {
-                       char c = data[2+PASSWORD_SIZE+i];
-                       if(c == 0)
-                               break;
-                       newpwd += c;
-               }
+                       InventoryList *ilist = player->inventory.getList("main");
+                       if(ilist == NULL)
+                               return;
 
-               infostream<<"Server: Client requests a password change from "
-                               <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
+                       // Get item
+                       InventoryItem *item = ilist->getItem(item_i);
+                       
+                       // If there is no item, it is not possible to add it anywhere
+                       if(item == NULL)
+                               return;
 
-               std::string playername = player->getName();
+                       // Requires build privs
+                       if(!build_priv)
+                       {
+                               infostream<<"Not allowing player to use item: "
+                                               "no build privileges"<<std::endl;
+                               return;
+                       }
 
-               if(m_authmanager.exists(playername) == false)
-               {
-                       infostream<<"Server: playername not found in authmanager"<<std::endl;
-                       // Wrong old password supplied!!
-                       SendChatMessage(peer_id, L"playername not found in authmanager");
-                       return;
-               }
+                       actionstream<<player->getName()<<" uses "<<item->getName()
+                                       <<", pointing at "<<pointed.dump()<<std::endl;
 
-               std::string checkpwd = m_authmanager.getPassword(playername);
+                       bool remove = item->use(m_env, srp, pointed);
+                       if(remove && g_settings->getBool("creative_mode") == false)
+                       {
+                               InventoryList *ilist = player->inventory.getList("main");
+                               if(ilist)
+                                       // Remove from inventory and send inventory
+                                       ilist->deleteItem(item_i);
+                       }
 
-               if(oldpwd != checkpwd)
+               } // action == 4
+
+               /*
+                       Catch invalid actions
+               */
+               else
                {
-                       infostream<<"Server: invalid old password"<<std::endl;
-                       // Wrong old password supplied!!
-                       SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
-                       return;
+                       infostream<<"WARNING: Server: Invalid action "
+                                       <<action<<std::endl;
                }
 
-               actionstream<<player->getName()<<" changes password"<<std::endl;
-
-               m_authmanager.setPassword(playername, newpwd);
-               
-               infostream<<"Server: password change successful for "<<playername
-                               <<std::endl;
-               SendChatMessage(peer_id, L"Password change successful");
-       }
-       else if(command == TOSERVER_PLAYERITEM)
-       {
-               if (datasize < 2+2)
-                       return;
+               // Complete add_to_inventory_later
+               srp->completeAddToInventoryLater(item_i);
 
-               u16 item = readU16(&data[2]);
-               player->wieldItem(item);
-               SendWieldedItem(player);
-       }
-       else if(command == TOSERVER_RESPAWN)
-       {
-               if(player->hp != 0)
-                       return;
-               
-               RespawnPlayer(player);
-               
-               actionstream<<player->getName()<<" respawns at "
-                               <<PP(player->getPosition()/BS)<<std::endl;
+               // Send inventory
+               // FIXME: Shouldn't be done unless something changed.
+               UpdateCrafting(player->peer_id);
+               SendInventory(player->peer_id);
        }
        else
        {
@@ -3865,6 +3813,31 @@ void Server::SendNodeDef(con::Connection &con, u16 peer_id,
        con.Send(peer_id, 0, data, true);
 }
 
+void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
+               ICraftItemDefManager *craftitemdef)
+{
+       DSTACK(__FUNCTION_NAME);
+       std::ostringstream os(std::ios_base::binary);
+
+       /*
+               u16 command
+               u32 length of the next item
+               serialized CraftItemDefManager
+       */
+       writeU16(os, TOCLIENT_CRAFTITEMDEF);
+       std::ostringstream tmp_os(std::ios::binary);
+       craftitemdef->serialize(tmp_os);
+       os<<serializeLongString(tmp_os.str());
+
+       // Make data buffer
+       std::string s = os.str();
+       infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
+                       <<s.size()<<std::endl;
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       con.Send(peer_id, 0, data, true);
+}
+
 /*
        Non-static send methods
 */
@@ -4481,7 +4454,7 @@ void Server::HandlePlayerHP(Player *player, s16 damage)
 void Server::RespawnPlayer(Player *player)
 {
        player->hp = 20;
-       ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
+       ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
        bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
        if(!repositioned){
                v3f pos = findSpawnPos(m_env->getServerMap());
@@ -4626,6 +4599,10 @@ ICraftDefManager* Server::getCraftDefManager()
 {
        return m_craftdef;
 }
+ICraftItemDefManager* Server::getCraftItemDefManager()
+{
+       return m_craftitemdef;
+}
 ITextureSource* Server::getTextureSource()
 {
        return NULL;
@@ -4647,6 +4624,10 @@ IWritableCraftDefManager* Server::getWritableCraftDefManager()
 {
        return m_craftdef;
 }
+IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
+{
+       return m_craftitemdef;
+}
 
 v3f findSpawnPos(ServerMap &map)
 {
@@ -4776,7 +4757,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
                m_env->addPlayer(player);
 
                /* Run scripts */
-               ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
+               ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
                scriptapi_on_newplayer(m_lua, srp);
 
                /* Add stuff to inventory */
index 673458882f23898d41ebb922e5f7c0210432112d..98c4f65d662395ea7d7611fd161ec17dc243ba13 100644 (file)
@@ -36,6 +36,7 @@ typedef struct lua_State lua_State;
 class IWritableToolDefManager;
 class IWritableNodeDefManager;
 class IWritableCraftDefManager;
+class IWritableCraftItemDefManager;
 
 /*
        Some random functions
@@ -495,12 +496,14 @@ public:
        virtual IToolDefManager* getToolDefManager();
        virtual INodeDefManager* getNodeDefManager();
        virtual ICraftDefManager* getCraftDefManager();
+       virtual ICraftItemDefManager* getCraftItemDefManager();
        virtual ITextureSource* getTextureSource();
        virtual u16 allocateUnknownNodeId(const std::string &name);
        
        IWritableToolDefManager* getWritableToolDefManager();
        IWritableNodeDefManager* getWritableNodeDefManager();
        IWritableCraftDefManager* getWritableCraftDefManager();
+       IWritableCraftItemDefManager* getWritableCraftItemDefManager();
 
 private:
 
@@ -523,6 +526,8 @@ private:
                        IToolDefManager *tooldef);
        static void SendNodeDef(con::Connection &con, u16 peer_id,
                        INodeDefManager *nodedef);
+       static void SendCraftItemDef(con::Connection &con, u16 peer_id,
+                       ICraftItemDefManager *nodedef);
        
        /*
                Non-static send methods.
@@ -644,6 +649,9 @@ private:
        // Craft definition manager
        IWritableCraftDefManager *m_craftdef;
        
+       // CraftItem definition manager
+       IWritableCraftItemDefManager *m_craftitemdef;
+       
        /*
                Threads
        */
index f415c86ce34b459401cb911465a5646a6cafcb71..afd704fd3f761fd04943b8276191886b74085d23 100644 (file)
@@ -225,7 +225,7 @@ void cmd_teleport(std::wostringstream &os,
        //ctx->player->setPosition(dest);
 
        // Use the ServerActiveObject interface of ServerRemotePlayer
-       ServerRemotePlayer *srp = (ServerRemotePlayer*)ctx->player;
+       ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(ctx->player);
        srp->setPos(dest);
        ctx->server->SendMovePlayer(ctx->player);
 
index 26dce007f510b03f8d9e2804a048799b14381849..9e9513a7c2a4e2ca7877d41d4025cd5c906e14b2 100644 (file)
@@ -123,6 +123,8 @@ public:
        // If all fits, eats item and returns true. Otherwise returns false.
        virtual bool addToInventory(InventoryItem *item)
                {return false;}
+       virtual void addToInventoryLater(InventoryItem *item)
+               {}
        virtual void setHP(s16 hp)
                {}
        virtual s16 getHP()
index 7ffbe716066cf467df04d392687494a1e3845313..87c81cb2abdba5cd2526b281ab20f39efb2f2952 100644 (file)
@@ -276,3 +276,103 @@ std::string translatePassword(std::string playername, std::wstring password)
 
 
 
+PointedThing::PointedThing():
+       type(POINTEDTHING_NOTHING),
+       node_undersurface(0,0,0),
+       node_abovesurface(0,0,0),
+       object_id(-1)
+{}
+
+std::string PointedThing::dump() const
+{
+       std::ostringstream os(std::ios::binary);
+       if(type == POINTEDTHING_NOTHING)
+       {
+               os<<"[nothing]";
+       }
+       else if(type == POINTEDTHING_NODE)
+       {
+               const v3s16 &u = node_undersurface;
+               const v3s16 &a = node_abovesurface;
+               os<<"[node under="<<u.X<<","<<u.Y<<","<<u.Z
+                       << " above="<<a.X<<","<<a.Y<<","<<a.Z<<"]";
+       }
+       else if(type == POINTEDTHING_OBJECT)
+       {
+               os<<"[object "<<object_id<<"]";
+       }
+       else
+       {
+               os<<"[unknown PointedThing]";
+       }
+       return os.str();
+}
+
+void PointedThing::serialize(std::ostream &os) const
+{
+       writeU8(os, 0); // version
+       writeU8(os, (u8)type);
+       if(type == POINTEDTHING_NOTHING)
+       {
+               // nothing
+       }
+       else if(type == POINTEDTHING_NODE)
+       {
+               writeV3S16(os, node_undersurface);
+               writeV3S16(os, node_abovesurface);
+       }
+       else if(type == POINTEDTHING_OBJECT)
+       {
+               writeS16(os, object_id);
+       }
+}
+
+void PointedThing::deSerialize(std::istream &is)
+{
+       int version = readU8(is);
+       if(version != 0) throw SerializationError(
+                       "unsupported PointedThing version");
+       type = (PointedThingType) readU8(is);
+       if(type == POINTEDTHING_NOTHING)
+       {
+               // nothing
+       }
+       else if(type == POINTEDTHING_NODE)
+       {
+               node_undersurface = readV3S16(is);
+               node_abovesurface = readV3S16(is);
+       }
+       else if(type == POINTEDTHING_OBJECT)
+       {
+               object_id = readS16(is);
+       }
+       else
+       {
+               throw SerializationError(
+                       "unsupported PointedThingType");
+       }
+}
+
+bool PointedThing::operator==(const PointedThing &pt2) const
+{
+       if(type != pt2.type)
+               return false;
+       if(type == POINTEDTHING_NODE)
+       {
+               if(node_undersurface != pt2.node_undersurface)
+                       return false;
+               if(node_abovesurface != pt2.node_abovesurface)
+                       return false;
+       }
+       else if(type == POINTEDTHING_OBJECT)
+       {
+               if(object_id != pt2.object_id)
+                       return false;
+       }
+       return true;
+}
+
+bool PointedThing::operator!=(const PointedThing &pt2) const
+{
+       return !(*this == pt2);
+}
index 4e469db8956c9092b4dc90552fd333a09eddeb19..47696cbf8b80bca964e9939174505d1efb8406ff 100644 (file)
@@ -104,13 +104,6 @@ inline s32 readS32(u8 *data){
        return (s32)readU32(data);
 }
 
-inline void writeF1000(u8 *data, f32 i){
-       writeS32(data, i*1000);
-}
-inline f32 readF1000(u8 *data){
-       return (f32)readS32(data)/1000.;
-}
-
 inline void writeS16(u8 *data, s16 i){
        writeU16(data, (u16)i);
 }
@@ -118,6 +111,20 @@ inline s16 readS16(u8 *data){
        return (s16)readU16(data);
 }
 
+inline void writeS8(u8 *data, s8 i){
+       writeU8(data, (u8)i);
+}
+inline s8 readS8(u8 *data){
+       return (s8)readU8(data);
+}
+
+inline void writeF1000(u8 *data, f32 i){
+       writeS32(data, i*1000);
+}
+inline f32 readF1000(u8 *data){
+       return (f32)readS32(data)/1000.;
+}
+
 inline void writeV3S32(u8 *data, v3s32 p)
 {
        writeS32(&data[0], p.X);
@@ -248,19 +255,45 @@ inline u32 readU32(std::istream &is)
        return readU32((u8*)buf);
 }
 
-inline void writeS32(std::ostream &os, u32 p)
+inline void writeS32(std::ostream &os, s32 p)
 {
        char buf[4];
        writeS32((u8*)buf, p);
        os.write(buf, 4);
 }
-inline u32 readS32(std::istream &is)
+inline s32 readS32(std::istream &is)
 {
        char buf[4];
        is.read(buf, 4);
        return readS32((u8*)buf);
 }
 
+inline void writeS16(std::ostream &os, s16 p)
+{
+       char buf[2];
+       writeS16((u8*)buf, p);
+       os.write(buf, 2);
+}
+inline s16 readS16(std::istream &is)
+{
+       char buf[2];
+       is.read(buf, 2);
+       return readS16((u8*)buf);
+}
+
+inline void writeS8(std::ostream &os, s8 p)
+{
+       char buf[1];
+       writeS8((u8*)buf, p);
+       os.write(buf, 1);
+}
+inline s8 readS8(std::istream &is)
+{
+       char buf[1];
+       is.read(buf, 1);
+       return readS8((u8*)buf);
+}
+
 inline void writeF1000(std::ostream &os, f32 p)
 {
        char buf[4];
@@ -1725,5 +1758,27 @@ protected:
 
 std::string translatePassword(std::string playername, std::wstring password);
 
+enum PointedThingType
+{
+       POINTEDTHING_NOTHING,
+       POINTEDTHING_NODE,
+       POINTEDTHING_OBJECT
+};
+
+struct PointedThing
+{
+       PointedThingType type;
+       v3s16 node_undersurface;
+       v3s16 node_abovesurface;
+       s16 object_id;
+
+       PointedThing();
+       std::string dump() const;
+       void serialize(std::ostream &os) const;
+       void deSerialize(std::istream &is);
+       bool operator==(const PointedThing &pt2) const;
+       bool operator!=(const PointedThing &pt2) const;
+};
+
 #endif