Detached inventories
authorPerttu Ahola <celeron55@gmail.com>
Tue, 24 Jul 2012 17:57:17 +0000 (20:57 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Tue, 24 Jul 2012 17:57:17 +0000 (20:57 +0300)
12 files changed:
doc/lua_api.txt
games/minimal/mods/experimental/init.lua
src/client.cpp
src/client.h
src/clientserver.h
src/guiFormSpecMenu.cpp
src/guiFormSpecMenu.h
src/inventorymanager.cpp
src/inventorymanager.h
src/scriptapi.cpp
src/server.cpp
src/server.h

index 176745a2d75207893c657e908ee6e6b7acf66d2c..c9fafd65fbc072076ea38118a226cdb745b1b4b9 100644 (file)
@@ -677,6 +677,7 @@ size[<W>,<H>]
 ^ deprecated: invsize[<W>,<H>;]
 
 list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;]
+list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]
 ^ Show an inventory list
 
 image[<X>,<Y>;<W>,<H>;<texture name>]
@@ -726,10 +727,12 @@ image_button_exit[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
 ^ When clicked, fields will be sent and the form will quit.
 
 Inventory location:
+
 - "context": Selected node metadata (deprecated: "current_name")
 - "current_player": Player to whom the menu is shown
 - "player:<name>": Any player
 - "nodemeta:<X>,<Y>,<Z>": Any node metadata
+- "detached:<name>": A detached inventory
 
 Helper functions
 -----------------
@@ -847,6 +850,9 @@ Inventory:
 minetest.get_inventory(location) -> InvRef
 ^ location = eg. {type="player", name="celeron55"}
                  {type="node", pos={x=, y=, z=}}
+                 {type="detached", name="creative"}
+minetest.create_detached_inventory(name) -> InvRef
+^ Creates a detached inventory. If it already exists, it is cleared.
 
 Item handling:
 minetest.inventorycube(img1, img2, img3)
index 3f50263e02aea88438fbcdeb893fd2b50ffabee0..498c64623f535befc36b7a5449393a99a03c69c0 100644 (file)
@@ -524,6 +524,12 @@ minetest.register_craft({
        end)
 end)]]
 
+-- Create a detached inventory
+local inv = minetest.create_detached_inventory("test_inventory")
+inv:set_size("main", 4*6)
+inv:add_item("main", "experimental:tester_tool_1")
+inv:add_item("main", "experimental:tnt 5")
+
 minetest.register_chatcommand("test1", {
        params = "",
        description = "Test 1: Modify player's inventory view",
@@ -538,6 +544,7 @@ minetest.register_chatcommand("test1", {
                                "list[current_player;main;5,3.5;8,4;]"..
                                "list[current_player;craft;8,0;3,3;]"..
                                "list[current_player;craftpreview;12,1;1,1;]"..
+                               "list[detached:test_inventory;main;0,0;4,6;0]"..
                                "button[0.5,7;2,1;button1;Button 1]"..
                                "button_exit[2.5,7;2,1;button2;Exit Button]"
                )
index 3a2edede38dd2409300f4c355844984b6237f746..e47bce1421f76559c09d344e9a44f9ad44d6ae3e 100644 (file)
@@ -304,6 +304,15 @@ Client::~Client()
                sleep_ms(100);
 
        delete m_inventory_from_server;
+
+       // Delete detached inventories
+       {
+               for(std::map<std::string, Inventory*>::iterator
+                               i = m_detached_inventories.begin();
+                               i != m_detached_inventories.end(); i++){
+                       delete i->second;
+               }
+       }
 }
 
 void Client::connect(Address address)
@@ -1698,6 +1707,24 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                assert(player != NULL);
                player->inventory_formspec = deSerializeLongString(is);
        }
+       else if(command == TOCLIENT_DETACHED_INVENTORY)
+       {
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               std::string name = deSerializeString(is);
+               
+               infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
+
+               Inventory *inv = NULL;
+               if(m_detached_inventories.count(name) > 0)
+                       inv = m_detached_inventories[name];
+               else{
+                       inv = new Inventory(m_itemdef);
+                       m_detached_inventories[name] = inv;
+               }
+               inv->deSerialize(is);
+       }
        else
        {
                infostream<<"Client: Ignoring unknown command "
@@ -2090,6 +2117,13 @@ Inventory* Client::getInventory(const InventoryLocation &loc)
                return meta->getInventory();
        }
        break;
+       case InventoryLocation::DETACHED:
+       {
+               if(m_detached_inventories.count(loc.name) == 0)
+                       return NULL;
+               return m_detached_inventories[loc.name];
+       }
+       break;
        default:
                assert(0);
        }
index f751220f7c48f3c7ade5faf47f731b98abb00641..154c8bb003646bd580e563ee3a0841dfeda548ad 100644 (file)
@@ -393,6 +393,10 @@ private:
 
        // Privileges
        std::set<std::string> m_privileges;
+
+       // Detached inventories
+       // key = name
+       std::map<std::string, Inventory*> m_detached_inventories;
 };
 
 #endif // !CLIENT_HEADER
index d432209627e46fe42ca8a1e509ab8388c07b9e3b..a6ba8fe45c82d837404024f7b407b5c2928aada3 100644 (file)
@@ -64,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        PROTOCOL_VERSION 12:
                TOSERVER_INVENTORY_FIELDS
                16-bit node ids
+               TOCLIENT_DETACHED_INVENTORY
 */
 
 #define PROTOCOL_VERSION 12
@@ -321,6 +322,14 @@ enum ToClientCommand
                u32 len
                u8[len] formspec
        */
+
+       TOCLIENT_DETACHED_INVENTORY = 0x43,
+       /*
+               [0] u16 command
+               u16 len
+               u8[len] name
+               [2] serialized inventory
+       */
 };
 
 enum ToServerCommand
index b2fee9c0dd9789a8d5730d0fec56fa7ab6944e2f..3eb056625fb8af9b9343cf5af47d142f4061d43e 100644 (file)
@@ -257,10 +257,13 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                                        <<", pos=("<<pos.X<<","<<pos.Y<<")"
                                        <<", geom=("<<geom.X<<","<<geom.Y<<")"
                                        <<std::endl;
-                       f.next("]");
+                       std::string start_i_s = f.next("]");
+                       s32 start_i = 0;
+                       if(start_i_s != "")
+                               start_i = stoi(start_i_s);
                        if(bp_set != 2)
-                               errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
-                       m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom));
+                               errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
+                       m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
                }
                else if(type == "image")
                {
@@ -531,13 +534,14 @@ GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
 
                for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
                {
+                       s32 item_i = i + s.start_item_i;
                        s32 x = (i%s.geom.X) * spacing.X;
                        s32 y = (i/s.geom.X) * spacing.Y;
                        v2s32 p0(x,y);
                        core::rect<s32> rect = imgrect + s.pos + p0;
                        if(rect.isPointInside(p))
                        {
-                               return ItemSpec(s.inventoryloc, s.listname, i);
+                               return ItemSpec(s.inventoryloc, s.listname, item_i);
                        }
                }
        }
@@ -576,13 +580,16 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
        
        for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
        {
+               u32 item_i = i + s.start_item_i;
+               if(item_i >= ilist->getSize())
+                       break;
                s32 x = (i%s.geom.X) * spacing.X;
                s32 y = (i/s.geom.X) * spacing.Y;
                v2s32 p(x,y);
                core::rect<s32> rect = imgrect + s.pos + p;
                ItemStack item;
                if(ilist)
-                       item = ilist->getItem(i);
+                       item = ilist->getItem(item_i);
 
                bool selected = m_selected_item
                        && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
index 8e9557566e3b86bc9d5a2fc4c851c72fe9494934..f0a5988e95ce100fcf785a7fdda9ec16edb68b2e 100644 (file)
@@ -86,11 +86,12 @@ class GUIFormSpecMenu : public GUIModalMenu
                }
                ListDrawSpec(const InventoryLocation &a_inventoryloc,
                                const std::string &a_listname,
-                               v2s32 a_pos, v2s32 a_geom):
+                               v2s32 a_pos, v2s32 a_geom, s32 a_start_item_i):
                        inventoryloc(a_inventoryloc),
                        listname(a_listname),
                        pos(a_pos),
-                       geom(a_geom)
+                       geom(a_geom),
+                       start_item_i(a_start_item_i)
                {
                }
 
@@ -98,6 +99,7 @@ class GUIFormSpecMenu : public GUIModalMenu
                std::string listname;
                v2s32 pos;
                v2s32 geom;
+               s32 start_item_i;
        };
 
        struct ImageDrawSpec
index 25257238b9bbe606b27dad049a49c15bf2cf12d6..1369cb0d7c402caafcb7251381b406643cbff4e4 100644 (file)
@@ -41,30 +41,25 @@ std::string InventoryLocation::dump() const
 
 void InventoryLocation::serialize(std::ostream &os) const
 {
-        switch(type){
-        case InventoryLocation::UNDEFINED:
-        {
-               os<<"undefined";
-       }
+    switch(type){
+    case InventoryLocation::UNDEFINED:
+        os<<"undefined";
+        break;
+    case InventoryLocation::CURRENT_PLAYER:
+        os<<"current_player";
         break;
-        case InventoryLocation::CURRENT_PLAYER:
-        {
-               os<<"current_player";
-        }
+    case InventoryLocation::PLAYER:
+        os<<"player:"<<name;
         break;
-        case InventoryLocation::PLAYER:
-        {
-               os<<"player:"<<name;
-        }
+    case InventoryLocation::NODEMETA:
+        os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
         break;
-        case InventoryLocation::NODEMETA:
-        {
-               os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
-        }
+    case InventoryLocation::DETACHED:
+        os<<"detached:"<<name;
         break;
-        default:
-                assert(0);
-        }
+    default:
+        assert(0);
+    }
 }
 
 void InventoryLocation::deSerialize(std::istream &is)
@@ -94,6 +89,11 @@ void InventoryLocation::deSerialize(std::istream &is)
                p.Y = stoi(fn.next(","));
                p.Z = stoi(fn.next(","));
        }
+       else if(tname == "detached")
+       {
+               type = InventoryLocation::DETACHED;
+               std::getline(is, name, '\n');
+       }
        else
        {
                infostream<<"Unknown InventoryLocation type=\""<<tname<<"\""<<std::endl;
index 009db48367ef82f4db07c348553b30566b5e716a..dae14f1a6c25da91ddeba69142039933d71d3800 100644 (file)
@@ -32,9 +32,10 @@ struct InventoryLocation
                CURRENT_PLAYER,
                PLAYER,
                NODEMETA,
+        DETACHED,
        } type;
 
-       std::string name; // PLAYER
+       std::string name; // PLAYER, DETACHED
        v3s16 p; // NODEMETA
 
        InventoryLocation()
@@ -59,6 +60,11 @@ struct InventoryLocation
                type = NODEMETA;
                p = p_;
        }
+       void setDetached(const std::string &name_)
+       {
+               type = DETACHED;
+               name = name_;
+       }
 
        void applyCurrentPlayer(const std::string &name_)
        {
@@ -80,13 +86,11 @@ public:
        InventoryManager(){}
        virtual ~InventoryManager(){}
        
-       // Get an inventory or set it modified (so it will be updated over
-       // network or so)
+       // Get an inventory (server and client)
        virtual Inventory* getInventory(const InventoryLocation &loc){return NULL;}
-       virtual std::string getInventoryOwner(const InventoryLocation &loc){return "";}
+    // Set modified (will be saved and sent over network; only on server)
        virtual void setInventoryModified(const InventoryLocation &loc){}
-
-       // Used on the client to send an action to the server
+    // Send inventory action to server (only on client)
        virtual void inventoryAction(InventoryAction *a){}
 };
 
index 12d2a8247a9bd763a72a023e8c74cd80695de3b4..9959ddd748d6d0afa1f52425f034f63e4e7c2225 100644 (file)
@@ -4567,6 +4567,9 @@ static int l_get_inventory(lua_State *L)
                lua_getfield(L, 1, "pos");
                v3s16 pos = check_v3s16(L, -1);
                loc.setNodeMeta(pos);
+       } else if(type == "detached"){
+               std::string name = checkstringfield(L, 1, "name");
+               loc.setDetached(name);
        }
        
        if(get_server(L)->getInventory(loc) != NULL)
@@ -4576,6 +4579,20 @@ static int l_get_inventory(lua_State *L)
        return 1;
 }
 
+// create_detached_inventory(name)
+static int l_create_detached_inventory(lua_State *L)
+{
+       const char *name = luaL_checkstring(L, 1);
+       if(get_server(L)->createDetachedInventory(name) != NULL){
+               InventoryLocation loc;
+               loc.setDetached(name);
+               InvRef::create(L, loc);
+       }else{
+               lua_pushnil(L);
+       }
+       return 1;
+}
+
 // get_dig_params(groups, tool_capabilities[, time_from_last_punch])
 static int l_get_dig_params(lua_State *L)
 {
@@ -4849,6 +4866,7 @@ static const struct luaL_Reg minetest_f [] = {
        {"chat_send_player", l_chat_send_player},
        {"get_player_privs", l_get_player_privs},
        {"get_inventory", l_get_inventory},
+       {"create_detached_inventory", l_create_detached_inventory},
        {"get_dig_params", l_get_dig_params},
        {"get_hit_params", l_get_hit_params},
        {"get_current_modname", l_get_current_modname},
index b3cbea6a447ed76c2111ea0a3b923d29365bbccd..c7698a10629ca7300904bb90b20106e938c0c209 100644 (file)
@@ -1160,6 +1160,15 @@ Server::~Server()
        // Deinitialize scripting
        infostream<<"Server: Deinitializing scripting"<<std::endl;
        script_deinit(m_lua);
+
+       // Delete detached inventories
+       {
+               for(std::map<std::string, Inventory*>::iterator
+                               i = m_detached_inventories.begin();
+                               i != m_detached_inventories.end(); i++){
+                       delete i->second;
+               }
+       }
 }
 
 void Server::start(unsigned short port)
@@ -2250,10 +2259,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                // Send inventory
                UpdateCrafting(peer_id);
                SendInventory(peer_id);
-               
+
                // Send HP
                SendPlayerHP(peer_id);
                
+               // Send detached inventories
+               sendDetachedInventories(peer_id);
+               
                // Show death screen if necessary
                if(player->hp == 0)
                        SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
@@ -2532,30 +2544,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                delete a;
                                return;
                        }
-
-                       // If player is not an admin, check for ownership of src and dst
-                       /*if(!checkPriv(player->getName(), "server"))
-                       {
-                               std::string owner_from = getInventoryOwner(ma->from_inv);
-                               if(owner_from != "" && owner_from != player->getName())
-                               {
-                                       infostream<<"WARNING: "<<player->getName()
-                                               <<" tried to access an inventory that"
-                                               <<" belongs to "<<owner_from<<std::endl;
-                                       delete a;
-                                       return;
-                               }
-
-                               std::string owner_to = getInventoryOwner(ma->to_inv);
-                               if(owner_to != "" && owner_to != player->getName())
-                               {
-                                       infostream<<"WARNING: "<<player->getName()
-                                               <<" tried to access an inventory that"
-                                               <<" belongs to "<<owner_to<<std::endl;
-                                       delete a;
-                                       return;
-                               }
-                       }*/
                }
                /*
                        Handle restrictions and special cases of the drop action
@@ -2574,19 +2562,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                delete a;
                                return;
                        }
-                       // If player is not an admin, check for ownership
-                       /*else if(!checkPriv(player->getName(), "server"))
-                       {
-                               std::string owner_from = getInventoryOwner(da->from_inv);
-                               if(owner_from != "" && owner_from != player->getName())
-                               {
-                                       infostream<<"WARNING: "<<player->getName()
-                                               <<" tried to access an inventory that"
-                                               <<" belongs to "<<owner_from<<std::endl;
-                                       delete a;
-                                       return;
-                               }
-                       }*/
                }
                /*
                        Handle restrictions and special cases of the craft action
@@ -2611,20 +2586,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                delete a;
                                return;
                        }
-
-                       // If player is not an admin, check for ownership of inventory
-                       /*if(!checkPriv(player->getName(), "server"))
-                       {
-                               std::string owner_craft = getInventoryOwner(ca->craft_inv);
-                               if(owner_craft != "" && owner_craft != player->getName())
-                               {
-                                       infostream<<"WARNING: "<<player->getName()
-                                               <<" tried to access an inventory that"
-                                               <<" belongs to "<<owner_craft<<std::endl;
-                                       delete a;
-                                       return;
-                               }
-                       }*/
                }
                
                // Do the action
@@ -3318,6 +3279,13 @@ Inventory* Server::getInventory(const InventoryLocation &loc)
                return meta->getInventory();
        }
        break;
+       case InventoryLocation::DETACHED:
+       {
+               if(m_detached_inventories.count(loc.name) == 0)
+                       return NULL;
+               return m_detached_inventories[loc.name];
+       }
+       break;
        default:
                assert(0);
        }
@@ -3352,6 +3320,11 @@ void Server::setInventoryModified(const InventoryLocation &loc)
                setBlockNotSent(blockpos);
        }
        break;
+       case InventoryLocation::DETACHED:
+       {
+               sendDetachedInventoryToAll(loc.name);
+       }
+       break;
        default:
                assert(0);
        }
@@ -4309,6 +4282,51 @@ void Server::sendRequestedMedia(u16 peer_id,
        }
 }
 
+void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
+{
+       if(m_detached_inventories.count(name) == 0){
+               errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
+               return;
+       }
+       Inventory *inv = m_detached_inventories[name];
+
+       std::ostringstream os(std::ios_base::binary);
+       writeU16(os, TOCLIENT_DETACHED_INVENTORY);
+       os<<serializeString(name);
+       inv->serialize(os);
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       m_con.Send(peer_id, 0, data, true);
+}
+
+void Server::sendDetachedInventoryToAll(const std::string &name)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       for(core::map<u16, RemoteClient*>::Iterator
+                       i = m_clients.getIterator();
+                       i.atEnd() == false; i++){
+               RemoteClient *client = i.getNode()->getValue();
+               sendDetachedInventory(name, client->peer_id);
+       }
+}
+
+void Server::sendDetachedInventories(u16 peer_id)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       for(std::map<std::string, Inventory*>::iterator
+                       i = m_detached_inventories.begin();
+                       i != m_detached_inventories.end(); i++){
+               const std::string &name = i->first;
+               //Inventory *inv = i->second;
+               sendDetachedInventory(name, peer_id);
+       }
+}
+
 /*
        Something random
 */
@@ -4493,6 +4511,21 @@ void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
        m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
 }
 
+Inventory* Server::createDetachedInventory(const std::string &name)
+{
+       if(m_detached_inventories.count(name) > 0){
+               infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
+               delete m_detached_inventories[name];
+       } else {
+               infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
+       }
+       Inventory *inv = new Inventory(m_itemdef);
+       assert(inv);
+       m_detached_inventories[name] = inv;
+       sendDetachedInventoryToAll(name);
+       return inv;
+}
+
 // IGameDef interface
 // Under envlock
 IItemDefManager* Server::getItemDefManager()
index f170cf7e11908903020569187599084564fc58d0..4316bc21fd4a64726c8b43e7ac6fb1cbdf7bcdbd 100644 (file)
@@ -538,6 +538,9 @@ public:
 
        void queueBlockEmerge(v3s16 blockpos, bool allow_generate);
        
+       // Creates or resets inventory
+       Inventory* createDetachedInventory(const std::string &name);
+       
        // Envlock and conlock should be locked when using Lua
        lua_State *getLua(){ return m_lua; }
        
@@ -627,6 +630,10 @@ private:
        void sendMediaAnnouncement(u16 peer_id);
        void sendRequestedMedia(u16 peer_id,
                        const core::list<MediaRequest> &tosend);
+       
+       void sendDetachedInventory(const std::string &name, u16 peer_id);
+       void sendDetachedInventoryToAll(const std::string &name);
+       void sendDetachedInventories(u16 peer_id);
 
        /*
                Something random
@@ -828,6 +835,12 @@ private:
        */
        std::map<s32, ServerPlayingSound> m_playing_sounds;
        s32 m_next_sound_id;
+
+       /*
+               Detached inventories (behind m_env_mutex)
+       */
+       // key = name
+       std::map<std::string, Inventory*> m_detached_inventories;
 };
 
 /*