Redefine NodeResolver interface and replace with callback mechanism
[oweals/minetest.git] / src / nodedef.cpp
1 /*
2 Minetest
3 Copyright (C) 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 "nodedef.h"
21
22 #include "main.h" // For g_settings
23 #include "itemdef.h"
24 #ifndef SERVER
25 #include "tile.h"
26 #include "mesh.h"
27 #include <IMeshManipulator.h>
28 #endif
29 #include "log.h"
30 #include "settings.h"
31 #include "nameidmapping.h"
32 #include "util/numeric.h"
33 #include "util/serialize.h"
34 #include "exceptions.h"
35 #include "debug.h"
36 #include "gamedef.h"
37
38 /*
39         NodeBox
40 */
41
42 void NodeBox::reset()
43 {
44         type = NODEBOX_REGULAR;
45         // default is empty
46         fixed.clear();
47         // default is sign/ladder-like
48         wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
49         wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
50         wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
51 }
52
53 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
54 {
55         int version = protocol_version >= 21 ? 2 : 1;
56         writeU8(os, version);
57
58         if (version == 1 && type == NODEBOX_LEVELED)
59                 writeU8(os, NODEBOX_FIXED);
60         else
61                 writeU8(os, type);
62
63         if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
64         {
65                 writeU16(os, fixed.size());
66                 for(std::vector<aabb3f>::const_iterator
67                                 i = fixed.begin();
68                                 i != fixed.end(); i++)
69                 {
70                         writeV3F1000(os, i->MinEdge);
71                         writeV3F1000(os, i->MaxEdge);
72                 }
73         }
74         else if(type == NODEBOX_WALLMOUNTED)
75         {
76                 writeV3F1000(os, wall_top.MinEdge);
77                 writeV3F1000(os, wall_top.MaxEdge);
78                 writeV3F1000(os, wall_bottom.MinEdge);
79                 writeV3F1000(os, wall_bottom.MaxEdge);
80                 writeV3F1000(os, wall_side.MinEdge);
81                 writeV3F1000(os, wall_side.MaxEdge);
82         }
83 }
84
85 void NodeBox::deSerialize(std::istream &is)
86 {
87         int version = readU8(is);
88         if(version < 1 || version > 2)
89                 throw SerializationError("unsupported NodeBox version");
90
91         reset();
92
93         type = (enum NodeBoxType)readU8(is);
94
95         if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
96         {
97                 u16 fixed_count = readU16(is);
98                 while(fixed_count--)
99                 {
100                         aabb3f box;
101                         box.MinEdge = readV3F1000(is);
102                         box.MaxEdge = readV3F1000(is);
103                         fixed.push_back(box);
104                 }
105         }
106         else if(type == NODEBOX_WALLMOUNTED)
107         {
108                 wall_top.MinEdge = readV3F1000(is);
109                 wall_top.MaxEdge = readV3F1000(is);
110                 wall_bottom.MinEdge = readV3F1000(is);
111                 wall_bottom.MaxEdge = readV3F1000(is);
112                 wall_side.MinEdge = readV3F1000(is);
113                 wall_side.MaxEdge = readV3F1000(is);
114         }
115 }
116
117 /*
118         TileDef
119 */
120
121 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
122 {
123         if(protocol_version >= 17)
124                 writeU8(os, 1);
125         else
126                 writeU8(os, 0);
127         os<<serializeString(name);
128         writeU8(os, animation.type);
129         writeU16(os, animation.aspect_w);
130         writeU16(os, animation.aspect_h);
131         writeF1000(os, animation.length);
132         if(protocol_version >= 17)
133                 writeU8(os, backface_culling);
134 }
135
136 void TileDef::deSerialize(std::istream &is)
137 {
138         int version = readU8(is);
139         name = deSerializeString(is);
140         animation.type = (TileAnimationType)readU8(is);
141         animation.aspect_w = readU16(is);
142         animation.aspect_h = readU16(is);
143         animation.length = readF1000(is);
144         if(version >= 1)
145                 backface_culling = readU8(is);
146 }
147
148 /*
149         SimpleSoundSpec serialization
150 */
151
152 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
153                 std::ostream &os)
154 {
155         os<<serializeString(ss.name);
156         writeF1000(os, ss.gain);
157 }
158 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
159 {
160         ss.name = deSerializeString(is);
161         ss.gain = readF1000(is);
162 }
163
164 /*
165         ContentFeatures
166 */
167
168 ContentFeatures::ContentFeatures()
169 {
170         reset();
171 }
172
173 ContentFeatures::~ContentFeatures()
174 {
175 }
176
177 void ContentFeatures::reset()
178 {
179         /*
180                 Cached stuff
181         */
182 #ifndef SERVER
183         solidness = 2;
184         visual_solidness = 0;
185         backface_culling = true;
186 #endif
187         has_on_construct = false;
188         has_on_destruct = false;
189         has_after_destruct = false;
190         /*
191                 Actual data
192
193                 NOTE: Most of this is always overridden by the default values given
194                       in builtin.lua
195         */
196         name = "";
197         groups.clear();
198         // Unknown nodes can be dug
199         groups["dig_immediate"] = 2;
200         drawtype = NDT_NORMAL;
201         mesh = "";
202 #ifndef SERVER
203         for(u32 i = 0; i < 24; i++)
204                 mesh_ptr[i] = NULL;
205 #endif
206         visual_scale = 1.0;
207         for(u32 i = 0; i < 6; i++)
208                 tiledef[i] = TileDef();
209         for(u16 j = 0; j < CF_SPECIAL_COUNT; j++)
210                 tiledef_special[j] = TileDef();
211         alpha = 255;
212         post_effect_color = video::SColor(0, 0, 0, 0);
213         param_type = CPT_NONE;
214         param_type_2 = CPT2_NONE;
215         is_ground_content = false;
216         light_propagates = false;
217         sunlight_propagates = false;
218         walkable = true;
219         pointable = true;
220         diggable = true;
221         climbable = false;
222         buildable_to = false;
223         rightclickable = true;
224         leveled = 0;
225         liquid_type = LIQUID_NONE;
226         liquid_alternative_flowing = "";
227         liquid_alternative_source = "";
228         liquid_viscosity = 0;
229         liquid_renewable = true;
230         freezemelt = "";
231         liquid_range = LIQUID_LEVEL_MAX+1;
232         drowning = 0;
233         light_source = 0;
234         damage_per_second = 0;
235         node_box = NodeBox();
236         selection_box = NodeBox();
237         collision_box = NodeBox();
238         waving = 0;
239         legacy_facedir_simple = false;
240         legacy_wallmounted = false;
241         sound_footstep = SimpleSoundSpec();
242         sound_dig = SimpleSoundSpec("__group");
243         sound_dug = SimpleSoundSpec();
244 }
245
246 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
247 {
248         if(protocol_version < 24){
249                 serializeOld(os, protocol_version);
250                 return;
251         }
252
253         writeU8(os, 7); // version
254         os<<serializeString(name);
255         writeU16(os, groups.size());
256         for(ItemGroupList::const_iterator
257                         i = groups.begin(); i != groups.end(); i++){
258                 os<<serializeString(i->first);
259                 writeS16(os, i->second);
260         }
261         writeU8(os, drawtype);
262         writeF1000(os, visual_scale);
263         writeU8(os, 6);
264         for(u32 i = 0; i < 6; i++)
265                 tiledef[i].serialize(os, protocol_version);
266         writeU8(os, CF_SPECIAL_COUNT);
267         for(u32 i = 0; i < CF_SPECIAL_COUNT; i++){
268                 tiledef_special[i].serialize(os, protocol_version);
269         }
270         writeU8(os, alpha);
271         writeU8(os, post_effect_color.getAlpha());
272         writeU8(os, post_effect_color.getRed());
273         writeU8(os, post_effect_color.getGreen());
274         writeU8(os, post_effect_color.getBlue());
275         writeU8(os, param_type);
276         writeU8(os, param_type_2);
277         writeU8(os, is_ground_content);
278         writeU8(os, light_propagates);
279         writeU8(os, sunlight_propagates);
280         writeU8(os, walkable);
281         writeU8(os, pointable);
282         writeU8(os, diggable);
283         writeU8(os, climbable);
284         writeU8(os, buildable_to);
285         os<<serializeString(""); // legacy: used to be metadata_name
286         writeU8(os, liquid_type);
287         os<<serializeString(liquid_alternative_flowing);
288         os<<serializeString(liquid_alternative_source);
289         writeU8(os, liquid_viscosity);
290         writeU8(os, liquid_renewable);
291         writeU8(os, light_source);
292         writeU32(os, damage_per_second);
293         node_box.serialize(os, protocol_version);
294         selection_box.serialize(os, protocol_version);
295         writeU8(os, legacy_facedir_simple);
296         writeU8(os, legacy_wallmounted);
297         serializeSimpleSoundSpec(sound_footstep, os);
298         serializeSimpleSoundSpec(sound_dig, os);
299         serializeSimpleSoundSpec(sound_dug, os);
300         writeU8(os, rightclickable);
301         writeU8(os, drowning);
302         writeU8(os, leveled);
303         writeU8(os, liquid_range);
304         writeU8(os, waving);
305         // Stuff below should be moved to correct place in a version that otherwise changes
306         // the protocol version
307         os<<serializeString(mesh);
308         collision_box.serialize(os, protocol_version);
309 }
310
311 void ContentFeatures::deSerialize(std::istream &is)
312 {
313         int version = readU8(is);
314         if(version != 7){
315                 deSerializeOld(is, version);
316                 return;
317         }
318
319         name = deSerializeString(is);
320         groups.clear();
321         u32 groups_size = readU16(is);
322         for(u32 i = 0; i < groups_size; i++){
323                 std::string name = deSerializeString(is);
324                 int value = readS16(is);
325                 groups[name] = value;
326         }
327         drawtype = (enum NodeDrawType)readU8(is);
328         visual_scale = readF1000(is);
329         if(readU8(is) != 6)
330                 throw SerializationError("unsupported tile count");
331         for(u32 i = 0; i < 6; i++)
332                 tiledef[i].deSerialize(is);
333         if(readU8(is) != CF_SPECIAL_COUNT)
334                 throw SerializationError("unsupported CF_SPECIAL_COUNT");
335         for(u32 i = 0; i < CF_SPECIAL_COUNT; i++)
336                 tiledef_special[i].deSerialize(is);
337         alpha = readU8(is);
338         post_effect_color.setAlpha(readU8(is));
339         post_effect_color.setRed(readU8(is));
340         post_effect_color.setGreen(readU8(is));
341         post_effect_color.setBlue(readU8(is));
342         param_type = (enum ContentParamType)readU8(is);
343         param_type_2 = (enum ContentParamType2)readU8(is);
344         is_ground_content = readU8(is);
345         light_propagates = readU8(is);
346         sunlight_propagates = readU8(is);
347         walkable = readU8(is);
348         pointable = readU8(is);
349         diggable = readU8(is);
350         climbable = readU8(is);
351         buildable_to = readU8(is);
352         deSerializeString(is); // legacy: used to be metadata_name
353         liquid_type = (enum LiquidType)readU8(is);
354         liquid_alternative_flowing = deSerializeString(is);
355         liquid_alternative_source = deSerializeString(is);
356         liquid_viscosity = readU8(is);
357         liquid_renewable = readU8(is);
358         light_source = readU8(is);
359         damage_per_second = readU32(is);
360         node_box.deSerialize(is);
361         selection_box.deSerialize(is);
362         legacy_facedir_simple = readU8(is);
363         legacy_wallmounted = readU8(is);
364         deSerializeSimpleSoundSpec(sound_footstep, is);
365         deSerializeSimpleSoundSpec(sound_dig, is);
366         deSerializeSimpleSoundSpec(sound_dug, is);
367         rightclickable = readU8(is);
368         drowning = readU8(is);
369         leveled = readU8(is);
370         liquid_range = readU8(is);
371         waving = readU8(is);
372         // If you add anything here, insert it primarily inside the try-catch
373         // block to not need to increase the version.
374         try{
375                 // Stuff below should be moved to correct place in a version that
376                 // otherwise changes the protocol version
377         mesh = deSerializeString(is);
378         collision_box.deSerialize(is);
379         }catch(SerializationError &e) {};
380 }
381
382 /*
383         CNodeDefManager
384 */
385
386 class CNodeDefManager: public IWritableNodeDefManager {
387 public:
388         CNodeDefManager();
389         virtual ~CNodeDefManager();
390         void clear();
391         virtual IWritableNodeDefManager *clone();
392         inline virtual const ContentFeatures& get(content_t c) const;
393         inline virtual const ContentFeatures& get(const MapNode &n) const;
394         virtual bool getId(const std::string &name, content_t &result) const;
395         virtual content_t getId(const std::string &name) const;
396         virtual void getIds(const std::string &name, std::set<content_t> &result) const;
397         virtual const ContentFeatures& get(const std::string &name) const;
398         content_t allocateId();
399         virtual content_t set(const std::string &name, const ContentFeatures &def);
400         virtual content_t allocateDummy(const std::string &name);
401         virtual void updateAliases(IItemDefManager *idef);
402         virtual void updateTextures(IGameDef *gamedef);
403         void serialize(std::ostream &os, u16 protocol_version);
404         void deSerialize(std::istream &is);
405
406         virtual void pendNodeResolve(NodeResolveInfo *nri);
407         virtual void cancelNodeResolve(NodeResolver *resolver);
408         virtual void runNodeResolverCallbacks();
409
410         virtual bool getIdFromResolveInfo(NodeResolveInfo *nri,
411                 const std::string &node_alt, content_t c_fallback, content_t &result);
412         virtual bool getIdsFromResolveInfo(NodeResolveInfo *nri,
413                 std::vector<content_t> &result);
414
415 private:
416         void addNameIdMapping(content_t i, std::string name);
417 #ifndef SERVER
418         void fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef,
419                 u32 shader_id, bool use_normal_texture, bool backface_culling,
420                 u8 alpha, u8 material_type);
421 #endif
422
423         // Features indexed by id
424         std::vector<ContentFeatures> m_content_features;
425
426         // A mapping for fast converting back and forth between names and ids
427         NameIdMapping m_name_id_mapping;
428
429         // Like m_name_id_mapping, but only from names to ids, and includes
430         // item aliases too. Updated by updateAliases()
431         // Note: Not serialized.
432
433         std::map<std::string, content_t> m_name_id_mapping_with_aliases;
434
435         // A mapping from groups to a list of content_ts (and their levels)
436         // that belong to it.  Necessary for a direct lookup in getIds().
437         // Note: Not serialized.
438         std::map<std::string, GroupItems> m_group_to_items;
439
440         // Next possibly free id
441         content_t m_next_id;
442
443         // List of node strings and node resolver callbacks to perform
444         std::list<NodeResolveInfo *> m_pending_node_lookups;
445 };
446
447
448 CNodeDefManager::CNodeDefManager()
449 {
450         clear();
451 }
452
453
454 CNodeDefManager::~CNodeDefManager()
455 {
456 #ifndef SERVER
457         for (u32 i = 0; i < m_content_features.size(); i++) {
458                 ContentFeatures *f = &m_content_features[i];
459                 for (u32 j = 0; j < 24; j++) {
460                         if (f->mesh_ptr[j])
461                                 f->mesh_ptr[j]->drop();
462                 }
463         }
464 #endif
465 }
466
467
468 void CNodeDefManager::clear()
469 {
470         m_content_features.clear();
471         m_name_id_mapping.clear();
472         m_name_id_mapping_with_aliases.clear();
473         m_group_to_items.clear();
474         m_next_id = 0;
475
476         u32 initial_length = 0;
477         initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
478         initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
479         initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
480         m_content_features.resize(initial_length);
481
482         // Set CONTENT_UNKNOWN
483         {
484                 ContentFeatures f;
485                 f.name = "unknown";
486                 // Insert directly into containers
487                 content_t c = CONTENT_UNKNOWN;
488                 m_content_features[c] = f;
489                 addNameIdMapping(c, f.name);
490         }
491
492         // Set CONTENT_AIR
493         {
494                 ContentFeatures f;
495                 f.name                = "air";
496                 f.drawtype            = NDT_AIRLIKE;
497                 f.param_type          = CPT_LIGHT;
498                 f.light_propagates    = true;
499                 f.sunlight_propagates = true;
500                 f.walkable            = false;
501                 f.pointable           = false;
502                 f.diggable            = false;
503                 f.buildable_to        = true;
504                 f.is_ground_content   = true;
505                 // Insert directly into containers
506                 content_t c = CONTENT_AIR;
507                 m_content_features[c] = f;
508                 addNameIdMapping(c, f.name);
509         }
510
511         // Set CONTENT_IGNORE
512         {
513                 ContentFeatures f;
514                 f.name                = "ignore";
515                 f.drawtype            = NDT_AIRLIKE;
516                 f.param_type          = CPT_NONE;
517                 f.light_propagates    = false;
518                 f.sunlight_propagates = false;
519                 f.walkable            = false;
520                 f.pointable           = false;
521                 f.diggable            = false;
522                 f.buildable_to        = true; // A way to remove accidental CONTENT_IGNOREs
523                 f.is_ground_content   = true;
524                 // Insert directly into containers
525                 content_t c = CONTENT_IGNORE;
526                 m_content_features[c] = f;
527                 addNameIdMapping(c, f.name);
528         }
529 }
530
531
532 IWritableNodeDefManager *CNodeDefManager::clone()
533 {
534         CNodeDefManager *mgr = new CNodeDefManager();
535         *mgr = *this;
536         return mgr;
537 }
538
539
540 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
541 {
542         return c < m_content_features.size()
543                         ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
544 }
545
546
547 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
548 {
549         return get(n.getContent());
550 }
551
552
553 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
554 {
555         std::map<std::string, content_t>::const_iterator
556                 i = m_name_id_mapping_with_aliases.find(name);
557         if(i == m_name_id_mapping_with_aliases.end())
558                 return false;
559         result = i->second;
560         return true;
561 }
562
563
564 content_t CNodeDefManager::getId(const std::string &name) const
565 {
566         content_t id = CONTENT_IGNORE;
567         getId(name, id);
568         return id;
569 }
570
571
572 void CNodeDefManager::getIds(const std::string &name,
573                 std::set<content_t> &result) const
574 {
575         //TimeTaker t("getIds", NULL, PRECISION_MICRO);
576         if (name.substr(0,6) != "group:") {
577                 content_t id = CONTENT_IGNORE;
578                 if(getId(name, id))
579                         result.insert(id);
580                 return;
581         }
582         std::string group = name.substr(6);
583
584         std::map<std::string, GroupItems>::const_iterator
585                 i = m_group_to_items.find(group);
586         if (i == m_group_to_items.end())
587                 return;
588
589         const GroupItems &items = i->second;
590         for (GroupItems::const_iterator j = items.begin();
591                 j != items.end(); ++j) {
592                 if ((*j).second != 0)
593                         result.insert((*j).first);
594         }
595         //printf("getIds: %dus\n", t.stop());
596 }
597
598
599 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
600 {
601         content_t id = CONTENT_UNKNOWN;
602         getId(name, id);
603         return get(id);
604 }
605
606
607 // returns CONTENT_IGNORE if no free ID found
608 content_t CNodeDefManager::allocateId()
609 {
610         for (content_t id = m_next_id;
611                         id >= m_next_id; // overflow?
612                         ++id) {
613                 while (id >= m_content_features.size()) {
614                         m_content_features.push_back(ContentFeatures());
615                 }
616                 const ContentFeatures &f = m_content_features[id];
617                 if (f.name == "") {
618                         m_next_id = id + 1;
619                         return id;
620                 }
621         }
622         // If we arrive here, an overflow occurred in id.
623         // That means no ID was found
624         return CONTENT_IGNORE;
625 }
626
627
628 // IWritableNodeDefManager
629 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
630 {
631         assert(name != "");
632         assert(name == def.name);
633
634         // Don't allow redefining ignore (but allow air and unknown)
635         if (name == "ignore") {
636                 infostream << "NodeDefManager: WARNING: Ignoring "
637                         "CONTENT_IGNORE redefinition"<<std::endl;
638                 return CONTENT_IGNORE;
639         }
640
641         content_t id = CONTENT_IGNORE;
642         if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
643                 // Get new id
644                 id = allocateId();
645                 if (id == CONTENT_IGNORE) {
646                         infostream << "NodeDefManager: WARNING: Absolute "
647                                 "limit reached" << std::endl;
648                         return CONTENT_IGNORE;
649                 }
650                 assert(id != CONTENT_IGNORE);
651                 addNameIdMapping(id, name);
652         }
653         m_content_features[id] = def;
654         verbosestream << "NodeDefManager: registering content id \"" << id
655                 << "\": name=\"" << def.name << "\""<<std::endl;
656
657         // Add this content to the list of all groups it belongs to
658         // FIXME: This should remove a node from groups it no longer
659         // belongs to when a node is re-registered
660         for (ItemGroupList::const_iterator i = def.groups.begin();
661                 i != def.groups.end(); ++i) {
662                 std::string group_name = i->first;
663
664                 std::map<std::string, GroupItems>::iterator
665                         j = m_group_to_items.find(group_name);
666                 if (j == m_group_to_items.end()) {
667                         m_group_to_items[group_name].push_back(
668                                         std::make_pair(id, i->second));
669                 } else {
670                         GroupItems &items = j->second;
671                         items.push_back(std::make_pair(id, i->second));
672                 }
673         }
674         return id;
675 }
676
677
678 content_t CNodeDefManager::allocateDummy(const std::string &name)
679 {
680         assert(name != "");
681         ContentFeatures f;
682         f.name = name;
683         return set(name, f);
684 }
685
686
687 void CNodeDefManager::updateAliases(IItemDefManager *idef)
688 {
689         std::set<std::string> all = idef->getAll();
690         m_name_id_mapping_with_aliases.clear();
691         for (std::set<std::string>::iterator
692                         i = all.begin(); i != all.end(); i++) {
693                 std::string name = *i;
694                 std::string convert_to = idef->getAlias(name);
695                 content_t id;
696                 if (m_name_id_mapping.getId(convert_to, id)) {
697                         m_name_id_mapping_with_aliases.insert(
698                                         std::make_pair(name, id));
699                 }
700         }
701 }
702
703
704 void CNodeDefManager::updateTextures(IGameDef *gamedef)
705 {
706 #ifndef SERVER
707         infostream << "CNodeDefManager::updateTextures(): Updating "
708                 "textures in node definitions" << std::endl;
709
710         ITextureSource *tsrc = gamedef->tsrc();
711         IShaderSource *shdsrc = gamedef->getShaderSource();
712         scene::ISceneManager* smgr = gamedef->getSceneManager();
713         scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
714
715         bool new_style_water           = g_settings->getBool("new_style_water");
716         bool new_style_leaves          = g_settings->getBool("new_style_leaves");
717         bool connected_glass           = g_settings->getBool("connected_glass");
718         bool opaque_water              = g_settings->getBool("opaque_water");
719         bool enable_shaders            = g_settings->getBool("enable_shaders");
720         bool enable_bumpmapping        = g_settings->getBool("enable_bumpmapping");
721         bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
722         bool enable_mesh_cache         = g_settings->getBool("enable_mesh_cache");
723
724         bool use_normal_texture = enable_shaders &&
725                 (enable_bumpmapping || enable_parallax_occlusion);
726
727         for (u32 i = 0; i < m_content_features.size(); i++) {
728                 ContentFeatures *f = &m_content_features[i];
729
730                 // Figure out the actual tiles to use
731                 TileDef tiledef[6];
732                 for (u32 j = 0; j < 6; j++) {
733                         tiledef[j] = f->tiledef[j];
734                         if (tiledef[j].name == "")
735                                 tiledef[j].name = "unknown_node.png";
736                 }
737
738                 bool is_liquid = false;
739                 bool is_water_surface = false;
740
741                 u8 material_type = (f->alpha == 255) ?
742                         TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
743
744                 switch (f->drawtype) {
745                 default:
746                 case NDT_NORMAL:
747                         f->solidness = 2;
748                         break;
749                 case NDT_AIRLIKE:
750                         f->solidness = 0;
751                         break;
752                 case NDT_LIQUID:
753                         assert(f->liquid_type == LIQUID_SOURCE);
754                         if (opaque_water)
755                                 f->alpha = 255;
756                         if (new_style_water){
757                                 f->solidness = 0;
758                         } else {
759                                 f->solidness = 1;
760                                 f->backface_culling = false;
761                         }
762                         is_liquid = true;
763                         break;
764                 case NDT_FLOWINGLIQUID:
765                         assert(f->liquid_type == LIQUID_FLOWING);
766                         f->solidness = 0;
767                         if (opaque_water)
768                                 f->alpha = 255;
769                         is_liquid = true;
770                         break;
771                 case NDT_GLASSLIKE:
772                         f->solidness = 0;
773                         f->visual_solidness = 1;
774                         break;
775                 case NDT_GLASSLIKE_FRAMED:
776                         f->solidness = 0;
777                         f->visual_solidness = 1;
778                         break;
779                 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
780                         f->solidness = 0;
781                         f->visual_solidness = 1;
782                         f->drawtype = connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
783                         break;
784                 case NDT_ALLFACES:
785                         f->solidness = 0;
786                         f->visual_solidness = 1;
787                         break;
788                 case NDT_ALLFACES_OPTIONAL:
789                         if (new_style_leaves) {
790                                 f->drawtype = NDT_ALLFACES;
791                                 f->solidness = 0;
792                                 f->visual_solidness = 1;
793                         } else {
794                                 f->drawtype = NDT_NORMAL;
795                                 f->solidness = 2;
796                                 for (u32 i = 0; i < 6; i++)
797                                         tiledef[i].name += std::string("^[noalpha");
798                         }
799                         if (f->waving == 1)
800                                 material_type = TILE_MATERIAL_WAVING_LEAVES;
801                         break;
802                 case NDT_PLANTLIKE:
803                         f->solidness = 0;
804                         f->backface_culling = false;
805                         if (f->waving == 1)
806                                 material_type = TILE_MATERIAL_WAVING_PLANTS;
807                         break;
808                 case NDT_FIRELIKE:
809                         f->backface_culling = false;
810                         f->solidness = 0;
811                         break;
812                 case NDT_MESH:
813                         f->solidness = 0;
814                         f->backface_culling = false;
815                         break;
816                 case NDT_TORCHLIKE:
817                 case NDT_SIGNLIKE:
818                 case NDT_FENCELIKE:
819                 case NDT_RAILLIKE:
820                 case NDT_NODEBOX:
821                         f->solidness = 0;
822                         break;
823                 }
824
825                 if (is_liquid) {
826                         material_type = (f->alpha == 255) ?
827                                 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
828                         if (f->name == "default:water_source")
829                                 is_water_surface = true;
830                 }
831
832                 u32 tile_shader[6];
833                 for (u16 j = 0; j < 6; j++) {
834                         tile_shader[j] = shdsrc->getShader("nodes_shader",
835                                 material_type, f->drawtype);
836                 }
837
838                 if (is_water_surface) {
839                         tile_shader[0] = shdsrc->getShader("water_surface_shader",
840                                 material_type, f->drawtype);
841                 }
842
843                 // Tiles (fill in f->tiles[])
844                 for (u16 j = 0; j < 6; j++) {
845                         fillTileAttribs(tsrc, &f->tiles[j], &tiledef[j], tile_shader[j],
846                                 use_normal_texture, f->backface_culling, f->alpha, material_type);
847                 }
848
849                 // Special tiles (fill in f->special_tiles[])
850                 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
851                         fillTileAttribs(tsrc, &f->special_tiles[j], &f->tiledef_special[j],
852                                 tile_shader[j], use_normal_texture,
853                                 f->tiledef_special[j].backface_culling, f->alpha, material_type);
854                 }
855
856                 if ((f->drawtype == NDT_MESH) && (f->mesh != "")) {
857                         // Meshnode drawtype
858                         // Read the mesh and apply scale
859                         f->mesh_ptr[0] = gamedef->getMesh(f->mesh);
860                         if (f->mesh_ptr[0]){
861                                 v3f scale = v3f(1.0, 1.0, 1.0) * BS * f->visual_scale;
862                                 scaleMesh(f->mesh_ptr[0], scale);
863                                 recalculateBoundingBox(f->mesh_ptr[0]);
864                                 meshmanip->recalculateNormals(f->mesh_ptr[0], true, false);
865                         }
866                 } else if ((f->drawtype == NDT_NODEBOX) &&
867                                 ((f->node_box.type == NODEBOX_REGULAR) ||
868                                 (f->node_box.type == NODEBOX_FIXED)) &&
869                                 (!f->node_box.fixed.empty())) {
870                         //Convert regular nodebox nodes to meshnodes
871                         //Change the drawtype and apply scale
872                         f->drawtype = NDT_MESH;
873                         f->mesh_ptr[0] = convertNodeboxNodeToMesh(f);
874                         v3f scale = v3f(1.0, 1.0, 1.0) * f->visual_scale;
875                         scaleMesh(f->mesh_ptr[0], scale);
876                         recalculateBoundingBox(f->mesh_ptr[0]);
877                         meshmanip->recalculateNormals(f->mesh_ptr[0], true, false);
878                 }
879
880                 //Cache 6dfacedir and wallmounted rotated clones of meshes
881                 if (enable_mesh_cache && f->mesh_ptr[0] && (f->param_type_2 == CPT2_FACEDIR)) {
882                         for (u16 j = 1; j < 24; j++) {
883                                 f->mesh_ptr[j] = cloneMesh(f->mesh_ptr[0]);
884                                 rotateMeshBy6dFacedir(f->mesh_ptr[j], j);
885                                 recalculateBoundingBox(f->mesh_ptr[j]);
886                                 meshmanip->recalculateNormals(f->mesh_ptr[j], true, false);
887                         }
888                 } else if (enable_mesh_cache && f->mesh_ptr[0] && (f->param_type_2 == CPT2_WALLMOUNTED)) {
889                         static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
890                         for (u16 j = 1; j < 6; j++) {
891                                 f->mesh_ptr[j] = cloneMesh(f->mesh_ptr[0]);
892                                 rotateMeshBy6dFacedir(f->mesh_ptr[j], wm_to_6d[j]);
893                                 recalculateBoundingBox(f->mesh_ptr[j]);
894                                 meshmanip->recalculateNormals(f->mesh_ptr[j], true, false);
895                         }
896                         rotateMeshBy6dFacedir(f->mesh_ptr[0], wm_to_6d[0]);
897                         recalculateBoundingBox(f->mesh_ptr[0]);
898                         meshmanip->recalculateNormals(f->mesh_ptr[0], true, false);
899                 }
900         }
901 #endif
902 }
903
904
905 #ifndef SERVER
906 void CNodeDefManager::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
907                 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
908                 bool backface_culling, u8 alpha, u8 material_type)
909 {
910         tile->shader_id     = shader_id;
911         tile->texture       = tsrc->getTexture(tiledef->name, &tile->texture_id);
912         tile->alpha         = alpha;
913         tile->material_type = material_type;
914
915         // Normal texture
916         if (use_normal_texture)
917                 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
918
919         // Material flags
920         tile->material_flags = 0;
921         if (backface_culling)
922                 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
923         if (tiledef->animation.type == TAT_VERTICAL_FRAMES)
924                 tile->material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
925
926         // Animation parameters
927         int frame_count = 1;
928         if (tile->material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) {
929                 // Get texture size to determine frame count by aspect ratio
930                 v2u32 size = tile->texture->getOriginalSize();
931                 int frame_height = (float)size.X /
932                                 (float)tiledef->animation.aspect_w *
933                                 (float)tiledef->animation.aspect_h;
934                 frame_count = size.Y / frame_height;
935                 int frame_length_ms = 1000.0 * tiledef->animation.length / frame_count;
936                 tile->animation_frame_count = frame_count;
937                 tile->animation_frame_length_ms = frame_length_ms;
938         }
939
940         if (frame_count == 1) {
941                 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
942         } else {
943                 std::ostringstream os(std::ios::binary);
944                 tile->frames.resize(frame_count);
945
946                 for (int i = 0; i < frame_count; i++) {
947
948                         FrameSpec frame;
949
950                         os.str("");
951                         os << tiledef->name << "^[verticalframe:"
952                                 << frame_count << ":" << i;
953
954                         frame.texture = tsrc->getTexture(os.str(), &frame.texture_id);
955                         if (tile->normal_texture)
956                                 frame.normal_texture = tsrc->getNormalTexture(os.str());
957                         tile->frames[i] = frame;
958                 }
959         }
960 }
961 #endif
962
963
964 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version)
965 {
966         writeU8(os, 1); // version
967         u16 count = 0;
968         std::ostringstream os2(std::ios::binary);
969         for (u32 i = 0; i < m_content_features.size(); i++) {
970                 if (i == CONTENT_IGNORE || i == CONTENT_AIR
971                                 || i == CONTENT_UNKNOWN)
972                         continue;
973                 ContentFeatures *f = &m_content_features[i];
974                 if (f->name == "")
975                         continue;
976                 writeU16(os2, i);
977                 // Wrap it in a string to allow different lengths without
978                 // strict version incompatibilities
979                 std::ostringstream wrapper_os(std::ios::binary);
980                 f->serialize(wrapper_os, protocol_version);
981                 os2<<serializeString(wrapper_os.str());
982
983                 assert(count + 1 > count); // must not overflow
984                 count++;
985         }
986         writeU16(os, count);
987         os << serializeLongString(os2.str());
988 }
989
990
991 void CNodeDefManager::deSerialize(std::istream &is)
992 {
993         clear();
994         int version = readU8(is);
995         if (version != 1)
996                 throw SerializationError("unsupported NodeDefinitionManager version");
997         u16 count = readU16(is);
998         std::istringstream is2(deSerializeLongString(is), std::ios::binary);
999         ContentFeatures f;
1000         for (u16 n = 0; n < count; n++) {
1001                 u16 i = readU16(is2);
1002
1003                 // Read it from the string wrapper
1004                 std::string wrapper = deSerializeString(is2);
1005                 std::istringstream wrapper_is(wrapper, std::ios::binary);
1006                 f.deSerialize(wrapper_is);
1007
1008                 // Check error conditions
1009                 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1010                         infostream << "NodeDefManager::deSerialize(): WARNING: "
1011                                 "not changing builtin node " << i << std::endl;
1012                         continue;
1013                 }
1014                 if (f.name == "") {
1015                         infostream << "NodeDefManager::deSerialize(): WARNING: "
1016                                 "received empty name" << std::endl;
1017                         continue;
1018                 }
1019
1020                 // Ignore aliases
1021                 u16 existing_id;
1022                 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1023                         infostream << "NodeDefManager::deSerialize(): WARNING: "
1024                                 "already defined with different ID: " << f.name << std::endl;
1025                         continue;
1026                 }
1027
1028                 // All is ok, add node definition with the requested ID
1029                 if (i >= m_content_features.size())
1030                         m_content_features.resize((u32)(i) + 1);
1031                 m_content_features[i] = f;
1032                 addNameIdMapping(i, f.name);
1033                 verbosestream << "deserialized " << f.name << std::endl;
1034         }
1035 }
1036
1037
1038 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1039 {
1040         m_name_id_mapping.set(i, name);
1041         m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1042 }
1043
1044
1045 IWritableNodeDefManager *createNodeDefManager()
1046 {
1047         return new CNodeDefManager();
1048 }
1049
1050
1051 //// Serialization of old ContentFeatures formats
1052 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version)
1053 {
1054         if (protocol_version == 13)
1055         {
1056                 writeU8(os, 5); // version
1057                 os<<serializeString(name);
1058                 writeU16(os, groups.size());
1059                 for (ItemGroupList::const_iterator
1060                                 i = groups.begin(); i != groups.end(); i++) {
1061                         os<<serializeString(i->first);
1062                         writeS16(os, i->second);
1063                 }
1064                 writeU8(os, drawtype);
1065                 writeF1000(os, visual_scale);
1066                 writeU8(os, 6);
1067                 for (u32 i = 0; i < 6; i++)
1068                         tiledef[i].serialize(os, protocol_version);
1069                 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1070                 writeU8(os, 2);
1071                 for (u32 i = 0; i < 2; i++)
1072                         tiledef_special[i].serialize(os, protocol_version);
1073                 writeU8(os, alpha);
1074                 writeU8(os, post_effect_color.getAlpha());
1075                 writeU8(os, post_effect_color.getRed());
1076                 writeU8(os, post_effect_color.getGreen());
1077                 writeU8(os, post_effect_color.getBlue());
1078                 writeU8(os, param_type);
1079                 writeU8(os, param_type_2);
1080                 writeU8(os, is_ground_content);
1081                 writeU8(os, light_propagates);
1082                 writeU8(os, sunlight_propagates);
1083                 writeU8(os, walkable);
1084                 writeU8(os, pointable);
1085                 writeU8(os, diggable);
1086                 writeU8(os, climbable);
1087                 writeU8(os, buildable_to);
1088                 os<<serializeString(""); // legacy: used to be metadata_name
1089                 writeU8(os, liquid_type);
1090                 os<<serializeString(liquid_alternative_flowing);
1091                 os<<serializeString(liquid_alternative_source);
1092                 writeU8(os, liquid_viscosity);
1093                 writeU8(os, light_source);
1094                 writeU32(os, damage_per_second);
1095                 node_box.serialize(os, protocol_version);
1096                 selection_box.serialize(os, protocol_version);
1097                 writeU8(os, legacy_facedir_simple);
1098                 writeU8(os, legacy_wallmounted);
1099                 serializeSimpleSoundSpec(sound_footstep, os);
1100                 serializeSimpleSoundSpec(sound_dig, os);
1101                 serializeSimpleSoundSpec(sound_dug, os);
1102         }
1103         else if (protocol_version > 13 && protocol_version < 24) {
1104                 writeU8(os, 6); // version
1105                 os<<serializeString(name);
1106                 writeU16(os, groups.size());
1107                 for (ItemGroupList::const_iterator
1108                         i = groups.begin(); i != groups.end(); i++) {
1109                                 os<<serializeString(i->first);
1110                                 writeS16(os, i->second);
1111                 }
1112                 writeU8(os, drawtype);
1113                 writeF1000(os, visual_scale);
1114                 writeU8(os, 6);
1115                 for (u32 i = 0; i < 6; i++)
1116                         tiledef[i].serialize(os, protocol_version);
1117                 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1118                 writeU8(os, 2);
1119                 for (u32 i = 0; i < 2; i++)
1120                         tiledef_special[i].serialize(os, protocol_version);
1121                 writeU8(os, alpha);
1122                 writeU8(os, post_effect_color.getAlpha());
1123                 writeU8(os, post_effect_color.getRed());
1124                 writeU8(os, post_effect_color.getGreen());
1125                 writeU8(os, post_effect_color.getBlue());
1126                 writeU8(os, param_type);
1127                 writeU8(os, param_type_2);
1128                 writeU8(os, is_ground_content);
1129                 writeU8(os, light_propagates);
1130                 writeU8(os, sunlight_propagates);
1131                 writeU8(os, walkable);
1132                 writeU8(os, pointable);
1133                 writeU8(os, diggable);
1134                 writeU8(os, climbable);
1135                 writeU8(os, buildable_to);
1136                 os<<serializeString(""); // legacy: used to be metadata_name
1137                 writeU8(os, liquid_type);
1138                 os<<serializeString(liquid_alternative_flowing);
1139                 os<<serializeString(liquid_alternative_source);
1140                 writeU8(os, liquid_viscosity);
1141                 writeU8(os, liquid_renewable);
1142                 writeU8(os, light_source);
1143                 writeU32(os, damage_per_second);
1144                 node_box.serialize(os, protocol_version);
1145                 selection_box.serialize(os, protocol_version);
1146                 writeU8(os, legacy_facedir_simple);
1147                 writeU8(os, legacy_wallmounted);
1148                 serializeSimpleSoundSpec(sound_footstep, os);
1149                 serializeSimpleSoundSpec(sound_dig, os);
1150                 serializeSimpleSoundSpec(sound_dug, os);
1151                 writeU8(os, rightclickable);
1152                 writeU8(os, drowning);
1153                 writeU8(os, leveled);
1154                 writeU8(os, liquid_range);
1155         } else
1156                 throw SerializationError("ContentFeatures::serialize(): "
1157                         "Unsupported version requested");
1158 }
1159
1160
1161 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1162 {
1163         if (version == 5) // In PROTOCOL_VERSION 13
1164         {
1165                 name = deSerializeString(is);
1166                 groups.clear();
1167                 u32 groups_size = readU16(is);
1168                 for(u32 i=0; i<groups_size; i++){
1169                         std::string name = deSerializeString(is);
1170                         int value = readS16(is);
1171                         groups[name] = value;
1172                 }
1173                 drawtype = (enum NodeDrawType)readU8(is);
1174                 visual_scale = readF1000(is);
1175                 if (readU8(is) != 6)
1176                         throw SerializationError("unsupported tile count");
1177                 for (u32 i = 0; i < 6; i++)
1178                         tiledef[i].deSerialize(is);
1179                 if (readU8(is) != CF_SPECIAL_COUNT)
1180                         throw SerializationError("unsupported CF_SPECIAL_COUNT");
1181                 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1182                         tiledef_special[i].deSerialize(is);
1183                 alpha = readU8(is);
1184                 post_effect_color.setAlpha(readU8(is));
1185                 post_effect_color.setRed(readU8(is));
1186                 post_effect_color.setGreen(readU8(is));
1187                 post_effect_color.setBlue(readU8(is));
1188                 param_type = (enum ContentParamType)readU8(is);
1189                 param_type_2 = (enum ContentParamType2)readU8(is);
1190                 is_ground_content = readU8(is);
1191                 light_propagates = readU8(is);
1192                 sunlight_propagates = readU8(is);
1193                 walkable = readU8(is);
1194                 pointable = readU8(is);
1195                 diggable = readU8(is);
1196                 climbable = readU8(is);
1197                 buildable_to = readU8(is);
1198                 deSerializeString(is); // legacy: used to be metadata_name
1199                 liquid_type = (enum LiquidType)readU8(is);
1200                 liquid_alternative_flowing = deSerializeString(is);
1201                 liquid_alternative_source = deSerializeString(is);
1202                 liquid_viscosity = readU8(is);
1203                 light_source = readU8(is);
1204                 damage_per_second = readU32(is);
1205                 node_box.deSerialize(is);
1206                 selection_box.deSerialize(is);
1207                 legacy_facedir_simple = readU8(is);
1208                 legacy_wallmounted = readU8(is);
1209                 deSerializeSimpleSoundSpec(sound_footstep, is);
1210                 deSerializeSimpleSoundSpec(sound_dig, is);
1211                 deSerializeSimpleSoundSpec(sound_dug, is);
1212         } else if (version == 6) {
1213                 name = deSerializeString(is);
1214                 groups.clear();
1215                 u32 groups_size = readU16(is);
1216                 for (u32 i = 0; i < groups_size; i++) {
1217                         std::string name = deSerializeString(is);
1218                         int     value = readS16(is);
1219                         groups[name] = value;
1220                 }
1221                 drawtype = (enum NodeDrawType)readU8(is);
1222                 visual_scale = readF1000(is);
1223                 if (readU8(is) != 6)
1224                         throw SerializationError("unsupported tile count");
1225                 for (u32 i = 0; i < 6; i++)
1226                         tiledef[i].deSerialize(is);
1227                 // CF_SPECIAL_COUNT in version 6 = 2
1228                 if (readU8(is) != 2)
1229                         throw SerializationError("unsupported CF_SPECIAL_COUNT");
1230                 for (u32 i = 0; i < 2; i++)
1231                         tiledef_special[i].deSerialize(is);
1232                 alpha = readU8(is);
1233                 post_effect_color.setAlpha(readU8(is));
1234                 post_effect_color.setRed(readU8(is));
1235                 post_effect_color.setGreen(readU8(is));
1236                 post_effect_color.setBlue(readU8(is));
1237                 param_type = (enum ContentParamType)readU8(is);
1238                 param_type_2 = (enum ContentParamType2)readU8(is);
1239                 is_ground_content = readU8(is);
1240                 light_propagates = readU8(is);
1241                 sunlight_propagates = readU8(is);
1242                 walkable = readU8(is);
1243                 pointable = readU8(is);
1244                 diggable = readU8(is);
1245                 climbable = readU8(is);
1246                 buildable_to = readU8(is);
1247                 deSerializeString(is); // legacy: used to be metadata_name
1248                 liquid_type = (enum LiquidType)readU8(is);
1249                 liquid_alternative_flowing = deSerializeString(is);
1250                 liquid_alternative_source = deSerializeString(is);
1251                 liquid_viscosity = readU8(is);
1252                 liquid_renewable = readU8(is);
1253                 light_source = readU8(is);
1254                 damage_per_second = readU32(is);
1255                 node_box.deSerialize(is);
1256                 selection_box.deSerialize(is);
1257                 legacy_facedir_simple = readU8(is);
1258                 legacy_wallmounted = readU8(is);
1259                 deSerializeSimpleSoundSpec(sound_footstep, is);
1260                 deSerializeSimpleSoundSpec(sound_dig, is);
1261                 deSerializeSimpleSoundSpec(sound_dug, is);
1262                 rightclickable = readU8(is);
1263                 drowning = readU8(is);
1264                 leveled = readU8(is);
1265                 liquid_range = readU8(is);
1266         } else {
1267                 throw SerializationError("unsupported ContentFeatures version");
1268         }
1269 }
1270
1271
1272 void CNodeDefManager::pendNodeResolve(NodeResolveInfo *nri)
1273 {
1274         nri->resolver->m_ndef = this;
1275         m_pending_node_lookups.push_back(nri);
1276 }
1277
1278
1279 void CNodeDefManager::cancelNodeResolve(NodeResolver *resolver)
1280 {
1281         for (std::list<NodeResolveInfo *>::iterator
1282                         it = m_pending_node_lookups.begin();
1283                         it != m_pending_node_lookups.end();
1284                         ++it) {
1285                 NodeResolveInfo *nri = *it;
1286                 if (resolver == nri->resolver) {
1287                         it = m_pending_node_lookups.erase(it);
1288                         delete nri;
1289                 }
1290         }
1291 }
1292
1293
1294 void CNodeDefManager::runNodeResolverCallbacks()
1295 {
1296         while (!m_pending_node_lookups.empty()) {
1297                 NodeResolveInfo *nri = m_pending_node_lookups.front();
1298                 m_pending_node_lookups.pop_front();
1299                 nri->resolver->resolveNodeNames(nri);
1300                 nri->resolver->m_lookup_done = true;
1301                 delete nri;
1302         }
1303 }
1304
1305
1306 bool CNodeDefManager::getIdFromResolveInfo(NodeResolveInfo *nri,
1307         const std::string &node_alt, content_t c_fallback, content_t &result)
1308 {
1309         if (nri->nodenames.empty()) {
1310                 result = c_fallback;
1311                 errorstream << "CNodeDefManager::getIdFromResolveInfo: empty "
1312                         "nodenames list" << std::endl;
1313                 return false;
1314         }
1315
1316         content_t c;
1317         std::string name = nri->nodenames.front();
1318         nri->nodenames.pop_front();
1319
1320         bool success = getId(name, c);
1321         if (!success && node_alt != "") {
1322                 name = node_alt;
1323                 success = getId(name, c);
1324         }
1325
1326         if (!success) {
1327                 errorstream << "CNodeDefManager::getIdFromResolveInfo: Failed to "
1328                         "resolve node name '" << name << "'." << std::endl;
1329                 c = c_fallback;
1330         }
1331
1332         result = c;
1333         return success;
1334 }
1335
1336
1337 bool CNodeDefManager::getIdsFromResolveInfo(NodeResolveInfo *nri,
1338         std::vector<content_t> &result)
1339 {
1340         if (nri->nodename_sizes.empty()) {
1341                 errorstream << "CNodeDefManager::getIdsFromResolveInfo: empty "
1342                         "nodename_sizes list" << std::endl;
1343                 return false;
1344         }
1345
1346         size_t nitems = nri->nodename_sizes.front();
1347         nri->nodename_sizes.pop_front();
1348
1349         while (nitems--) {
1350                 if (nri->nodenames.empty()) {
1351                         errorstream << "" << std::endl;
1352                         return false;
1353                 }
1354
1355                 content_t c;
1356                 if (getId(nri->nodenames.front(), c)) {
1357                         result.push_back(c);
1358                 } else {
1359                         errorstream << "CNodeDefManager::getIdsFromResolveInfo: empty "
1360                                 "nodenames list" << std::endl;
1361                 }
1362
1363                 nri->nodenames.pop_front();
1364         }
1365
1366         return true;
1367 }