Fix mouse events sent to wrong GUI elements when dragging
[oweals/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 <algorithm>
24 #include <sstream>
25 #include "log.h"
26 #include "itemdef.h"
27 #include "util/strfnd.h"
28 #include "content_mapnode.h" // For loading legacy MaterialItems
29 #include "nameidmapping.h" // For loading legacy MaterialItems
30 #include "util/serialize.h"
31 #include "util/string.h"
32
33 /*
34         ItemStack
35 */
36
37 static content_t content_translate_from_19_to_internal(content_t c_from)
38 {
39         for (const auto &tt : trans_table_19) {
40                 if(tt[1] == c_from) {
41                         return tt[0];
42                 }
43         }
44         return c_from;
45 }
46
47 ItemStack::ItemStack(const std::string &name_, u16 count_,
48                 u16 wear_, IItemDefManager *itemdef) :
49         name(itemdef->getAlias(name_)),
50         count(count_),
51         wear(wear_)
52 {
53         if (name.empty() || count == 0)
54                 clear();
55         else if (itemdef->get(name).type == ITEM_TOOL)
56                 count = 1;
57 }
58
59 void ItemStack::serialize(std::ostream &os) const
60 {
61         if (empty())
62                 return;
63
64         // Check how many parts of the itemstring are needed
65         int parts = 1;
66         if(count != 1)
67                 parts = 2;
68         if(wear != 0)
69                 parts = 3;
70         if (!metadata.empty())
71                 parts = 4;
72
73         os<<serializeJsonStringIfNeeded(name);
74         if(parts >= 2)
75                 os<<" "<<count;
76         if(parts >= 3)
77                 os<<" "<<wear;
78         if (parts >= 4) {
79                 os << " ";
80                 metadata.serialize(os);
81         }
82 }
83
84 void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
85 {
86         clear();
87
88         // Read name
89         name = deSerializeJsonStringIfNeeded(is);
90
91         // Skip space
92         std::string tmp;
93         std::getline(is, tmp, ' ');
94         if(!tmp.empty())
95                 throw SerializationError("Unexpected text after item name");
96
97         if(name == "MaterialItem")
98         {
99                 // Obsoleted on 2011-07-30
100
101                 u16 material;
102                 is>>material;
103                 u16 materialcount;
104                 is>>materialcount;
105                 // Convert old materials
106                 if(material <= 0xff)
107                         material = content_translate_from_19_to_internal(material);
108                 if(material > 0xfff)
109                         throw SerializationError("Too large material number");
110                 // Convert old id to name
111                 NameIdMapping legacy_nimap;
112                 content_mapnode_get_name_id_mapping(&legacy_nimap);
113                 legacy_nimap.getName(material, name);
114                 if(name.empty())
115                         name = "unknown_block";
116                 if (itemdef)
117                         name = itemdef->getAlias(name);
118                 count = materialcount;
119         }
120         else if(name == "MaterialItem2")
121         {
122                 // Obsoleted on 2011-11-16
123
124                 u16 material;
125                 is>>material;
126                 u16 materialcount;
127                 is>>materialcount;
128                 if(material > 0xfff)
129                         throw SerializationError("Too large material number");
130                 // Convert old id to name
131                 NameIdMapping legacy_nimap;
132                 content_mapnode_get_name_id_mapping(&legacy_nimap);
133                 legacy_nimap.getName(material, name);
134                 if(name.empty())
135                         name = "unknown_block";
136                 if (itemdef)
137                         name = itemdef->getAlias(name);
138                 count = materialcount;
139         }
140         else if(name == "node" || name == "NodeItem" || name == "MaterialItem3"
141                         || name == "craft" || name == "CraftItem")
142         {
143                 // Obsoleted on 2012-01-07
144
145                 std::string all;
146                 std::getline(is, all, '\n');
147                 // First attempt to read inside ""
148                 Strfnd fnd(all);
149                 fnd.next("\"");
150                 // If didn't skip to end, we have ""s
151                 if(!fnd.at_end()){
152                         name = fnd.next("\"");
153                 } else { // No luck, just read a word then
154                         fnd.start(all);
155                         name = fnd.next(" ");
156                 }
157                 fnd.skip_over(" ");
158                 if (itemdef)
159                         name = itemdef->getAlias(name);
160                 count = stoi(trim(fnd.next("")));
161                 if(count == 0)
162                         count = 1;
163         }
164         else if(name == "MBOItem")
165         {
166                 // Obsoleted on 2011-10-14
167                 throw SerializationError("MBOItem not supported anymore");
168         }
169         else if(name == "tool" || name == "ToolItem")
170         {
171                 // Obsoleted on 2012-01-07
172
173                 std::string all;
174                 std::getline(is, all, '\n');
175                 // First attempt to read inside ""
176                 Strfnd fnd(all);
177                 fnd.next("\"");
178                 // If didn't skip to end, we have ""s
179                 if(!fnd.at_end()){
180                         name = fnd.next("\"");
181                 } else { // No luck, just read a word then
182                         fnd.start(all);
183                         name = fnd.next(" ");
184                 }
185                 count = 1;
186                 // Then read wear
187                 fnd.skip_over(" ");
188                 if (itemdef)
189                         name = itemdef->getAlias(name);
190                 wear = stoi(trim(fnd.next("")));
191         }
192         else
193         {
194                 do  // This loop is just to allow "break;"
195                 {
196                         // The real thing
197
198                         // Apply item aliases
199                         if (itemdef)
200                                 name = itemdef->getAlias(name);
201
202                         // Read the count
203                         std::string count_str;
204                         std::getline(is, count_str, ' ');
205                         if (count_str.empty()) {
206                                 count = 1;
207                                 break;
208                         }
209
210                         count = stoi(count_str);
211
212                         // Read the wear
213                         std::string wear_str;
214                         std::getline(is, wear_str, ' ');
215                         if(wear_str.empty())
216                                 break;
217
218                         wear = stoi(wear_str);
219
220                         // Read metadata
221                         metadata.deSerialize(is);
222
223                         // In case fields are added after metadata, skip space here:
224                         //std::getline(is, tmp, ' ');
225                         //if(!tmp.empty())
226                         //      throw SerializationError("Unexpected text after metadata");
227
228                 } while(false);
229         }
230
231         if (name.empty() || count == 0)
232                 clear();
233         else if (itemdef && itemdef->get(name).type == ITEM_TOOL)
234                 count = 1;
235 }
236
237 void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef)
238 {
239         std::istringstream is(str, std::ios::binary);
240         deSerialize(is, itemdef);
241 }
242
243 std::string ItemStack::getItemString() const
244 {
245         std::ostringstream os(std::ios::binary);
246         serialize(os);
247         return os.str();
248 }
249
250 std::string ItemStack::getDescription(IItemDefManager *itemdef) const
251 {
252         std::string desc = metadata.getString("description");
253         if (desc.empty())
254                 desc = getDefinition(itemdef).description;
255         return desc.empty() ? name : desc;
256 }
257
258
259 ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef)
260 {
261         // If the item is empty or the position invalid, bail out
262         if(newitem.empty())
263         {
264                 // nothing can be added trivially
265         }
266         // If this is an empty item, it's an easy job.
267         else if(empty())
268         {
269                 *this = newitem;
270                 newitem.clear();
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                 newitem.clear();
310         }
311         // If item name or metadata differs, bail out
312         else if (name != newitem.name
313                 || metadata != newitem.metadata)
314         {
315                 // cannot be added
316         }
317         // If the item fits fully, delete it
318         else if(newitem.count <= freeSpace(itemdef))
319         {
320                 newitem.clear();
321         }
322         // Else the item does not fit fully. Return the rest.
323         else
324         {
325                 u16 freespace = freeSpace(itemdef);
326                 newitem.remove(freespace);
327         }
328
329         if(restitem)
330                 *restitem = newitem;
331
332         return newitem.empty();
333 }
334
335 ItemStack ItemStack::takeItem(u32 takecount)
336 {
337         if(takecount == 0 || count == 0)
338                 return ItemStack();
339
340         ItemStack result = *this;
341         if(takecount >= count)
342         {
343                 // Take all
344                 clear();
345         }
346         else
347         {
348                 // Take part
349                 remove(takecount);
350                 result.count = takecount;
351         }
352         return result;
353 }
354
355 ItemStack ItemStack::peekItem(u32 peekcount) const
356 {
357         if(peekcount == 0 || count == 0)
358                 return ItemStack();
359
360         ItemStack result = *this;
361         if(peekcount < count)
362                 result.count = peekcount;
363         return result;
364 }
365
366 /*
367         Inventory
368 */
369
370 InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef):
371         m_name(name),
372         m_size(size),
373         m_itemdef(itemdef)
374 {
375         clearItems();
376 }
377
378 void InventoryList::clearItems()
379 {
380         m_items.clear();
381
382         for (u32 i=0; i < m_size; i++) {
383                 m_items.emplace_back();
384         }
385
386         setModified();
387 }
388
389 void InventoryList::setSize(u32 newsize)
390 {
391         if (newsize == m_items.size())
392                 return;
393
394         m_items.resize(newsize);
395         m_size = newsize;
396         setModified();
397 }
398
399 void InventoryList::setWidth(u32 newwidth)
400 {
401         m_width = newwidth;
402         setModified();
403 }
404
405 void InventoryList::setName(const std::string &name)
406 {
407         m_name = name;
408         setModified();
409 }
410
411 void InventoryList::serialize(std::ostream &os, bool incremental) const
412 {
413         //os.imbue(std::locale("C"));
414
415         os<<"Width "<<m_width<<"\n";
416
417         for (const auto &item : m_items) {
418                 if (item.empty()) {
419                         os<<"Empty";
420                 } else {
421                         os<<"Item ";
422                         item.serialize(os);
423                 }
424                 // TODO: Implement this:
425                 // if (!incremental || item.checkModified())
426                 // os << "Keep";
427                 os<<"\n";
428         }
429
430         os<<"EndInventoryList\n";
431 }
432
433 void InventoryList::deSerialize(std::istream &is)
434 {
435         //is.imbue(std::locale("C"));
436         setModified();
437
438         u32 item_i = 0;
439         m_width = 0;
440
441         while (is.good()) {
442                 std::string line;
443                 std::getline(is, line, '\n');
444
445                 std::istringstream iss(line);
446                 //iss.imbue(std::locale("C"));
447
448                 std::string name;
449                 std::getline(iss, name, ' ');
450
451                 if (name == "EndInventoryList" || name == "end") {
452                         // If partial incremental: Clear leftover items (should not happen!)
453                         for (size_t i = item_i; i < m_items.size(); ++i)
454                                 m_items[i].clear();
455                         return;
456                 }
457
458                 if (name == "Width") {
459                         iss >> m_width;
460                         if (iss.fail())
461                                 throw SerializationError("incorrect width property");
462                 }
463                 else if(name == "Item")
464                 {
465                         if(item_i > getSize() - 1)
466                                 throw SerializationError("too many items");
467                         ItemStack item;
468                         item.deSerialize(iss, m_itemdef);
469                         m_items[item_i++] = item;
470                 }
471                 else if(name == "Empty")
472                 {
473                         if(item_i > getSize() - 1)
474                                 throw SerializationError("too many items");
475                         m_items[item_i++].clear();
476                 } else if (name == "Keep") {
477                         ++item_i; // Unmodified item
478                 }
479         }
480
481         // Contents given to deSerialize() were not terminated properly: throw error.
482
483         std::ostringstream ss;
484         ss << "Malformatted inventory list. list="
485                 << m_name << ", read " << item_i << " of " << getSize()
486                 << " ItemStacks." << std::endl;
487         throw SerializationError(ss.str());
488 }
489
490 InventoryList::InventoryList(const InventoryList &other)
491 {
492         *this = other;
493 }
494
495 InventoryList & InventoryList::operator = (const InventoryList &other)
496 {
497         m_items = other.m_items;
498         m_size = other.m_size;
499         m_width = other.m_width;
500         m_name = other.m_name;
501         m_itemdef = other.m_itemdef;
502         //setDirty(true);
503
504         return *this;
505 }
506
507 bool InventoryList::operator == (const InventoryList &other) const
508 {
509         if(m_size != other.m_size)
510                 return false;
511         if(m_width != other.m_width)
512                 return false;
513         if(m_name != other.m_name)
514                 return false;
515         for (u32 i = 0; i < m_items.size(); i++)
516                 if (m_items[i] != other.m_items[i])
517                         return false;
518
519         return true;
520 }
521
522 const std::string &InventoryList::getName() const
523 {
524         return m_name;
525 }
526
527 u32 InventoryList::getSize() const
528 {
529         return m_items.size();
530 }
531
532 u32 InventoryList::getWidth() const
533 {
534         return m_width;
535 }
536
537 u32 InventoryList::getUsedSlots() const
538 {
539         u32 num = 0;
540         for (const auto &m_item : m_items) {
541                 if (!m_item.empty())
542                         num++;
543         }
544         return num;
545 }
546
547 u32 InventoryList::getFreeSlots() const
548 {
549         return getSize() - getUsedSlots();
550 }
551
552 const ItemStack& InventoryList::getItem(u32 i) const
553 {
554         assert(i < m_size); // Pre-condition
555         return m_items[i];
556 }
557
558 ItemStack& InventoryList::getItem(u32 i)
559 {
560         assert(i < m_size); // Pre-condition
561         return m_items[i];
562 }
563
564 ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
565 {
566         if(i >= m_items.size())
567                 return newitem;
568
569         ItemStack olditem = m_items[i];
570         m_items[i] = newitem;
571         setModified();
572         return olditem;
573 }
574
575 void InventoryList::deleteItem(u32 i)
576 {
577         assert(i < m_items.size()); // Pre-condition
578         m_items[i].clear();
579         setModified();
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                 setModified();
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                         ItemStack leftover = removed.addItem(i->takeItem(still_to_remove),
684                                         m_itemdef);
685                         // Allow oversized stacks
686                         removed.count += leftover.count;
687
688                         if (removed.count == item.count)
689                                 break;
690                 }
691         }
692         if (!removed.empty())
693                 setModified();
694         return removed;
695 }
696
697 ItemStack InventoryList::takeItem(u32 i, u32 takecount)
698 {
699         if(i >= m_items.size())
700                 return ItemStack();
701
702         ItemStack taken = m_items[i].takeItem(takecount);
703         if (!taken.empty())
704                 setModified();
705         return taken;
706 }
707
708 void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
709 {
710         // Take item from source list
711         ItemStack item1;
712         if (count == 0)
713                 item1 = changeItem(i, ItemStack());
714         else
715                 item1 = takeItem(i, count);
716
717         if (item1.empty())
718                 return;
719
720         ItemStack leftover;
721         leftover = dest->addItem(item1);
722
723         if (!leftover.empty()) {
724                 // Add the remaining part back to the source item
725                 addItem(i, leftover);
726         }
727 }
728
729 u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
730                 u32 count, bool swap_if_needed, bool *did_swap)
731 {
732         if(this == dest && i == dest_i)
733                 return count;
734
735         // Take item from source list
736         ItemStack item1;
737         if(count == 0)
738                 item1 = changeItem(i, ItemStack());
739         else
740                 item1 = takeItem(i, count);
741
742         if(item1.empty())
743                 return 0;
744
745         // Try to add the item to destination list
746         u32 oldcount = item1.count;
747         item1 = dest->addItem(dest_i, item1);
748
749         // If something is returned, the item was not fully added
750         if(!item1.empty())
751         {
752                 // If olditem is returned, nothing was added.
753                 bool nothing_added = (item1.count == oldcount);
754
755                 // If something else is returned, part of the item was left unadded.
756                 // Add the other part back to the source item
757                 addItem(i, item1);
758
759                 // If olditem is returned, nothing was added.
760                 // Swap the items
761                 if (nothing_added && swap_if_needed) {
762                         // Tell that we swapped
763                         if (did_swap != NULL) {
764                                 *did_swap = true;
765                         }
766                         // Take item from source list
767                         item1 = changeItem(i, ItemStack());
768                         // Adding was not possible, swap the items.
769                         ItemStack item2 = dest->changeItem(dest_i, item1);
770                         // Put item from destination list to the source list
771                         changeItem(i, item2);
772                 }
773         }
774         return (oldcount - item1.count);
775 }
776
777 /*
778         Inventory
779 */
780
781 Inventory::~Inventory()
782 {
783         clear();
784 }
785
786 void Inventory::clear()
787 {
788         for (auto &m_list : m_lists) {
789                 delete m_list;
790         }
791         m_lists.clear();
792         setModified();
793 }
794
795 Inventory::Inventory(IItemDefManager *itemdef)
796 {
797         m_itemdef = itemdef;
798         setModified();
799 }
800
801 Inventory::Inventory(const Inventory &other)
802 {
803         *this = other;
804 }
805
806 Inventory & Inventory::operator = (const Inventory &other)
807 {
808         // Gracefully handle self assignment
809         if(this != &other)
810         {
811                 clear();
812                 m_itemdef = other.m_itemdef;
813                 for (InventoryList *list : other.m_lists) {
814                         m_lists.push_back(new InventoryList(*list));
815                 }
816                 setModified();
817         }
818         return *this;
819 }
820
821 bool Inventory::operator == (const Inventory &other) const
822 {
823         if(m_lists.size() != other.m_lists.size())
824                 return false;
825
826         for(u32 i=0; i<m_lists.size(); i++)
827         {
828                 if(*m_lists[i] != *other.m_lists[i])
829                         return false;
830         }
831         return true;
832 }
833
834 void Inventory::serialize(std::ostream &os, bool incremental) const
835 {
836         //std::cout << "Serialize " << (int)incremental << ", n=" << m_lists.size() << std::endl;
837         for (const InventoryList *list : m_lists) {
838                 if (!incremental || list->checkModified()) {
839                         os << "List " << list->getName() << " " << list->getSize() << "\n";
840                         list->serialize(os, incremental);
841                 } else {
842                         os << "KeepList " << list->getName() << "\n";
843                 }
844         }
845
846         os<<"EndInventory\n";
847 }
848
849 void Inventory::deSerialize(std::istream &is)
850 {
851         std::vector<InventoryList *> new_lists;
852         new_lists.reserve(m_lists.size());
853
854         while (is.good()) {
855                 std::string line;
856                 std::getline(is, line, '\n');
857
858                 std::istringstream iss(line);
859
860                 std::string name;
861                 std::getline(iss, name, ' ');
862
863                 if (name == "EndInventory" || name == "end") {
864                         // Remove all lists that were not sent
865                         for (auto &list : m_lists) {
866                                 if (std::find(new_lists.begin(), new_lists.end(), list) != new_lists.end())
867                                         continue;
868
869                                 delete list;
870                                 list = nullptr;
871                                 setModified();
872                         }
873                         m_lists.erase(std::remove(m_lists.begin(), m_lists.end(),
874                                         nullptr), m_lists.end());
875                         return;
876                 }
877
878                 if (name == "List") {
879                         std::string listname;
880                         u32 listsize;
881
882                         std::getline(iss, listname, ' ');
883                         iss>>listsize;
884
885                         InventoryList *list = getList(listname);
886                         bool create_new = !list;
887                         if (create_new)
888                                 list = new InventoryList(listname, listsize, m_itemdef);
889                         else
890                                 list->setSize(listsize);
891                         list->deSerialize(is);
892
893                         new_lists.push_back(list);
894                         if (create_new)
895                                 m_lists.push_back(list);
896
897                 } else if (name == "KeepList") {
898                         // Incrementally sent list
899                         std::string listname;
900                         std::getline(iss, listname, ' ');
901
902                         InventoryList *list = getList(listname);
903                         if (list) {
904                                 new_lists.push_back(list);
905                         } else {
906                                 errorstream << "Inventory::deSerialize(): Tried to keep list '" <<
907                                         listname << "' which is non-existent." << std::endl;
908                         }
909                 }
910                 // Any additional fields will throw errors when received by a client
911                 // older than PROTOCOL_VERSION 38
912         }
913
914         // Contents given to deSerialize() were not terminated properly: throw error.
915
916         std::ostringstream ss;
917         ss << "Malformatted inventory (damaged?). "
918                 << m_lists.size() << " lists read." << std::endl;
919         throw SerializationError(ss.str());
920 }
921
922 InventoryList * Inventory::addList(const std::string &name, u32 size)
923 {
924         setModified();
925         s32 i = getListIndex(name);
926         if(i != -1)
927         {
928                 if(m_lists[i]->getSize() != size)
929                 {
930                         delete m_lists[i];
931                         m_lists[i] = new InventoryList(name, size, m_itemdef);
932                         m_lists[i]->setModified();
933                 }
934                 return m_lists[i];
935         }
936
937
938         //don't create list with invalid name
939         if (name.find(' ') != std::string::npos)
940                 return nullptr;
941
942         InventoryList *list = new InventoryList(name, size, m_itemdef);
943         list->setModified();
944         m_lists.push_back(list);
945         return list;
946 }
947
948 InventoryList * Inventory::getList(const std::string &name)
949 {
950         s32 i = getListIndex(name);
951         if(i == -1)
952                 return NULL;
953         return m_lists[i];
954 }
955
956 std::vector<const InventoryList*> Inventory::getLists()
957 {
958         std::vector<const InventoryList*> lists;
959         for (auto list : m_lists) {
960                 lists.push_back(list);
961         }
962         return lists;
963 }
964
965 bool Inventory::deleteList(const std::string &name)
966 {
967         s32 i = getListIndex(name);
968         if(i == -1)
969                 return false;
970
971         setModified();
972         delete m_lists[i];
973         m_lists.erase(m_lists.begin() + i);
974         return true;
975 }
976
977 const InventoryList * Inventory::getList(const std::string &name) const
978 {
979         s32 i = getListIndex(name);
980         if(i == -1)
981                 return NULL;
982         return m_lists[i];
983 }
984
985 const s32 Inventory::getListIndex(const std::string &name) const
986 {
987         for(u32 i=0; i<m_lists.size(); i++)
988         {
989                 if(m_lists[i]->getName() == name)
990                         return i;
991         }
992         return -1;
993 }
994
995 //END