diff options
-rw-r--r-- | doc/lua_api.txt | 6 | ||||
-rw-r--r-- | games/minimal/mods/experimental/init.lua | 7 | ||||
-rw-r--r-- | src/client.cpp | 34 | ||||
-rw-r--r-- | src/client.h | 4 | ||||
-rw-r--r-- | src/clientserver.h | 9 | ||||
-rw-r--r-- | src/guiFormSpecMenu.cpp | 17 | ||||
-rw-r--r-- | src/guiFormSpecMenu.h | 6 | ||||
-rw-r--r-- | src/inventorymanager.cpp | 40 | ||||
-rw-r--r-- | src/inventorymanager.h | 16 | ||||
-rw-r--r-- | src/scriptapi.cpp | 18 | ||||
-rw-r--r-- | src/server.cpp | 137 | ||||
-rw-r--r-- | src/server.h | 13 |
12 files changed, 222 insertions, 85 deletions
diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 176745a2d..c9fafd65f 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -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) diff --git a/games/minimal/mods/experimental/init.lua b/games/minimal/mods/experimental/init.lua index 3f50263e0..498c64623 100644 --- a/games/minimal/mods/experimental/init.lua +++ b/games/minimal/mods/experimental/init.lua @@ -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]" ) diff --git a/src/client.cpp b/src/client.cpp index 3a2edede3..e47bce142 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -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); } diff --git a/src/client.h b/src/client.h index f751220f7..154c8bb00 100644 --- a/src/client.h +++ b/src/client.h @@ -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 diff --git a/src/clientserver.h b/src/clientserver.h index d43220962..a6ba8fe45 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -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 diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index b2fee9c0d..3eb056625 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -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 diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 8e9557566..f0a5988e9 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -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 diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 25257238b..1369cb0d7 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -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; diff --git a/src/inventorymanager.h b/src/inventorymanager.h index 009db4836..dae14f1a6 100644 --- a/src/inventorymanager.h +++ b/src/inventorymanager.h @@ -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){} }; diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 12d2a8247..9959ddd74 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -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}, diff --git a/src/server.cpp b/src/server.cpp index b3cbea6a4..c7698a106 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -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() diff --git a/src/server.h b/src/server.h index f170cf7e1..4316bc21f 100644 --- a/src/server.h +++ b/src/server.h @@ -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; }; /* |