]> git.lizzy.rs Git - minetest.git/blob - src/nodedef.cpp
Mgflat, Mgv7: Fix noise crash on world exit.
[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 "itemdef.h"
23 #ifndef SERVER
24 #include "client/tile.h"
25 #include "mesh.h"
26 #include "client.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 #include "mapnode.h"
38 #include <fstream> // Used in applyTextureOverrides()
39
40 /*
41         NodeBox
42 */
43
44 void NodeBox::reset()
45 {
46         type = NODEBOX_REGULAR;
47         // default is empty
48         fixed.clear();
49         // default is sign/ladder-like
50         wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
51         wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
52         wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
53         // no default for other parts
54         connect_top.clear();
55         connect_bottom.clear();
56         connect_front.clear();
57         connect_left.clear();
58         connect_back.clear();
59         connect_right.clear();
60 }
61
62 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
63 {
64         // Protocol >= 21
65         int version = 2;
66         if (protocol_version >= 27)
67                 version = 3;
68         writeU8(os, version);
69
70         switch (type) {
71         case NODEBOX_LEVELED:
72         case NODEBOX_FIXED:
73                 if (version == 1)
74                         writeU8(os, NODEBOX_FIXED);
75                 else
76                         writeU8(os, type);
77
78                 writeU16(os, fixed.size());
79                 for (std::vector<aabb3f>::const_iterator
80                                 i = fixed.begin();
81                                 i != fixed.end(); ++i)
82                 {
83                         writeV3F1000(os, i->MinEdge);
84                         writeV3F1000(os, i->MaxEdge);
85                 }
86                 break;
87         case NODEBOX_WALLMOUNTED:
88                 writeU8(os, type);
89
90                 writeV3F1000(os, wall_top.MinEdge);
91                 writeV3F1000(os, wall_top.MaxEdge);
92                 writeV3F1000(os, wall_bottom.MinEdge);
93                 writeV3F1000(os, wall_bottom.MaxEdge);
94                 writeV3F1000(os, wall_side.MinEdge);
95                 writeV3F1000(os, wall_side.MaxEdge);
96                 break;
97         case NODEBOX_CONNECTED:
98                 if (version <= 2) {
99                         // send old clients nodes that can't be walked through
100                         // to prevent abuse
101                         writeU8(os, NODEBOX_FIXED);
102
103                         writeU16(os, 1);
104                         writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2));
105                         writeV3F1000(os, v3f(BS/2, BS/2, BS/2));
106                 } else {
107                         writeU8(os, type);
108
109 #define WRITEBOX(box) do { \
110                 writeU16(os, (box).size()); \
111                 for (std::vector<aabb3f>::const_iterator \
112                                 i = (box).begin(); \
113                                 i != (box).end(); ++i) { \
114                         writeV3F1000(os, i->MinEdge); \
115                         writeV3F1000(os, i->MaxEdge); \
116                 }; } while (0)
117
118                         WRITEBOX(fixed);
119                         WRITEBOX(connect_top);
120                         WRITEBOX(connect_bottom);
121                         WRITEBOX(connect_front);
122                         WRITEBOX(connect_left);
123                         WRITEBOX(connect_back);
124                         WRITEBOX(connect_right);
125                 }
126                 break;
127         default:
128                 writeU8(os, type);
129                 break;
130         }
131 }
132
133 void NodeBox::deSerialize(std::istream &is)
134 {
135         int version = readU8(is);
136         if (version < 1 || version > 3)
137                 throw SerializationError("unsupported NodeBox version");
138
139         reset();
140
141         type = (enum NodeBoxType)readU8(is);
142
143         if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
144         {
145                 u16 fixed_count = readU16(is);
146                 while(fixed_count--)
147                 {
148                         aabb3f box;
149                         box.MinEdge = readV3F1000(is);
150                         box.MaxEdge = readV3F1000(is);
151                         fixed.push_back(box);
152                 }
153         }
154         else if(type == NODEBOX_WALLMOUNTED)
155         {
156                 wall_top.MinEdge = readV3F1000(is);
157                 wall_top.MaxEdge = readV3F1000(is);
158                 wall_bottom.MinEdge = readV3F1000(is);
159                 wall_bottom.MaxEdge = readV3F1000(is);
160                 wall_side.MinEdge = readV3F1000(is);
161                 wall_side.MaxEdge = readV3F1000(is);
162         }
163         else if (type == NODEBOX_CONNECTED)
164         {
165 #define READBOXES(box) do { \
166                 count = readU16(is); \
167                 (box).reserve(count); \
168                 while (count--) { \
169                         v3f min = readV3F1000(is); \
170                         v3f max = readV3F1000(is); \
171                         (box).push_back(aabb3f(min, max)); }; } while (0)
172
173                 u16 count;
174
175                 READBOXES(fixed);
176                 READBOXES(connect_top);
177                 READBOXES(connect_bottom);
178                 READBOXES(connect_front);
179                 READBOXES(connect_left);
180                 READBOXES(connect_back);
181                 READBOXES(connect_right);
182         }
183 }
184
185 /*
186         TileDef
187 */
188
189 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
190 {
191         if (protocol_version >= 30)
192                 writeU8(os, 4);
193         else if (protocol_version >= 29)
194                 writeU8(os, 3);
195         else if (protocol_version >= 26)
196                 writeU8(os, 2);
197         else
198                 writeU8(os, 1);
199
200         os << serializeString(name);
201         animation.serialize(os, protocol_version);
202         writeU8(os, backface_culling);
203         if (protocol_version >= 26) {
204                 writeU8(os, tileable_horizontal);
205                 writeU8(os, tileable_vertical);
206         }
207         if (protocol_version >= 30) {
208                 writeU8(os, has_color);
209                 if (has_color) {
210                         writeU8(os, color.getRed());
211                         writeU8(os, color.getGreen());
212                         writeU8(os, color.getBlue());
213                 }
214         }
215 }
216
217 void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
218 {
219         int version = readU8(is);
220         name = deSerializeString(is);
221         animation.deSerialize(is, version >= 3 ? 29 : 26);
222         if (version >= 1)
223                 backface_culling = readU8(is);
224         if (version >= 2) {
225                 tileable_horizontal = readU8(is);
226                 tileable_vertical = readU8(is);
227         }
228         if (version >= 4) {
229                 has_color = readU8(is);
230                 if (has_color) {
231                         color.setRed(readU8(is));
232                         color.setGreen(readU8(is));
233                         color.setBlue(readU8(is));
234                 }
235         }
236
237         if ((contenfeatures_version < 8) &&
238                 ((drawtype == NDT_MESH) ||
239                  (drawtype == NDT_FIRELIKE) ||
240                  (drawtype == NDT_LIQUID) ||
241                  (drawtype == NDT_PLANTLIKE)))
242                 backface_culling = false;
243 }
244
245
246 /*
247         SimpleSoundSpec serialization
248 */
249
250 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
251                 std::ostream &os)
252 {
253         os<<serializeString(ss.name);
254         writeF1000(os, ss.gain);
255 }
256 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
257 {
258         ss.name = deSerializeString(is);
259         ss.gain = readF1000(is);
260 }
261
262 void TextureSettings::readSettings()
263 {
264         connected_glass                = g_settings->getBool("connected_glass");
265         opaque_water                   = g_settings->getBool("opaque_water");
266         bool enable_shaders            = g_settings->getBool("enable_shaders");
267         bool enable_bumpmapping        = g_settings->getBool("enable_bumpmapping");
268         bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
269         bool smooth_lighting           = g_settings->getBool("smooth_lighting");
270         enable_mesh_cache              = g_settings->getBool("enable_mesh_cache");
271         enable_minimap                 = g_settings->getBool("enable_minimap");
272         std::string leaves_style_str   = g_settings->get("leaves_style");
273
274         // Mesh cache is not supported in combination with smooth lighting
275         if (smooth_lighting)
276                 enable_mesh_cache = false;
277
278         use_normal_texture = enable_shaders &&
279                 (enable_bumpmapping || enable_parallax_occlusion);
280         if (leaves_style_str == "fancy") {
281                 leaves_style = LEAVES_FANCY;
282         } else if (leaves_style_str == "simple") {
283                 leaves_style = LEAVES_SIMPLE;
284         } else {
285                 leaves_style = LEAVES_OPAQUE;
286         }
287 }
288
289 /*
290         ContentFeatures
291 */
292
293 ContentFeatures::ContentFeatures()
294 {
295         reset();
296 }
297
298 ContentFeatures::~ContentFeatures()
299 {
300 }
301
302 void ContentFeatures::reset()
303 {
304         /*
305                 Cached stuff
306         */
307 #ifndef SERVER
308         solidness = 2;
309         visual_solidness = 0;
310         backface_culling = true;
311
312 #endif
313         has_on_construct = false;
314         has_on_destruct = false;
315         has_after_destruct = false;
316         /*
317                 Actual data
318
319                 NOTE: Most of this is always overridden by the default values given
320                       in builtin.lua
321         */
322         name = "";
323         groups.clear();
324         // Unknown nodes can be dug
325         groups["dig_immediate"] = 2;
326         drawtype = NDT_NORMAL;
327         mesh = "";
328 #ifndef SERVER
329         for(u32 i = 0; i < 24; i++)
330                 mesh_ptr[i] = NULL;
331         minimap_color = video::SColor(0, 0, 0, 0);
332 #endif
333         visual_scale = 1.0;
334         for(u32 i = 0; i < 6; i++)
335                 tiledef[i] = TileDef();
336         for(u16 j = 0; j < CF_SPECIAL_COUNT; j++)
337                 tiledef_special[j] = TileDef();
338         alpha = 255;
339         post_effect_color = video::SColor(0, 0, 0, 0);
340         param_type = CPT_NONE;
341         param_type_2 = CPT2_NONE;
342         is_ground_content = false;
343         light_propagates = false;
344         sunlight_propagates = false;
345         walkable = true;
346         pointable = true;
347         diggable = true;
348         climbable = false;
349         buildable_to = false;
350         floodable = false;
351         rightclickable = true;
352         leveled = 0;
353         liquid_type = LIQUID_NONE;
354         liquid_alternative_flowing = "";
355         liquid_alternative_source = "";
356         liquid_viscosity = 0;
357         liquid_renewable = true;
358         liquid_range = LIQUID_LEVEL_MAX+1;
359         drowning = 0;
360         light_source = 0;
361         damage_per_second = 0;
362         node_box = NodeBox();
363         selection_box = NodeBox();
364         collision_box = NodeBox();
365         waving = 0;
366         legacy_facedir_simple = false;
367         legacy_wallmounted = false;
368         sound_footstep = SimpleSoundSpec();
369         sound_dig = SimpleSoundSpec("__group");
370         sound_dug = SimpleSoundSpec();
371         connects_to.clear();
372         connects_to_ids.clear();
373         connect_sides = 0;
374         color = video::SColor(0xFFFFFFFF);
375         palette_name = "";
376         palette = NULL;
377 }
378
379 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
380 {
381         if (protocol_version < 31) {
382                 serializeOld(os, protocol_version);
383                 return;
384         }
385
386         // version
387         writeU8(os, 10);
388
389         // general
390         os << serializeString(name);
391         writeU16(os, groups.size());
392         for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end();
393                 ++i) {
394                 os << serializeString(i->first);
395                 writeS16(os, i->second);
396         }
397         writeU8(os, param_type);
398         writeU8(os, param_type_2);
399
400         // visual
401         writeU8(os, drawtype);
402         os << serializeString(mesh);
403         writeF1000(os, visual_scale);
404         writeU8(os, 6);
405         for (u32 i = 0; i < 6; i++)
406                 tiledef[i].serialize(os, protocol_version);
407         for (u32 i = 0; i < 6; i++)
408                 tiledef_overlay[i].serialize(os, protocol_version);
409         writeU8(os, CF_SPECIAL_COUNT);
410         for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
411                 tiledef_special[i].serialize(os, protocol_version);
412         }
413         writeU8(os, alpha);
414         writeU8(os, color.getRed());
415         writeU8(os, color.getGreen());
416         writeU8(os, color.getBlue());
417         os << serializeString(palette_name);
418         writeU8(os, waving);
419         writeU8(os, connect_sides);
420         writeU16(os, connects_to_ids.size());
421         for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
422                 i != connects_to_ids.end(); ++i)
423                 writeU16(os, *i);
424         writeU8(os, post_effect_color.getAlpha());
425         writeU8(os, post_effect_color.getRed());
426         writeU8(os, post_effect_color.getGreen());
427         writeU8(os, post_effect_color.getBlue());
428         writeU8(os, leveled);
429
430         // lighting
431         writeU8(os, light_propagates);
432         writeU8(os, sunlight_propagates);
433         writeU8(os, light_source);
434
435         // map generation
436         writeU8(os, is_ground_content);
437
438         // interaction
439         writeU8(os, walkable);
440         writeU8(os, pointable);
441         writeU8(os, diggable);
442         writeU8(os, climbable);
443         writeU8(os, buildable_to);
444         writeU8(os, rightclickable);
445         writeU32(os, damage_per_second);
446
447         // liquid
448         writeU8(os, liquid_type);
449         os << serializeString(liquid_alternative_flowing);
450         os << serializeString(liquid_alternative_source);
451         writeU8(os, liquid_viscosity);
452         writeU8(os, liquid_renewable);
453         writeU8(os, liquid_range);
454         writeU8(os, drowning);
455         writeU8(os, floodable);
456
457         // node boxes
458         node_box.serialize(os, protocol_version);
459         selection_box.serialize(os, protocol_version);
460         collision_box.serialize(os, protocol_version);
461
462         // sound
463         serializeSimpleSoundSpec(sound_footstep, os);
464         serializeSimpleSoundSpec(sound_dig, os);
465         serializeSimpleSoundSpec(sound_dug, os);
466
467         // legacy
468         writeU8(os, legacy_facedir_simple);
469         writeU8(os, legacy_wallmounted);
470 }
471
472 void ContentFeatures::correctAlpha()
473 {
474         if (alpha == 0 || alpha == 255)
475                 return;
476
477         for (u32 i = 0; i < 6; i++) {
478                 std::stringstream s;
479                 s << tiledef[i].name << "^[noalpha^[opacity:" << ((int)alpha);
480                 tiledef[i].name = s.str();
481         }
482
483         for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
484                 std::stringstream s;
485                 s << tiledef_special[i].name << "^[noalpha^[opacity:" << ((int)alpha);
486                 tiledef_special[i].name = s.str();
487         }
488 }
489
490 void ContentFeatures::deSerialize(std::istream &is)
491 {
492         // version detection
493         int version = readU8(is);
494         if (version < 9) {
495                 deSerializeOld(is, version);
496                 return;
497         } else if (version > 10) {
498                 throw SerializationError("unsupported ContentFeatures version");
499         }
500
501         // general
502         name = deSerializeString(is);
503         groups.clear();
504         u32 groups_size = readU16(is);
505         for (u32 i = 0; i < groups_size; i++) {
506                 std::string name = deSerializeString(is);
507                 int value = readS16(is);
508                 groups[name] = value;
509         }
510         param_type = (enum ContentParamType) readU8(is);
511         param_type_2 = (enum ContentParamType2) readU8(is);
512
513         // visual
514         drawtype = (enum NodeDrawType) readU8(is);
515         mesh = deSerializeString(is);
516         visual_scale = readF1000(is);
517         if (readU8(is) != 6)
518                 throw SerializationError("unsupported tile count");
519         for (u32 i = 0; i < 6; i++)
520                 tiledef[i].deSerialize(is, version, drawtype);
521         if (version >= 10)
522                 for (u32 i = 0; i < 6; i++)
523                         tiledef_overlay[i].deSerialize(is, version, drawtype);
524         if (readU8(is) != CF_SPECIAL_COUNT)
525                 throw SerializationError("unsupported CF_SPECIAL_COUNT");
526         for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
527                 tiledef_special[i].deSerialize(is, version, drawtype);
528         alpha = readU8(is);
529         color.setRed(readU8(is));
530         color.setGreen(readU8(is));
531         color.setBlue(readU8(is));
532         palette_name = deSerializeString(is);
533         waving = readU8(is);
534         connect_sides = readU8(is);
535         u16 connects_to_size = readU16(is);
536         connects_to_ids.clear();
537         for (u16 i = 0; i < connects_to_size; i++)
538                 connects_to_ids.insert(readU16(is));
539         post_effect_color.setAlpha(readU8(is));
540         post_effect_color.setRed(readU8(is));
541         post_effect_color.setGreen(readU8(is));
542         post_effect_color.setBlue(readU8(is));
543         leveled = readU8(is);
544
545         // lighting-related
546         light_propagates = readU8(is);
547         sunlight_propagates = readU8(is);
548         light_source = readU8(is);
549         light_source = MYMIN(light_source, LIGHT_MAX);
550
551         // map generation
552         is_ground_content = readU8(is);
553
554         // interaction
555         walkable = readU8(is);
556         pointable = readU8(is);
557         diggable = readU8(is);
558         climbable = readU8(is);
559         buildable_to = readU8(is);
560         rightclickable = readU8(is);
561         damage_per_second = readU32(is);
562
563         // liquid
564         liquid_type = (enum LiquidType) readU8(is);
565         liquid_alternative_flowing = deSerializeString(is);
566         liquid_alternative_source = deSerializeString(is);
567         liquid_viscosity = readU8(is);
568         liquid_renewable = readU8(is);
569         liquid_range = readU8(is);
570         drowning = readU8(is);
571         floodable = readU8(is);
572
573         // node boxes
574         node_box.deSerialize(is);
575         selection_box.deSerialize(is);
576         collision_box.deSerialize(is);
577
578         // sounds
579         deSerializeSimpleSoundSpec(sound_footstep, is);
580         deSerializeSimpleSoundSpec(sound_dig, is);
581         deSerializeSimpleSoundSpec(sound_dug, is);
582
583         // read legacy properties
584         legacy_facedir_simple = readU8(is);
585         legacy_wallmounted = readU8(is);
586 }
587
588 #ifndef SERVER
589 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
590                 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
591                 bool backface_culling, u8 material_type)
592 {
593         tile->shader_id     = shader_id;
594         tile->texture       = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
595         tile->material_type = material_type;
596
597         // Normal texture and shader flags texture
598         if (use_normal_texture) {
599                 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
600         }
601         tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
602
603         // Material flags
604         tile->material_flags = 0;
605         if (backface_culling)
606                 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
607         if (tiledef->animation.type != TAT_NONE)
608                 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
609         if (tiledef->tileable_horizontal)
610                 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
611         if (tiledef->tileable_vertical)
612                 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
613
614         // Color
615         tile->has_color = tiledef->has_color;
616         if (tiledef->has_color)
617                 tile->color = tiledef->color;
618         else
619                 tile->color = color;
620
621         // Animation parameters
622         int frame_count = 1;
623         if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
624                 int frame_length_ms;
625                 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
626                                 &frame_count, &frame_length_ms, NULL);
627                 tile->animation_frame_count = frame_count;
628                 tile->animation_frame_length_ms = frame_length_ms;
629         }
630
631         if (frame_count == 1) {
632                 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
633         } else {
634                 std::ostringstream os(std::ios::binary);
635                 tile->frames.resize(frame_count);
636
637                 for (int i = 0; i < frame_count; i++) {
638
639                         FrameSpec frame;
640
641                         os.str("");
642                         os << tiledef->name;
643                         tiledef->animation.getTextureModifer(os,
644                                         tile->texture->getOriginalSize(), i);
645
646                         frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
647                         if (tile->normal_texture)
648                                 frame.normal_texture = tsrc->getNormalTexture(os.str());
649                         frame.flags_texture = tile->flags_texture;
650                         tile->frames[i] = frame;
651                 }
652         }
653 }
654 #endif
655
656 #ifndef SERVER
657 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
658         scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
659 {
660         // minimap pixel color - the average color of a texture
661         if (tsettings.enable_minimap && tiledef[0].name != "")
662                 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
663
664         // Figure out the actual tiles to use
665         TileDef tdef[6];
666         for (u32 j = 0; j < 6; j++) {
667                 tdef[j] = tiledef[j];
668                 if (tdef[j].name == "")
669                         tdef[j].name = "unknown_node.png";
670         }
671
672         bool is_liquid = false;
673         bool is_water_surface = false;
674
675         u8 material_type = (alpha == 255) ?
676                 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
677
678         switch (drawtype) {
679         default:
680         case NDT_NORMAL:
681                 solidness = 2;
682                 break;
683         case NDT_AIRLIKE:
684                 solidness = 0;
685                 break;
686         case NDT_LIQUID:
687                 assert(liquid_type == LIQUID_SOURCE);
688                 if (tsettings.opaque_water)
689                         alpha = 255;
690                 solidness = 1;
691                 is_liquid = true;
692                 break;
693         case NDT_FLOWINGLIQUID:
694                 assert(liquid_type == LIQUID_FLOWING);
695                 solidness = 0;
696                 if (tsettings.opaque_water)
697                         alpha = 255;
698                 is_liquid = true;
699                 break;
700         case NDT_GLASSLIKE:
701                 solidness = 0;
702                 visual_solidness = 1;
703                 break;
704         case NDT_GLASSLIKE_FRAMED:
705                 solidness = 0;
706                 visual_solidness = 1;
707                 break;
708         case NDT_GLASSLIKE_FRAMED_OPTIONAL:
709                 solidness = 0;
710                 visual_solidness = 1;
711                 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
712                 break;
713         case NDT_ALLFACES:
714                 solidness = 0;
715                 visual_solidness = 1;
716                 break;
717         case NDT_ALLFACES_OPTIONAL:
718                 if (tsettings.leaves_style == LEAVES_FANCY) {
719                         drawtype = NDT_ALLFACES;
720                         solidness = 0;
721                         visual_solidness = 1;
722                 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
723                         for (u32 j = 0; j < 6; j++) {
724                                 if (tiledef_special[j].name != "")
725                                         tdef[j].name = tiledef_special[j].name;
726                         }
727                         drawtype = NDT_GLASSLIKE;
728                         solidness = 0;
729                         visual_solidness = 1;
730                 } else {
731                         drawtype = NDT_NORMAL;
732                         solidness = 2;
733                         for (u32 i = 0; i < 6; i++)
734                                 tdef[i].name += std::string("^[noalpha");
735                 }
736                 if (waving == 1)
737                         material_type = TILE_MATERIAL_WAVING_LEAVES;
738                 break;
739         case NDT_PLANTLIKE:
740                 solidness = 0;
741                 if (waving == 1)
742                         material_type = TILE_MATERIAL_WAVING_PLANTS;
743                 break;
744         case NDT_FIRELIKE:
745                 solidness = 0;
746                 break;
747         case NDT_MESH:
748                 solidness = 0;
749                 break;
750         case NDT_TORCHLIKE:
751         case NDT_SIGNLIKE:
752         case NDT_FENCELIKE:
753         case NDT_RAILLIKE:
754         case NDT_NODEBOX:
755                 solidness = 0;
756                 break;
757         }
758
759         if (is_liquid) {
760                 material_type = (alpha == 255) ?
761                         TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
762                 if (name == "default:water_source")
763                         is_water_surface = true;
764         }
765
766         // Vertex alpha is no longer supported, correct if necessary.
767         correctAlpha();
768
769         u32 tile_shader[6];
770         for (u16 j = 0; j < 6; j++) {
771                 tile_shader[j] = shdsrc->getShader("nodes_shader",
772                         material_type, drawtype);
773         }
774
775         if (is_water_surface) {
776                 tile_shader[0] = shdsrc->getShader("water_surface_shader",
777                         material_type, drawtype);
778         }
779
780         // Tiles (fill in f->tiles[])
781         for (u16 j = 0; j < 6; j++) {
782                 fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader[j],
783                         tsettings.use_normal_texture,
784                         tiledef[j].backface_culling, material_type);
785                 if (tiledef_overlay[j].name!="")
786                         fillTileAttribs(tsrc, &tiles[j].layers[1], &tiledef_overlay[j],
787                                 tile_shader[j], tsettings.use_normal_texture,
788                                 tiledef[j].backface_culling, material_type);
789         }
790
791         // Special tiles (fill in f->special_tiles[])
792         for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
793                 fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tiledef_special[j],
794                         tile_shader[j], tsettings.use_normal_texture,
795                         tiledef_special[j].backface_culling, material_type);
796         }
797
798         if (param_type_2 == CPT2_COLOR ||
799                         param_type_2 == CPT2_COLORED_FACEDIR ||
800                         param_type_2 == CPT2_COLORED_WALLMOUNTED)
801                 palette = tsrc->getPalette(palette_name);
802
803         if ((drawtype == NDT_MESH) && (mesh != "")) {
804                 // Meshnode drawtype
805                 // Read the mesh and apply scale
806                 mesh_ptr[0] = client->getMesh(mesh);
807                 if (mesh_ptr[0]){
808                         v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
809                         scaleMesh(mesh_ptr[0], scale);
810                         recalculateBoundingBox(mesh_ptr[0]);
811                         meshmanip->recalculateNormals(mesh_ptr[0], true, false);
812                 }
813         } else if ((drawtype == NDT_NODEBOX) &&
814                         ((node_box.type == NODEBOX_REGULAR) ||
815                         (node_box.type == NODEBOX_FIXED)) &&
816                         (!node_box.fixed.empty())) {
817                 //Convert regular nodebox nodes to meshnodes
818                 //Change the drawtype and apply scale
819                 drawtype = NDT_MESH;
820                 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
821                 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
822                 scaleMesh(mesh_ptr[0], scale);
823                 recalculateBoundingBox(mesh_ptr[0]);
824                 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
825         }
826
827         //Cache 6dfacedir and wallmounted rotated clones of meshes
828         if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
829                         (param_type_2 == CPT2_FACEDIR
830                         || param_type_2 == CPT2_COLORED_FACEDIR)) {
831                 for (u16 j = 1; j < 24; j++) {
832                         mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
833                         rotateMeshBy6dFacedir(mesh_ptr[j], j);
834                         recalculateBoundingBox(mesh_ptr[j]);
835                         meshmanip->recalculateNormals(mesh_ptr[j], true, false);
836                 }
837         } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
838                         && (param_type_2 == CPT2_WALLMOUNTED ||
839                         param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
840                 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
841                 for (u16 j = 1; j < 6; j++) {
842                         mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
843                         rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
844                         recalculateBoundingBox(mesh_ptr[j]);
845                         meshmanip->recalculateNormals(mesh_ptr[j], true, false);
846                 }
847                 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
848                 recalculateBoundingBox(mesh_ptr[0]);
849                 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
850         }
851 }
852 #endif
853
854 /*
855         CNodeDefManager
856 */
857
858 class CNodeDefManager: public IWritableNodeDefManager {
859 public:
860         CNodeDefManager();
861         virtual ~CNodeDefManager();
862         void clear();
863         virtual IWritableNodeDefManager *clone();
864         inline virtual const ContentFeatures& get(content_t c) const;
865         inline virtual const ContentFeatures& get(const MapNode &n) const;
866         virtual bool getId(const std::string &name, content_t &result) const;
867         virtual content_t getId(const std::string &name) const;
868         virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
869         virtual const ContentFeatures& get(const std::string &name) const;
870         content_t allocateId();
871         virtual content_t set(const std::string &name, const ContentFeatures &def);
872         virtual content_t allocateDummy(const std::string &name);
873         virtual void removeNode(const std::string &name);
874         virtual void updateAliases(IItemDefManager *idef);
875         virtual void applyTextureOverrides(const std::string &override_filepath);
876         virtual void updateTextures(IGameDef *gamedef,
877                 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
878                 void *progress_cbk_args);
879         void serialize(std::ostream &os, u16 protocol_version) const;
880         void deSerialize(std::istream &is);
881
882         inline virtual bool getNodeRegistrationStatus() const;
883         inline virtual void setNodeRegistrationStatus(bool completed);
884
885         virtual void pendNodeResolve(NodeResolver *nr);
886         virtual bool cancelNodeResolveCallback(NodeResolver *nr);
887         virtual void runNodeResolveCallbacks();
888         virtual void resetNodeResolveState();
889         virtual void mapNodeboxConnections();
890         virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
891         virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
892         {
893                 return m_selection_box_int_union;
894         }
895
896 private:
897         void addNameIdMapping(content_t i, std::string name);
898         /*!
899          * Recalculates m_selection_box_int_union based on
900          * m_selection_box_union.
901          */
902         void fixSelectionBoxIntUnion();
903
904         // Features indexed by id
905         std::vector<ContentFeatures> m_content_features;
906
907         // A mapping for fast converting back and forth between names and ids
908         NameIdMapping m_name_id_mapping;
909
910         // Like m_name_id_mapping, but only from names to ids, and includes
911         // item aliases too. Updated by updateAliases()
912         // Note: Not serialized.
913
914         UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
915
916         // A mapping from groups to a list of content_ts (and their levels)
917         // that belong to it.  Necessary for a direct lookup in getIds().
918         // Note: Not serialized.
919         UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
920
921         // Next possibly free id
922         content_t m_next_id;
923
924         // NodeResolvers to callback once node registration has ended
925         std::vector<NodeResolver *> m_pending_resolve_callbacks;
926
927         // True when all nodes have been registered
928         bool m_node_registration_complete;
929
930         //! The union of all nodes' selection boxes.
931         aabb3f m_selection_box_union;
932         /*!
933          * The smallest box in node coordinates that
934          * contains all nodes' selection boxes.
935          */
936         core::aabbox3d<s16> m_selection_box_int_union;
937 };
938
939
940 CNodeDefManager::CNodeDefManager()
941 {
942         clear();
943 }
944
945
946 CNodeDefManager::~CNodeDefManager()
947 {
948 #ifndef SERVER
949         for (u32 i = 0; i < m_content_features.size(); i++) {
950                 ContentFeatures *f = &m_content_features[i];
951                 for (u32 j = 0; j < 24; j++) {
952                         if (f->mesh_ptr[j])
953                                 f->mesh_ptr[j]->drop();
954                 }
955         }
956 #endif
957 }
958
959
960 void CNodeDefManager::clear()
961 {
962         m_content_features.clear();
963         m_name_id_mapping.clear();
964         m_name_id_mapping_with_aliases.clear();
965         m_group_to_items.clear();
966         m_next_id = 0;
967         m_selection_box_union.reset(0,0,0);
968         m_selection_box_int_union.reset(0,0,0);
969
970         resetNodeResolveState();
971
972         u32 initial_length = 0;
973         initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
974         initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
975         initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
976         m_content_features.resize(initial_length);
977
978         // Set CONTENT_UNKNOWN
979         {
980                 ContentFeatures f;
981                 f.name = "unknown";
982                 // Insert directly into containers
983                 content_t c = CONTENT_UNKNOWN;
984                 m_content_features[c] = f;
985                 addNameIdMapping(c, f.name);
986         }
987
988         // Set CONTENT_AIR
989         {
990                 ContentFeatures f;
991                 f.name                = "air";
992                 f.drawtype            = NDT_AIRLIKE;
993                 f.param_type          = CPT_LIGHT;
994                 f.light_propagates    = true;
995                 f.sunlight_propagates = true;
996                 f.walkable            = false;
997                 f.pointable           = false;
998                 f.diggable            = false;
999                 f.buildable_to        = true;
1000                 f.floodable           = true;
1001                 f.is_ground_content   = true;
1002                 // Insert directly into containers
1003                 content_t c = CONTENT_AIR;
1004                 m_content_features[c] = f;
1005                 addNameIdMapping(c, f.name);
1006         }
1007
1008         // Set CONTENT_IGNORE
1009         {
1010                 ContentFeatures f;
1011                 f.name                = "ignore";
1012                 f.drawtype            = NDT_AIRLIKE;
1013                 f.param_type          = CPT_NONE;
1014                 f.light_propagates    = false;
1015                 f.sunlight_propagates = false;
1016                 f.walkable            = false;
1017                 f.pointable           = false;
1018                 f.diggable            = false;
1019                 f.buildable_to        = true; // A way to remove accidental CONTENT_IGNOREs
1020                 f.is_ground_content   = true;
1021                 // Insert directly into containers
1022                 content_t c = CONTENT_IGNORE;
1023                 m_content_features[c] = f;
1024                 addNameIdMapping(c, f.name);
1025         }
1026 }
1027
1028
1029 IWritableNodeDefManager *CNodeDefManager::clone()
1030 {
1031         CNodeDefManager *mgr = new CNodeDefManager();
1032         *mgr = *this;
1033         return mgr;
1034 }
1035
1036
1037 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1038 {
1039         return c < m_content_features.size()
1040                         ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1041 }
1042
1043
1044 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1045 {
1046         return get(n.getContent());
1047 }
1048
1049
1050 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1051 {
1052         UNORDERED_MAP<std::string, content_t>::const_iterator
1053                 i = m_name_id_mapping_with_aliases.find(name);
1054         if(i == m_name_id_mapping_with_aliases.end())
1055                 return false;
1056         result = i->second;
1057         return true;
1058 }
1059
1060
1061 content_t CNodeDefManager::getId(const std::string &name) const
1062 {
1063         content_t id = CONTENT_IGNORE;
1064         getId(name, id);
1065         return id;
1066 }
1067
1068
1069 bool CNodeDefManager::getIds(const std::string &name,
1070                 std::set<content_t> &result) const
1071 {
1072         //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1073         if (name.substr(0,6) != "group:") {
1074                 content_t id = CONTENT_IGNORE;
1075                 bool exists = getId(name, id);
1076                 if (exists)
1077                         result.insert(id);
1078                 return exists;
1079         }
1080         std::string group = name.substr(6);
1081
1082         UNORDERED_MAP<std::string, GroupItems>::const_iterator
1083                 i = m_group_to_items.find(group);
1084         if (i == m_group_to_items.end())
1085                 return true;
1086
1087         const GroupItems &items = i->second;
1088         for (GroupItems::const_iterator j = items.begin();
1089                 j != items.end(); ++j) {
1090                 if ((*j).second != 0)
1091                         result.insert((*j).first);
1092         }
1093         //printf("getIds: %dus\n", t.stop());
1094         return true;
1095 }
1096
1097
1098 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1099 {
1100         content_t id = CONTENT_UNKNOWN;
1101         getId(name, id);
1102         return get(id);
1103 }
1104
1105
1106 // returns CONTENT_IGNORE if no free ID found
1107 content_t CNodeDefManager::allocateId()
1108 {
1109         for (content_t id = m_next_id;
1110                         id >= m_next_id; // overflow?
1111                         ++id) {
1112                 while (id >= m_content_features.size()) {
1113                         m_content_features.push_back(ContentFeatures());
1114                 }
1115                 const ContentFeatures &f = m_content_features[id];
1116                 if (f.name == "") {
1117                         m_next_id = id + 1;
1118                         return id;
1119                 }
1120         }
1121         // If we arrive here, an overflow occurred in id.
1122         // That means no ID was found
1123         return CONTENT_IGNORE;
1124 }
1125
1126
1127 /*!
1128  * Returns the smallest box that contains all boxes
1129  * in the vector. Box_union is expanded.
1130  * @param[in]      boxes     the vector containing the boxes
1131  * @param[in, out] box_union the union of the arguments
1132  */
1133 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1134 {
1135         for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1136                         it != boxes.end(); ++it) {
1137                 box_union->addInternalBox(*it);
1138         }
1139 }
1140
1141
1142 /*!
1143  * Returns a box that contains the nodebox in every case.
1144  * The argument node_union is expanded.
1145  * @param[in]      nodebox  the nodebox to be measured
1146  * @param[in]      features  used to decide whether the nodebox
1147  * can be rotated
1148  * @param[in, out] box_union the union of the arguments
1149  */
1150 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1151         aabb3f *box_union)
1152 {
1153         switch(nodebox.type) {
1154                 case NODEBOX_FIXED:
1155                 case NODEBOX_LEVELED: {
1156                         // Raw union
1157                         aabb3f half_processed(0, 0, 0, 0, 0, 0);
1158                         boxVectorUnion(nodebox.fixed, &half_processed);
1159                         // Set leveled boxes to maximal
1160                         if (nodebox.type == NODEBOX_LEVELED) {
1161                                 half_processed.MaxEdge.Y = +BS / 2;
1162                         }
1163                         if (features.param_type_2 == CPT2_FACEDIR ||
1164                                         features.param_type_2 == CPT2_COLORED_FACEDIR) {
1165                                 // Get maximal coordinate
1166                                 f32 coords[] = {
1167                                         fabsf(half_processed.MinEdge.X),
1168                                         fabsf(half_processed.MinEdge.Y),
1169                                         fabsf(half_processed.MinEdge.Z),
1170                                         fabsf(half_processed.MaxEdge.X),
1171                                         fabsf(half_processed.MaxEdge.Y),
1172                                         fabsf(half_processed.MaxEdge.Z) };
1173                                 f32 max = 0;
1174                                 for (int i = 0; i < 6; i++) {
1175                                         if (max < coords[i]) {
1176                                                 max = coords[i];
1177                                         }
1178                                 }
1179                                 // Add the union of all possible rotated boxes
1180                                 box_union->addInternalPoint(-max, -max, -max);
1181                                 box_union->addInternalPoint(+max, +max, +max);
1182                         } else {
1183                                 box_union->addInternalBox(half_processed);
1184                         }
1185                         break;
1186                 }
1187                 case NODEBOX_WALLMOUNTED: {
1188                         // Add fix boxes
1189                         box_union->addInternalBox(nodebox.wall_top);
1190                         box_union->addInternalBox(nodebox.wall_bottom);
1191                         // Find maximal coordinate in the X-Z plane
1192                         f32 coords[] = {
1193                                 fabsf(nodebox.wall_side.MinEdge.X),
1194                                 fabsf(nodebox.wall_side.MinEdge.Z),
1195                                 fabsf(nodebox.wall_side.MaxEdge.X),
1196                                 fabsf(nodebox.wall_side.MaxEdge.Z) };
1197                         f32 max = 0;
1198                         for (int i = 0; i < 4; i++) {
1199                                 if (max < coords[i]) {
1200                                         max = coords[i];
1201                                 }
1202                         }
1203                         // Add the union of all possible rotated boxes
1204                         box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1205                         box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1206                         break;
1207                 }
1208                 case NODEBOX_CONNECTED: {
1209                         // Add all possible connected boxes
1210                         boxVectorUnion(nodebox.fixed,          box_union);
1211                         boxVectorUnion(nodebox.connect_top,    box_union);
1212                         boxVectorUnion(nodebox.connect_bottom, box_union);
1213                         boxVectorUnion(nodebox.connect_front,  box_union);
1214                         boxVectorUnion(nodebox.connect_left,   box_union);
1215                         boxVectorUnion(nodebox.connect_back,   box_union);
1216                         boxVectorUnion(nodebox.connect_right,  box_union);
1217                         break;
1218                 }
1219                 default: {
1220                         // NODEBOX_REGULAR
1221                         box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1222                         box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1223                 }
1224         }
1225 }
1226
1227
1228 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1229 {
1230         m_selection_box_int_union.MinEdge.X = floorf(
1231                 m_selection_box_union.MinEdge.X / BS + 0.5f);
1232         m_selection_box_int_union.MinEdge.Y = floorf(
1233                 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1234         m_selection_box_int_union.MinEdge.Z = floorf(
1235                 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1236         m_selection_box_int_union.MaxEdge.X = ceilf(
1237                 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1238         m_selection_box_int_union.MaxEdge.Y = ceilf(
1239                 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1240         m_selection_box_int_union.MaxEdge.Z = ceilf(
1241                 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1242 }
1243
1244
1245 // IWritableNodeDefManager
1246 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1247 {
1248         // Pre-conditions
1249         assert(name != "");
1250         assert(name == def.name);
1251
1252         // Don't allow redefining ignore (but allow air and unknown)
1253         if (name == "ignore") {
1254                 warningstream << "NodeDefManager: Ignoring "
1255                         "CONTENT_IGNORE redefinition"<<std::endl;
1256                 return CONTENT_IGNORE;
1257         }
1258
1259         content_t id = CONTENT_IGNORE;
1260         if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1261                 // Get new id
1262                 id = allocateId();
1263                 if (id == CONTENT_IGNORE) {
1264                         warningstream << "NodeDefManager: Absolute "
1265                                 "limit reached" << std::endl;
1266                         return CONTENT_IGNORE;
1267                 }
1268                 assert(id != CONTENT_IGNORE);
1269                 addNameIdMapping(id, name);
1270         }
1271         m_content_features[id] = def;
1272         verbosestream << "NodeDefManager: registering content id \"" << id
1273                 << "\": name=\"" << def.name << "\""<<std::endl;
1274
1275         getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1276         fixSelectionBoxIntUnion();
1277         // Add this content to the list of all groups it belongs to
1278         // FIXME: This should remove a node from groups it no longer
1279         // belongs to when a node is re-registered
1280         for (ItemGroupList::const_iterator i = def.groups.begin();
1281                 i != def.groups.end(); ++i) {
1282                 std::string group_name = i->first;
1283
1284                 UNORDERED_MAP<std::string, GroupItems>::iterator
1285                         j = m_group_to_items.find(group_name);
1286                 if (j == m_group_to_items.end()) {
1287                         m_group_to_items[group_name].push_back(
1288                                 std::make_pair(id, i->second));
1289                 } else {
1290                         GroupItems &items = j->second;
1291                         items.push_back(std::make_pair(id, i->second));
1292                 }
1293         }
1294         return id;
1295 }
1296
1297
1298 content_t CNodeDefManager::allocateDummy(const std::string &name)
1299 {
1300         assert(name != "");     // Pre-condition
1301         ContentFeatures f;
1302         f.name = name;
1303         return set(name, f);
1304 }
1305
1306
1307 void CNodeDefManager::removeNode(const std::string &name)
1308 {
1309         // Pre-condition
1310         assert(name != "");
1311
1312         // Erase name from name ID mapping
1313         content_t id = CONTENT_IGNORE;
1314         if (m_name_id_mapping.getId(name, id)) {
1315                 m_name_id_mapping.eraseName(name);
1316                 m_name_id_mapping_with_aliases.erase(name);
1317         }
1318
1319         // Erase node content from all groups it belongs to
1320         for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1321                         m_group_to_items.begin();
1322                         iter_groups != m_group_to_items.end();) {
1323                 GroupItems &items = iter_groups->second;
1324                 for (GroupItems::iterator iter_groupitems = items.begin();
1325                                 iter_groupitems != items.end();) {
1326                         if (iter_groupitems->first == id)
1327                                 items.erase(iter_groupitems++);
1328                         else
1329                                 iter_groupitems++;
1330                 }
1331
1332                 // Check if group is empty
1333                 if (items.size() == 0)
1334                         m_group_to_items.erase(iter_groups++);
1335                 else
1336                         iter_groups++;
1337         }
1338 }
1339
1340
1341 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1342 {
1343         std::set<std::string> all;
1344         idef->getAll(all);
1345         m_name_id_mapping_with_aliases.clear();
1346         for (std::set<std::string>::const_iterator
1347                         i = all.begin(); i != all.end(); ++i) {
1348                 const std::string &name = *i;
1349                 const std::string &convert_to = idef->getAlias(name);
1350                 content_t id;
1351                 if (m_name_id_mapping.getId(convert_to, id)) {
1352                         m_name_id_mapping_with_aliases.insert(
1353                                 std::make_pair(name, id));
1354                 }
1355         }
1356 }
1357
1358 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1359 {
1360         infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1361                 "overrides to textures from " << override_filepath << std::endl;
1362
1363         std::ifstream infile(override_filepath.c_str());
1364         std::string line;
1365         int line_c = 0;
1366         while (std::getline(infile, line)) {
1367                 line_c++;
1368                 if (trim(line) == "")
1369                         continue;
1370                 std::vector<std::string> splitted = str_split(line, ' ');
1371                 if (splitted.size() != 3) {
1372                         errorstream << override_filepath
1373                                 << ":" << line_c << " Could not apply texture override \""
1374                                 << line << "\": Syntax error" << std::endl;
1375                         continue;
1376                 }
1377
1378                 content_t id;
1379                 if (!getId(splitted[0], id))
1380                         continue; // Ignore unknown node
1381
1382                 ContentFeatures &nodedef = m_content_features[id];
1383
1384                 if (splitted[1] == "top")
1385                         nodedef.tiledef[0].name = splitted[2];
1386                 else if (splitted[1] == "bottom")
1387                         nodedef.tiledef[1].name = splitted[2];
1388                 else if (splitted[1] == "right")
1389                         nodedef.tiledef[2].name = splitted[2];
1390                 else if (splitted[1] == "left")
1391                         nodedef.tiledef[3].name = splitted[2];
1392                 else if (splitted[1] == "back")
1393                         nodedef.tiledef[4].name = splitted[2];
1394                 else if (splitted[1] == "front")
1395                         nodedef.tiledef[5].name = splitted[2];
1396                 else if (splitted[1] == "all" || splitted[1] == "*")
1397                         for (int i = 0; i < 6; i++)
1398                                 nodedef.tiledef[i].name = splitted[2];
1399                 else if (splitted[1] == "sides")
1400                         for (int i = 2; i < 6; i++)
1401                                 nodedef.tiledef[i].name = splitted[2];
1402                 else {
1403                         errorstream << override_filepath
1404                                 << ":" << line_c << " Could not apply texture override \""
1405                                 << line << "\": Unknown node side \""
1406                                 << splitted[1] << "\"" << std::endl;
1407                         continue;
1408                 }
1409         }
1410 }
1411
1412 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1413         void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1414         void *progress_callback_args)
1415 {
1416 #ifndef SERVER
1417         infostream << "CNodeDefManager::updateTextures(): Updating "
1418                 "textures in node definitions" << std::endl;
1419
1420         Client *client = (Client *)gamedef;
1421         ITextureSource *tsrc = client->tsrc();
1422         IShaderSource *shdsrc = client->getShaderSource();
1423         scene::ISceneManager* smgr = client->getSceneManager();
1424         scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1425         TextureSettings tsettings;
1426         tsettings.readSettings();
1427
1428         u32 size = m_content_features.size();
1429
1430         for (u32 i = 0; i < size; i++) {
1431                 ContentFeatures *f = &(m_content_features[i]);
1432                 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1433                 progress_callback(progress_callback_args, i, size);
1434         }
1435 #endif
1436 }
1437
1438 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1439 {
1440         writeU8(os, 1); // version
1441         u16 count = 0;
1442         std::ostringstream os2(std::ios::binary);
1443         for (u32 i = 0; i < m_content_features.size(); i++) {
1444                 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1445                                 || i == CONTENT_UNKNOWN)
1446                         continue;
1447                 const ContentFeatures *f = &m_content_features[i];
1448                 if (f->name == "")
1449                         continue;
1450                 writeU16(os2, i);
1451                 // Wrap it in a string to allow different lengths without
1452                 // strict version incompatibilities
1453                 std::ostringstream wrapper_os(std::ios::binary);
1454                 f->serialize(wrapper_os, protocol_version);
1455                 os2<<serializeString(wrapper_os.str());
1456
1457                 // must not overflow
1458                 u16 next = count + 1;
1459                 FATAL_ERROR_IF(next < count, "Overflow");
1460                 count++;
1461         }
1462         writeU16(os, count);
1463         os << serializeLongString(os2.str());
1464 }
1465
1466
1467 void CNodeDefManager::deSerialize(std::istream &is)
1468 {
1469         clear();
1470         int version = readU8(is);
1471         if (version != 1)
1472                 throw SerializationError("unsupported NodeDefinitionManager version");
1473         u16 count = readU16(is);
1474         std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1475         ContentFeatures f;
1476         for (u16 n = 0; n < count; n++) {
1477                 u16 i = readU16(is2);
1478
1479                 // Read it from the string wrapper
1480                 std::string wrapper = deSerializeString(is2);
1481                 std::istringstream wrapper_is(wrapper, std::ios::binary);
1482                 f.deSerialize(wrapper_is);
1483
1484                 // Check error conditions
1485                 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1486                         warningstream << "NodeDefManager::deSerialize(): "
1487                                 "not changing builtin node " << i << std::endl;
1488                         continue;
1489                 }
1490                 if (f.name == "") {
1491                         warningstream << "NodeDefManager::deSerialize(): "
1492                                 "received empty name" << std::endl;
1493                         continue;
1494                 }
1495
1496                 // Ignore aliases
1497                 u16 existing_id;
1498                 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1499                         warningstream << "NodeDefManager::deSerialize(): "
1500                                 "already defined with different ID: " << f.name << std::endl;
1501                         continue;
1502                 }
1503
1504                 // All is ok, add node definition with the requested ID
1505                 if (i >= m_content_features.size())
1506                         m_content_features.resize((u32)(i) + 1);
1507                 m_content_features[i] = f;
1508                 addNameIdMapping(i, f.name);
1509                 verbosestream << "deserialized " << f.name << std::endl;
1510
1511                 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1512                 fixSelectionBoxIntUnion();
1513         }
1514 }
1515
1516
1517 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1518 {
1519         m_name_id_mapping.set(i, name);
1520         m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1521 }
1522
1523
1524 IWritableNodeDefManager *createNodeDefManager()
1525 {
1526         return new CNodeDefManager();
1527 }
1528
1529
1530 //// Serialization of old ContentFeatures formats
1531 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1532 {
1533         u8 compatible_param_type_2 = param_type_2;
1534         if ((protocol_version < 28)
1535                         && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1536                 compatible_param_type_2 = CPT2_NONE;
1537         else if (protocol_version < 30) {
1538                 if (compatible_param_type_2 == CPT2_COLOR)
1539                         compatible_param_type_2 = CPT2_NONE;
1540                 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1541                         compatible_param_type_2 = CPT2_FACEDIR;
1542                 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1543                         compatible_param_type_2 = CPT2_WALLMOUNTED;
1544         }
1545
1546         float compatible_visual_scale = visual_scale;
1547         if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1548                 compatible_visual_scale = sqrt(visual_scale);
1549
1550         TileDef compatible_tiles[6];
1551         for (u8 i = 0; i < 6; i++) {
1552                 compatible_tiles[i] = tiledef[i];
1553                 if (tiledef_overlay[i].name != "") {
1554                         std::stringstream s;
1555                         s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name
1556                                 << ")";
1557                         compatible_tiles[i].name = s.str();
1558                 }
1559         }
1560
1561         // Protocol >= 24
1562         if (protocol_version < 31) {
1563                 writeU8(os, protocol_version < 27 ? 7 : 8);
1564
1565                 os << serializeString(name);
1566                 writeU16(os, groups.size());
1567                 for (ItemGroupList::const_iterator i = groups.begin();
1568                                 i != groups.end(); ++i) {
1569                         os << serializeString(i->first);
1570                         writeS16(os, i->second);
1571                 }
1572                 writeU8(os, drawtype);
1573                 writeF1000(os, compatible_visual_scale);
1574                 writeU8(os, 6);
1575                 for (u32 i = 0; i < 6; i++)
1576                         compatible_tiles[i].serialize(os, protocol_version);
1577                 writeU8(os, CF_SPECIAL_COUNT);
1578                 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1579                         tiledef_special[i].serialize(os, protocol_version);
1580                 writeU8(os, alpha);
1581                 writeU8(os, post_effect_color.getAlpha());
1582                 writeU8(os, post_effect_color.getRed());
1583                 writeU8(os, post_effect_color.getGreen());
1584                 writeU8(os, post_effect_color.getBlue());
1585                 writeU8(os, param_type);
1586                 writeU8(os, compatible_param_type_2);
1587                 writeU8(os, is_ground_content);
1588                 writeU8(os, light_propagates);
1589                 writeU8(os, sunlight_propagates);
1590                 writeU8(os, walkable);
1591                 writeU8(os, pointable);
1592                 writeU8(os, diggable);
1593                 writeU8(os, climbable);
1594                 writeU8(os, buildable_to);
1595                 os << serializeString(""); // legacy: used to be metadata_name
1596                 writeU8(os, liquid_type);
1597                 os << serializeString(liquid_alternative_flowing);
1598                 os << serializeString(liquid_alternative_source);
1599                 writeU8(os, liquid_viscosity);
1600                 writeU8(os, liquid_renewable);
1601                 writeU8(os, light_source);
1602                 writeU32(os, damage_per_second);
1603                 node_box.serialize(os, protocol_version);
1604                 selection_box.serialize(os, protocol_version);
1605                 writeU8(os, legacy_facedir_simple);
1606                 writeU8(os, legacy_wallmounted);
1607                 serializeSimpleSoundSpec(sound_footstep, os);
1608                 serializeSimpleSoundSpec(sound_dig, os);
1609                 serializeSimpleSoundSpec(sound_dug, os);
1610                 writeU8(os, rightclickable);
1611                 writeU8(os, drowning);
1612                 writeU8(os, leveled);
1613                 writeU8(os, liquid_range);
1614                 writeU8(os, waving);
1615                 os << serializeString(mesh);
1616                 collision_box.serialize(os, protocol_version);
1617                 writeU8(os, floodable);
1618                 writeU16(os, connects_to_ids.size());
1619                 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1620                                 i != connects_to_ids.end(); ++i)
1621                         writeU16(os, *i);
1622                 writeU8(os, connect_sides);
1623         } else {
1624                 throw SerializationError("ContentFeatures::serialize(): "
1625                         "Unsupported version requested");
1626         }
1627 }
1628
1629 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1630 {
1631         if (version == 5) // In PROTOCOL_VERSION 13
1632         {
1633                 name = deSerializeString(is);
1634                 groups.clear();
1635                 u32 groups_size = readU16(is);
1636                 for(u32 i=0; i<groups_size; i++){
1637                         std::string name = deSerializeString(is);
1638                         int value = readS16(is);
1639                         groups[name] = value;
1640                 }
1641                 drawtype = (enum NodeDrawType)readU8(is);
1642
1643                 visual_scale = readF1000(is);
1644                 if (readU8(is) != 6)
1645                         throw SerializationError("unsupported tile count");
1646                 for (u32 i = 0; i < 6; i++)
1647                         tiledef[i].deSerialize(is, version, drawtype);
1648                 if (readU8(is) != CF_SPECIAL_COUNT)
1649                         throw SerializationError("unsupported CF_SPECIAL_COUNT");
1650                 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1651                         tiledef_special[i].deSerialize(is, version, drawtype);
1652                 alpha = readU8(is);
1653                 post_effect_color.setAlpha(readU8(is));
1654                 post_effect_color.setRed(readU8(is));
1655                 post_effect_color.setGreen(readU8(is));
1656                 post_effect_color.setBlue(readU8(is));
1657                 param_type = (enum ContentParamType)readU8(is);
1658                 param_type_2 = (enum ContentParamType2)readU8(is);
1659                 is_ground_content = readU8(is);
1660                 light_propagates = readU8(is);
1661                 sunlight_propagates = readU8(is);
1662                 walkable = readU8(is);
1663                 pointable = readU8(is);
1664                 diggable = readU8(is);
1665                 climbable = readU8(is);
1666                 buildable_to = readU8(is);
1667                 deSerializeString(is); // legacy: used to be metadata_name
1668                 liquid_type = (enum LiquidType)readU8(is);
1669                 liquid_alternative_flowing = deSerializeString(is);
1670                 liquid_alternative_source = deSerializeString(is);
1671                 liquid_viscosity = readU8(is);
1672                 light_source = readU8(is);
1673                 light_source = MYMIN(light_source, LIGHT_MAX);
1674                 damage_per_second = readU32(is);
1675                 node_box.deSerialize(is);
1676                 selection_box.deSerialize(is);
1677                 legacy_facedir_simple = readU8(is);
1678                 legacy_wallmounted = readU8(is);
1679                 deSerializeSimpleSoundSpec(sound_footstep, is);
1680                 deSerializeSimpleSoundSpec(sound_dig, is);
1681                 deSerializeSimpleSoundSpec(sound_dug, is);
1682         } else if (version == 6) {
1683                 name = deSerializeString(is);
1684                 groups.clear();
1685                 u32 groups_size = readU16(is);
1686                 for (u32 i = 0; i < groups_size; i++) {
1687                         std::string name = deSerializeString(is);
1688                         int     value = readS16(is);
1689                         groups[name] = value;
1690                 }
1691                 drawtype = (enum NodeDrawType)readU8(is);
1692                 visual_scale = readF1000(is);
1693                 if (readU8(is) != 6)
1694                         throw SerializationError("unsupported tile count");
1695                 for (u32 i = 0; i < 6; i++)
1696                         tiledef[i].deSerialize(is, version, drawtype);
1697                 // CF_SPECIAL_COUNT in version 6 = 2
1698                 if (readU8(is) != 2)
1699                         throw SerializationError("unsupported CF_SPECIAL_COUNT");
1700                 for (u32 i = 0; i < 2; i++)
1701                         tiledef_special[i].deSerialize(is, version, drawtype);
1702                 alpha = readU8(is);
1703                 post_effect_color.setAlpha(readU8(is));
1704                 post_effect_color.setRed(readU8(is));
1705                 post_effect_color.setGreen(readU8(is));
1706                 post_effect_color.setBlue(readU8(is));
1707                 param_type = (enum ContentParamType)readU8(is);
1708                 param_type_2 = (enum ContentParamType2)readU8(is);
1709                 is_ground_content = readU8(is);
1710                 light_propagates = readU8(is);
1711                 sunlight_propagates = readU8(is);
1712                 walkable = readU8(is);
1713                 pointable = readU8(is);
1714                 diggable = readU8(is);
1715                 climbable = readU8(is);
1716                 buildable_to = readU8(is);
1717                 deSerializeString(is); // legacy: used to be metadata_name
1718                 liquid_type = (enum LiquidType)readU8(is);
1719                 liquid_alternative_flowing = deSerializeString(is);
1720                 liquid_alternative_source = deSerializeString(is);
1721                 liquid_viscosity = readU8(is);
1722                 liquid_renewable = readU8(is);
1723                 light_source = readU8(is);
1724                 damage_per_second = readU32(is);
1725                 node_box.deSerialize(is);
1726                 selection_box.deSerialize(is);
1727                 legacy_facedir_simple = readU8(is);
1728                 legacy_wallmounted = readU8(is);
1729                 deSerializeSimpleSoundSpec(sound_footstep, is);
1730                 deSerializeSimpleSoundSpec(sound_dig, is);
1731                 deSerializeSimpleSoundSpec(sound_dug, is);
1732                 rightclickable = readU8(is);
1733                 drowning = readU8(is);
1734                 leveled = readU8(is);
1735                 liquid_range = readU8(is);
1736         } else if (version == 7 || version == 8){
1737                 name = deSerializeString(is);
1738                 groups.clear();
1739                 u32 groups_size = readU16(is);
1740                 for (u32 i = 0; i < groups_size; i++) {
1741                         std::string name = deSerializeString(is);
1742                         int value = readS16(is);
1743                         groups[name] = value;
1744                 }
1745                 drawtype = (enum NodeDrawType) readU8(is);
1746
1747                 visual_scale = readF1000(is);
1748                 if (readU8(is) != 6)
1749                         throw SerializationError("unsupported tile count");
1750                 for (u32 i = 0; i < 6; i++)
1751                         tiledef[i].deSerialize(is, version, drawtype);
1752                 if (readU8(is) != CF_SPECIAL_COUNT)
1753                         throw SerializationError("unsupported CF_SPECIAL_COUNT");
1754                 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1755                         tiledef_special[i].deSerialize(is, version, drawtype);
1756                 alpha = readU8(is);
1757                 post_effect_color.setAlpha(readU8(is));
1758                 post_effect_color.setRed(readU8(is));
1759                 post_effect_color.setGreen(readU8(is));
1760                 post_effect_color.setBlue(readU8(is));
1761                 param_type = (enum ContentParamType) readU8(is);
1762                 param_type_2 = (enum ContentParamType2) readU8(is);
1763                 is_ground_content = readU8(is);
1764                 light_propagates = readU8(is);
1765                 sunlight_propagates = readU8(is);
1766                 walkable = readU8(is);
1767                 pointable = readU8(is);
1768                 diggable = readU8(is);
1769                 climbable = readU8(is);
1770                 buildable_to = readU8(is);
1771                 deSerializeString(is); // legacy: used to be metadata_name
1772                 liquid_type = (enum LiquidType) readU8(is);
1773                 liquid_alternative_flowing = deSerializeString(is);
1774                 liquid_alternative_source = deSerializeString(is);
1775                 liquid_viscosity = readU8(is);
1776                 liquid_renewable = readU8(is);
1777                 light_source = readU8(is);
1778                 light_source = MYMIN(light_source, LIGHT_MAX);
1779                 damage_per_second = readU32(is);
1780                 node_box.deSerialize(is);
1781                 selection_box.deSerialize(is);
1782                 legacy_facedir_simple = readU8(is);
1783                 legacy_wallmounted = readU8(is);
1784                 deSerializeSimpleSoundSpec(sound_footstep, is);
1785                 deSerializeSimpleSoundSpec(sound_dig, is);
1786                 deSerializeSimpleSoundSpec(sound_dug, is);
1787                 rightclickable = readU8(is);
1788                 drowning = readU8(is);
1789                 leveled = readU8(is);
1790                 liquid_range = readU8(is);
1791                 waving = readU8(is);
1792                 try {
1793                         mesh = deSerializeString(is);
1794                         collision_box.deSerialize(is);
1795                         floodable = readU8(is);
1796                         u16 connects_to_size = readU16(is);
1797                         connects_to_ids.clear();
1798                         for (u16 i = 0; i < connects_to_size; i++)
1799                                 connects_to_ids.insert(readU16(is));
1800                         connect_sides = readU8(is);
1801                 } catch (SerializationError &e) {};
1802         }else{
1803                 throw SerializationError("unsupported ContentFeatures version");
1804         }
1805 }
1806
1807
1808 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1809 {
1810         return m_node_registration_complete;
1811 }
1812
1813
1814 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1815 {
1816         m_node_registration_complete = completed;
1817 }
1818
1819
1820 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1821 {
1822         nr->m_ndef = this;
1823         if (m_node_registration_complete)
1824                 nr->nodeResolveInternal();
1825         else
1826                 m_pending_resolve_callbacks.push_back(nr);
1827 }
1828
1829
1830 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1831 {
1832         size_t len = m_pending_resolve_callbacks.size();
1833         for (size_t i = 0; i != len; i++) {
1834                 if (nr != m_pending_resolve_callbacks[i])
1835                         continue;
1836
1837                 len--;
1838                 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1839                 m_pending_resolve_callbacks.resize(len);
1840                 return true;
1841         }
1842
1843         return false;
1844 }
1845
1846
1847 void CNodeDefManager::runNodeResolveCallbacks()
1848 {
1849         for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1850                 NodeResolver *nr = m_pending_resolve_callbacks[i];
1851                 nr->nodeResolveInternal();
1852         }
1853
1854         m_pending_resolve_callbacks.clear();
1855 }
1856
1857
1858 void CNodeDefManager::resetNodeResolveState()
1859 {
1860         m_node_registration_complete = false;
1861         m_pending_resolve_callbacks.clear();
1862 }
1863
1864 void CNodeDefManager::mapNodeboxConnections()
1865 {
1866         for (u32 i = 0; i < m_content_features.size(); i++) {
1867                 ContentFeatures *f = &m_content_features[i];
1868                 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1869                         continue;
1870                 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1871                                 it != f->connects_to.end(); ++it) {
1872                         getIds(*it, f->connects_to_ids);
1873                 }
1874         }
1875 }
1876
1877 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1878 {
1879         const ContentFeatures &f1 = get(from);
1880
1881         if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1882                 return false;
1883
1884         // lookup target in connected set
1885         if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1886                 return false;
1887
1888         const ContentFeatures &f2 = get(to);
1889
1890         if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1891                 // ignores actually looking if back connection exists
1892                 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1893
1894         // does to node declare usable faces?
1895         if (f2.connect_sides > 0) {
1896                 if ((f2.param_type_2 == CPT2_FACEDIR ||
1897                                 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1898                                 && (connect_face >= 4)) {
1899                         static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1900                                 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1901                                 0, // 4 - back
1902                                 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1903                                 0, // 8 - right
1904                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1905                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1906                                 0, // 16 - front
1907                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1908                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1909                                 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1910                                 };
1911                         return (f2.connect_sides
1912                                 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1913                 }
1914                 return (f2.connect_sides & connect_face);
1915         }
1916         // the target is just a regular node, so connect no matter back connection
1917         return true;
1918 }
1919
1920 ////
1921 //// NodeResolver
1922 ////
1923
1924 NodeResolver::NodeResolver()
1925 {
1926         m_ndef            = NULL;
1927         m_nodenames_idx   = 0;
1928         m_nnlistsizes_idx = 0;
1929         m_resolve_done    = false;
1930
1931         m_nodenames.reserve(16);
1932         m_nnlistsizes.reserve(4);
1933 }
1934
1935
1936 NodeResolver::~NodeResolver()
1937 {
1938         if (!m_resolve_done && m_ndef)
1939                 m_ndef->cancelNodeResolveCallback(this);
1940 }
1941
1942
1943 void NodeResolver::nodeResolveInternal()
1944 {
1945         m_nodenames_idx   = 0;
1946         m_nnlistsizes_idx = 0;
1947
1948         resolveNodeNames();
1949         m_resolve_done = true;
1950
1951         m_nodenames.clear();
1952         m_nnlistsizes.clear();
1953 }
1954
1955
1956 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1957         const std::string &node_alt, content_t c_fallback)
1958 {
1959         if (m_nodenames_idx == m_nodenames.size()) {
1960                 *result_out = c_fallback;
1961                 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1962                 return false;
1963         }
1964
1965         content_t c;
1966         std::string name = m_nodenames[m_nodenames_idx++];
1967
1968         bool success = m_ndef->getId(name, c);
1969         if (!success && node_alt != "") {
1970                 name = node_alt;
1971                 success = m_ndef->getId(name, c);
1972         }
1973
1974         if (!success) {
1975                 errorstream << "NodeResolver: failed to resolve node name '" << name
1976                         << "'." << std::endl;
1977                 c = c_fallback;
1978         }
1979
1980         *result_out = c;
1981         return success;
1982 }
1983
1984
1985 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1986         bool all_required, content_t c_fallback)
1987 {
1988         bool success = true;
1989
1990         if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1991                 errorstream << "NodeResolver: no more node lists" << std::endl;
1992                 return false;
1993         }
1994
1995         size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1996
1997         while (length--) {
1998                 if (m_nodenames_idx == m_nodenames.size()) {
1999                         errorstream << "NodeResolver: no more nodes in list" << std::endl;
2000                         return false;
2001                 }
2002
2003                 content_t c;
2004                 std::string &name = m_nodenames[m_nodenames_idx++];
2005
2006                 if (name.substr(0,6) != "group:") {
2007                         if (m_ndef->getId(name, c)) {
2008                                 result_out->push_back(c);
2009                         } else if (all_required) {
2010                                 errorstream << "NodeResolver: failed to resolve node name '"
2011                                         << name << "'." << std::endl;
2012                                 result_out->push_back(c_fallback);
2013                                 success = false;
2014                         }
2015                 } else {
2016                         std::set<content_t> cids;
2017                         std::set<content_t>::iterator it;
2018                         m_ndef->getIds(name, cids);
2019                         for (it = cids.begin(); it != cids.end(); ++it)
2020                                 result_out->push_back(*it);
2021                 }
2022         }
2023
2024         return success;
2025 }