]> git.lizzy.rs Git - dragonfireclient.git/blob - src/inventory.cpp
Add padding[] element to formspecs (#11821)
[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
464                 std::string name;
465                 std::getline(iss, name, ' ');
466
467                 if (name == "EndInventoryList" || name == "end") {
468                         // If partial incremental: Clear leftover items (should not happen!)
469                         for (size_t i = item_i; i < m_items.size(); ++i)
470                                 m_items[i].clear();
471                         return;
472                 }
473
474                 if (name == "Width") {
475                         iss >> m_width;
476                         if (iss.fail())
477                                 throw SerializationError("incorrect width property");
478                 }
479                 else if(name == "Item")
480                 {
481                         if(item_i > getSize() - 1)
482                                 throw SerializationError("too many items");
483                         ItemStack item;
484                         item.deSerialize(iss, m_itemdef);
485                         m_items[item_i++] = item;
486                 }
487                 else if(name == "Empty")
488                 {
489                         if(item_i > getSize() - 1)
490                                 throw SerializationError("too many items");
491                         m_items[item_i++].clear();
492                 } else if (name == "Keep") {
493                         ++item_i; // Unmodified item
494                 }
495         }
496
497         // Contents given to deSerialize() were not terminated properly: throw error.
498
499         std::ostringstream ss;
500         ss << "Malformatted inventory list. list="
501                 << m_name << ", read " << item_i << " of " << getSize()
502                 << " ItemStacks." << std::endl;
503         throw SerializationError(ss.str());
504 }
505
506 InventoryList::InventoryList(const InventoryList &other)
507 {
508         *this = other;
509 }
510
511 InventoryList & InventoryList::operator = (const InventoryList &other)
512 {
513         m_items = other.m_items;
514         m_size = other.m_size;
515         m_width = other.m_width;
516         m_name = other.m_name;
517         m_itemdef = other.m_itemdef;
518         //setDirty(true);
519
520         return *this;
521 }
522
523 bool InventoryList::operator == (const InventoryList &other) const
524 {
525         if(m_size != other.m_size)
526                 return false;
527         if(m_width != other.m_width)
528                 return false;
529         if(m_name != other.m_name)
530                 return false;
531         for (u32 i = 0; i < m_items.size(); i++)
532                 if (m_items[i] != other.m_items[i])
533                         return false;
534
535         return true;
536 }
537
538 const std::string &InventoryList::getName() const
539 {
540         return m_name;
541 }
542
543 u32 InventoryList::getSize() const
544 {
545         return m_items.size();
546 }
547
548 u32 InventoryList::getWidth() const
549 {
550         return m_width;
551 }
552
553 u32 InventoryList::getUsedSlots() const
554 {
555         u32 num = 0;
556         for (const auto &m_item : m_items) {
557                 if (!m_item.empty())
558                         num++;
559         }
560         return num;
561 }
562
563 const ItemStack& InventoryList::getItem(u32 i) const
564 {
565         assert(i < m_size); // Pre-condition
566         return m_items[i];
567 }
568
569 ItemStack& InventoryList::getItem(u32 i)
570 {
571         assert(i < m_size); // Pre-condition
572         return m_items[i];
573 }
574
575 ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
576 {
577         if(i >= m_items.size())
578                 return newitem;
579
580         ItemStack olditem = m_items[i];
581         m_items[i] = newitem;
582         setModified();
583         return olditem;
584 }
585
586 void InventoryList::deleteItem(u32 i)
587 {
588         assert(i < m_items.size()); // Pre-condition
589         m_items[i].clear();
590         setModified();
591 }
592
593 ItemStack InventoryList::addItem(const ItemStack &newitem_)
594 {
595         ItemStack newitem = newitem_;
596
597         if(newitem.empty())
598                 return newitem;
599
600         /*
601                 First try to find if it could be added to some existing items
602         */
603         for(u32 i=0; i<m_items.size(); i++)
604         {
605                 // Ignore empty slots
606                 if(m_items[i].empty())
607                         continue;
608                 // Try adding
609                 newitem = addItem(i, newitem);
610                 if(newitem.empty())
611                         return newitem; // All was eaten
612         }
613
614         /*
615                 Then try to add it to empty slots
616         */
617         for(u32 i=0; i<m_items.size(); i++)
618         {
619                 // Ignore unempty slots
620                 if(!m_items[i].empty())
621                         continue;
622                 // Try adding
623                 newitem = addItem(i, newitem);
624                 if(newitem.empty())
625                         return newitem; // All was eaten
626         }
627
628         // Return leftover
629         return newitem;
630 }
631
632 ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
633 {
634         if(i >= m_items.size())
635                 return newitem;
636
637         ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
638         if (leftover != newitem)
639                 setModified();
640         return leftover;
641 }
642
643 bool InventoryList::itemFits(const u32 i, const ItemStack &newitem,
644                 ItemStack *restitem) const
645 {
646         if(i >= m_items.size())
647         {
648                 if(restitem)
649                         *restitem = newitem;
650                 return false;
651         }
652
653         return m_items[i].itemFits(newitem, restitem, m_itemdef);
654 }
655
656 bool InventoryList::roomForItem(const ItemStack &item_) const
657 {
658         ItemStack item = item_;
659         ItemStack leftover;
660         for(u32 i=0; i<m_items.size(); i++)
661         {
662                 if(itemFits(i, item, &leftover))
663                         return true;
664                 item = leftover;
665         }
666         return false;
667 }
668
669 bool InventoryList::containsItem(const ItemStack &item, bool match_meta) const
670 {
671         u32 count = item.count;
672         if (count == 0)
673                 return true;
674
675         for (auto i = m_items.rbegin(); i != m_items.rend(); ++i) {
676                 if (count == 0)
677                         break;
678                 if (i->name == item.name && (!match_meta || (i->metadata == item.metadata))) {
679                         if (i->count >= count)
680                                 return true;
681
682                         count -= i->count;
683                 }
684         }
685         return false;
686 }
687
688 ItemStack InventoryList::removeItem(const ItemStack &item)
689 {
690         ItemStack removed;
691         for (auto i = m_items.rbegin(); i != m_items.rend(); ++i) {
692                 if (i->name == item.name) {
693                         u32 still_to_remove = item.count - removed.count;
694                         ItemStack leftover = removed.addItem(i->takeItem(still_to_remove),
695                                         m_itemdef);
696                         // Allow oversized stacks
697                         removed.count += leftover.count;
698
699                         if (removed.count == item.count)
700                                 break;
701                 }
702         }
703         if (!removed.empty())
704                 setModified();
705         return removed;
706 }
707
708 ItemStack InventoryList::takeItem(u32 i, u32 takecount)
709 {
710         if(i >= m_items.size())
711                 return ItemStack();
712
713         ItemStack taken = m_items[i].takeItem(takecount);
714         if (!taken.empty())
715                 setModified();
716         return taken;
717 }
718
719 void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
720 {
721         // Take item from source list
722         ItemStack item1;
723         if (count == 0)
724                 item1 = changeItem(i, ItemStack());
725         else
726                 item1 = takeItem(i, count);
727
728         if (item1.empty())
729                 return;
730
731         ItemStack leftover;
732         leftover = dest->addItem(item1);
733
734         if (!leftover.empty()) {
735                 // Add the remaining part back to the source item
736                 addItem(i, leftover);
737         }
738 }
739
740 u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
741                 u32 count, bool swap_if_needed, bool *did_swap)
742 {
743         if (this == dest && i == dest_i)
744                 return count;
745
746         // Take item from source list
747         ItemStack item1;
748         if (count == 0)
749                 item1 = changeItem(i, ItemStack());
750         else
751                 item1 = takeItem(i, count);
752
753         if (item1.empty())
754                 return 0;
755
756         // Try to add the item to destination list
757         u32 oldcount = item1.count;
758         item1 = dest->addItem(dest_i, item1);
759
760         // If something is returned, the item was not fully added
761         if (!item1.empty()) {
762                 // If olditem is returned, nothing was added.
763                 bool nothing_added = (item1.count == oldcount);
764
765                 // If something else is returned, part of the item was left unadded.
766                 // Add the other part back to the source item
767                 addItem(i, item1);
768
769                 // If olditem is returned, nothing was added.
770                 // Swap the items
771                 if (nothing_added && swap_if_needed) {
772                         // Tell that we swapped
773                         if (did_swap != NULL) {
774                                 *did_swap = true;
775                         }
776                         // Take item from source list
777                         item1 = changeItem(i, ItemStack());
778                         // Adding was not possible, swap the items.
779                         ItemStack item2 = dest->changeItem(dest_i, item1);
780                         // Put item from destination list to the source list
781                         changeItem(i, item2);
782                 }
783         }
784         return (oldcount - item1.count);
785 }
786
787 /*
788         Inventory
789 */
790
791 Inventory::~Inventory()
792 {
793         clear();
794 }
795
796 void Inventory::clear()
797 {
798         for (auto &m_list : m_lists) {
799                 delete m_list;
800         }
801         m_lists.clear();
802         setModified();
803 }
804
805 Inventory::Inventory(IItemDefManager *itemdef)
806 {
807         m_itemdef = itemdef;
808         setModified();
809 }
810
811 Inventory::Inventory(const Inventory &other)
812 {
813         *this = other;
814 }
815
816 Inventory & Inventory::operator = (const Inventory &other)
817 {
818         // Gracefully handle self assignment
819         if(this != &other)
820         {
821                 clear();
822                 m_itemdef = other.m_itemdef;
823                 for (InventoryList *list : other.m_lists) {
824                         m_lists.push_back(new InventoryList(*list));
825                 }
826                 setModified();
827         }
828         return *this;
829 }
830
831 bool Inventory::operator == (const Inventory &other) const
832 {
833         if(m_lists.size() != other.m_lists.size())
834                 return false;
835
836         for(u32 i=0; i<m_lists.size(); i++)
837         {
838                 if(*m_lists[i] != *other.m_lists[i])
839                         return false;
840         }
841         return true;
842 }
843
844 void Inventory::serialize(std::ostream &os, bool incremental) const
845 {
846         //std::cout << "Serialize " << (int)incremental << ", n=" << m_lists.size() << std::endl;
847         for (const InventoryList *list : m_lists) {
848                 if (!incremental || list->checkModified()) {
849                         os << "List " << list->getName() << " " << list->getSize() << "\n";
850                         list->serialize(os, incremental);
851                 } else {
852                         os << "KeepList " << list->getName() << "\n";
853                 }
854         }
855
856         os<<"EndInventory\n";
857 }
858
859 void Inventory::deSerialize(std::istream &is)
860 {
861         std::vector<InventoryList *> new_lists;
862         new_lists.reserve(m_lists.size());
863
864         while (is.good()) {
865                 std::string line;
866                 std::getline(is, line, '\n');
867
868                 std::istringstream iss(line);
869
870                 std::string name;
871                 std::getline(iss, name, ' ');
872
873                 if (name == "EndInventory" || name == "end") {
874                         // Remove all lists that were not sent
875                         for (auto &list : m_lists) {
876                                 if (std::find(new_lists.begin(), new_lists.end(), list) != new_lists.end())
877                                         continue;
878
879                                 delete list;
880                                 list = nullptr;
881                                 setModified();
882                         }
883                         m_lists.erase(std::remove(m_lists.begin(), m_lists.end(),
884                                         nullptr), m_lists.end());
885                         return;
886                 }
887
888                 if (name == "List") {
889                         std::string listname;
890                         u32 listsize;
891
892                         std::getline(iss, listname, ' ');
893                         iss>>listsize;
894
895                         InventoryList *list = getList(listname);
896                         bool create_new = !list;
897                         if (create_new)
898                                 list = new InventoryList(listname, listsize, m_itemdef);
899                         else
900                                 list->setSize(listsize);
901                         list->deSerialize(is);
902
903                         new_lists.push_back(list);
904                         if (create_new)
905                                 m_lists.push_back(list);
906
907                 } else if (name == "KeepList") {
908                         // Incrementally sent list
909                         std::string listname;
910                         std::getline(iss, listname, ' ');
911
912                         InventoryList *list = getList(listname);
913                         if (list) {
914                                 new_lists.push_back(list);
915                         } else {
916                                 errorstream << "Inventory::deSerialize(): Tried to keep list '" <<
917                                         listname << "' which is non-existent." << std::endl;
918                         }
919                 }
920                 // Any additional fields will throw errors when received by a client
921                 // older than PROTOCOL_VERSION 38
922         }
923
924         // Contents given to deSerialize() were not terminated properly: throw error.
925
926         std::ostringstream ss;
927         ss << "Malformatted inventory (damaged?). "
928                 << m_lists.size() << " lists read." << std::endl;
929         throw SerializationError(ss.str());
930 }
931
932 InventoryList * Inventory::addList(const std::string &name, u32 size)
933 {
934         setModified();
935
936         // Remove existing lists
937         s32 i = getListIndex(name);
938         if (i != -1) {
939                 delete m_lists[i];
940                 m_lists[i] = new InventoryList(name, size, m_itemdef);
941                 m_lists[i]->setModified();
942                 return m_lists[i];
943         }
944
945         //don't create list with invalid name
946         if (name.find(' ') != std::string::npos)
947                 return nullptr;
948
949         InventoryList *list = new InventoryList(name, size, m_itemdef);
950         list->setModified();
951         m_lists.push_back(list);
952         return list;
953 }
954
955 InventoryList * Inventory::getList(const std::string &name)
956 {
957         s32 i = getListIndex(name);
958         if(i == -1)
959                 return nullptr;
960         return m_lists[i];
961 }
962
963 std::vector<const InventoryList*> Inventory::getLists()
964 {
965         std::vector<const InventoryList*> lists;
966         lists.reserve(m_lists.size());
967         for (auto list : m_lists) {
968                 lists.push_back(list);
969         }
970         return lists;
971 }
972
973 bool Inventory::deleteList(const std::string &name)
974 {
975         s32 i = getListIndex(name);
976         if(i == -1)
977                 return false;
978
979         setModified();
980         delete m_lists[i];
981         m_lists.erase(m_lists.begin() + i);
982         return true;
983 }
984
985 const InventoryList *Inventory::getList(const std::string &name) const
986 {
987         s32 i = getListIndex(name);
988         if(i == -1)
989                 return nullptr;
990         return m_lists[i];
991 }
992
993 s32 Inventory::getListIndex(const std::string &name) const
994 {
995         for(u32 i=0; i<m_lists.size(); i++)
996         {
997                 if(m_lists[i]->getName() == name)
998                         return i;
999         }
1000         return -1;
1001 }
1002
1003 //END