Optimize updateFastFaceRow processing by removing some TileSpec copy (#5678)
[oweals/minetest.git] / src / inventory.cpp
index 1f55c5305de3af68ebaa07b2faef631b2bb60c2c..8617f72634b6dc7eefc43bfe3d908d46bff4ab77 100644 (file)
@@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <sstream>
 #include "log.h"
 #include "itemdef.h"
-#include "strfnd.h"
+#include "util/strfnd.h"
 #include "content_mapnode.h" // For loading legacy MaterialItems
 #include "nameidmapping.h" // For loading legacy MaterialItems
 #include "util/serialize.h"
@@ -45,88 +45,21 @@ static content_t content_translate_from_19_to_internal(content_t c_from)
        return c_from;
 }
 
-// If the string contains spaces, quotes or control characters, encodes as JSON.
-// Else returns the string unmodified.
-static std::string serializeJsonStringIfNeeded(const std::string &s)
+ItemStack::ItemStack(const std::string &name_, u16 count_,
+               u16 wear_, IItemDefManager *itemdef) :
+       name(itemdef->getAlias(name_)),
+       count(count_),
+       wear(wear_)
 {
-       for(size_t i = 0; i < s.size(); ++i)
-       {
-               if(s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"')
-                       return serializeJsonString(s);
-       }
-       return s;
-}
-
-// Parses a string serialized by serializeJsonStringIfNeeded.
-static std::string deSerializeJsonStringIfNeeded(std::istream &is)
-{
-       std::ostringstream tmp_os;
-       bool expect_initial_quote = true;
-       bool is_json = false;
-       bool was_backslash = false;
-       for(;;)
-       {
-               char c = is.get();
-               if(is.eof())
-                       break;
-               if(expect_initial_quote && c == '"')
-               {
-                       tmp_os << c;
-                       is_json = true;
-               }
-               else if(is_json)
-               {
-                       tmp_os << c;
-                       if(was_backslash)
-                               was_backslash = false;
-                       else if(c == '\\')
-                               was_backslash = true;
-                       else if(c == '"')
-                               break; // Found end of string
-               }
-               else
-               {
-                       if(c == ' ')
-                       {
-                               // Found end of word
-                               is.unget();
-                               break;
-                       }
-                       else
-                       {
-                               tmp_os << c;
-                       }
-               }
-               expect_initial_quote = false;
-       }
-       if(is_json)
-       {
-               std::istringstream tmp_is(tmp_os.str(), std::ios::binary);
-               return deSerializeJsonString(tmp_is);
-       }
-       else
-               return tmp_os.str();
-}
-
-
-ItemStack::ItemStack(std::string name_, u16 count_,
-               u16 wear_, std::string metadata_,
-               IItemDefManager *itemdef)
-{
-       name = itemdef->getAlias(name_);
-       count = count_;
-       wear = wear_;
-       metadata = metadata_;
-
-       if(name.empty() || count == 0)
+       if (name.empty() || count == 0)
                clear();
-       else if(itemdef->get(name).type == ITEM_TOOL)
+       else if (itemdef->get(name).type == ITEM_TOOL)
                count = 1;
 }
 
 void ItemStack::serialize(std::ostream &os) const
 {
-       DSTACK(__FUNCTION_NAME);
+       DSTACK(FUNCTION_NAME);
 
        if(empty())
                return;
@@ -137,7 +70,7 @@ void ItemStack::serialize(std::ostream &os) const
                parts = 2;
        if(wear != 0)
                parts = 3;
-       if(metadata != "")
+       if (!metadata.empty())
                parts = 4;
 
        os<<serializeJsonStringIfNeeded(name);
@@ -145,13 +78,15 @@ void ItemStack::serialize(std::ostream &os) const
                os<<" "<<count;
        if(parts >= 3)
                os<<" "<<wear;
-       if(parts >= 4)
-               os<<" "<<serializeJsonStringIfNeeded(metadata);
+       if (parts >= 4) {
+               os << " ";
+               metadata.serialize(os);
+       }
 }
 
 void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
 {
-       DSTACK(__FUNCTION_NAME);
+       DSTACK(FUNCTION_NAME);
 
        clear();
 
@@ -163,7 +98,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
        std::getline(is, tmp, ' ');
        if(!tmp.empty())
                throw SerializationError("Unexpected text after item name");
-       
+
        if(name == "MaterialItem")
        {
                // Obsoleted on 2011-07-30
@@ -183,7 +118,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                legacy_nimap.getName(material, name);
                if(name == "")
                        name = "unknown_block";
-               name = itemdef->getAlias(name);
+               if (itemdef)
+                       name = itemdef->getAlias(name);
                count = materialcount;
        }
        else if(name == "MaterialItem2")
@@ -202,7 +138,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                legacy_nimap.getName(material, name);
                if(name == "")
                        name = "unknown_block";
-               name = itemdef->getAlias(name);
+               if (itemdef)
+                       name = itemdef->getAlias(name);
                count = materialcount;
        }
        else if(name == "node" || name == "NodeItem" || name == "MaterialItem3"
@@ -216,14 +153,15 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                Strfnd fnd(all);
                fnd.next("\"");
                // If didn't skip to end, we have ""s
-               if(!fnd.atend()){
+               if(!fnd.at_end()){
                        name = fnd.next("\"");
                } else { // No luck, just read a word then
                        fnd.start(all);
                        name = fnd.next(" ");
                }
                fnd.skip_over(" ");
-               name = itemdef->getAlias(name);
+               if (itemdef)
+                       name = itemdef->getAlias(name);
                count = stoi(trim(fnd.next("")));
                if(count == 0)
                        count = 1;
@@ -243,7 +181,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                Strfnd fnd(all);
                fnd.next("\"");
                // If didn't skip to end, we have ""s
-               if(!fnd.atend()){
+               if(!fnd.at_end()){
                        name = fnd.next("\"");
                } else { // No luck, just read a word then
                        fnd.start(all);
@@ -252,7 +190,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                count = 1;
                // Then read wear
                fnd.skip_over(" ");
-               name = itemdef->getAlias(name);
+               if (itemdef)
+                       name = itemdef->getAlias(name);
                wear = stoi(trim(fnd.next("")));
        }
        else
@@ -262,7 +201,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                        // The real thing
 
                        // Apply item aliases
-                       name = itemdef->getAlias(name);
+                       if (itemdef)
+                               name = itemdef->getAlias(name);
 
                        // Read the count
                        std::string count_str;
@@ -284,7 +224,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                                wear = stoi(wear_str);
 
                        // Read metadata
-                       metadata = deSerializeJsonStringIfNeeded(is);
+                       metadata.deSerialize(is);
 
                        // In case fields are added after metadata, skip space here:
                        //std::getline(is, tmp, ' ');
@@ -294,9 +234,9 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                } while(false);
        }
 
-       if(name.empty() || count == 0)
+       if (name.empty() || count == 0)
                clear();
-       else if(itemdef->get(name).type == ITEM_TOOL)
+       else if (itemdef && itemdef->get(name).type == ITEM_TOOL)
                count = 1;
 }
 
@@ -308,12 +248,12 @@ void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef)
 
 std::string ItemStack::getItemString() const
 {
-       // Get item string
        std::ostringstream os(std::ios::binary);
        serialize(os);
        return os.str();
 }
 
+
 ItemStack ItemStack::addItem(const ItemStack &newitem_,
                IItemDefManager *itemdef)
 {
@@ -330,8 +270,9 @@ ItemStack ItemStack::addItem(const ItemStack &newitem_,
                *this = newitem;
                newitem.clear();
        }
-       // If item name differs, bail out
-       else if(name != newitem.name)
+       // If item name or metadata differs, bail out
+       else if (name != newitem.name
+               || metadata != newitem.metadata)
        {
                // cannot be added
        }
@@ -369,8 +310,9 @@ bool ItemStack::itemFits(const ItemStack &newitem_,
        {
                newitem.clear();
        }
-       // If item name differs, bail out
-       else if(name != newitem.name)
+       // If item name or metadata differs, bail out
+       else if (name != newitem.name
+               || metadata != newitem.metadata)
        {
                // cannot be added
        }
@@ -427,14 +369,13 @@ ItemStack ItemStack::peekItem(u32 peekcount) const
        Inventory
 */
 
-InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemdef)
+InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef):
+       m_name(name),
+       m_size(size),
+       m_width(0),
+       m_itemdef(itemdef)
 {
-       m_name = name;
-       m_size = size;
-       m_width = 0;
-       m_itemdef = itemdef;
        clearItems();
-       //m_dirty = false;
 }
 
 InventoryList::~InventoryList()
@@ -473,7 +414,7 @@ void InventoryList::setName(const std::string &name)
 void InventoryList::serialize(std::ostream &os) const
 {
        //os.imbue(std::locale("C"));
-       
+
        os<<"Width "<<m_width<<"\n";
 
        for(u32 i=0; i<m_items.size(); i++)
@@ -615,13 +556,13 @@ u32 InventoryList::getFreeSlots() const
 
 const ItemStack& InventoryList::getItem(u32 i) const
 {
-       assert(i < m_size);
+       assert(i < m_size); // Pre-condition
        return m_items[i];
 }
 
 ItemStack& InventoryList::getItem(u32 i)
 {
-       assert(i < m_size);
+       assert(i < m_size); // Pre-condition
        return m_items[i];
 }
 
@@ -638,7 +579,7 @@ ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
 
 void InventoryList::deleteItem(u32 i)
 {
-       assert(i < m_items.size());
+       assert(i < m_items.size()); // Pre-condition
        m_items[i].clear();
 }
 
@@ -648,7 +589,7 @@ ItemStack InventoryList::addItem(const ItemStack &newitem_)
 
        if(newitem.empty())
                return newitem;
-       
+
        /*
                First try to find if it could be added to some existing items
        */
@@ -725,7 +666,7 @@ bool InventoryList::containsItem(const ItemStack &item) const
                return true;
        for(std::vector<ItemStack>::const_reverse_iterator
                        i = m_items.rbegin();
-                       i != m_items.rend(); i++)
+                       i != m_items.rend(); ++i)
        {
                if(count == 0)
                        break;
@@ -745,7 +686,7 @@ ItemStack InventoryList::removeItem(const ItemStack &item)
        ItemStack removed;
        for(std::vector<ItemStack>::reverse_iterator
                        i = m_items.rbegin();
-                       i != m_items.rend(); i++)
+                       i != m_items.rend(); ++i)
        {
                if(i->name == item.name)
                {
@@ -769,18 +710,46 @@ ItemStack InventoryList::takeItem(u32 i, u32 takecount)
        return taken;
 }
 
-ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const
+void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
 {
-       if(i >= m_items.size())
-               return ItemStack();
+       // Take item from source list
+       ItemStack item1;
+       if (count == 0)
+               item1 = changeItem(i, ItemStack());
+       else
+               item1 = takeItem(i, 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;
+               }
+       }
+
+       // 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;
+               }
+       }
 
-       return m_items[i].peekItem(peekcount);
+       // If we reach this, the item was not fully added
+       // Add the remaining part back to the source item
+       addItem(i, item1);
 }
 
-void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count)
+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)
-               return;
+               return count;
 
        // Take item from source list
        ItemStack item1;
@@ -790,7 +759,7 @@ void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count)
                item1 = takeItem(i, count);
 
        if(item1.empty())
-               return;
+               return 0;
 
        // Try to add the item to destination list
        u32 oldcount = item1.count;
@@ -808,8 +777,11 @@ void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count)
 
                // If olditem is returned, nothing was added.
                // Swap the items
-               if(nothing_added)
-               {
+               if (nothing_added && swap_if_needed) {
+                       // Tell that we swapped
+                       if (did_swap != NULL) {
+                               *did_swap = true;
+                       }
                        // Take item from source list
                        item1 = changeItem(i, ItemStack());
                        // Adding was not possible, swap the items.
@@ -818,6 +790,7 @@ void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count)
                        changeItem(i, item2);
                }
        }
+       return (oldcount - item1.count);
 }
 
 /*
@@ -831,6 +804,7 @@ Inventory::~Inventory()
 
 void Inventory::clear()
 {
+       m_dirty = true;
        for(u32 i=0; i<m_lists.size(); i++)
        {
                delete m_lists[i];
@@ -840,6 +814,7 @@ void Inventory::clear()
 
 void Inventory::clearContents()
 {
+       m_dirty = true;
        for(u32 i=0; i<m_lists.size(); i++)
        {
                InventoryList *list = m_lists[i];
@@ -852,12 +827,14 @@ void Inventory::clearContents()
 
 Inventory::Inventory(IItemDefManager *itemdef)
 {
+       m_dirty = false;
        m_itemdef = itemdef;
 }
 
 Inventory::Inventory(const Inventory &other)
 {
        *this = other;
+       m_dirty = false;
 }
 
 Inventory & Inventory::operator = (const Inventory &other)
@@ -865,6 +842,7 @@ Inventory & Inventory::operator = (const Inventory &other)
        // Gracefully handle self assignment
        if(this != &other)
        {
+               m_dirty = true;
                clear();
                m_itemdef = other.m_itemdef;
                for(u32 i=0; i<other.m_lists.size(); i++)
@@ -882,7 +860,7 @@ bool Inventory::operator == (const Inventory &other) const
 
        for(u32 i=0; i<m_lists.size(); i++)
        {
-               if(m_lists[i] != other.m_lists[i])
+               if(*m_lists[i] != *other.m_lists[i])
                        return false;
        }
        return true;
@@ -938,13 +916,14 @@ void Inventory::deSerialize(std::istream &is)
                }
                else
                {
-                       throw SerializationError("invalid inventory specifier");
+                       throw SerializationError("invalid inventory specifier: " + name);
                }
        }
 }
 
 InventoryList * Inventory::addList(const std::string &name, u32 size)
 {
+       m_dirty = true;
        s32 i = getListIndex(name);
        if(i != -1)
        {
@@ -990,6 +969,7 @@ bool Inventory::deleteList(const std::string &name)
        s32 i = getListIndex(name);
        if(i == -1)
                return false;
+       m_dirty = true;
        delete m_lists[i];
        m_lists.erase(m_lists.begin() + i);
        return true;