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