diff --git a/src/client.cpp b/src/client.cpp index df6514a..82c6b3b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1102,6 +1102,36 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) m_inventory_updated = true; } } + break; + case TOCLIENT_INVENTORY_UPDATE: + { + if (datasize < 3) + return; + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + Player *player = m_env.getLocalPlayer(); + assert(player != NULL); + + u16 list_count = readU16(is); + for (int i=0; iinventory.getList(name); + if (!l) + return; + for (int k=0; kupdateItem(index,type,count); + } + } + + m_inventory_updated = true; + } + } //DEBUG break; case TOCLIENT_OBJECTDATA: diff --git a/src/clientserver.h b/src/clientserver.h index 57881e8..24ab9a6 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -29,7 +29,7 @@ #include "utility.h" -#define PROTOCOL_VERSION 8 +#define PROTOCOL_VERSION 9 /* the last protocol version used by 0.3.x minetest-c55 clients */ #define PROTOCOL_DOTTHREE 3 /* this is the oldest protocol that we will allow to connect @@ -221,11 +221,12 @@ enum ToClientCommand [0] u16 command [2] u16 player count [4] u16 field count - for each player: + for each player { u16 peer_id char[20] name u16 length of serialized chardef string serialized character definition + } */ TOCLIENT_ENV_EVENT = 0x41, @@ -236,6 +237,22 @@ enum ToClientCommand u16 length of serialised event data string serialised event data */ + + TOCLIENT_INVENTORY_UPDATE = 0x42, + /* + u16 command + u16 list count + for each list { + u16 length of serialised list name + string serialised list name + u16 slot count + for each slot { + u16 slot index + u16 content type + u16 count/wear + } + } + */ }; enum ToServerCommand diff --git a/src/inventory.cpp b/src/inventory.cpp index e9ccd91..69fb8b6 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -338,8 +338,10 @@ InventoryList::~InventoryList() void InventoryList::clearItems() { for (u32 i=0; iremove(1); m_items[i] = newitem->clone(); m_items[i]->setCount(1); + m_diff.add(m_name,i,m_items[i]); return newitem; } m_items[i] = newitem; + m_diff.add(m_name,i,m_items[i]); return to_item; } // If it is an empty position, it's an easy job. if (to_item == NULL) { m_items[i] = newitem; + m_diff.add(m_name,i,m_items[i]); return NULL; } @@ -624,6 +631,7 @@ InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem) // If the item fits fully in the slot, add counter and delete it if (newitem->getCount() <= to_item->freeSpace()) { to_item->add(newitem->getCount()); + m_diff.add(m_name,i,m_items[i]); delete newitem; return NULL; } @@ -634,11 +642,37 @@ InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem) if (!freespace) return newitem; to_item->add(freespace); + m_diff.add(m_name,i,m_items[i]); newitem->remove(freespace); return newitem; } } +void InventoryList::updateItem(u32 i, content_t type, u16 wear_count) +{ + if (type == CONTENT_IGNORE) { + if (m_items[i] != NULL) + delete m_items[i]; + m_items[i] = NULL; + return; + } + if (m_items[i] != NULL) { + if (m_items[i]->getContent() == type) { + if ( + (type&CONTENT_TOOLITEM_MASK) == CONTENT_TOOLITEM_MASK + || (type&CONTENT_CLOTHESITEM_MASK) == CONTENT_CLOTHESITEM_MASK + ) { + m_items[i]->setWear(wear_count); + }else{ + m_items[i]->setCount(wear_count); + } + return; + } + delete m_items[i]; + } + m_items[i] = InventoryItem::create(type,wear_count,wear_count); +} + bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem) { // If it is an empty position, it's an easy job. @@ -659,18 +693,19 @@ bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem) bool InventoryList::roomForItem(const InventoryItem *item) { - for(u32 i=0; icreateCookResult(); - if(!cook) + if (!cook) return false; bool room = roomForItem(cook); delete cook; @@ -689,11 +724,14 @@ InventoryItem * InventoryList::takeItem(u32 i, u32 count) if (count >= item->getCount()) { // Get the item by swapping NULL to its place - return changeItem(i, NULL); + InventoryItem *item = changeItem(i, NULL); + m_diff.add(m_name,i,m_items[i]); + return item; }else{ InventoryItem *item2 = item->clone(); item->remove(count); item2->setCount(count); + m_diff.add(m_name,i,m_items[i]); return item2; } @@ -719,6 +757,7 @@ void InventoryList::decrementMaterials(u16 count) InventoryItem *item = takeItem(i, count); if (item) delete item; + m_diff.add(m_name,i,m_items[i]); } } diff --git a/src/inventory.h b/src/inventory.h index fd89d89..dd987a1 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -519,6 +519,74 @@ private: u16 m_wear; }; +class InventoryDiffData +{ +public: + InventoryDiffData(u32 i, content_t t, u16 c): + index(i), + type(t), + wear_count(c) + { + } + InventoryDiffData(): + index(0), + type(CONTENT_IGNORE), + wear_count(0) + { + } + + u32 index; + content_t type; + u16 wear_count; +}; + +class InventoryDiff +{ +public: + InventoryDiff() + { + clear(); + } + ~InventoryDiff() + { + clear(); + } + + void clear() + { + m_data.clear(); + } + + void add(std::string list, u32 index, content_t type, u16 count_wear) + { + m_data[list][index] = InventoryDiffData(index,type,count_wear); + } + + void add(std::string list, u32 index, InventoryItem *item) + { + if (item == NULL) { + add(list,index,CONTENT_IGNORE,0); + }else if ( + (item->getContent()&CONTENT_TOOLITEM_MASK) == CONTENT_TOOLITEM_MASK + || (item->getContent()&CONTENT_CLOTHESITEM_MASK) == CONTENT_CLOTHESITEM_MASK + ) { + add(list,index,item->getContent(),item->getWear()); + }else{ + add(list,index,item->getContent(),item->getCount()); + } + } + + void merge(InventoryDiff &other) + { + for (std::map >::iterator i = other.m_data.begin(); i != other.m_data.end(); i++) { + m_data[i->first].swap(i->second); + i->second.clear(); + } + } + + std::map > m_data; +}; + class InventoryList { public: @@ -579,6 +647,9 @@ public: // If can be added fully, NULL is returned. InventoryItem * addItem(u32 i, InventoryItem *newitem); + // Updates item type/count/wear + void updateItem(u32 i, content_t type, u16 wear_count); + // Checks whether the item could be added to the given slot bool itemFits(const u32 i, const InventoryItem *newitem); @@ -601,6 +672,9 @@ public: void print(std::ostream &o); + void addDiff(u32 index, InventoryItem *item) {m_diff.add(m_name,index,item);} + InventoryDiff &getDiff() {return m_diff;} + private: core::array m_items; u32 m_size; @@ -608,7 +682,7 @@ private: std::map m_allowed; std::map m_denied; bool m_stackable; - //bool m_dirty; + InventoryDiff m_diff; }; class Inventory @@ -634,16 +708,26 @@ public: InventoryItem * addItem(const std::string &listname, InventoryItem *newitem) { InventoryList *list = getList(listname); - if(list == NULL) + if (list == NULL) return newitem; return list->addItem(newitem); } + InventoryDiff &getDiff() + { + m_diff.clear(); + for (u32 i=0; igetDiff(); + m_diff.merge(diff); + } + return m_diff; + } private: // -1 if not found const s32 getListIndex(const std::string &name) const; core::array m_lists; + InventoryDiff m_diff; }; class Player; diff --git a/src/server.cpp b/src/server.cpp index 60ade0b..e673915 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2024,7 +2024,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Send inventory to player UpdateCrafting(peer_id); - SendInventory(peer_id); + SendInventory(peer_id,true); // Send player items to all players SendPlayerItems(); @@ -2151,15 +2151,17 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) thrown = content_craftitem_features(item->getContent()).shot_item; if (thrown == CONTENT_IGNORE) return; - item_i = i; if (g_settings->getBool("tool_wear")) { bool weared_out = titem->addWear(1000); if (weared_out) { InventoryList *mlist = player->inventory.getList("main"); mlist->deleteItem(item_i); + }else{ + ilist->addDiff(item_i,titem); } SendInventory(player->peer_id); } + item_i = i; } if (g_settings->getBool("droppable_inventory") == false || (getPlayerPrivs(player) & PRIV_BUILD) == 0) { @@ -2403,8 +2405,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } if (titem && g_settings->getBool("tool_wear")) { - if (titem->addWear(wear)) + if (titem->addWear(wear)) { mlist->deleteItem(item_i); + }else{ + mlist->addDiff(item_i,titem); + } SendInventory(player->peer_id); } } @@ -2550,9 +2555,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) ToolItem *titem = (ToolItem*)wield; if ((getPlayerPrivs(player) & PRIV_SERVER) == 0 && g_settings->getBool("tool_wear")) { bool weared_out = titem->addWear(10000); + InventoryList *mlist = player->inventory.getList("main"); if (weared_out) { - InventoryList *mlist = player->inventory.getList("main"); mlist->deleteItem(item_i); + }else{ + mlist->addDiff(item_i,titem); } SendInventory(player->peer_id); } @@ -2792,9 +2799,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) ToolItem *titem = (ToolItem*)wield; if (g_settings->getBool("tool_wear")) { bool weared_out = titem->addWear(1000); + InventoryList *mlist = player->inventory.getList("main"); if (weared_out) { - InventoryList *mlist = player->inventory.getList("main"); mlist->deleteItem(item_i); + }else{ + mlist->addDiff(item_i,titem); } SendInventory(player->peer_id); } @@ -2826,9 +2835,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) ToolItem *titem = (ToolItem*)wield; if (g_settings->getBool("tool_wear")) { bool weared_out = titem->addWear(1000); + InventoryList *mlist = player->inventory.getList("main"); if (weared_out) { - InventoryList *mlist = player->inventory.getList("main"); mlist->deleteItem(item_i); + }else{ + mlist->addDiff(item_i,titem); } SendInventory(player->peer_id); } @@ -2943,9 +2954,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) ToolItem *titem = (ToolItem*)wield; if (g_settings->getBool("tool_wear")) { bool weared_out = titem->addWear(200); + InventoryList *mlist = player->inventory.getList("main"); if (weared_out) { - InventoryList *mlist = player->inventory.getList("main"); mlist->deleteItem(item_i); + }else{ + mlist->addDiff(item_i,titem); } SendInventory(player->peer_id); } @@ -2993,6 +3006,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) ilist->deleteItem(item_i); }else{ wield->remove(1); + ilist->addDiff(item_i,wield); } // Send inventory UpdateCrafting(peer_id); @@ -3557,7 +3571,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) std::string dug_s = std::string("ToolItem ") + tool->getToolName() + "_water 1"; std::istringstream is(dug_s, std::ios::binary); item = InventoryItem::deSerialize(is); - mlist->changeItem(item_i,item); + InventoryItem *ritem = mlist->changeItem(item_i,item); + if (ritem) + delete ritem; item = NULL; UpdateCrafting(player->peer_id); SendInventory(player->peer_id); @@ -3592,14 +3608,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { u16 peer_id = *i; RemoteClient *client = getClient(peer_id); - if(client==NULL) + if (client==NULL) continue; client->SetBlocksNotSent(modified_blocks); } std::string dug_s = std::string("ToolItem ") + tool->getToolName() + "_water 1"; std::istringstream is(dug_s, std::ios::binary); item = InventoryItem::deSerialize(is); - mlist->changeItem(item_i,item); + InventoryItem *ritem = mlist->changeItem(item_i,item); + if (ritem) + delete ritem; item = NULL; UpdateCrafting(player->peer_id); SendInventory(player->peer_id); @@ -3618,7 +3636,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) std::string dug_s = std::string("ToolItem ") + tool->getToolName() + "_lava 1"; std::istringstream is(dug_s, std::ios::binary); item = InventoryItem::deSerialize(is); - mlist->changeItem(item_i,item); + InventoryItem *ritem = mlist->changeItem(item_i,item); + if (ritem) + delete ritem; item = NULL; } UpdateCrafting(player->peer_id); @@ -3675,13 +3695,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) && material != CONTENT_WATER && material != CONTENT_LAVA ) { + bool dosend = false; if (item != NULL) { // Add a item to inventory player->inventory.addItem("main", item); // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); + dosend = true; } item = NULL; @@ -3704,6 +3724,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) player->inventory.addItem("main", item); // Send inventory + dosend = true; + } + if (dosend) { UpdateCrafting(player->peer_id); SendInventory(player->peer_id); } @@ -4028,10 +4051,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) InventoryList *ilist = player->inventory.getList("main"); if(g_settings->getBool("infinite_inventory") == false && ilist) { // Remove from inventory and send inventory - if (mitem->getCount() == 1) + if (mitem->getCount() == 1) { ilist->deleteItem(item_i); - else + }else{ mitem->remove(1); + ilist->addDiff(item_i,mitem); + } // Send inventory UpdateCrafting(peer_id); SendInventory(peer_id); @@ -4233,12 +4258,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if (g_settings->getBool("infinite_inventory") == false) { // Delete the right amount of items from the slot u16 dropcount = item->getDropCount(); + InventoryList *ilist = player->inventory.getList("main"); // Delete item if all gone if (item->getCount() <= dropcount) { if (item->getCount() < dropcount) infostream<<"WARNING: Server: dropped more items" <<" than the slot contains"<inventory.getList("main"); // Remove from inventory and send inventory if (ilist) ilist->deleteItem(item_i); @@ -4246,6 +4271,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Else decrement it else{ item->remove(dropcount); + ilist->addDiff(item_i,item); } // Send inventory UpdateCrafting(peer_id); @@ -4287,6 +4313,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if (g_settings->getBool("infinite_inventory") == false) { // Delete the right amount of items from the slot u16 dropcount = item->getDropCount(); + InventoryList *ilist = player->inventory.getList("main"); // Delete item if all gone if (item->getCount() <= dropcount) { @@ -4294,12 +4321,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) infostream<<"WARNING: Server: dropped more items" <<" than the slot contains"<inventory.getList("main"); if (ilist) // Remove from inventory and send inventory ilist->deleteItem(item_i); }else{ item->remove(dropcount); + ilist->addDiff(item_i,item); } // Send inventory @@ -4368,6 +4395,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if (g_settings->getBool("infinite_inventory") == false) { // Delete the right amount of items from the slot u16 dropcount = item->getDropCount(); + InventoryList *ilist = player->inventory.getList("main"); // Delete item if all gone if (item->getCount() <= dropcount) { @@ -4375,7 +4403,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) infostream<<"WARNING: Server: dropped more items" <<" than the slot contains"<inventory.getList("main"); if(ilist) // Remove from inventory and send inventory ilist->deleteItem(item_i); @@ -4383,6 +4410,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Else decrement it else{ item->remove(dropcount); + ilist->addDiff(item_i,item); } // Send inventory @@ -4482,8 +4510,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) fw /= bonus; w = fw; } - if (i->addWear(w)) + if (i->addWear(w)) { l->deleteItem(0); + }else{ + l->addDiff(0,i); + } } SendInventory(player->peer_id); return; @@ -4652,7 +4683,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) dir.rotateXZBy(player->getYaw()); pos += dir; ServerActiveObject *obj = item->createSAO(&m_env,0,pos); - m_env.addActiveObject(obj); + if (obj) + m_env.addActiveObject(obj); } if (g_settings->getBool("infinite_inventory") == false) { list->deleteItem(0); @@ -4662,9 +4694,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } // Eat the action delete a; - } - else - { + }else{ // Send inventory UpdateCrafting(player->peer_id); SendInventory(player->peer_id); @@ -5017,8 +5047,7 @@ void Server::inventoryModified(InventoryContext *c, std::string id) Strfnd fn(id); std::string id0 = fn.next(":"); - if(id0 == "nodemeta") - { + if (id0 == "nodemeta") { v3s16 p; p.X = mystoi(fn.next(",")); p.Y = mystoi(fn.next(",")); @@ -5026,15 +5055,14 @@ void Server::inventoryModified(InventoryContext *c, std::string id) v3s16 blockpos = getNodeBlockPos(p); NodeMetadata *meta = m_env.getMap().getNodeMetadata(p); - if(meta) + if (meta) meta->inventoryModified(); MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos); - if(block) + if (block) block->raiseModified(MOD_STATE_WRITE_NEEDED); setBlockNotSent(blockpos); - return; } @@ -5222,29 +5250,59 @@ void Server::SendPlayerInfos() m_con.SendToAll(0, data, true); } -void Server::SendInventory(u16 peer_id) +void Server::SendInventory(u16 peer_id, bool full) { DSTACK(__FUNCTION_NAME); Player* player = m_env.getPlayer(peer_id); assert(player); - /* - Serialize it - */ + // get also clears, so get diff for full and partial sends + InventoryDiff idiff = player->inventory.getDiff(); - std::ostringstream os; + if (full) { + /* + Serialize it + */ - player->inventory.serialize(os); + std::ostringstream os(std::ios_base::binary); - std::string s = os.str(); + writeU16(os,TOCLIENT_INVENTORY); + player->inventory.serialize(os); - SharedBuffer data(s.size()+2); - writeU16(&data[0], TOCLIENT_INVENTORY); - memcpy(&data[2], s.c_str(), s.size()); + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); - // Send as reliable - m_con.Send(peer_id, 0, data, true); + // Send as reliable + m_con.Send(peer_id, 0, data, true); + return; + } + { + if (idiff.m_data.size() == 0) + return; + + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_INVENTORY_UPDATE); + writeU16(os, idiff.m_data.size()); + for (std::map >::iterator l = idiff.m_data.begin(); l != idiff.m_data.end(); l++) { + os<first); + writeU16(os,l->second.size()); + for (std::map::iterator i = l->second.begin(); i != l->second.end(); i++) { + writeU16(os,i->second.index); + writeU16(os,i->second.type); + writeU16(os,i->second.wear_count); + } + } + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + + // Send as reliable + m_con.Send(peer_id, 0, data, true); + } } std::string getWieldedItemString(const Player *player) @@ -5842,15 +5900,12 @@ void Server::UpdateCrafting(u16 peer_id) if(rlist && rlist->getUsedSlots() == 0) player->craftresult_is_preview = true; - if(rlist && player->craftresult_is_preview) - { + if (rlist && player->craftresult_is_preview) { rlist->clearItems(); } - if(clist && rlist && player->craftresult_is_preview) - { + if (clist && rlist && player->craftresult_is_preview) { InventoryItem *items[9]; - for(u16 i=0; i<9; i++) - { + for (u16 i=0; i<9; i++) { items[i] = clist->getItem(i); } diff --git a/src/server.h b/src/server.h index 1d58170..f4e1363 100644 --- a/src/server.h +++ b/src/server.h @@ -533,7 +533,7 @@ private: // Envlock and conlock should be locked when calling these void SendObjectData(float dtime); void SendPlayerInfos(); - void SendInventory(u16 peer_id); + void SendInventory(u16 peer_id, bool full=false); // send animation info about player to all void SendPlayerAnim(const Player *player, u8 animation_id); // send wielded item info about all players to all players