]> git.lizzy.rs Git - dragonfireclient.git/blob - src/nodedef.cpp
f528a5a502145c6e8ea6c4a021902eddb4ee132c
[dragonfireclient.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/mesh.h"
25 #include "client/shader.h"
26 #include "client/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 #include <algorithm>
42 #include <cmath>
43
44 /*
45         NodeBox
46 */
47
48 void NodeBox::reset()
49 {
50         type = NODEBOX_REGULAR;
51         // default is empty
52         fixed.clear();
53         // default is sign/ladder-like
54         wall_top = aabb3f(-BS / 2, BS / 2 - BS / 16., -BS / 2, BS / 2, BS / 2, BS / 2);
55         wall_bottom = aabb3f(
56                         -BS / 2, -BS / 2, -BS / 2, BS / 2, -BS / 2 + BS / 16., BS / 2);
57         wall_side = aabb3f(-BS / 2, -BS / 2, -BS / 2, -BS / 2 + BS / 16., BS / 2, BS / 2);
58         // no default for other parts
59         connect_top.clear();
60         connect_bottom.clear();
61         connect_front.clear();
62         connect_left.clear();
63         connect_back.clear();
64         connect_right.clear();
65         disconnected_top.clear();
66         disconnected_bottom.clear();
67         disconnected_front.clear();
68         disconnected_left.clear();
69         disconnected_back.clear();
70         disconnected_right.clear();
71         disconnected.clear();
72         disconnected_sides.clear();
73 }
74
75 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
76 {
77         // Protocol >= 36
78         const u8 version = 6;
79         writeU8(os, version);
80
81         switch (type) {
82         case NODEBOX_LEVELED:
83         case NODEBOX_FIXED:
84                 writeU8(os, type);
85
86                 writeU16(os, fixed.size());
87                 for (const aabb3f &nodebox : fixed) {
88                         writeV3F32(os, nodebox.MinEdge);
89                         writeV3F32(os, nodebox.MaxEdge);
90                 }
91                 break;
92         case NODEBOX_WALLMOUNTED:
93                 writeU8(os, type);
94
95                 writeV3F32(os, wall_top.MinEdge);
96                 writeV3F32(os, wall_top.MaxEdge);
97                 writeV3F32(os, wall_bottom.MinEdge);
98                 writeV3F32(os, wall_bottom.MaxEdge);
99                 writeV3F32(os, wall_side.MinEdge);
100                 writeV3F32(os, wall_side.MaxEdge);
101                 break;
102         case NODEBOX_CONNECTED:
103                 writeU8(os, type);
104
105 #define WRITEBOX(box)                                                                    \
106         writeU16(os, (box).size());                                                      \
107         for (const aabb3f &i : (box)) {                                                  \
108                 writeV3F32(os, i.MinEdge);                                               \
109                 writeV3F32(os, i.MaxEdge);                                               \
110         };
111
112                 WRITEBOX(fixed);
113                 WRITEBOX(connect_top);
114                 WRITEBOX(connect_bottom);
115                 WRITEBOX(connect_front);
116                 WRITEBOX(connect_left);
117                 WRITEBOX(connect_back);
118                 WRITEBOX(connect_right);
119                 WRITEBOX(disconnected_top);
120                 WRITEBOX(disconnected_bottom);
121                 WRITEBOX(disconnected_front);
122                 WRITEBOX(disconnected_left);
123                 WRITEBOX(disconnected_back);
124                 WRITEBOX(disconnected_right);
125                 WRITEBOX(disconnected);
126                 WRITEBOX(disconnected_sides);
127                 break;
128         default:
129                 writeU8(os, type);
130                 break;
131         }
132 }
133
134 void NodeBox::deSerialize(std::istream &is)
135 {
136         int version = readU8(is);
137         if (version < 6)
138                 throw SerializationError("unsupported NodeBox version");
139
140         reset();
141
142         type = (enum NodeBoxType)readU8(is);
143
144         if (type == NODEBOX_FIXED || type == NODEBOX_LEVELED) {
145                 u16 fixed_count = readU16(is);
146                 while (fixed_count--) {
147                         aabb3f box;
148                         box.MinEdge = readV3F32(is);
149                         box.MaxEdge = readV3F32(is);
150                         fixed.push_back(box);
151                 }
152         } else if (type == NODEBOX_WALLMOUNTED) {
153                 wall_top.MinEdge = readV3F32(is);
154                 wall_top.MaxEdge = readV3F32(is);
155                 wall_bottom.MinEdge = readV3F32(is);
156                 wall_bottom.MaxEdge = readV3F32(is);
157                 wall_side.MinEdge = readV3F32(is);
158                 wall_side.MaxEdge = readV3F32(is);
159         } else if (type == NODEBOX_CONNECTED) {
160 #define READBOXES(box)                                                                   \
161         {                                                                                \
162                 count = readU16(is);                                                     \
163                 (box).reserve(count);                                                    \
164                 while (count--) {                                                        \
165                         v3f min = readV3F32(is);                                         \
166                         v3f max = readV3F32(is);                                         \
167                         (box).emplace_back(min, max);                                    \
168                 };                                                                       \
169         }
170
171                 u16 count;
172
173                 READBOXES(fixed);
174                 READBOXES(connect_top);
175                 READBOXES(connect_bottom);
176                 READBOXES(connect_front);
177                 READBOXES(connect_left);
178                 READBOXES(connect_back);
179                 READBOXES(connect_right);
180                 READBOXES(disconnected_top);
181                 READBOXES(disconnected_bottom);
182                 READBOXES(disconnected_front);
183                 READBOXES(disconnected_left);
184                 READBOXES(disconnected_back);
185                 READBOXES(disconnected_right);
186                 READBOXES(disconnected);
187                 READBOXES(disconnected_sides);
188         }
189 }
190
191 /*
192         TileDef
193 */
194
195 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
196 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
197 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
198 #define TILE_FLAG_HAS_COLOR (1 << 3)
199 #define TILE_FLAG_HAS_SCALE (1 << 4)
200 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
201
202 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
203 {
204         // protocol_version >= 36
205         u8 version = 6;
206         writeU8(os, version);
207
208         os << serializeString(name);
209         animation.serialize(os, version);
210         bool has_scale = scale > 0;
211         u16 flags = 0;
212         if (backface_culling)
213                 flags |= TILE_FLAG_BACKFACE_CULLING;
214         if (tileable_horizontal)
215                 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
216         if (tileable_vertical)
217                 flags |= TILE_FLAG_TILEABLE_VERTICAL;
218         if (has_color)
219                 flags |= TILE_FLAG_HAS_COLOR;
220         if (has_scale)
221                 flags |= TILE_FLAG_HAS_SCALE;
222         if (align_style != ALIGN_STYLE_NODE)
223                 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
224         writeU16(os, flags);
225         if (has_color) {
226                 writeU8(os, color.getRed());
227                 writeU8(os, color.getGreen());
228                 writeU8(os, color.getBlue());
229         }
230         if (has_scale)
231                 writeU8(os, scale);
232         if (align_style != ALIGN_STYLE_NODE)
233                 writeU8(os, align_style);
234 }
235
236 void TileDef::deSerialize(
237                 std::istream &is, u8 contentfeatures_version, NodeDrawType drawtype)
238 {
239         int version = readU8(is);
240         if (version < 6)
241                 throw SerializationError("unsupported TileDef version");
242         name = deSerializeString(is);
243         animation.deSerialize(is, version);
244         u16 flags = readU16(is);
245         backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
246         tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
247         tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
248         has_color = flags & TILE_FLAG_HAS_COLOR;
249         bool has_scale = flags & TILE_FLAG_HAS_SCALE;
250         bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
251         if (has_color) {
252                 color.setRed(readU8(is));
253                 color.setGreen(readU8(is));
254                 color.setBlue(readU8(is));
255         }
256         scale = has_scale ? readU8(is) : 0;
257         if (has_align_style)
258                 align_style = static_cast<AlignStyle>(readU8(is));
259         else
260                 align_style = ALIGN_STYLE_NODE;
261 }
262
263 void TextureSettings::readSettings()
264 {
265         connected_glass = g_settings->getBool("connected_glass");
266         opaque_water = g_settings->getBool("opaque_water");
267         bool enable_shaders = g_settings->getBool("enable_shaders");
268         bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
269         bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
270         bool smooth_lighting = g_settings->getBool("smooth_lighting");
271         enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
272         enable_minimap = g_settings->getBool("enable_minimap");
273         node_texture_size = g_settings->getU16("texture_min_size");
274         std::string leaves_style_str = g_settings->get("leaves_style");
275         std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
276         std::string autoscale_mode_str = g_settings->get("autoscale_mode");
277
278         // Mesh cache is not supported in combination with smooth lighting
279         if (smooth_lighting)
280                 enable_mesh_cache = false;
281
282         use_normal_texture = enable_shaders &&
283                              (enable_bumpmapping || enable_parallax_occlusion);
284         if (leaves_style_str == "fancy") {
285                 leaves_style = LEAVES_FANCY;
286         } else if (leaves_style_str == "simple") {
287                 leaves_style = LEAVES_SIMPLE;
288         } else {
289                 leaves_style = LEAVES_OPAQUE;
290         }
291
292         if (world_aligned_mode_str == "enable")
293                 world_aligned_mode = WORLDALIGN_ENABLE;
294         else if (world_aligned_mode_str == "force_solid")
295                 world_aligned_mode = WORLDALIGN_FORCE;
296         else if (world_aligned_mode_str == "force_nodebox")
297                 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
298         else
299                 world_aligned_mode = WORLDALIGN_DISABLE;
300
301         if (autoscale_mode_str == "enable")
302                 autoscale_mode = AUTOSCALE_ENABLE;
303         else if (autoscale_mode_str == "force")
304                 autoscale_mode = AUTOSCALE_FORCE;
305         else
306                 autoscale_mode = AUTOSCALE_DISABLE;
307 }
308
309 /*
310         ContentFeatures
311 */
312
313 ContentFeatures::ContentFeatures()
314 {
315         reset();
316 }
317
318 ContentFeatures::~ContentFeatures()
319 {
320 #ifndef SERVER
321         for (u16 j = 0; j < 6; j++) {
322                 delete tiles[j].layers[0].frames;
323                 delete tiles[j].layers[1].frames;
324         }
325         for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
326                 delete special_tiles[j].layers[0].frames;
327 #endif
328 }
329
330 void ContentFeatures::reset()
331 {
332         /*
333                 Cached stuff
334         */
335 #ifndef SERVER
336         solidness = 0;
337         visual_solidness = 0;
338         backface_culling = true;
339
340 #endif
341         has_on_construct = false;
342         has_on_destruct = false;
343         has_after_destruct = false;
344         /*
345                 Actual data
346
347                 NOTE: Most of this is always overridden by the default values given
348                       in builtin.lua
349         */
350         name = "";
351         groups.clear();
352         // Unknown nodes can be dug
353         groups["dig_immediate"] = 2;
354         drawtype = NDT_NORMAL;
355         mesh = "";
356 #ifndef SERVER
357         for (auto &i : mesh_ptr)
358                 i = NULL;
359         minimap_color = video::SColor(0, 0, 0, 0);
360 #endif
361         visual_scale = 1.0;
362         for (auto &i : tiledef)
363                 i = TileDef();
364         for (auto &j : tiledef_special)
365                 j = TileDef();
366         alpha = 255;
367         post_effect_color = video::SColor(0, 0, 0, 0);
368         param_type = CPT_NONE;
369         param_type_2 = CPT2_NONE;
370         is_ground_content = false;
371         light_propagates = false;
372         sunlight_propagates = false;
373         walkable = true;
374         pointable = true;
375         diggable = true;
376         climbable = false;
377         buildable_to = false;
378         floodable = false;
379         rightclickable = true;
380         leveled = 0;
381         leveled_max = LEVELED_MAX;
382         liquid_type = LIQUID_NONE;
383         liquid_alternative_flowing = "";
384         liquid_alternative_flowing_id = CONTENT_IGNORE;
385         liquid_alternative_source = "";
386         liquid_alternative_source_id = CONTENT_IGNORE;
387         liquid_viscosity = 0;
388         liquid_renewable = true;
389         liquid_range = LIQUID_LEVEL_MAX + 1;
390         drowning = 0;
391         light_source = 0;
392         damage_per_second = 0;
393         node_box = NodeBox();
394         selection_box = NodeBox();
395         collision_box = NodeBox();
396         waving = 0;
397         legacy_facedir_simple = false;
398         legacy_wallmounted = false;
399         sound_footstep = SimpleSoundSpec();
400         sound_dig = SimpleSoundSpec("__group");
401         sound_dug = SimpleSoundSpec();
402         connects_to.clear();
403         connects_to_ids.clear();
404         connect_sides = 0;
405         color = video::SColor(0xFFFFFFFF);
406         palette_name = "";
407         palette = NULL;
408         node_dig_prediction = "air";
409 }
410
411 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
412 {
413         const u8 version = CONTENTFEATURES_VERSION;
414         writeU8(os, version);
415
416         // general
417         os << serializeString(name);
418         writeU16(os, groups.size());
419         for (const auto &group : groups) {
420                 os << serializeString(group.first);
421                 writeS16(os, group.second);
422         }
423         writeU8(os, param_type);
424         writeU8(os, param_type_2);
425
426         // visual
427         writeU8(os, drawtype);
428         os << serializeString(mesh);
429         writeF32(os, visual_scale);
430         writeU8(os, 6);
431         for (const TileDef &td : tiledef)
432                 td.serialize(os, protocol_version);
433         for (const TileDef &td : tiledef_overlay)
434                 td.serialize(os, protocol_version);
435         writeU8(os, CF_SPECIAL_COUNT);
436         for (const TileDef &td : tiledef_special) {
437                 td.serialize(os, protocol_version);
438         }
439         writeU8(os, alpha);
440         writeU8(os, color.getRed());
441         writeU8(os, color.getGreen());
442         writeU8(os, color.getBlue());
443         os << serializeString(palette_name);
444         writeU8(os, waving);
445         writeU8(os, connect_sides);
446         writeU16(os, connects_to_ids.size());
447         for (u16 connects_to_id : connects_to_ids)
448                 writeU16(os, connects_to_id);
449         writeARGB8(os, post_effect_color);
450         writeU8(os, leveled);
451
452         // lighting
453         writeU8(os, light_propagates);
454         writeU8(os, sunlight_propagates);
455         writeU8(os, light_source);
456
457         // map generation
458         writeU8(os, is_ground_content);
459
460         // interaction
461         writeU8(os, walkable);
462         writeU8(os, pointable);
463         writeU8(os, diggable);
464         writeU8(os, climbable);
465         writeU8(os, buildable_to);
466         writeU8(os, rightclickable);
467         writeU32(os, damage_per_second);
468
469         // liquid
470         writeU8(os, liquid_type);
471         os << serializeString(liquid_alternative_flowing);
472         os << serializeString(liquid_alternative_source);
473         writeU8(os, liquid_viscosity);
474         writeU8(os, liquid_renewable);
475         writeU8(os, liquid_range);
476         writeU8(os, drowning);
477         writeU8(os, floodable);
478
479         // node boxes
480         node_box.serialize(os, protocol_version);
481         selection_box.serialize(os, protocol_version);
482         collision_box.serialize(os, protocol_version);
483
484         // sound
485         sound_footstep.serialize(os, version);
486         sound_dig.serialize(os, version);
487         sound_dug.serialize(os, version);
488
489         // legacy
490         writeU8(os, legacy_facedir_simple);
491         writeU8(os, legacy_wallmounted);
492
493         os << serializeString(node_dig_prediction);
494         writeU8(os, leveled_max);
495 }
496
497 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
498 {
499         // alpha == 0 means that the node is using texture alpha
500         if (alpha == 0 || alpha == 255)
501                 return;
502
503         for (int i = 0; i < length; i++) {
504                 if (tiles[i].name.empty())
505                         continue;
506                 std::stringstream s;
507                 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
508                 tiles[i].name = s.str();
509         }
510 }
511
512 void ContentFeatures::deSerialize(std::istream &is)
513 {
514         // version detection
515         const u8 version = readU8(is);
516         if (version < CONTENTFEATURES_VERSION)
517                 throw SerializationError("unsupported ContentFeatures version");
518
519         // general
520         name = deSerializeString(is);
521         groups.clear();
522         u32 groups_size = readU16(is);
523         for (u32 i = 0; i < groups_size; i++) {
524                 std::string name = deSerializeString(is);
525                 int value = readS16(is);
526                 groups[name] = value;
527         }
528         param_type = (enum ContentParamType)readU8(is);
529         param_type_2 = (enum ContentParamType2)readU8(is);
530
531         // visual
532         drawtype = (enum NodeDrawType)readU8(is);
533         mesh = deSerializeString(is);
534         visual_scale = readF32(is);
535         if (readU8(is) != 6)
536                 throw SerializationError("unsupported tile count");
537         for (TileDef &td : tiledef)
538                 td.deSerialize(is, version, drawtype);
539         for (TileDef &td : tiledef_overlay)
540                 td.deSerialize(is, version, drawtype);
541         if (readU8(is) != CF_SPECIAL_COUNT)
542                 throw SerializationError("unsupported CF_SPECIAL_COUNT");
543         for (TileDef &td : tiledef_special)
544                 td.deSerialize(is, version, drawtype);
545         alpha = readU8(is);
546         color.setRed(readU8(is));
547         color.setGreen(readU8(is));
548         color.setBlue(readU8(is));
549         palette_name = deSerializeString(is);
550         waving = readU8(is);
551         connect_sides = readU8(is);
552         u16 connects_to_size = readU16(is);
553         connects_to_ids.clear();
554         for (u16 i = 0; i < connects_to_size; i++)
555                 connects_to_ids.push_back(readU16(is));
556         post_effect_color = readARGB8(is);
557         leveled = readU8(is);
558
559         // lighting-related
560         light_propagates = readU8(is);
561         sunlight_propagates = readU8(is);
562         light_source = readU8(is);
563         light_source = MYMIN(light_source, LIGHT_MAX);
564
565         // map generation
566         is_ground_content = readU8(is);
567
568         // interaction
569         walkable = readU8(is);
570         pointable = readU8(is);
571         diggable = readU8(is);
572         climbable = readU8(is);
573         buildable_to = readU8(is);
574         rightclickable = readU8(is);
575         damage_per_second = readU32(is);
576
577         // liquid
578         liquid_type = (enum LiquidType)readU8(is);
579         liquid_alternative_flowing = deSerializeString(is);
580         liquid_alternative_source = deSerializeString(is);
581         liquid_viscosity = readU8(is);
582         liquid_renewable = readU8(is);
583         liquid_range = readU8(is);
584         drowning = readU8(is);
585         floodable = readU8(is);
586
587         // node boxes
588         node_box.deSerialize(is);
589         selection_box.deSerialize(is);
590         collision_box.deSerialize(is);
591
592         // sounds
593         sound_footstep.deSerialize(is, version);
594         sound_dig.deSerialize(is, version);
595         sound_dug.deSerialize(is, version);
596
597         // read legacy properties
598         legacy_facedir_simple = readU8(is);
599         legacy_wallmounted = readU8(is);
600
601         try {
602                 node_dig_prediction = deSerializeString(is);
603                 u8 tmp_leveled_max = readU8(is);
604                 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
605                         throw SerializationError("");
606                 leveled_max = tmp_leveled_max;
607         } catch (SerializationError &e) {
608         };
609 }
610
611 #ifndef SERVER
612 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer, const TileSpec &tile,
613                 const TileDef &tiledef, video::SColor color, u8 material_type,
614                 u32 shader_id, bool backface_culling, const TextureSettings &tsettings)
615 {
616         layer->shader_id = shader_id;
617         layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
618         layer->material_type = material_type;
619
620         bool has_scale = tiledef.scale > 0;
621         bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
622                              (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
623         if (use_autoscale && layer->texture) {
624                 auto texture_size = layer->texture->getOriginalSize();
625                 float base_size = tsettings.node_texture_size;
626                 float size = std::fmin(texture_size.Width, texture_size.Height);
627                 layer->scale = std::fmax(base_size, size) / base_size;
628         } else if (has_scale) {
629                 layer->scale = tiledef.scale;
630         } else {
631                 layer->scale = 1;
632         }
633         if (!tile.world_aligned)
634                 layer->scale = 1;
635
636         // Normal texture and shader flags texture
637         if (tsettings.use_normal_texture) {
638                 layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
639         }
640         layer->flags_texture =
641                         tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
642
643         // Material flags
644         layer->material_flags = 0;
645         if (backface_culling)
646                 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
647         if (tiledef.animation.type != TAT_NONE)
648                 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
649         if (tiledef.tileable_horizontal)
650                 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
651         if (tiledef.tileable_vertical)
652                 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
653
654         // Color
655         layer->has_color = tiledef.has_color;
656         if (tiledef.has_color)
657                 layer->color = tiledef.color;
658         else
659                 layer->color = color;
660
661         // Animation parameters
662         int frame_count = 1;
663         if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
664                 int frame_length_ms;
665                 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
666                                 &frame_count, &frame_length_ms, NULL);
667                 layer->animation_frame_count = frame_count;
668                 layer->animation_frame_length_ms = frame_length_ms;
669         }
670
671         if (frame_count == 1) {
672                 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
673         } else {
674                 std::ostringstream os(std::ios::binary);
675                 if (!layer->frames) {
676                         layer->frames = new std::vector<FrameSpec>();
677                 }
678                 layer->frames->resize(frame_count);
679
680                 for (int i = 0; i < frame_count; i++) {
681
682                         FrameSpec frame;
683
684                         os.str("");
685                         os << tiledef.name;
686                         tiledef.animation.getTextureModifer(
687                                         os, layer->texture->getOriginalSize(), i);
688
689                         frame.texture = tsrc->getTextureForMesh(
690                                         os.str(), &frame.texture_id);
691                         if (layer->normal_texture)
692                                 frame.normal_texture = tsrc->getNormalTexture(os.str());
693                         frame.flags_texture = layer->flags_texture;
694                         (*layer->frames)[i] = frame;
695                 }
696         }
697 }
698 #endif
699
700 #ifndef SERVER
701 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
702 {
703         if (style == ALIGN_STYLE_WORLD)
704                 return true;
705         if (mode == WORLDALIGN_DISABLE)
706                 return false;
707         if (style == ALIGN_STYLE_USER_DEFINED)
708                 return true;
709         if (drawtype == NDT_NORMAL)
710                 return mode >= WORLDALIGN_FORCE;
711         if (drawtype == NDT_NODEBOX)
712                 return mode >= WORLDALIGN_FORCE_NODEBOX;
713         return false;
714 }
715
716 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
717                 scene::IMeshManipulator *meshmanip, Client *client,
718                 const TextureSettings &tsettings)
719 {
720         // minimap pixel color - the average color of a texture
721         if (tsettings.enable_minimap && !tiledef[0].name.empty())
722                 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
723
724         // Figure out the actual tiles to use
725         TileDef tdef[6];
726         for (u32 j = 0; j < 6; j++) {
727                 tdef[j] = tiledef[j];
728                 if (tdef[j].name.empty())
729                         tdef[j].name = "unknown_node.png";
730         }
731         // also the overlay tiles
732         TileDef tdef_overlay[6];
733         for (u32 j = 0; j < 6; j++)
734                 tdef_overlay[j] = tiledef_overlay[j];
735         // also the special tiles
736         TileDef tdef_spec[6];
737         for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
738                 tdef_spec[j] = tiledef_special[j];
739
740         bool is_liquid = false;
741
742         u8 material_type = (alpha == 255) ? TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
743
744         switch (drawtype) {
745         default:
746         case NDT_NORMAL:
747                 material_type = (alpha == 255) ? TILE_MATERIAL_OPAQUE
748                                                : TILE_MATERIAL_ALPHA;
749                 solidness = 2;
750                 break;
751         case NDT_AIRLIKE:
752                 solidness = 0;
753                 break;
754         case NDT_LIQUID:
755                 assert(liquid_type == LIQUID_SOURCE);
756                 if (tsettings.opaque_water)
757                         alpha = 255;
758                 solidness = 1;
759                 is_liquid = true;
760                 break;
761         case NDT_FLOWINGLIQUID:
762                 assert(liquid_type == LIQUID_FLOWING);
763                 solidness = 0;
764                 if (tsettings.opaque_water)
765                         alpha = 255;
766                 is_liquid = true;
767                 break;
768         case NDT_GLASSLIKE:
769                 solidness = 0;
770                 visual_solidness = 1;
771                 break;
772         case NDT_GLASSLIKE_FRAMED:
773                 solidness = 0;
774                 visual_solidness = 1;
775                 break;
776         case NDT_GLASSLIKE_FRAMED_OPTIONAL:
777                 solidness = 0;
778                 visual_solidness = 1;
779                 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED
780                                                      : NDT_GLASSLIKE;
781                 break;
782         case NDT_ALLFACES:
783                 solidness = 0;
784                 visual_solidness = 1;
785                 break;
786         case NDT_ALLFACES_OPTIONAL:
787                 if (tsettings.leaves_style == LEAVES_FANCY) {
788                         drawtype = NDT_ALLFACES;
789                         solidness = 0;
790                         visual_solidness = 1;
791                 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
792                         for (u32 j = 0; j < 6; j++) {
793                                 if (!tdef_spec[j].name.empty())
794                                         tdef[j].name = tdef_spec[j].name;
795                         }
796                         drawtype = NDT_GLASSLIKE;
797                         solidness = 0;
798                         visual_solidness = 1;
799                 } else {
800                         drawtype = NDT_NORMAL;
801                         solidness = 2;
802                         for (TileDef &td : tdef)
803                                 td.name += std::string("^[noalpha");
804                 }
805                 if (waving >= 1)
806                         material_type = TILE_MATERIAL_WAVING_LEAVES;
807                 break;
808         case NDT_PLANTLIKE:
809                 solidness = 0;
810                 if (waving >= 1)
811                         material_type = TILE_MATERIAL_WAVING_PLANTS;
812                 break;
813         case NDT_FIRELIKE:
814                 solidness = 0;
815                 break;
816         case NDT_MESH:
817         case NDT_NODEBOX:
818                 solidness = 0;
819                 if (waving == 1)
820                         material_type = TILE_MATERIAL_WAVING_PLANTS;
821                 else if (waving == 2)
822                         material_type = TILE_MATERIAL_WAVING_LEAVES;
823                 else if (waving == 3)
824                         material_type = TILE_MATERIAL_WAVING_LIQUID_BASIC;
825                 break;
826         case NDT_TORCHLIKE:
827         case NDT_SIGNLIKE:
828         case NDT_FENCELIKE:
829         case NDT_RAILLIKE:
830                 solidness = 0;
831                 break;
832         case NDT_PLANTLIKE_ROOTED:
833                 solidness = 2;
834                 break;
835         }
836
837         if (is_liquid) {
838                 // Vertex alpha is no longer supported, correct if necessary.
839                 correctAlpha(tdef, 6);
840                 correctAlpha(tdef_overlay, 6);
841                 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
842
843                 if (waving == 3) {
844                         material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE
845                                                        : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT;
846                 } else {
847                         material_type = (alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE
848                                                        : TILE_MATERIAL_LIQUID_TRANSPARENT;
849                 }
850         }
851
852         u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
853
854         u8 overlay_material = material_type;
855         if (overlay_material == TILE_MATERIAL_OPAQUE)
856                 overlay_material = TILE_MATERIAL_BASIC;
857         else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
858                 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
859
860         u32 overlay_shader =
861                         shdsrc->getShader("nodes_shader", overlay_material, drawtype);
862
863         // Tiles (fill in f->tiles[])
864         for (u16 j = 0; j < 6; j++) {
865                 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
866                                 tsettings.world_aligned_mode, drawtype);
867                 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j], color,
868                                 material_type, tile_shader, tdef[j].backface_culling,
869                                 tsettings);
870                 if (!tdef_overlay[j].name.empty())
871                         fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j],
872                                         tdef_overlay[j], color, overlay_material,
873                                         overlay_shader, tdef[j].backface_culling,
874                                         tsettings);
875         }
876
877         u8 special_material = material_type;
878         if (drawtype == NDT_PLANTLIKE_ROOTED) {
879                 if (waving == 1)
880                         special_material = TILE_MATERIAL_WAVING_PLANTS;
881                 else if (waving == 2)
882                         special_material = TILE_MATERIAL_WAVING_LEAVES;
883         }
884         u32 special_shader =
885                         shdsrc->getShader("nodes_shader", special_material, drawtype);
886
887         // Special tiles (fill in f->special_tiles[])
888         for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
889                 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j],
890                                 tdef_spec[j], color, special_material, special_shader,
891                                 tdef_spec[j].backface_culling, tsettings);
892
893         if (param_type_2 == CPT2_COLOR || param_type_2 == CPT2_COLORED_FACEDIR ||
894                         param_type_2 == CPT2_COLORED_WALLMOUNTED)
895                 palette = tsrc->getPalette(palette_name);
896
897         if (drawtype == NDT_MESH && !mesh.empty()) {
898                 // Meshnode drawtype
899                 // Read the mesh and apply scale
900                 mesh_ptr[0] = client->getMesh(mesh);
901                 if (mesh_ptr[0]) {
902                         v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
903                         scaleMesh(mesh_ptr[0], scale);
904                         recalculateBoundingBox(mesh_ptr[0]);
905                         meshmanip->recalculateNormals(mesh_ptr[0], true, false);
906                 }
907         }
908
909         // Cache 6dfacedir and wallmounted rotated clones of meshes
910         if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
911                         (param_type_2 == CPT2_FACEDIR ||
912                                         param_type_2 == CPT2_COLORED_FACEDIR)) {
913                 for (u16 j = 1; j < 24; j++) {
914                         mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
915                         rotateMeshBy6dFacedir(mesh_ptr[j], j);
916                         recalculateBoundingBox(mesh_ptr[j]);
917                         meshmanip->recalculateNormals(mesh_ptr[j], true, false);
918                 }
919         } else if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
920                         (param_type_2 == CPT2_WALLMOUNTED ||
921                                         param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
922                 static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
923                 for (u16 j = 1; j < 6; j++) {
924                         mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
925                         rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
926                         recalculateBoundingBox(mesh_ptr[j]);
927                         meshmanip->recalculateNormals(mesh_ptr[j], true, false);
928                 }
929                 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
930                 recalculateBoundingBox(mesh_ptr[0]);
931                 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
932         }
933 }
934 #endif
935
936 /*
937         NodeDefManager
938 */
939
940 NodeDefManager::NodeDefManager()
941 {
942         clear();
943 }
944
945 NodeDefManager::~NodeDefManager()
946 {
947 #ifndef SERVER
948         for (ContentFeatures &f : m_content_features) {
949                 for (auto &j : f.mesh_ptr) {
950                         if (j)
951                                 j->drop();
952                 }
953         }
954 #endif
955 }
956
957 void NodeDefManager::clear()
958 {
959         m_content_features.clear();
960         m_name_id_mapping.clear();
961         m_name_id_mapping_with_aliases.clear();
962         m_group_to_items.clear();
963         m_next_id = 0;
964         m_selection_box_union.reset(0, 0, 0);
965         m_selection_box_int_union.reset(0, 0, 0);
966
967         resetNodeResolveState();
968
969         u32 initial_length = 0;
970         initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
971         initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
972         initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
973         m_content_features.resize(initial_length);
974
975         // Set CONTENT_UNKNOWN
976         {
977                 ContentFeatures f;
978                 f.name = "unknown";
979                 // Insert directly into containers
980                 content_t c = CONTENT_UNKNOWN;
981                 m_content_features[c] = f;
982                 addNameIdMapping(c, f.name);
983         }
984
985         // Set CONTENT_AIR
986         {
987                 ContentFeatures f;
988                 f.name = "air";
989                 f.drawtype = NDT_AIRLIKE;
990                 f.param_type = CPT_LIGHT;
991                 f.light_propagates = true;
992                 f.sunlight_propagates = true;
993                 f.walkable = false;
994                 f.pointable = false;
995                 f.diggable = false;
996                 f.buildable_to = true;
997                 f.floodable = true;
998                 f.is_ground_content = true;
999                 // Insert directly into containers
1000                 content_t c = CONTENT_AIR;
1001                 m_content_features[c] = f;
1002                 addNameIdMapping(c, f.name);
1003         }
1004
1005         // Set CONTENT_IGNORE
1006         {
1007                 ContentFeatures f;
1008                 f.name = "ignore";
1009                 f.drawtype = NDT_AIRLIKE;
1010                 f.param_type = CPT_NONE;
1011                 f.light_propagates = false;
1012                 f.sunlight_propagates = false;
1013                 f.walkable = false;
1014                 f.pointable = false;
1015                 f.diggable = false;
1016                 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1017                 f.is_ground_content = true;
1018                 // Insert directly into containers
1019                 content_t c = CONTENT_IGNORE;
1020                 m_content_features[c] = f;
1021                 addNameIdMapping(c, f.name);
1022         }
1023 }
1024
1025 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1026 {
1027         std::unordered_map<std::string, content_t>::const_iterator i =
1028                         m_name_id_mapping_with_aliases.find(name);
1029         if (i == m_name_id_mapping_with_aliases.end())
1030                 return false;
1031         result = i->second;
1032         return true;
1033 }
1034
1035 content_t NodeDefManager::getId(const std::string &name) const
1036 {
1037         content_t id = CONTENT_IGNORE;
1038         getId(name, id);
1039         return id;
1040 }
1041
1042 bool NodeDefManager::getIds(const std::string &name, std::vector<content_t> &result) const
1043 {
1044         // TimeTaker t("getIds", NULL, PRECISION_MICRO);
1045         if (name.substr(0, 6) != "group:") {
1046                 content_t id = CONTENT_IGNORE;
1047                 bool exists = getId(name, id);
1048                 if (exists)
1049                         result.push_back(id);
1050                 return exists;
1051         }
1052         std::string group = name.substr(6);
1053
1054         std::unordered_map<std::string, std::vector<content_t>>::const_iterator i =
1055                         m_group_to_items.find(group);
1056         if (i == m_group_to_items.end())
1057                 return true;
1058
1059         const std::vector<content_t> &items = i->second;
1060         result.insert(result.end(), items.begin(), items.end());
1061         // printf("getIds: %dus\n", t.stop());
1062         return true;
1063 }
1064
1065 const ContentFeatures &NodeDefManager::get(const std::string &name) const
1066 {
1067         content_t id = CONTENT_UNKNOWN;
1068         getId(name, id);
1069         return get(id);
1070 }
1071
1072 // returns CONTENT_IGNORE if no free ID found
1073 content_t NodeDefManager::allocateId()
1074 {
1075         for (content_t id = m_next_id; id >= m_next_id; // overflow?
1076                         ++id) {
1077                 while (id >= m_content_features.size()) {
1078                         m_content_features.emplace_back();
1079                 }
1080                 const ContentFeatures &f = m_content_features[id];
1081                 if (f.name.empty()) {
1082                         m_next_id = id + 1;
1083                         return id;
1084                 }
1085         }
1086         // If we arrive here, an overflow occurred in id.
1087         // That means no ID was found
1088         return CONTENT_IGNORE;
1089 }
1090
1091 /*!
1092  * Returns the smallest box that contains all boxes
1093  * in the vector. Box_union is expanded.
1094  * @param[in]      boxes     the vector containing the boxes
1095  * @param[in, out] box_union the union of the arguments
1096  */
1097 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1098 {
1099         for (const aabb3f &box : boxes) {
1100                 box_union->addInternalBox(box);
1101         }
1102 }
1103
1104 /*!
1105  * Returns a box that contains the nodebox in every case.
1106  * The argument node_union is expanded.
1107  * @param[in]      nodebox  the nodebox to be measured
1108  * @param[in]      features  used to decide whether the nodebox
1109  * can be rotated
1110  * @param[in, out] box_union the union of the arguments
1111  */
1112 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1113                 aabb3f *box_union)
1114 {
1115         switch (nodebox.type) {
1116         case NODEBOX_FIXED:
1117         case NODEBOX_LEVELED: {
1118                 // Raw union
1119                 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1120                 boxVectorUnion(nodebox.fixed, &half_processed);
1121                 // Set leveled boxes to maximal
1122                 if (nodebox.type == NODEBOX_LEVELED) {
1123                         half_processed.MaxEdge.Y = +BS / 2;
1124                 }
1125                 if (features.param_type_2 == CPT2_FACEDIR ||
1126                                 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1127                         // Get maximal coordinate
1128                         f32 coords[] = {fabsf(half_processed.MinEdge.X),
1129                                         fabsf(half_processed.MinEdge.Y),
1130                                         fabsf(half_processed.MinEdge.Z),
1131                                         fabsf(half_processed.MaxEdge.X),
1132                                         fabsf(half_processed.MaxEdge.Y),
1133                                         fabsf(half_processed.MaxEdge.Z)};
1134                         f32 max = 0;
1135                         for (float coord : coords) {
1136                                 if (max < coord) {
1137                                         max = coord;
1138                                 }
1139                         }
1140                         // Add the union of all possible rotated boxes
1141                         box_union->addInternalPoint(-max, -max, -max);
1142                         box_union->addInternalPoint(+max, +max, +max);
1143                 } else {
1144                         box_union->addInternalBox(half_processed);
1145                 }
1146                 break;
1147         }
1148         case NODEBOX_WALLMOUNTED: {
1149                 // Add fix boxes
1150                 box_union->addInternalBox(nodebox.wall_top);
1151                 box_union->addInternalBox(nodebox.wall_bottom);
1152                 // Find maximal coordinate in the X-Z plane
1153                 f32 coords[] = {fabsf(nodebox.wall_side.MinEdge.X),
1154                                 fabsf(nodebox.wall_side.MinEdge.Z),
1155                                 fabsf(nodebox.wall_side.MaxEdge.X),
1156                                 fabsf(nodebox.wall_side.MaxEdge.Z)};
1157                 f32 max = 0;
1158                 for (float coord : coords) {
1159                         if (max < coord) {
1160                                 max = coord;
1161                         }
1162                 }
1163                 // Add the union of all possible rotated boxes
1164                 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1165                 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1166                 break;
1167         }
1168         case NODEBOX_CONNECTED: {
1169                 // Add all possible connected boxes
1170                 boxVectorUnion(nodebox.fixed, box_union);
1171                 boxVectorUnion(nodebox.connect_top, box_union);
1172                 boxVectorUnion(nodebox.connect_bottom, box_union);
1173                 boxVectorUnion(nodebox.connect_front, box_union);
1174                 boxVectorUnion(nodebox.connect_left, box_union);
1175                 boxVectorUnion(nodebox.connect_back, box_union);
1176                 boxVectorUnion(nodebox.connect_right, box_union);
1177                 boxVectorUnion(nodebox.disconnected_top, box_union);
1178                 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1179                 boxVectorUnion(nodebox.disconnected_front, box_union);
1180                 boxVectorUnion(nodebox.disconnected_left, box_union);
1181                 boxVectorUnion(nodebox.disconnected_back, box_union);
1182                 boxVectorUnion(nodebox.disconnected_right, box_union);
1183                 boxVectorUnion(nodebox.disconnected, box_union);
1184                 boxVectorUnion(nodebox.disconnected_sides, box_union);
1185                 break;
1186         }
1187         default: {
1188                 // NODEBOX_REGULAR
1189                 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1190                 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1191         }
1192         }
1193 }
1194
1195 inline void NodeDefManager::fixSelectionBoxIntUnion()
1196 {
1197         m_selection_box_int_union.MinEdge.X =
1198                         floorf(m_selection_box_union.MinEdge.X / BS + 0.5f);
1199         m_selection_box_int_union.MinEdge.Y =
1200                         floorf(m_selection_box_union.MinEdge.Y / BS + 0.5f);
1201         m_selection_box_int_union.MinEdge.Z =
1202                         floorf(m_selection_box_union.MinEdge.Z / BS + 0.5f);
1203         m_selection_box_int_union.MaxEdge.X =
1204                         ceilf(m_selection_box_union.MaxEdge.X / BS - 0.5f);
1205         m_selection_box_int_union.MaxEdge.Y =
1206                         ceilf(m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1207         m_selection_box_int_union.MaxEdge.Z =
1208                         ceilf(m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1209 }
1210
1211 void NodeDefManager::eraseIdFromGroups(content_t id)
1212 {
1213         // For all groups in m_group_to_items...
1214         for (auto iter_groups = m_group_to_items.begin();
1215                         iter_groups != m_group_to_items.end();) {
1216                 // Get the group items vector.
1217                 std::vector<content_t> &items = iter_groups->second;
1218
1219                 // Remove any occurence of the id in the group items vector.
1220                 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1221
1222                 // If group is empty, erase its vector from the map.
1223                 if (items.empty())
1224                         iter_groups = m_group_to_items.erase(iter_groups);
1225                 else
1226                         ++iter_groups;
1227         }
1228 }
1229
1230 // IWritableNodeDefManager
1231 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &d)
1232 {
1233         ContentFeatures def = d;
1234
1235         // Pre-conditions
1236         assert(name != "");
1237         assert(name != "ignore");
1238         assert(name == def.name);
1239
1240         content_t id = CONTENT_IGNORE;
1241
1242         if (m_name_id_mapping.getId(name, id)) {
1243 #ifndef SERVER
1244                 ContentFeatures old_def = get(name);
1245                 for (u32 j = 0; j < 6; j++)
1246                         if (def.tiledef[j].name.empty())
1247                                 def.tiledef[j] = old_def.tiledef[j];
1248                 for (u32 j = 0; j < 6; j++)
1249                         if (def.tiledef_overlay[j].name.empty())
1250                                 def.tiledef_overlay[j] = old_def.tiledef_overlay[j];
1251                 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
1252                         if (def.tiledef_special[j].name.empty())
1253                                 def.tiledef_special[j] = old_def.tiledef_special[j];
1254 #endif
1255         } else {
1256                 // Get new id
1257                 id = allocateId();
1258                 if (id == CONTENT_IGNORE) {
1259                         warningstream << "NodeDefManager: Absolute "
1260                                          "limit reached"
1261                                       << std::endl;
1262                         return CONTENT_IGNORE;
1263                 }
1264                 assert(id != CONTENT_IGNORE);
1265                 addNameIdMapping(id, name);
1266         }
1267
1268         // If there is already ContentFeatures registered for this id, clear old groups
1269         if (id < m_content_features.size())
1270                 eraseIdFromGroups(id);
1271
1272         m_content_features[id] = def;
1273         verbosestream << "NodeDefManager: registering content id \"" << id
1274                       << "\": name=\"" << def.name << "\"" << std::endl;
1275
1276         getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1277         fixSelectionBoxIntUnion();
1278
1279         // Add this content to the list of all groups it belongs to
1280         for (const auto &group : def.groups) {
1281                 const std::string &group_name = group.first;
1282                 m_group_to_items[group_name].push_back(id);
1283         }
1284
1285         return id;
1286 }
1287
1288 content_t NodeDefManager::allocateDummy(const std::string &name)
1289 {
1290         assert(name != ""); // Pre-condition
1291         ContentFeatures f;
1292         f.name = name;
1293         return set(name, f);
1294 }
1295
1296 void NodeDefManager::removeNode(const std::string &name)
1297 {
1298         // Pre-condition
1299         assert(name != "");
1300
1301         // Erase name from name ID mapping
1302         content_t id = CONTENT_IGNORE;
1303         if (m_name_id_mapping.getId(name, id)) {
1304                 m_name_id_mapping.eraseName(name);
1305                 m_name_id_mapping_with_aliases.erase(name);
1306         }
1307
1308         eraseIdFromGroups(id);
1309 }
1310
1311 void NodeDefManager::updateAliases(IItemDefManager *idef)
1312 {
1313         std::set<std::string> all;
1314         idef->getAll(all);
1315         m_name_id_mapping_with_aliases.clear();
1316         for (const std::string &name : all) {
1317                 const std::string &convert_to = idef->getAlias(name);
1318                 content_t id;
1319                 if (m_name_id_mapping.getId(convert_to, id)) {
1320                         m_name_id_mapping_with_aliases.insert(std::make_pair(name, id));
1321                 }
1322         }
1323 }
1324
1325 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1326 {
1327         infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1328                       "overrides to textures"
1329                    << std::endl;
1330
1331         for (const TextureOverride &texture_override : overrides) {
1332                 content_t id;
1333                 if (!getId(texture_override.id, id))
1334                         continue; // Ignore unknown node
1335
1336                 ContentFeatures &nodedef = m_content_features[id];
1337
1338                 if (texture_override.hasTarget(OverrideTarget::TOP))
1339                         nodedef.tiledef[0].name = texture_override.texture;
1340
1341                 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1342                         nodedef.tiledef[1].name = texture_override.texture;
1343
1344                 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1345                         nodedef.tiledef[2].name = texture_override.texture;
1346
1347                 if (texture_override.hasTarget(OverrideTarget::LEFT))
1348                         nodedef.tiledef[3].name = texture_override.texture;
1349
1350                 if (texture_override.hasTarget(OverrideTarget::BACK))
1351                         nodedef.tiledef[4].name = texture_override.texture;
1352
1353                 if (texture_override.hasTarget(OverrideTarget::FRONT))
1354                         nodedef.tiledef[5].name = texture_override.texture;
1355         }
1356 }
1357
1358 void NodeDefManager::updateTextures(IGameDef *gamedef,
1359                 void (*progress_callback)(
1360                                 void *progress_args, u32 progress, u32 max_progress),
1361                 void *progress_callback_args)
1362 {
1363 #ifndef SERVER
1364         infostream << "NodeDefManager::updateTextures(): Updating "
1365                       "textures in node definitions"
1366                    << std::endl;
1367
1368         Client *client = (Client *)gamedef;
1369         ITextureSource *tsrc = client->tsrc();
1370         IShaderSource *shdsrc = client->getShaderSource();
1371         scene::IMeshManipulator *meshmanip =
1372                         RenderingEngine::get_scene_manager()->getMeshManipulator();
1373         TextureSettings tsettings;
1374         tsettings.readSettings();
1375
1376         u32 size = m_content_features.size();
1377
1378         for (u32 i = 0; i < size; i++) {
1379                 ContentFeatures *f = &(m_content_features[i]);
1380                 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1381                 progress_callback(progress_callback_args, i, size);
1382         }
1383 #endif
1384 }
1385
1386 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1387 {
1388         writeU8(os, 1); // version
1389         u16 count = 0;
1390         std::ostringstream os2(std::ios::binary);
1391         for (u32 i = 0; i < m_content_features.size(); i++) {
1392                 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN)
1393                         continue;
1394                 const ContentFeatures *f = &m_content_features[i];
1395                 if (f->name.empty())
1396                         continue;
1397                 writeU16(os2, i);
1398                 // Wrap it in a string to allow different lengths without
1399                 // strict version incompatibilities
1400                 std::ostringstream wrapper_os(std::ios::binary);
1401                 f->serialize(wrapper_os, protocol_version);
1402                 os2 << serializeString(wrapper_os.str());
1403
1404                 // must not overflow
1405                 u16 next = count + 1;
1406                 FATAL_ERROR_IF(next < count, "Overflow");
1407                 count++;
1408         }
1409         writeU16(os, count);
1410         os << serializeLongString(os2.str());
1411 }
1412
1413 void NodeDefManager::deSerialize(std::istream &is)
1414 {
1415         clear();
1416         int version = readU8(is);
1417         if (version != 1)
1418                 throw SerializationError("unsupported NodeDefinitionManager version");
1419         u16 count = readU16(is);
1420         std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1421         ContentFeatures f;
1422         for (u16 n = 0; n < count; n++) {
1423                 u16 i = readU16(is2);
1424
1425                 // Read it from the string wrapper
1426                 std::string wrapper = deSerializeString(is2);
1427                 std::istringstream wrapper_is(wrapper, std::ios::binary);
1428                 f.deSerialize(wrapper_is);
1429
1430                 // Check error conditions
1431                 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1432                         warningstream << "NodeDefManager::deSerialize(): "
1433                                          "not changing builtin node "
1434                                       << i << std::endl;
1435                         continue;
1436                 }
1437                 if (f.name.empty()) {
1438                         warningstream << "NodeDefManager::deSerialize(): "
1439                                          "received empty name"
1440                                       << std::endl;
1441                         continue;
1442                 }
1443
1444                 // Ignore aliases
1445                 u16 existing_id;
1446                 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1447                         warningstream << "NodeDefManager::deSerialize(): "
1448                                          "already defined with different ID: "
1449                                       << f.name << std::endl;
1450                         continue;
1451                 }
1452
1453                 // All is ok, add node definition with the requested ID
1454                 if (i >= m_content_features.size())
1455                         m_content_features.resize((u32)(i) + 1);
1456                 m_content_features[i] = f;
1457                 addNameIdMapping(i, f.name);
1458                 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1459
1460                 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1461                 fixSelectionBoxIntUnion();
1462         }
1463
1464         // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1465         // are not sent, resolve them client-side too.
1466         resolveCrossrefs();
1467 }
1468
1469 void NodeDefManager::addNameIdMapping(content_t i, std::string name)
1470 {
1471         m_name_id_mapping.set(i, name);
1472         m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1473 }
1474
1475 NodeDefManager *createNodeDefManager()
1476 {
1477         return new NodeDefManager();
1478 }
1479
1480 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1481 {
1482         nr->m_ndef = this;
1483         if (m_node_registration_complete)
1484                 nr->nodeResolveInternal();
1485         else
1486                 m_pending_resolve_callbacks.push_back(nr);
1487 }
1488
1489 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1490 {
1491         size_t len = m_pending_resolve_callbacks.size();
1492         for (size_t i = 0; i != len; i++) {
1493                 if (nr != m_pending_resolve_callbacks[i])
1494                         continue;
1495
1496                 len--;
1497                 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1498                 m_pending_resolve_callbacks.resize(len);
1499                 return true;
1500         }
1501
1502         return false;
1503 }
1504
1505 void NodeDefManager::runNodeResolveCallbacks()
1506 {
1507         for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1508                 NodeResolver *nr = m_pending_resolve_callbacks[i];
1509                 nr->nodeResolveInternal();
1510         }
1511
1512         m_pending_resolve_callbacks.clear();
1513 }
1514
1515 void NodeDefManager::resetNodeResolveState()
1516 {
1517         m_node_registration_complete = false;
1518         m_pending_resolve_callbacks.clear();
1519 }
1520
1521 static void removeDupes(std::vector<content_t> &list)
1522 {
1523         std::sort(list.begin(), list.end());
1524         auto new_end = std::unique(list.begin(), list.end());
1525         list.erase(new_end, list.end());
1526 }
1527
1528 void NodeDefManager::resolveCrossrefs()
1529 {
1530         for (ContentFeatures &f : m_content_features) {
1531                 if (f.liquid_type != LIQUID_NONE) {
1532                         f.liquid_alternative_flowing_id =
1533                                         getId(f.liquid_alternative_flowing);
1534                         f.liquid_alternative_source_id =
1535                                         getId(f.liquid_alternative_source);
1536                         continue;
1537                 }
1538                 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1539                         continue;
1540
1541                 for (const std::string &name : f.connects_to) {
1542                         getIds(name, f.connects_to_ids);
1543                 }
1544                 removeDupes(f.connects_to_ids);
1545         }
1546 }
1547
1548 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face) const
1549 {
1550         const ContentFeatures &f1 = get(from);
1551
1552         if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1553                 return false;
1554
1555         // lookup target in connected set
1556         if (!CONTAINS(f1.connects_to_ids, to.param0))
1557                 return false;
1558
1559         const ContentFeatures &f2 = get(to);
1560
1561         if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1562                 // ignores actually looking if back connection exists
1563                 return CONTAINS(f2.connects_to_ids, from.param0);
1564
1565         // does to node declare usable faces?
1566         if (f2.connect_sides > 0) {
1567                 if ((f2.param_type_2 == CPT2_FACEDIR ||
1568                                     f2.param_type_2 == CPT2_COLORED_FACEDIR) &&
1569                                 (connect_face >= 4)) {
1570                         static const u8 rot[33 * 4] = {
1571                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
1572                                         32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1573                                         0, // 4 - back
1574                                         8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1575                                         0, // 8 - right
1576                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1577                                         16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1578                                         0, // 16 - front
1579                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1580                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1581                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 16,
1582                                         8, 4 // 32 - left
1583                         };
1584                         return (f2.connect_sides &
1585                                         rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1586                 }
1587                 return (f2.connect_sides & connect_face);
1588         }
1589         // the target is just a regular node, so connect no matter back connection
1590         return true;
1591 }
1592
1593 ////
1594 //// NodeResolver
1595 ////
1596
1597 NodeResolver::NodeResolver()
1598 {
1599         m_nodenames.reserve(16);
1600         m_nnlistsizes.reserve(4);
1601 }
1602
1603 NodeResolver::~NodeResolver()
1604 {
1605         if (!m_resolve_done && m_ndef)
1606                 m_ndef->cancelNodeResolveCallback(this);
1607 }
1608
1609 void NodeResolver::cloneTo(NodeResolver *res) const
1610 {
1611         FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1612                                         " after resolving has completed");
1613         /* We don't actually do anything significant. Since the node resolving has
1614          * already completed, the class that called us will already have the
1615          * resolved IDs in its data structures (which it copies on its own) */
1616         res->m_ndef = m_ndef;
1617         res->m_resolve_done = true;
1618 }
1619
1620 void NodeResolver::nodeResolveInternal()
1621 {
1622         m_nodenames_idx = 0;
1623         m_nnlistsizes_idx = 0;
1624
1625         resolveNodeNames();
1626         m_resolve_done = true;
1627
1628         m_nodenames.clear();
1629         m_nnlistsizes.clear();
1630 }
1631
1632 bool NodeResolver::getIdFromNrBacklog(content_t *result_out, const std::string &node_alt,
1633                 content_t c_fallback, bool error_on_fallback)
1634 {
1635         if (m_nodenames_idx == m_nodenames.size()) {
1636                 *result_out = c_fallback;
1637                 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1638                 return false;
1639         }
1640
1641         content_t c;
1642         std::string name = m_nodenames[m_nodenames_idx++];
1643
1644         bool success = m_ndef->getId(name, c);
1645         if (!success && !node_alt.empty()) {
1646                 name = node_alt;
1647                 success = m_ndef->getId(name, c);
1648         }
1649
1650         if (!success) {
1651                 if (error_on_fallback)
1652                         errorstream << "NodeResolver: failed to resolve node name '"
1653                                     << name << "'." << std::endl;
1654                 c = c_fallback;
1655         }
1656
1657         *result_out = c;
1658         return success;
1659 }
1660
1661 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1662                 bool all_required, content_t c_fallback)
1663 {
1664         bool success = true;
1665
1666         if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1667                 errorstream << "NodeResolver: no more node lists" << std::endl;
1668                 return false;
1669         }
1670
1671         size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1672
1673         while (length--) {
1674                 if (m_nodenames_idx == m_nodenames.size()) {
1675                         errorstream << "NodeResolver: no more nodes in list" << std::endl;
1676                         return false;
1677                 }
1678
1679                 content_t c;
1680                 std::string &name = m_nodenames[m_nodenames_idx++];
1681
1682                 if (name.substr(0, 6) != "group:") {
1683                         if (m_ndef->getId(name, c)) {
1684                                 result_out->push_back(c);
1685                         } else if (all_required) {
1686                                 errorstream << "NodeResolver: failed to resolve node "
1687                                                "name '"
1688                                             << name << "'." << std::endl;
1689                                 result_out->push_back(c_fallback);
1690                                 success = false;
1691                         }
1692                 } else {
1693                         m_ndef->getIds(name, *result_out);
1694                 }
1695         }
1696
1697         return success;
1698 }