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