]> git.lizzy.rs Git - dragonfireclient.git/blob - src/inventory.cpp
Merge branch 'master' of https://github.com/EliasFleckenstein03/dragonfireclient
[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 std::string ItemStack::getShortDescription(IItemDefManager *itemdef) const
262 {
263         std::string desc = metadata.getString("short_description");
264         if (desc.empty())
265                 desc = getDefinition(itemdef).short_description;
266         if (!desc.empty())
267                 return desc;
268         // no short_description because of old server version or modified builtin
269         // return first line of description
270         std::stringstream sstr(getDescription(itemdef));
271         std::getline(sstr, desc, '\n');
272         return desc;
273 }
274
275
276 ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef)
277 {
278         // If the item is empty or the position invalid, bail out
279         if(newitem.empty())
280         {
281                 // nothing can be added trivially
282         }
283         // If this is an empty item, it's an easy job.
284         else if(empty())
285         {
286                 *this = newitem;
287                 newitem.clear();
288         }
289         // If item name or metadata differs, bail out
290         else if (name != newitem.name
291                 || metadata != newitem.metadata)
292         {
293                 // cannot be added
294         }
295         // If the item fits fully, add counter and delete it
296         else if(newitem.count <= freeSpace(itemdef))
297         {
298                 add(newitem.count);
299                 newitem.clear();
300         }
301         // Else the item does not fit fully. Add all that fits and return
302         // the rest.
303         else
304         {
305                 u16 freespace = freeSpace(itemdef);
306                 add(freespace);
307                 newitem.remove(freespace);
308         }
309
310         return newitem;
311 }
312
313 bool ItemStack::itemFits(ItemStack newitem,
314                 ItemStack *restitem,
315                 IItemDefManager *itemdef) const
316 {
317
318         // If the item is empty or the position invalid, bail out
319         if(newitem.empty())
320         {
321                 // nothing can be added trivially
322         }
323         // If this is an empty item, it's an easy job.
324         else if(empty())
325         {
326                 newitem.clear();
327         }
328         // If item name or metadata differs, bail out
329         else if (name != newitem.name
330                 || metadata != newitem.metadata)
331         {
332                 // cannot be added
333         }
334         // If the item fits fully, delete it
335         else if(newitem.count <= freeSpace(itemdef))
336         {
337                 newitem.clear();
338         }
339         // Else the item does not fit fully. Return the rest.
340         else
341         {
342                 u16 freespace = freeSpace(itemdef);
343                 newitem.remove(freespace);
344         }
345
346         if(restitem)
347                 *restitem = newitem;
348
349         return newitem.empty();
350 }
351
352 ItemStack ItemStack::takeItem(u32 takecount)
353 {
354         if(takecount == 0 || count == 0)
355                 return ItemStack();
356
357         ItemStack result = *this;
358         if(takecount >= count)
359         {
360                 // Take all
361                 clear();
362         }
363         else
364         {
365                 // Take part
366                 remove(takecount);
367                 result.count = takecount;
368         }
369         return result;
370 }
371
372 ItemStack ItemStack::peekItem(u32 peekcount) const
373 {
374         if(peekcount == 0 || count == 0)
375                 return ItemStack();
376
377         ItemStack result = *this;
378         if(peekcount < count)
379                 result.count = peekcount;
380         return result;
381 }
382
383 /*
384         Inventory
385 */
386
387 InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef):
388         m_name(name),
389         m_size(size),
390         m_itemdef(itemdef)
391 {
392         clearItems();
393 }
394
395 void InventoryList::clearItems()
396 {
397         m_items.clear();
398
399         for (u32 i=0; i < m_size; i++) {
400                 m_items.emplace_back();
401         }
402
403         setModified();
404 }
405
406 void InventoryList::setSize(u32 newsize)
407 {
408         if (newsize == m_items.size())
409                 return;
410
411         m_items.resize(newsize);
412         m_size = newsize;
413         setModified();
414 }
415
416 void InventoryList::setWidth(u32 newwidth)
417 {
418         m_width = newwidth;
419         setModified();
420 }
421
422 void InventoryList::setName(const std::string &name)
423 {
424         m_name = name;
425         setModified();
426 }
427
428 void InventoryList::serialize(std::ostream &os, bool incremental) const
429 {
430         //os.imbue(std::locale("C"));
431
432         os<<"Width "<<m_width<<"\n";
433
434         for (const auto &item : m_items) {
435                 if (item.empty()) {
436                         os<<"Empty";
437                 } else {
438                         os<<"Item ";
439                         item.serialize(os);
440                 }
441                 // TODO: Implement this:
442                 // if (!incremental || item.checkModified())
443                 // os << "Keep";
444                 os<<"\n";
445         }
446
447         os<<"EndInventoryList\n";
448 }
449
450 void InventoryList::deSerialize(std::istream &is)
451 {
452         //is.imbue(std::locale("C"));
453         setModified();
454
455         u32 item_i = 0;
456         m_width = 0;
457
458         while (is.good()) {
459                 std::string line;
460                 std::getline(is, line, '\n');
461
462                 std::istringstream iss(line);
463                 //iss.imbue(std::locale("C"));
464
465                 std::string name;
466                 std::getline(iss, name, ' ');
467
468                 if (name == "EndInventoryList" || name == "end") {
469                         // If partial incremental: Clear leftover items (should not happen!)
470                         for (size_t i = item_i; i < m_items.size(); ++i)
471                                 m_items[i].clear();
472                         return;
473                 }
474
475                 if (name == "Width") {
476                         iss >> m_width;
477                         if (iss.fail())
478                                 throw SerializationError("incorrect width property");
479                 }
480                 else if(name == "Item")
481                 {
482                         if(item_i > getSize() - 1)
483                                 throw SerializationError("too many items");
484                         ItemStack item;
485                         item.deSerialize(iss, m_itemdef);
486                         m_items[item_i++] = item;
487                 }
488                 else if(name == "Empty")
489                 {
490                         if(item_i > getSize() - 1)
491                                 throw SerializationError("too many items");
492                         m_items[item_i++].clear();
493                 } else if (name == "Keep") {
494                         ++item_i; // Unmodified item
495                 }
496         }
497
498         // Contents given to deSerialize() were not terminated properly: throw error.
499
500         std::ostringstream ss;
501         ss << "Malformatted inventory list. list="
502                 << m_name << ", read " << item_i << " of " << getSize()
503                 << " ItemStacks." << std::endl;
504         throw SerializationError(ss.str());
505 }
506
507 InventoryList::InventoryList(const InventoryList &other)
508 {
509         *this = other;
510 }
511
512 InventoryList & InventoryList::operator = (const InventoryList &other)
513 {
514         m_items = other.m_items;
515         m_size = other.m_size;
516         m_width = other.m_width;
517         m_name = other.m_name;
518         m_itemdef = other.m_itemdef;
519         //setDirty(true);
520
521         return *this;
522 }
523
524 bool InventoryList::operator == (const InventoryList &other) const
525 {
526         if(m_size != other.m_size)
527                 return false;
528         if(m_width != other.m_width)
529                 return false;
530         if(m_name != other.m_name)
531                 return false;
532         for (u32 i = 0; i < m_items.size(); i++)
533                 if (m_items[i] != other.m_items[i])
534                         return false;
535
536         return true;
537 }
538
539 const std::string &InventoryList::getName() const
540 {
541         return m_name;
542 }
543
544 u32 InventoryList::getSize() const
545 {
546         return m_items.size();
547 }
548
549 u32 InventoryList::getWidth() const
550 {
551         return m_width;
552 }
553
554 u32 InventoryList::getUsedSlots() const
555 {
556         u32 num = 0;
557         for (const auto &m_item : m_items) {
558                 if (!m_item.empty())
559                         num++;
560         }
561         return num;
562 }
563
564 u32 InventoryList::getFreeSlots() const
565 {
566         return getSize() - getUsedSlots();
567 }
568
569 const ItemStack& InventoryList::getItem(u32 i) const
570 {
571         assert(i < m_size); // Pre-condition
572         return m_items[i];
573 }
574
575 ItemStack& InventoryList::getItem(u32 i)
576 {
577         assert(i < m_size); // Pre-condition
578         return m_items[i];
579 }
580
581 ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
582 {
583         if(i >= m_items.size())
584                 return newitem;
585
586         ItemStack olditem = m_items[i];
587         m_items[i] = newitem;
588         setModified();
589         return olditem;
590 }
591
592 void InventoryList::deleteItem(u32 i)
593 {
594         assert(i < m_items.size()); // Pre-condition
595         m_items[i].clear();
596         setModified();
597 }
598
599 ItemStack InventoryList::addItem(const ItemStack &newitem_)
600 {
601         ItemStack newitem = newitem_;
602
603         if(newitem.empty())
604                 return newitem;
605
606         /*
607                 First try to find if it could be added to some existing items
608         */
609         for(u32 i=0; i<m_items.size(); i++)
610         {
611                 // Ignore empty 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         /*
621                 Then try to add it to empty slots
622         */
623         for(u32 i=0; i<m_items.size(); i++)
624         {
625                 // Ignore unempty slots
626                 if(!m_items[i].empty())
627                         continue;
628                 // Try adding
629                 newitem = addItem(i, newitem);
630                 if(newitem.empty())
631                         return newitem; // All was eaten
632         }
633
634         // Return leftover
635         return newitem;
636 }
637
638 ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
639 {
640         if(i >= m_items.size())
641                 return newitem;
642
643         ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
644         if (leftover != newitem)
645                 setModified();
646         return leftover;
647 }
648
649 bool InventoryList::itemFits(const u32 i, const ItemStack &newitem,
650                 ItemStack *restitem) const
651 {
652         if(i >= m_items.size())
653         {
654                 if(restitem)
655                         *restitem = newitem;
656                 return false;
657         }
658
659         return m_items[i].itemFits(newitem, restitem, m_itemdef);
660 }
661
662 bool InventoryList::roomForItem(const ItemStack &item_) const
663 {
664         ItemStack item = item_;
665         ItemStack leftover;
666         for(u32 i=0; i<m_items.size(); i++)
667         {
668                 if(itemFits(i, item, &leftover))
669                         return true;
670                 item = leftover;
671         }
672         return false;
673 }
674
675 bool InventoryList::containsItem(const ItemStack &item, bool match_meta) const
676 {
677         u32 count = item.count;
678         if (count == 0)
679                 return true;
680
681         for (auto i = m_items.rbegin(); i != m_items.rend(); ++i) {
682                 if (count == 0)
683                         break;
684                 if (i->name == item.name && (!match_meta || (i->metadata == item.metadata))) {
685                         if (i->count >= count)
686                                 return true;
687
688                         count -= i->count;
689                 }
690         }
691         return false;
692 }
693
694 ItemStack InventoryList::removeItem(const ItemStack &item)
695 {
696         ItemStack removed;
697         for (auto i = m_items.rbegin(); i != m_items.rend(); ++i) {
698                 if (i->name == item.name) {
699                         u32 still_to_remove = item.count - removed.count;
700                         ItemStack leftover = removed.addItem(i->takeItem(still_to_remove),
701                                         m_itemdef);
702                         // Allow oversized stacks
703                         removed.count += leftover.count;
704
705                         if (removed.count == item.count)
706                                 break;
707                 }
708         }
709         if (!removed.empty())
710                 setModified();
711         return removed;
712 }
713
714 ItemStack InventoryList::takeItem(u32 i, u32 takecount)
715 {
716         if(i >= m_items.size())
717                 return ItemStack();
718
719         ItemStack taken = m_items[i].takeItem(takecount);
720         if (!taken.empty())
721                 setModified();
722         return taken;
723 }
724
725 void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
726 {
727         // Take item from source list
728         ItemStack item1;
729         if (count == 0)
730                 item1 = changeItem(i, ItemStack());
731         else
732                 item1 = takeItem(i, count);
733
734         if (item1.empty())
735                 return;
736
737         ItemStack leftover;
738         leftover = dest->addItem(item1);
739
740         if (!leftover.empty()) {
741                 // Add the remaining part back to the source item
742                 addItem(i, leftover);
743         }
744 }
745
746 u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
747                 u32 count, bool swap_if_needed, bool *did_swap)
748 {
749         if (this == dest && i == dest_i)
750                 return count;
751
752         // Take item from source list
753         ItemStack item1;
754         if (count == 0)
755                 item1 = changeItem(i, ItemStack());
756         else
757                 item1 = takeItem(i, count);
758
759         if (item1.empty())
760                 return 0;
761
762         // Try to add the item to destination list
763         u32 oldcount = item1.count;
764         item1 = dest->addItem(dest_i, item1);
765
766         // If something is returned, the item was not fully added
767         if (!item1.empty()) {
768                 // If olditem is returned, nothing was added.
769                 bool nothing_added = (item1.count == oldcount);
770
771                 // If something else is returned, part of the item was left unadded.
772                 // Add the other part back to the source item
773                 addItem(i, item1);
774
775                 // If olditem is returned, nothing was added.
776                 // Swap the items
777                 if (nothing_added && swap_if_needed) {
778                         // Tell that we swapped
779                         if (did_swap != NULL) {
780                                 *did_swap = true;
781                         }
782                         // Take item from source list
783                         item1 = changeItem(i, ItemStack());
784                         // Adding was not possible, swap the items.
785                         ItemStack item2 = dest->changeItem(dest_i, item1);
786                         // Put item from destination list to the source list
787                         changeItem(i, item2);
788                 }
789         }
790         return (oldcount - item1.count);
791 }
792
793 /*
794         Inventory
795 */
796
797 Inventory::~Inventory()
798 {
799         clear();
800 }
801
802 void Inventory::clear()
803 {
804         for (auto &m_list : m_lists) {
805                 delete m_list;
806         }
807         m_lists.clear();
808         setModified();
809 }
810
811 Inventory::Inventory(IItemDefManager *itemdef)
812 {
813         m_itemdef = itemdef;
814         setModified();
815 }
816
817 Inventory::Inventory(const Inventory &other)
818 {
819         *this = other;
820 }
821
822 Inventory & Inventory::operator = (const Inventory &other)
823 {
824         // Gracefully handle self assignment
825         if(this != &other)
826         {
827                 clear();
828                 m_itemdef = other.m_itemdef;
829                 for (InventoryList *list : other.m_lists) {
830                         m_lists.push_back(new InventoryList(*list));
831                 }
832                 setModified();
833         }
834         return *this;
835 }
836
837 bool Inventory::operator == (const Inventory &other) const
838 {
839         if(m_lists.size() != other.m_lists.size())
840                 return false;
841
842         for(u32 i=0; i<m_lists.size(); i++)
843         {
844                 if(*m_lists[i] != *other.m_lists[i])
845                         return false;
846         }
847         return true;
848 }
849
850 void Inventory::serialize(std::ostream &os, bool incremental) const
851 {
852         //std::cout << "Serialize " << (int)incremental << ", n=" << m_lists.size() << std::endl;
853         for (const InventoryList *list : m_lists) {
854                 if (!incremental || list->checkModified()) {
855                         os << "List " << list->getName() << " " << list->getSize() << "\n";
856                         list->serialize(os, incremental);
857                 } else {
858                         os << "KeepList " << list->getName() << "\n";
859                 }
860         }
861
862         os<<"EndInventory\n";
863 }
864
865 void Inventory::deSerialize(std::istream &is)
866 {
867         std::vector<InventoryList *> new_lists;
868         new_lists.reserve(m_lists.size());
869
870         while (is.good()) {
871                 std::string line;
872                 std::getline(is, line, '\n');
873
874                 std::istringstream iss(line);
875
876                 std::string name;
877                 std::getline(iss, name, ' ');
878
879                 if (name == "EndInventory" || name == "end") {
880                         // Remove all lists that were not sent
881                         for (auto &list : m_lists) {
882                                 if (std::find(new_lists.begin(), new_lists.end(), list) != new_lists.end())
883                                         continue;
884
885                                 delete list;
886                                 list = nullptr;
887                                 setModified();
888                         }
889                         m_lists.erase(std::remove(m_lists.begin(), m_lists.end(),
890                                         nullptr), m_lists.end());
891                         return;
892                 }
893
894                 if (name == "List") {
895                         std::string listname;
896                         u32 listsize;
897
898                         std::getline(iss, listname, ' ');
899                         iss>>listsize;
900
901                         InventoryList *list = getList(listname);
902                         bool create_new = !list;
903                         if (create_new)
904                                 list = new InventoryList(listname, listsize, m_itemdef);
905                         else
906                                 list->setSize(listsize);
907                         list->deSerialize(is);
908
909                         new_lists.push_back(list);
910                         if (create_new)
911                                 m_lists.push_back(list);
912
913                 } else if (name == "KeepList") {
914                         // Incrementally sent list
915                         std::string listname;
916                         std::getline(iss, listname, ' ');
917
918                         InventoryList *list = getList(listname);
919                         if (list) {
920                                 new_lists.push_back(list);
921                         } else {
922                                 errorstream << "Inventory::deSerialize(): Tried to keep list '" <<
923                                         listname << "' which is non-existent." << std::endl;
924                         }
925                 }
926                 // Any additional fields will throw errors when received by a client
927                 // older than PROTOCOL_VERSION 38
928         }
929
930         // Contents given to deSerialize() were not terminated properly: throw error.
931
932         std::ostringstream ss;
933         ss << "Malformatted inventory (damaged?). "
934                 << m_lists.size() << " lists read." << std::endl;
935         throw SerializationError(ss.str());
936 }
937
938 InventoryList * Inventory::addList(const std::string &name, u32 size)
939 {
940         setModified();
941         s32 i = getListIndex(name);
942         if(i != -1)
943         {
944                 if(m_lists[i]->getSize() != size)
945                 {
946                         delete m_lists[i];
947                         m_lists[i] = new InventoryList(name, size, m_itemdef);
948                         m_lists[i]->setModified();
949                 }
950                 return m_lists[i];
951         }
952
953
954         //don't create list with invalid name
955         if (name.find(' ') != std::string::npos)
956                 return nullptr;
957
958         InventoryList *list = new InventoryList(name, size, m_itemdef);
959         list->setModified();
960         m_lists.push_back(list);
961         return list;
962 }
963
964 InventoryList * Inventory::getList(const std::string &name)
965 {
966         s32 i = getListIndex(name);
967         if(i == -1)
968                 return NULL;
969         return m_lists[i];
970 }
971
972 std::vector<const InventoryList*> Inventory::getLists()
973 {
974         std::vector<const InventoryList*> lists;
975         for (auto list : m_lists) {
976                 lists.push_back(list);
977         }
978         return lists;
979 }
980
981 bool Inventory::deleteList(const std::string &name)
982 {
983         s32 i = getListIndex(name);
984         if(i == -1)
985                 return false;
986
987         setModified();
988         delete m_lists[i];
989         m_lists.erase(m_lists.begin() + i);
990         return true;
991 }
992
993 const InventoryList * Inventory::getList(const std::string &name) const
994 {
995         s32 i = getListIndex(name);
996         if(i == -1)
997                 return NULL;
998         return m_lists[i];
999 }
1000
1001 const s32 Inventory::getListIndex(const std::string &name) const
1002 {
1003         for(u32 i=0; i<m_lists.size(); i++)
1004         {
1005                 if(m_lists[i]->getName() == name)
1006                         return i;
1007         }
1008         return -1;
1009 }
1010
1011 //END