X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Finventory.cpp;h=1ef9b13cd2b7cefe9a6c8ef7d967ea1ef201b875;hb=ed0882fd58fb0f663cc115d23a11643874facc06;hp=82325997ee1cbc1151f84e354b9ea971927bb799;hpb=1d086aee7cc193bed2f8ca09cc2e020f509378f1;p=minetest.git diff --git a/src/inventory.cpp b/src/inventory.cpp index 82325997e..1ef9b13cd 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventory.h" #include "serialization.h" #include "debug.h" +#include #include #include "log.h" #include "itemdef.h" @@ -55,37 +56,36 @@ ItemStack::ItemStack(const std::string &name_, u16 count_, count = 1; } -void ItemStack::serialize(std::ostream &os) const +void ItemStack::serialize(std::ostream &os, bool serialize_meta) const { - DSTACK(FUNCTION_NAME); - - if(empty()) + if (empty()) return; // Check how many parts of the itemstring are needed int parts = 1; - if(count != 1) - parts = 2; - if(wear != 0) - parts = 3; if (!metadata.empty()) parts = 4; + else if (wear != 0) + parts = 3; + else if (count != 1) + parts = 2; - os<= 2) - os<<" "<= 3) - os<<" "<= 2) + os << " " << count; + if (parts >= 3) + os << " " << wear; if (parts >= 4) { os << " "; - metadata.serialize(os); + if (serialize_meta) + metadata.serialize(os); + else + os << ""; } } void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) { - DSTACK(FUNCTION_NAME); - clear(); // Read name @@ -243,13 +243,35 @@ void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef) deSerialize(is, itemdef); } -std::string ItemStack::getItemString() const +std::string ItemStack::getItemString(bool include_meta) const { std::ostringstream os(std::ios::binary); - serialize(os); + serialize(os, include_meta); return os.str(); } +std::string ItemStack::getDescription(IItemDefManager *itemdef) const +{ + std::string desc = metadata.getString("description"); + if (desc.empty()) + desc = getDefinition(itemdef).description; + return desc.empty() ? name : desc; +} + +std::string ItemStack::getShortDescription(IItemDefManager *itemdef) const +{ + std::string desc = metadata.getString("short_description"); + if (desc.empty()) + desc = getDefinition(itemdef).short_description; + if (!desc.empty()) + return desc; + // no short_description because of old server version or modified builtin + // return first line of description + std::stringstream sstr(getDescription(itemdef)); + std::getline(sstr, desc, '\n'); + return desc; +} + ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef) { @@ -261,17 +283,8 @@ ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef) // If this is an empty item, it's an easy job. else if(empty()) { - const u16 stackMax = newitem.getStackMax(itemdef); - *this = newitem; - - // If the item fits fully, delete it - if (count <= stackMax) { - newitem.clear(); - } else { // Else the item does not fit fully. Return the rest. - count = stackMax; - newitem.remove(count); - } + newitem.clear(); } // If item name or metadata differs, bail out else if (name != newitem.name @@ -310,14 +323,7 @@ bool ItemStack::itemFits(ItemStack newitem, // If this is an empty item, it's an easy job. else if(empty()) { - const u16 stackMax = newitem.getStackMax(itemdef); - - // If the item fits fully, delete it - if (newitem.count <= stackMax) { - newitem.clear(); - } else { // Else the item does not fit fully. Return the rest. - newitem.remove(stackMax); - } + newitem.clear(); } // If item name or metadata differs, bail out else if (name != newitem.name @@ -339,6 +345,7 @@ bool ItemStack::itemFits(ItemStack newitem, if(restitem) *restitem = newitem; + return newitem.empty(); } @@ -393,27 +400,32 @@ void InventoryList::clearItems() m_items.emplace_back(); } - //setDirty(true); + setModified(); } void InventoryList::setSize(u32 newsize) { - if(newsize != m_items.size()) - m_items.resize(newsize); + if (newsize == m_items.size()) + return; + + m_items.resize(newsize); m_size = newsize; + setModified(); } void InventoryList::setWidth(u32 newwidth) { m_width = newwidth; + setModified(); } void InventoryList::setName(const std::string &name) { m_name = name; + setModified(); } -void InventoryList::serialize(std::ostream &os) const +void InventoryList::serialize(std::ostream &os, bool incremental) const { //os.imbue(std::locale("C")); @@ -426,6 +438,9 @@ void InventoryList::serialize(std::ostream &os) const os<<"Item "; item.serialize(os); } + // TODO: Implement this: + // if (!incremental || item.checkModified()) + // os << "Keep"; os<<"\n"; } @@ -435,13 +450,12 @@ void InventoryList::serialize(std::ostream &os) const void InventoryList::deSerialize(std::istream &is) { //is.imbue(std::locale("C")); + setModified(); - clearItems(); u32 item_i = 0; m_width = 0; - for(;;) - { + while (is.good()) { std::string line; std::getline(is, line, '\n'); @@ -451,13 +465,11 @@ void InventoryList::deSerialize(std::istream &is) std::string name; std::getline(iss, name, ' '); - if (name == "EndInventoryList") { - break; - } - - // This is a temporary backwards compatibility fix - if (name == "end") { - break; + if (name == "EndInventoryList" || name == "end") { + // If partial incremental: Clear leftover items (should not happen!) + for (size_t i = item_i; i < m_items.size(); ++i) + m_items[i].clear(); + return; } if (name == "Width") { @@ -478,8 +490,18 @@ void InventoryList::deSerialize(std::istream &is) if(item_i > getSize() - 1) throw SerializationError("too many items"); m_items[item_i++].clear(); + } else if (name == "Keep") { + ++item_i; // Unmodified item } } + + // Contents given to deSerialize() were not terminated properly: throw error. + + std::ostringstream ss; + ss << "Malformatted inventory list. list=" + << m_name << ", read " << item_i << " of " << getSize() + << " ItemStacks." << std::endl; + throw SerializationError(ss.str()); } InventoryList::InventoryList(const InventoryList &other) @@ -507,14 +529,9 @@ bool InventoryList::operator == (const InventoryList &other) const return false; if(m_name != other.m_name) return false; - for(u32 i=0; iname == item.name) { u32 still_to_remove = item.count - removed.count; - removed.addItem(i->takeItem(still_to_remove), m_itemdef); + ItemStack leftover = removed.addItem(i->takeItem(still_to_remove), + m_itemdef); + // Allow oversized stacks + removed.count += leftover.count; + if (removed.count == item.count) break; } } + if (!removed.empty()) + setModified(); return removed; } @@ -693,8 +717,8 @@ ItemStack InventoryList::takeItem(u32 i, u32 takecount) return ItemStack(); ItemStack taken = m_items[i].takeItem(takecount); - //if(!taken.empty()) - // setDirty(true); + if (!taken.empty()) + setModified(); return taken; } @@ -710,43 +734,29 @@ void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count) if (item1.empty()) return; - // Try to add the item to destination list - u32 dest_size = dest->getSize(); - // First try all the non-empty slots - for (u32 dest_i = 0; dest_i < dest_size; dest_i++) { - if (!m_items[dest_i].empty()) { - item1 = dest->addItem(dest_i, item1); - if (item1.empty()) return; - } - } + ItemStack leftover; + leftover = dest->addItem(item1); - // Then try all the empty ones - for (u32 dest_i = 0; dest_i < dest_size; dest_i++) { - if (m_items[dest_i].empty()) { - item1 = dest->addItem(dest_i, item1); - if (item1.empty()) return; - } + if (!leftover.empty()) { + // Add the remaining part back to the source item + addItem(i, leftover); } - - // If we reach this, the item was not fully added - // Add the remaining part back to the source item - addItem(i, item1); } u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count, bool swap_if_needed, bool *did_swap) { - if(this == dest && i == dest_i) + if (this == dest && i == dest_i) return count; // Take item from source list ItemStack item1; - if(count == 0) + if (count == 0) item1 = changeItem(i, ItemStack()); else item1 = takeItem(i, count); - if(item1.empty()) + if (item1.empty()) return 0; // Try to add the item to destination list @@ -754,8 +764,7 @@ u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, item1 = dest->addItem(dest_i, item1); // If something is returned, the item was not fully added - if(!item1.empty()) - { + if (!item1.empty()) { // If olditem is returned, nothing was added. bool nothing_added = (item1.count == oldcount); @@ -792,33 +801,22 @@ Inventory::~Inventory() void Inventory::clear() { - m_dirty = true; for (auto &m_list : m_lists) { delete m_list; } m_lists.clear(); -} - -void Inventory::clearContents() -{ - m_dirty = true; - for (InventoryList *list : m_lists) { - for (u32 j=0; jgetSize(); j++) { - list->deleteItem(j); - } - } + setModified(); } Inventory::Inventory(IItemDefManager *itemdef) { - m_dirty = false; m_itemdef = itemdef; + setModified(); } Inventory::Inventory(const Inventory &other) { *this = other; - m_dirty = false; } Inventory & Inventory::operator = (const Inventory &other) @@ -826,12 +824,12 @@ Inventory & Inventory::operator = (const Inventory &other) // Gracefully handle self assignment if(this != &other) { - m_dirty = true; clear(); m_itemdef = other.m_itemdef; for (InventoryList *list : other.m_lists) { m_lists.push_back(new InventoryList(*list)); } + setModified(); } return *this; } @@ -849,11 +847,16 @@ bool Inventory::operator == (const Inventory &other) const return true; } -void Inventory::serialize(std::ostream &os) const +void Inventory::serialize(std::ostream &os, bool incremental) const { - for (InventoryList *list : m_lists) { - os<<"List "<getName()<<" "<getSize()<<"\n"; - list->serialize(os); + //std::cout << "Serialize " << (int)incremental << ", n=" << m_lists.size() << std::endl; + for (const InventoryList *list : m_lists) { + if (!incremental || list->checkModified()) { + os << "List " << list->getName() << " " << list->getSize() << "\n"; + list->serialize(os, incremental); + } else { + os << "KeepList " << list->getName() << "\n"; + } } os<<"EndInventory\n"; @@ -861,10 +864,10 @@ void Inventory::serialize(std::ostream &os) const void Inventory::deSerialize(std::istream &is) { - clear(); + std::vector new_lists; + new_lists.reserve(m_lists.size()); - for(;;) - { + while (is.good()) { std::string line; std::getline(is, line, '\n'); @@ -873,13 +876,19 @@ void Inventory::deSerialize(std::istream &is) std::string name; std::getline(iss, name, ' '); - if (name == "EndInventory") { - break; - } + if (name == "EndInventory" || name == "end") { + // Remove all lists that were not sent + for (auto &list : m_lists) { + if (std::find(new_lists.begin(), new_lists.end(), list) != new_lists.end()) + continue; - // This is a temporary backwards compatibility fix - if (name == "end") { - break; + delete list; + list = nullptr; + setModified(); + } + m_lists.erase(std::remove(m_lists.begin(), m_lists.end(), + nullptr), m_lists.end()); + return; } if (name == "List") { @@ -889,21 +898,46 @@ void Inventory::deSerialize(std::istream &is) std::getline(iss, listname, ' '); iss>>listsize; - InventoryList *list = new InventoryList(listname, listsize, m_itemdef); + InventoryList *list = getList(listname); + bool create_new = !list; + if (create_new) + list = new InventoryList(listname, listsize, m_itemdef); + else + list->setSize(listsize); list->deSerialize(is); - m_lists.push_back(list); - } - else - { - throw SerializationError("invalid inventory specifier: " + name); + new_lists.push_back(list); + if (create_new) + m_lists.push_back(list); + + } else if (name == "KeepList") { + // Incrementally sent list + std::string listname; + std::getline(iss, listname, ' '); + + InventoryList *list = getList(listname); + if (list) { + new_lists.push_back(list); + } else { + errorstream << "Inventory::deSerialize(): Tried to keep list '" << + listname << "' which is non-existent." << std::endl; + } } + // Any additional fields will throw errors when received by a client + // older than PROTOCOL_VERSION 38 } + + // Contents given to deSerialize() were not terminated properly: throw error. + + std::ostringstream ss; + ss << "Malformatted inventory (damaged?). " + << m_lists.size() << " lists read." << std::endl; + throw SerializationError(ss.str()); } InventoryList * Inventory::addList(const std::string &name, u32 size) { - m_dirty = true; + setModified(); s32 i = getListIndex(name); if(i != -1) { @@ -911,15 +945,18 @@ InventoryList * Inventory::addList(const std::string &name, u32 size) { delete m_lists[i]; m_lists[i] = new InventoryList(name, size, m_itemdef); + m_lists[i]->setModified(); } return m_lists[i]; } //don't create list with invalid name - if (name.find(' ') != std::string::npos) return NULL; + if (name.find(' ') != std::string::npos) + return nullptr; InventoryList *list = new InventoryList(name, size, m_itemdef); + list->setModified(); m_lists.push_back(list); return list; } @@ -946,7 +983,8 @@ bool Inventory::deleteList(const std::string &name) s32 i = getListIndex(name); if(i == -1) return false; - m_dirty = true; + + setModified(); delete m_lists[i]; m_lists.erase(m_lists.begin() + i); return true;