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