#include "log.h"
#include "itemdef.h"
#include "util/strfnd.h"
+#include "content_mapnode.h" // For loading legacy MaterialItems
+#include "nameidmapping.h" // For loading legacy MaterialItems
#include "util/serialize.h"
#include "util/string.h"
ItemStack
*/
+static content_t content_translate_from_19_to_internal(content_t c_from)
+{
+ for (const auto &tt : trans_table_19) {
+ if(tt[1] == c_from) {
+ return tt[0];
+ }
+ }
+ return c_from;
+}
+
ItemStack::ItemStack(const std::string &name_, u16 count_,
u16 wear_, IItemDefManager *itemdef) :
name(itemdef->getAlias(name_)),
void ItemStack::serialize(std::ostream &os) const
{
- DSTACK(FUNCTION_NAME);
-
- if(empty())
+ if (empty())
return;
// Check how many parts of the itemstring are needed
void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
{
- DSTACK(FUNCTION_NAME);
-
clear();
// Read name
if(!tmp.empty())
throw SerializationError("Unexpected text after item name");
- do { // This loop is just to allow "break;"
- // The real thing
+ if(name == "MaterialItem")
+ {
+ // Obsoleted on 2011-07-30
+
+ u16 material;
+ is>>material;
+ u16 materialcount;
+ is>>materialcount;
+ // Convert old materials
+ if(material <= 0xff)
+ material = content_translate_from_19_to_internal(material);
+ if(material > 0xfff)
+ throw SerializationError("Too large material number");
+ // Convert old id to name
+ NameIdMapping legacy_nimap;
+ content_mapnode_get_name_id_mapping(&legacy_nimap);
+ legacy_nimap.getName(material, name);
+ if(name.empty())
+ name = "unknown_block";
+ if (itemdef)
+ name = itemdef->getAlias(name);
+ count = materialcount;
+ }
+ else if(name == "MaterialItem2")
+ {
+ // Obsoleted on 2011-11-16
+
+ u16 material;
+ is>>material;
+ u16 materialcount;
+ is>>materialcount;
+ if(material > 0xfff)
+ throw SerializationError("Too large material number");
+ // Convert old id to name
+ NameIdMapping legacy_nimap;
+ content_mapnode_get_name_id_mapping(&legacy_nimap);
+ legacy_nimap.getName(material, name);
+ if(name.empty())
+ name = "unknown_block";
+ if (itemdef)
+ name = itemdef->getAlias(name);
+ count = materialcount;
+ }
+ else if(name == "node" || name == "NodeItem" || name == "MaterialItem3"
+ || name == "craft" || name == "CraftItem")
+ {
+ // Obsoleted on 2012-01-07
+
+ std::string all;
+ std::getline(is, all, '\n');
+ // First attempt to read inside ""
+ Strfnd fnd(all);
+ fnd.next("\"");
+ // If didn't skip to end, we have ""s
+ if(!fnd.at_end()){
+ name = fnd.next("\"");
+ } else { // No luck, just read a word then
+ fnd.start(all);
+ name = fnd.next(" ");
+ }
+ fnd.skip_over(" ");
+ if (itemdef)
+ name = itemdef->getAlias(name);
+ count = stoi(trim(fnd.next("")));
+ if(count == 0)
+ count = 1;
+ }
+ else if(name == "MBOItem")
+ {
+ // Obsoleted on 2011-10-14
+ throw SerializationError("MBOItem not supported anymore");
+ }
+ else if(name == "tool" || name == "ToolItem")
+ {
+ // Obsoleted on 2012-01-07
- // Apply item aliases
+ std::string all;
+ std::getline(is, all, '\n');
+ // First attempt to read inside ""
+ Strfnd fnd(all);
+ fnd.next("\"");
+ // If didn't skip to end, we have ""s
+ if(!fnd.at_end()){
+ name = fnd.next("\"");
+ } else { // No luck, just read a word then
+ fnd.start(all);
+ name = fnd.next(" ");
+ }
+ count = 1;
+ // Then read wear
+ fnd.skip_over(" ");
if (itemdef)
name = itemdef->getAlias(name);
+ wear = stoi(trim(fnd.next("")));
+ }
+ else
+ {
+ do // This loop is just to allow "break;"
+ {
+ // The real thing
+
+ // Apply item aliases
+ if (itemdef)
+ name = itemdef->getAlias(name);
+
+ // Read the count
+ std::string count_str;
+ std::getline(is, count_str, ' ');
+ if (count_str.empty()) {
+ count = 1;
+ break;
+ }
- // Read the count
- std::string count_str;
- std::getline(is, count_str, ' ');
- if (count_str.empty()) {
- count = 1;
- break;
- } else {
count = stoi(count_str);
- }
- // Read the wear
- std::string wear_str;
- std::getline(is, wear_str, ' ');
- if (wear_str.empty())
- break;
- else
+ // Read the wear
+ std::string wear_str;
+ std::getline(is, wear_str, ' ');
+ if(wear_str.empty())
+ break;
+
wear = stoi(wear_str);
- // Read metadata
- metadata.deSerialize(is);
+ // Read metadata
+ metadata.deSerialize(is);
- } while(false);
+ // In case fields are added after metadata, skip space here:
+ //std::getline(is, tmp, ' ');
+ //if(!tmp.empty())
+ // throw SerializationError("Unexpected text after metadata");
+
+ } while(false);
+ }
if (name.empty() || count == 0)
clear();
}
-ItemStack ItemStack::addItem(const ItemStack &newitem_,
- IItemDefManager *itemdef)
+ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef)
{
- ItemStack newitem = newitem_;
-
// If the item is empty or the position invalid, bail out
if(newitem.empty())
{
return newitem;
}
-bool ItemStack::itemFits(const ItemStack &newitem_,
+bool ItemStack::itemFits(ItemStack newitem,
ItemStack *restitem,
IItemDefManager *itemdef) const
{
- ItemStack newitem = newitem_;
// If the item is empty or the position invalid, bail out
if(newitem.empty())
newitem.clear();
}
// Else the item does not fit fully. Return the rest.
- // the rest.
else
{
u16 freespace = freeSpace(itemdef);
if(restitem)
*restitem = newitem;
+
return newitem.empty();
}
InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef):
m_name(name),
m_size(size),
- m_width(0),
m_itemdef(itemdef)
{
clearItems();
}
-InventoryList::~InventoryList()
-{
-}
-
void InventoryList::clearItems()
{
m_items.clear();
- for(u32 i=0; i<m_size; i++)
- {
- m_items.push_back(ItemStack());
+ for (u32 i=0; i < m_size; i++) {
+ m_items.emplace_back();
}
//setDirty(true);
os<<"Width "<<m_width<<"\n";
- for(u32 i=0; i<m_items.size(); i++)
- {
- const ItemStack &item = m_items[i];
- if(item.empty())
- {
+ for (const auto &item : m_items) {
+ if (item.empty()) {
os<<"Empty";
- }
- else
- {
+ } else {
os<<"Item ";
item.serialize(os);
}
u32 item_i = 0;
m_width = 0;
- for(;;)
- {
+ while (is.good()) {
std::string line;
std::getline(is, line, '\n');
std::string name;
std::getline(iss, name, ' ');
- if(name == "EndInventoryList")
- {
- break;
- }
+ if (name == "EndInventoryList")
+ return;
+
// This is a temporary backwards compatibility fix
- else if(name == "end")
- {
- break;
- }
- else if(name == "Width")
- {
+ if (name == "end")
+ return;
+
+ if (name == "Width") {
iss >> m_width;
if (iss.fail())
throw SerializationError("incorrect width property");
m_items[item_i++].clear();
}
}
+
+ // 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)
return false;
if(m_name != other.m_name)
return false;
- for(u32 i=0; i<m_items.size(); i++)
- {
- ItemStack s1 = m_items[i];
- ItemStack s2 = other.m_items[i];
- if(s1.name != s2.name || s1.wear!= s2.wear || s1.count != s2.count ||
- s1.metadata != s2.metadata)
+ for (u32 i = 0; i < m_items.size(); i++)
+ if (m_items[i] != other.m_items[i])
return false;
- }
return true;
}
u32 InventoryList::getUsedSlots() const
{
u32 num = 0;
- for(u32 i=0; i<m_items.size(); i++)
- {
- if(!m_items[i].empty())
+ for (const auto &m_item : m_items) {
+ if (!m_item.empty())
num++;
}
return num;
return false;
}
-bool InventoryList::containsItem(const ItemStack &item) const
+bool InventoryList::containsItem(const ItemStack &item, bool match_meta) const
{
u32 count = item.count;
- if(count == 0)
+ if (count == 0)
return true;
- for(std::vector<ItemStack>::const_reverse_iterator
- i = m_items.rbegin();
- i != m_items.rend(); ++i)
- {
- if(count == 0)
+
+ for (auto i = m_items.rbegin(); i != m_items.rend(); ++i) {
+ if (count == 0)
break;
- if(i->name == item.name)
- {
- if(i->count >= count)
+ if (i->name == item.name && (!match_meta || (i->metadata == item.metadata))) {
+ if (i->count >= count)
return true;
- else
- count -= i->count;
+
+ count -= i->count;
}
}
return false;
ItemStack InventoryList::removeItem(const ItemStack &item)
{
ItemStack removed;
- for(std::vector<ItemStack>::reverse_iterator
- i = m_items.rbegin();
- i != m_items.rend(); ++i)
- {
- if(i->name == item.name)
- {
+ for (auto i = m_items.rbegin(); i != m_items.rend(); ++i) {
+ if (i->name == item.name) {
u32 still_to_remove = item.count - removed.count;
removed.addItem(i->takeItem(still_to_remove), m_itemdef);
- if(removed.count == item.count)
+ if (removed.count == item.count)
break;
}
}
void Inventory::clear()
{
m_dirty = true;
- for(u32 i=0; i<m_lists.size(); i++)
- {
- delete m_lists[i];
+ for (auto &m_list : m_lists) {
+ delete m_list;
}
m_lists.clear();
}
void Inventory::clearContents()
{
m_dirty = true;
- for(u32 i=0; i<m_lists.size(); i++)
- {
- InventoryList *list = m_lists[i];
- for(u32 j=0; j<list->getSize(); j++)
- {
+ for (InventoryList *list : m_lists) {
+ for (u32 j=0; j<list->getSize(); j++) {
list->deleteItem(j);
}
}
m_dirty = true;
clear();
m_itemdef = other.m_itemdef;
- for(u32 i=0; i<other.m_lists.size(); i++)
- {
- m_lists.push_back(new InventoryList(*other.m_lists[i]));
+ for (InventoryList *list : other.m_lists) {
+ m_lists.push_back(new InventoryList(*list));
}
}
return *this;
void Inventory::serialize(std::ostream &os) const
{
- for(u32 i=0; i<m_lists.size(); i++)
- {
- InventoryList *list = m_lists[i];
+ for (InventoryList *list : m_lists) {
os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
list->serialize(os);
}
{
clear();
- for(;;)
- {
+ while (is.good()) {
std::string line;
std::getline(is, line, '\n');
std::string name;
std::getline(iss, name, ' ');
- if(name == "EndInventory")
- {
- break;
- }
+ if (name == "EndInventory")
+ return;
+
// This is a temporary backwards compatibility fix
- else if(name == "end")
- {
- break;
- }
- else if(name == "List")
- {
+ if (name == "end")
+ return;
+
+ if (name == "List") {
std::string listname;
u32 listsize;
throw SerializationError("invalid inventory specifier: " + name);
}
}
+
+ // 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)
}
return m_lists[i];
}
- else
- {
- //don't create list with invalid name
- if (name.find(" ") != std::string::npos) return NULL;
- InventoryList *list = new InventoryList(name, size, m_itemdef);
- m_lists.push_back(list);
- return list;
- }
+
+ //don't create list with invalid name
+ if (name.find(' ') != std::string::npos) return NULL;
+
+ InventoryList *list = new InventoryList(name, size, m_itemdef);
+ m_lists.push_back(list);
+ return list;
}
InventoryList * Inventory::getList(const std::string &name)
std::vector<const InventoryList*> Inventory::getLists()
{
std::vector<const InventoryList*> lists;
- for(u32 i=0; i<m_lists.size(); i++)
- {
- InventoryList *list = m_lists[i];
+ for (auto list : m_lists) {
lists.push_back(list);
}
return lists;