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