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