]> git.lizzy.rs Git - minetest.git/blob - src/inventory.cpp
Fix various player save issues (performance penalty on sql backends + bugs)
[minetest.git] / src / inventory.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "inventory.h"
21 #include "serialization.h"
22 #include "debug.h"
23 #include <sstream>
24 #include "log.h"
25 #include "itemdef.h"
26 #include "util/strfnd.h"
27 #include "content_mapnode.h" // For loading legacy MaterialItems
28 #include "nameidmapping.h" // For loading legacy MaterialItems
29 #include "util/serialize.h"
30 #include "util/string.h"
31
32 /*
33         ItemStack
34 */
35
36 static content_t content_translate_from_19_to_internal(content_t c_from)
37 {
38         for (const auto &tt : trans_table_19) {
39                 if(tt[1] == c_from) {
40                         return tt[0];
41                 }
42         }
43         return c_from;
44 }
45
46 ItemStack::ItemStack(const std::string &name_, u16 count_,
47                 u16 wear_, IItemDefManager *itemdef) :
48         name(itemdef->getAlias(name_)),
49         count(count_),
50         wear(wear_)
51 {
52         if (name.empty() || count == 0)
53                 clear();
54         else if (itemdef->get(name).type == ITEM_TOOL)
55                 count = 1;
56 }
57
58 void ItemStack::serialize(std::ostream &os) const
59 {
60         if (empty())
61                 return;
62
63         // Check how many parts of the itemstring are needed
64         int parts = 1;
65         if(count != 1)
66                 parts = 2;
67         if(wear != 0)
68                 parts = 3;
69         if (!metadata.empty())
70                 parts = 4;
71
72         os<<serializeJsonStringIfNeeded(name);
73         if(parts >= 2)
74                 os<<" "<<count;
75         if(parts >= 3)
76                 os<<" "<<wear;
77         if (parts >= 4) {
78                 os << " ";
79                 metadata.serialize(os);
80         }
81 }
82
83 void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
84 {
85         clear();
86
87         // Read name
88         name = deSerializeJsonStringIfNeeded(is);
89
90         // Skip space
91         std::string tmp;
92         std::getline(is, tmp, ' ');
93         if(!tmp.empty())
94                 throw SerializationError("Unexpected text after item name");
95
96         if(name == "MaterialItem")
97         {
98                 // Obsoleted on 2011-07-30
99
100                 u16 material;
101                 is>>material;
102                 u16 materialcount;
103                 is>>materialcount;
104                 // Convert old materials
105                 if(material <= 0xff)
106                         material = content_translate_from_19_to_internal(material);
107                 if(material > 0xfff)
108                         throw SerializationError("Too large material number");
109                 // Convert old id to name
110                 NameIdMapping legacy_nimap;
111                 content_mapnode_get_name_id_mapping(&legacy_nimap);
112                 legacy_nimap.getName(material, name);
113                 if(name.empty())
114                         name = "unknown_block";
115                 if (itemdef)
116                         name = itemdef->getAlias(name);
117                 count = materialcount;
118         }
119         else if(name == "MaterialItem2")
120         {
121                 // Obsoleted on 2011-11-16
122
123                 u16 material;
124                 is>>material;
125                 u16 materialcount;
126                 is>>materialcount;
127                 if(material > 0xfff)
128                         throw SerializationError("Too large material number");
129                 // Convert old id to name
130                 NameIdMapping legacy_nimap;
131                 content_mapnode_get_name_id_mapping(&legacy_nimap);
132                 legacy_nimap.getName(material, name);
133                 if(name.empty())
134                         name = "unknown_block";
135                 if (itemdef)
136                         name = itemdef->getAlias(name);
137                 count = materialcount;
138         }
139         else if(name == "node" || name == "NodeItem" || name == "MaterialItem3"
140                         || name == "craft" || name == "CraftItem")
141         {
142                 // Obsoleted on 2012-01-07
143
144                 std::string all;
145                 std::getline(is, all, '\n');
146                 // First attempt to read inside ""
147                 Strfnd fnd(all);
148                 fnd.next("\"");
149                 // If didn't skip to end, we have ""s
150                 if(!fnd.at_end()){
151                         name = fnd.next("\"");
152                 } else { // No luck, just read a word then
153                         fnd.start(all);
154                         name = fnd.next(" ");
155                 }
156                 fnd.skip_over(" ");
157                 if (itemdef)
158                         name = itemdef->getAlias(name);
159                 count = stoi(trim(fnd.next("")));
160                 if(count == 0)
161                         count = 1;
162         }
163         else if(name == "MBOItem")
164         {
165                 // Obsoleted on 2011-10-14
166                 throw SerializationError("MBOItem not supported anymore");
167         }
168         else if(name == "tool" || name == "ToolItem")
169         {
170                 // Obsoleted on 2012-01-07
171
172                 std::string all;
173                 std::getline(is, all, '\n');
174                 // First attempt to read inside ""
175                 Strfnd fnd(all);
176                 fnd.next("\"");
177                 // If didn't skip to end, we have ""s
178                 if(!fnd.at_end()){
179                         name = fnd.next("\"");
180                 } else { // No luck, just read a word then
181                         fnd.start(all);
182                         name = fnd.next(" ");
183                 }
184                 count = 1;
185                 // Then read wear
186                 fnd.skip_over(" ");
187                 if (itemdef)
188                         name = itemdef->getAlias(name);
189                 wear = stoi(trim(fnd.next("")));
190         }
191         else
192         {
193                 do  // This loop is just to allow "break;"
194                 {
195                         // The real thing
196
197                         // Apply item aliases
198                         if (itemdef)
199                                 name = itemdef->getAlias(name);
200
201                         // Read the count
202                         std::string count_str;
203                         std::getline(is, count_str, ' ');
204                         if (count_str.empty()) {
205                                 count = 1;
206                                 break;
207                         }
208
209                         count = stoi(count_str);
210
211                         // Read the wear
212                         std::string wear_str;
213                         std::getline(is, wear_str, ' ');
214                         if(wear_str.empty())
215                                 break;
216
217                         wear = stoi(wear_str);
218
219                         // Read metadata
220                         metadata.deSerialize(is);
221
222                         // In case fields are added after metadata, skip space here:
223                         //std::getline(is, tmp, ' ');
224                         //if(!tmp.empty())
225                         //      throw SerializationError("Unexpected text after metadata");
226
227                 } while(false);
228         }
229
230         if (name.empty() || count == 0)
231                 clear();
232         else if (itemdef && itemdef->get(name).type == ITEM_TOOL)
233                 count = 1;
234 }
235
236 void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef)
237 {
238         std::istringstream is(str, std::ios::binary);
239         deSerialize(is, itemdef);
240 }
241
242 std::string ItemStack::getItemString() const
243 {
244         std::ostringstream os(std::ios::binary);
245         serialize(os);
246         return os.str();
247 }
248
249
250 ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef)
251 {
252         // If the item is empty or the position invalid, bail out
253         if(newitem.empty())
254         {
255                 // nothing can be added trivially
256         }
257         // If this is an empty item, it's an easy job.
258         else if(empty())
259         {
260                 const u16 stackMax = newitem.getStackMax(itemdef);
261
262                 *this = newitem;
263
264                 // If the item fits fully, delete it
265                 if (count <= stackMax) {
266                         newitem.clear();
267                 } else { // Else the item does not fit fully. Return the rest.
268                         count = stackMax;
269                         newitem.remove(count);
270                 }
271         }
272         // If item name or metadata differs, bail out
273         else if (name != newitem.name
274                 || metadata != newitem.metadata)
275         {
276                 // cannot be added
277         }
278         // If the item fits fully, add counter and delete it
279         else if(newitem.count <= freeSpace(itemdef))
280         {
281                 add(newitem.count);
282                 newitem.clear();
283         }
284         // Else the item does not fit fully. Add all that fits and return
285         // the rest.
286         else
287         {
288                 u16 freespace = freeSpace(itemdef);
289                 add(freespace);
290                 newitem.remove(freespace);
291         }
292
293         return newitem;
294 }
295
296 bool ItemStack::itemFits(ItemStack newitem,
297                 ItemStack *restitem,
298                 IItemDefManager *itemdef) const
299 {
300
301         // If the item is empty or the position invalid, bail out
302         if(newitem.empty())
303         {
304                 // nothing can be added trivially
305         }
306         // If this is an empty item, it's an easy job.
307         else if(empty())
308         {
309                 const u16 stackMax = newitem.getStackMax(itemdef);
310
311                 // If the item fits fully, delete it
312                 if (newitem.count <= stackMax) {
313                         newitem.clear();
314                 } else { // Else the item does not fit fully. Return the rest.
315                         newitem.remove(stackMax);
316                 }
317         }
318         // If item name or metadata differs, bail out
319         else if (name != newitem.name
320                 || metadata != newitem.metadata)
321         {
322                 // cannot be added
323         }
324         // If the item fits fully, delete it
325         else if(newitem.count <= freeSpace(itemdef))
326         {
327                 newitem.clear();
328         }
329         // Else the item does not fit fully. Return the rest.
330         else
331         {
332                 u16 freespace = freeSpace(itemdef);
333                 newitem.remove(freespace);
334         }
335
336         if(restitem)
337                 *restitem = newitem;
338         return newitem.empty();
339 }
340
341 ItemStack ItemStack::takeItem(u32 takecount)
342 {
343         if(takecount == 0 || count == 0)
344                 return ItemStack();
345
346         ItemStack result = *this;
347         if(takecount >= count)
348         {
349                 // Take all
350                 clear();
351         }
352         else
353         {
354                 // Take part
355                 remove(takecount);
356                 result.count = takecount;
357         }
358         return result;
359 }
360
361 ItemStack ItemStack::peekItem(u32 peekcount) const
362 {
363         if(peekcount == 0 || count == 0)
364                 return ItemStack();
365
366         ItemStack result = *this;
367         if(peekcount < count)
368                 result.count = peekcount;
369         return result;
370 }
371
372 /*
373         Inventory
374 */
375
376 InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef):
377         m_name(name),
378         m_size(size),
379         m_itemdef(itemdef)
380 {
381         clearItems();
382 }
383
384 void InventoryList::clearItems()
385 {
386         m_items.clear();
387
388         for (u32 i=0; i < m_size; i++) {
389                 m_items.emplace_back();
390         }
391
392         //setDirty(true);
393 }
394
395 void InventoryList::setSize(u32 newsize)
396 {
397         if(newsize != m_items.size())
398                 m_items.resize(newsize);
399         m_size = newsize;
400 }
401
402 void InventoryList::setWidth(u32 newwidth)
403 {
404         m_width = newwidth;
405 }
406
407 void InventoryList::setName(const std::string &name)
408 {
409         m_name = name;
410 }
411
412 void InventoryList::serialize(std::ostream &os) const
413 {
414         //os.imbue(std::locale("C"));
415
416         os<<"Width "<<m_width<<"\n";
417
418         for (const auto &item : m_items) {
419                 if (item.empty()) {
420                         os<<"Empty";
421                 } else {
422                         os<<"Item ";
423                         item.serialize(os);
424                 }
425                 os<<"\n";
426         }
427
428         os<<"EndInventoryList\n";
429 }
430
431 void InventoryList::deSerialize(std::istream &is)
432 {
433         //is.imbue(std::locale("C"));
434
435         clearItems();
436         u32 item_i = 0;
437         m_width = 0;
438
439         while (is.good()) {
440                 std::string line;
441                 std::getline(is, line, '\n');
442
443                 std::istringstream iss(line);
444                 //iss.imbue(std::locale("C"));
445
446                 std::string name;
447                 std::getline(iss, name, ' ');
448
449                 if (name == "EndInventoryList")
450                         return;
451
452                 // This is a temporary backwards compatibility fix
453                 if (name == "end")
454                         return;
455
456                 if (name == "Width") {
457                         iss >> m_width;
458                         if (iss.fail())
459                                 throw SerializationError("incorrect width property");
460                 }
461                 else if(name == "Item")
462                 {
463                         if(item_i > getSize() - 1)
464                                 throw SerializationError("too many items");
465                         ItemStack item;
466                         item.deSerialize(iss, m_itemdef);
467                         m_items[item_i++] = item;
468                 }
469                 else if(name == "Empty")
470                 {
471                         if(item_i > getSize() - 1)
472                                 throw SerializationError("too many items");
473                         m_items[item_i++].clear();
474                 }
475         }
476
477         // Contents given to deSerialize() were not terminated properly: throw error.
478
479         std::ostringstream ss;
480         ss << "Malformatted inventory list. list="
481                 << m_name << ", read " << item_i << " of " << getSize()
482                 << " ItemStacks." << std::endl;
483         throw SerializationError(ss.str());
484 }
485
486 InventoryList::InventoryList(const InventoryList &other)
487 {
488         *this = other;
489 }
490
491 InventoryList & InventoryList::operator = (const InventoryList &other)
492 {
493         m_items = other.m_items;
494         m_size = other.m_size;
495         m_width = other.m_width;
496         m_name = other.m_name;
497         m_itemdef = other.m_itemdef;
498         //setDirty(true);
499
500         return *this;
501 }
502
503 bool InventoryList::operator == (const InventoryList &other) const
504 {
505         if(m_size != other.m_size)
506                 return false;
507         if(m_width != other.m_width)
508                 return false;
509         if(m_name != other.m_name)
510                 return false;
511         for(u32 i=0; i<m_items.size(); i++)
512         {
513                 ItemStack s1 = m_items[i];
514                 ItemStack s2 = other.m_items[i];
515                 if(s1.name != s2.name || s1.wear!= s2.wear || s1.count != s2.count ||
516                                 s1.metadata != s2.metadata)
517                         return false;
518         }
519
520         return true;
521 }
522
523 const std::string &InventoryList::getName() const
524 {
525         return m_name;
526 }
527
528 u32 InventoryList::getSize() const
529 {
530         return m_items.size();
531 }
532
533 u32 InventoryList::getWidth() const
534 {
535         return m_width;
536 }
537
538 u32 InventoryList::getUsedSlots() const
539 {
540         u32 num = 0;
541         for (const auto &m_item : m_items) {
542                 if (!m_item.empty())
543                         num++;
544         }
545         return num;
546 }
547
548 u32 InventoryList::getFreeSlots() const
549 {
550         return getSize() - getUsedSlots();
551 }
552
553 const ItemStack& InventoryList::getItem(u32 i) const
554 {
555         assert(i < m_size); // Pre-condition
556         return m_items[i];
557 }
558
559 ItemStack& InventoryList::getItem(u32 i)
560 {
561         assert(i < m_size); // Pre-condition
562         return m_items[i];
563 }
564
565 ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
566 {
567         if(i >= m_items.size())
568                 return newitem;
569
570         ItemStack olditem = m_items[i];
571         m_items[i] = newitem;
572         //setDirty(true);
573         return olditem;
574 }
575
576 void InventoryList::deleteItem(u32 i)
577 {
578         assert(i < m_items.size()); // Pre-condition
579         m_items[i].clear();
580 }
581
582 ItemStack InventoryList::addItem(const ItemStack &newitem_)
583 {
584         ItemStack newitem = newitem_;
585
586         if(newitem.empty())
587                 return newitem;
588
589         /*
590                 First try to find if it could be added to some existing items
591         */
592         for(u32 i=0; i<m_items.size(); i++)
593         {
594                 // Ignore empty slots
595                 if(m_items[i].empty())
596                         continue;
597                 // Try adding
598                 newitem = addItem(i, newitem);
599                 if(newitem.empty())
600                         return newitem; // All was eaten
601         }
602
603         /*
604                 Then try to add it to empty slots
605         */
606         for(u32 i=0; i<m_items.size(); i++)
607         {
608                 // Ignore unempty slots
609                 if(!m_items[i].empty())
610                         continue;
611                 // Try adding
612                 newitem = addItem(i, newitem);
613                 if(newitem.empty())
614                         return newitem; // All was eaten
615         }
616
617         // Return leftover
618         return newitem;
619 }
620
621 ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
622 {
623         if(i >= m_items.size())
624                 return newitem;
625
626         ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
627         //if(leftover != newitem)
628         //      setDirty(true);
629         return leftover;
630 }
631
632 bool InventoryList::itemFits(const u32 i, const ItemStack &newitem,
633                 ItemStack *restitem) const
634 {
635         if(i >= m_items.size())
636         {
637                 if(restitem)
638                         *restitem = newitem;
639                 return false;
640         }
641
642         return m_items[i].itemFits(newitem, restitem, m_itemdef);
643 }
644
645 bool InventoryList::roomForItem(const ItemStack &item_) const
646 {
647         ItemStack item = item_;
648         ItemStack leftover;
649         for(u32 i=0; i<m_items.size(); i++)
650         {
651                 if(itemFits(i, item, &leftover))
652                         return true;
653                 item = leftover;
654         }
655         return false;
656 }
657
658 bool InventoryList::containsItem(const ItemStack &item, bool match_meta) const
659 {
660         u32 count = item.count;
661         if (count == 0)
662                 return true;
663
664         for (auto i = m_items.rbegin(); i != m_items.rend(); ++i) {
665                 if (count == 0)
666                         break;
667                 if (i->name == item.name && (!match_meta || (i->metadata == item.metadata))) {
668                         if (i->count >= count)
669                                 return true;
670
671                         count -= i->count;
672                 }
673         }
674         return false;
675 }
676
677 ItemStack InventoryList::removeItem(const ItemStack &item)
678 {
679         ItemStack removed;
680         for (auto i = m_items.rbegin(); i != m_items.rend(); ++i) {
681                 if (i->name == item.name) {
682                         u32 still_to_remove = item.count - removed.count;
683                         removed.addItem(i->takeItem(still_to_remove), m_itemdef);
684                         if (removed.count == item.count)
685                                 break;
686                 }
687         }
688         return removed;
689 }
690
691 ItemStack InventoryList::takeItem(u32 i, u32 takecount)
692 {
693         if(i >= m_items.size())
694                 return ItemStack();
695
696         ItemStack taken = m_items[i].takeItem(takecount);
697         //if(!taken.empty())
698         //      setDirty(true);
699         return taken;
700 }
701
702 void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
703 {
704         // Take item from source list
705         ItemStack item1;
706         if (count == 0)
707                 item1 = changeItem(i, ItemStack());
708         else
709                 item1 = takeItem(i, count);
710
711         if (item1.empty())
712                 return;
713
714         // Try to add the item to destination list
715         u32 dest_size = dest->getSize();
716         // First try all the non-empty slots
717         for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
718                 if (!m_items[dest_i].empty()) {
719                         item1 = dest->addItem(dest_i, item1);
720                         if (item1.empty()) return;
721                 }
722         }
723
724         // Then try all the empty ones
725         for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
726                 if (m_items[dest_i].empty()) {
727                         item1 = dest->addItem(dest_i, item1);
728                         if (item1.empty()) return;
729                 }
730         }
731
732         // If we reach this, the item was not fully added
733         // Add the remaining part back to the source item
734         addItem(i, item1);
735 }
736
737 u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
738                 u32 count, bool swap_if_needed, bool *did_swap)
739 {
740         if(this == dest && i == dest_i)
741                 return count;
742
743         // Take item from source list
744         ItemStack item1;
745         if(count == 0)
746                 item1 = changeItem(i, ItemStack());
747         else
748                 item1 = takeItem(i, count);
749
750         if(item1.empty())
751                 return 0;
752
753         // Try to add the item to destination list
754         u32 oldcount = item1.count;
755         item1 = dest->addItem(dest_i, item1);
756
757         // If something is returned, the item was not fully added
758         if(!item1.empty())
759         {
760                 // If olditem is returned, nothing was added.
761                 bool nothing_added = (item1.count == oldcount);
762
763                 // If something else is returned, part of the item was left unadded.
764                 // Add the other part back to the source item
765                 addItem(i, item1);
766
767                 // If olditem is returned, nothing was added.
768                 // Swap the items
769                 if (nothing_added && swap_if_needed) {
770                         // Tell that we swapped
771                         if (did_swap != NULL) {
772                                 *did_swap = true;
773                         }
774                         // Take item from source list
775                         item1 = changeItem(i, ItemStack());
776                         // Adding was not possible, swap the items.
777                         ItemStack item2 = dest->changeItem(dest_i, item1);
778                         // Put item from destination list to the source list
779                         changeItem(i, item2);
780                 }
781         }
782         return (oldcount - item1.count);
783 }
784
785 /*
786         Inventory
787 */
788
789 Inventory::~Inventory()
790 {
791         clear();
792 }
793
794 void Inventory::clear()
795 {
796         m_dirty = true;
797         for (auto &m_list : m_lists) {
798                 delete m_list;
799         }
800         m_lists.clear();
801 }
802
803 void Inventory::clearContents()
804 {
805         m_dirty = true;
806         for (InventoryList *list : m_lists) {
807                 for (u32 j=0; j<list->getSize(); j++) {
808                         list->deleteItem(j);
809                 }
810         }
811 }
812
813 Inventory::Inventory(IItemDefManager *itemdef)
814 {
815         m_dirty = false;
816         m_itemdef = itemdef;
817 }
818
819 Inventory::Inventory(const Inventory &other)
820 {
821         *this = other;
822         m_dirty = false;
823 }
824
825 Inventory & Inventory::operator = (const Inventory &other)
826 {
827         // Gracefully handle self assignment
828         if(this != &other)
829         {
830                 m_dirty = true;
831                 clear();
832                 m_itemdef = other.m_itemdef;
833                 for (InventoryList *list : other.m_lists) {
834                         m_lists.push_back(new InventoryList(*list));
835                 }
836         }
837         return *this;
838 }
839
840 bool Inventory::operator == (const Inventory &other) const
841 {
842         if(m_lists.size() != other.m_lists.size())
843                 return false;
844
845         for(u32 i=0; i<m_lists.size(); i++)
846         {
847                 if(*m_lists[i] != *other.m_lists[i])
848                         return false;
849         }
850         return true;
851 }
852
853 void Inventory::serialize(std::ostream &os) const
854 {
855         for (InventoryList *list : m_lists) {
856                 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
857                 list->serialize(os);
858         }
859
860         os<<"EndInventory\n";
861 }
862
863 void Inventory::deSerialize(std::istream &is)
864 {
865         clear();
866
867         while (is.good()) {
868                 std::string line;
869                 std::getline(is, line, '\n');
870
871                 std::istringstream iss(line);
872
873                 std::string name;
874                 std::getline(iss, name, ' ');
875
876                 if (name == "EndInventory")
877                         return;
878
879                 // This is a temporary backwards compatibility fix
880                 if (name == "end")
881                         return;
882
883                 if (name == "List") {
884                         std::string listname;
885                         u32 listsize;
886
887                         std::getline(iss, listname, ' ');
888                         iss>>listsize;
889
890                         InventoryList *list = new InventoryList(listname, listsize, m_itemdef);
891                         list->deSerialize(is);
892
893                         m_lists.push_back(list);
894                 }
895                 else
896                 {
897                         throw SerializationError("invalid inventory specifier: " + name);
898                 }
899         }
900
901         // Contents given to deSerialize() were not terminated properly: throw error.
902
903         std::ostringstream ss;
904         ss << "Malformatted inventory (damaged?). "
905                 << m_lists.size() << " lists read." << std::endl;
906         throw SerializationError(ss.str());
907 }
908
909 InventoryList * Inventory::addList(const std::string &name, u32 size)
910 {
911         m_dirty = true;
912         s32 i = getListIndex(name);
913         if(i != -1)
914         {
915                 if(m_lists[i]->getSize() != size)
916                 {
917                         delete m_lists[i];
918                         m_lists[i] = new InventoryList(name, size, m_itemdef);
919                 }
920                 return m_lists[i];
921         }
922
923
924         //don't create list with invalid name
925         if (name.find(' ') != std::string::npos) return NULL;
926
927         InventoryList *list = new InventoryList(name, size, m_itemdef);
928         m_lists.push_back(list);
929         return list;
930 }
931
932 InventoryList * Inventory::getList(const std::string &name)
933 {
934         s32 i = getListIndex(name);
935         if(i == -1)
936                 return NULL;
937         return m_lists[i];
938 }
939
940 std::vector<const InventoryList*> Inventory::getLists()
941 {
942         std::vector<const InventoryList*> lists;
943         for (auto list : m_lists) {
944                 lists.push_back(list);
945         }
946         return lists;
947 }
948
949 bool Inventory::deleteList(const std::string &name)
950 {
951         s32 i = getListIndex(name);
952         if(i == -1)
953                 return false;
954         m_dirty = true;
955         delete m_lists[i];
956         m_lists.erase(m_lists.begin() + i);
957         return true;
958 }
959
960 const InventoryList * Inventory::getList(const std::string &name) const
961 {
962         s32 i = getListIndex(name);
963         if(i == -1)
964                 return NULL;
965         return m_lists[i];
966 }
967
968 const s32 Inventory::getListIndex(const std::string &name) const
969 {
970         for(u32 i=0; i<m_lists.size(); i++)
971         {
972                 if(m_lists[i]->getName() == name)
973                         return i;
974         }
975         return -1;
976 }
977
978 //END