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