]> git.lizzy.rs Git - dragonfireclient.git/blob - src/nodedef.cpp
Reduce size of ContentFeatures structure
[dragonfireclient.git] / src / nodedef.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "nodedef.h"
21
22 #include "itemdef.h"
23 #ifndef SERVER
24 #include "client/mesh.h"
25 #include "client/shader.h"
26 #include "client/client.h"
27 #include "client/renderingengine.h"
28 #include "client/tile.h"
29 #include <IMeshManipulator.h>
30 #endif
31 #include "log.h"
32 #include "settings.h"
33 #include "nameidmapping.h"
34 #include "util/numeric.h"
35 #include "util/serialize.h"
36 #include "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                         drawtype = NDT_NORMAL;
905                         solidness = 2;
906                         for (TileDef &td : tdef)
907                                 td.name += std::string("^[noalpha");
908                 }
909                 if (waving >= 1)
910                         material_type = TILE_MATERIAL_WAVING_LEAVES;
911                 break;
912         case NDT_PLANTLIKE:
913                 solidness = 0;
914                 if (waving >= 1)
915                         material_type = TILE_MATERIAL_WAVING_PLANTS;
916                 break;
917         case NDT_FIRELIKE:
918                 solidness = 0;
919                 break;
920         case NDT_MESH:
921         case NDT_NODEBOX:
922                 solidness = 0;
923                 if (waving == 1) {
924                         material_type = TILE_MATERIAL_WAVING_PLANTS;
925                 } else if (waving == 2) {
926                         material_type = TILE_MATERIAL_WAVING_LEAVES;
927                 } else if (waving == 3) {
928                         material_type = alpha == ALPHAMODE_OPAQUE ?
929                                 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
930                                 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
931                 }
932                 break;
933         case NDT_TORCHLIKE:
934         case NDT_SIGNLIKE:
935         case NDT_FENCELIKE:
936         case NDT_RAILLIKE:
937                 solidness = 0;
938                 break;
939         case NDT_PLANTLIKE_ROOTED:
940                 solidness = 2;
941                 break;
942         }
943
944         if (is_liquid) {
945                 if (waving == 3) {
946                         material_type = alpha == ALPHAMODE_OPAQUE ?
947                                 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
948                                 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
949                 } else {
950                         material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
951                                 TILE_MATERIAL_LIQUID_TRANSPARENT;
952                 }
953         }
954
955         u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
956
957         MaterialType overlay_material = material_type;
958         if (overlay_material == TILE_MATERIAL_OPAQUE)
959                 overlay_material = TILE_MATERIAL_BASIC;
960         else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
961                 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
962
963         u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
964
965         // Tiles (fill in f->tiles[])
966         for (u16 j = 0; j < 6; j++) {
967                 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
968                                 tsettings.world_aligned_mode, drawtype);
969                 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
970                                 color, material_type, tile_shader,
971                                 tdef[j].backface_culling, tsettings);
972                 if (!tdef_overlay[j].name.empty())
973                         fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
974                                         color, overlay_material, overlay_shader,
975                                         tdef[j].backface_culling, tsettings);
976         }
977
978         MaterialType special_material = material_type;
979         if (drawtype == NDT_PLANTLIKE_ROOTED) {
980                 if (waving == 1)
981                         special_material = TILE_MATERIAL_WAVING_PLANTS;
982                 else if (waving == 2)
983                         special_material = TILE_MATERIAL_WAVING_LEAVES;
984         }
985         u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
986
987         // Special tiles (fill in f->special_tiles[])
988         for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
989                 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
990                                 color, special_material, special_shader,
991                                 tdef_spec[j].backface_culling, tsettings);
992
993         if (param_type_2 == CPT2_COLOR ||
994                         param_type_2 == CPT2_COLORED_FACEDIR ||
995                         param_type_2 == CPT2_COLORED_WALLMOUNTED ||
996                         param_type_2 == CPT2_COLORED_DEGROTATE)
997                 palette = tsrc->getPalette(palette_name);
998
999         if (drawtype == NDT_MESH && !mesh.empty()) {
1000                 // Meshnode drawtype
1001                 // Read the mesh and apply scale
1002                 mesh_ptr[0] = client->getMesh(mesh);
1003                 if (mesh_ptr[0]){
1004                         v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
1005                         scaleMesh(mesh_ptr[0], scale);
1006                         recalculateBoundingBox(mesh_ptr[0]);
1007                         meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1008                 }
1009         }
1010
1011         //Cache 6dfacedir and wallmounted rotated clones of meshes
1012         if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
1013                         (param_type_2 == CPT2_FACEDIR
1014                         || param_type_2 == CPT2_COLORED_FACEDIR)) {
1015                 for (u16 j = 1; j < 24; j++) {
1016                         mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
1017                         rotateMeshBy6dFacedir(mesh_ptr[j], j);
1018                         recalculateBoundingBox(mesh_ptr[j]);
1019                         meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1020                 }
1021         } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
1022                         && (param_type_2 == CPT2_WALLMOUNTED ||
1023                         param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
1024                 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
1025                 for (u16 j = 1; j < 6; j++) {
1026                         mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
1027                         rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
1028                         recalculateBoundingBox(mesh_ptr[j]);
1029                         meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1030                 }
1031                 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
1032                 recalculateBoundingBox(mesh_ptr[0]);
1033                 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1034         }
1035 }
1036 #endif
1037
1038 /*
1039         NodeDefManager
1040 */
1041
1042
1043
1044
1045 NodeDefManager::NodeDefManager()
1046 {
1047         clear();
1048 }
1049
1050
1051 NodeDefManager::~NodeDefManager()
1052 {
1053 #ifndef SERVER
1054         for (ContentFeatures &f : m_content_features) {
1055                 for (auto &j : f.mesh_ptr) {
1056                         if (j)
1057                                 j->drop();
1058                 }
1059         }
1060 #endif
1061 }
1062
1063
1064 void NodeDefManager::clear()
1065 {
1066         m_content_features.clear();
1067         m_name_id_mapping.clear();
1068         m_name_id_mapping_with_aliases.clear();
1069         m_group_to_items.clear();
1070         m_next_id = 0;
1071         m_selection_box_union.reset(0,0,0);
1072         m_selection_box_int_union.reset(0,0,0);
1073
1074         resetNodeResolveState();
1075
1076         u32 initial_length = 0;
1077         initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1078         initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1079         initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1080         m_content_features.resize(initial_length);
1081
1082         // Set CONTENT_UNKNOWN
1083         {
1084                 ContentFeatures f;
1085                 f.name = "unknown";
1086                 for (int t = 0; t < 6; t++)
1087                         f.tiledef[t].name = "unknown_node.png";
1088                 // Insert directly into containers
1089                 content_t c = CONTENT_UNKNOWN;
1090                 m_content_features[c] = f;
1091                 addNameIdMapping(c, f.name);
1092         }
1093
1094         // Set CONTENT_AIR
1095         {
1096                 ContentFeatures f;
1097                 f.name                = "air";
1098                 f.drawtype            = NDT_AIRLIKE;
1099                 f.param_type          = CPT_LIGHT;
1100                 f.light_propagates    = true;
1101                 f.sunlight_propagates = true;
1102                 f.walkable            = false;
1103                 f.pointable           = false;
1104                 f.diggable            = false;
1105                 f.buildable_to        = true;
1106                 f.floodable           = true;
1107                 f.is_ground_content   = true;
1108                 // Insert directly into containers
1109                 content_t c = CONTENT_AIR;
1110                 m_content_features[c] = f;
1111                 addNameIdMapping(c, f.name);
1112         }
1113
1114         // Set CONTENT_IGNORE
1115         {
1116                 ContentFeatures f;
1117                 f.name                = "ignore";
1118                 f.drawtype            = NDT_AIRLIKE;
1119                 f.param_type          = CPT_NONE;
1120                 f.light_propagates    = false;
1121                 f.sunlight_propagates = false;
1122                 f.walkable            = false;
1123                 f.pointable           = false;
1124                 f.diggable            = false;
1125                 f.buildable_to        = true; // A way to remove accidental CONTENT_IGNOREs
1126                 f.is_ground_content   = true;
1127                 // Insert directly into containers
1128                 content_t c = CONTENT_IGNORE;
1129                 m_content_features[c] = f;
1130                 addNameIdMapping(c, f.name);
1131         }
1132 }
1133
1134
1135 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1136 {
1137         std::unordered_map<std::string, content_t>::const_iterator
1138                 i = m_name_id_mapping_with_aliases.find(name);
1139         if(i == m_name_id_mapping_with_aliases.end())
1140                 return false;
1141         result = i->second;
1142         return true;
1143 }
1144
1145
1146 content_t NodeDefManager::getId(const std::string &name) const
1147 {
1148         content_t id = CONTENT_IGNORE;
1149         getId(name, id);
1150         return id;
1151 }
1152
1153
1154 bool NodeDefManager::getIds(const std::string &name,
1155                 std::vector<content_t> &result) const
1156 {
1157         //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1158         if (name.substr(0,6) != "group:") {
1159                 content_t id = CONTENT_IGNORE;
1160                 bool exists = getId(name, id);
1161                 if (exists)
1162                         result.push_back(id);
1163                 return exists;
1164         }
1165         std::string group = name.substr(6);
1166
1167         std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1168                 i = m_group_to_items.find(group);
1169         if (i == m_group_to_items.end())
1170                 return true;
1171
1172         const std::vector<content_t> &items = i->second;
1173         result.insert(result.end(), items.begin(), items.end());
1174         //printf("getIds: %dus\n", t.stop());
1175         return true;
1176 }
1177
1178
1179 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1180 {
1181         content_t id = CONTENT_UNKNOWN;
1182         getId(name, id);
1183         return get(id);
1184 }
1185
1186
1187 // returns CONTENT_IGNORE if no free ID found
1188 content_t NodeDefManager::allocateId()
1189 {
1190         for (content_t id = m_next_id;
1191                         id >= m_next_id; // overflow?
1192                         ++id) {
1193                 while (id >= m_content_features.size()) {
1194                         m_content_features.emplace_back();
1195                 }
1196                 const ContentFeatures &f = m_content_features[id];
1197                 if (f.name.empty()) {
1198                         m_next_id = id + 1;
1199                         return id;
1200                 }
1201         }
1202         // If we arrive here, an overflow occurred in id.
1203         // That means no ID was found
1204         return CONTENT_IGNORE;
1205 }
1206
1207
1208 /*!
1209  * Returns the smallest box that contains all boxes
1210  * in the vector. Box_union is expanded.
1211  * @param[in]      boxes     the vector containing the boxes
1212  * @param[in, out] box_union the union of the arguments
1213  */
1214 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1215 {
1216         for (const aabb3f &box : boxes) {
1217                 box_union->addInternalBox(box);
1218         }
1219 }
1220
1221
1222 /*!
1223  * Returns a box that contains the nodebox in every case.
1224  * The argument node_union is expanded.
1225  * @param[in]      nodebox  the nodebox to be measured
1226  * @param[in]      features  used to decide whether the nodebox
1227  * can be rotated
1228  * @param[in, out] box_union the union of the arguments
1229  */
1230 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1231         aabb3f *box_union)
1232 {
1233         switch(nodebox.type) {
1234                 case NODEBOX_FIXED:
1235                 case NODEBOX_LEVELED: {
1236                         // Raw union
1237                         aabb3f half_processed(0, 0, 0, 0, 0, 0);
1238                         boxVectorUnion(nodebox.fixed, &half_processed);
1239                         // Set leveled boxes to maximal
1240                         if (nodebox.type == NODEBOX_LEVELED) {
1241                                 half_processed.MaxEdge.Y = +BS / 2;
1242                         }
1243                         if (features.param_type_2 == CPT2_FACEDIR ||
1244                                         features.param_type_2 == CPT2_COLORED_FACEDIR) {
1245                                 // Get maximal coordinate
1246                                 f32 coords[] = {
1247                                         fabsf(half_processed.MinEdge.X),
1248                                         fabsf(half_processed.MinEdge.Y),
1249                                         fabsf(half_processed.MinEdge.Z),
1250                                         fabsf(half_processed.MaxEdge.X),
1251                                         fabsf(half_processed.MaxEdge.Y),
1252                                         fabsf(half_processed.MaxEdge.Z) };
1253                                 f32 max = 0;
1254                                 for (float coord : coords) {
1255                                         if (max < coord) {
1256                                                 max = coord;
1257                                         }
1258                                 }
1259                                 // Add the union of all possible rotated boxes
1260                                 box_union->addInternalPoint(-max, -max, -max);
1261                                 box_union->addInternalPoint(+max, +max, +max);
1262                         } else {
1263                                 box_union->addInternalBox(half_processed);
1264                         }
1265                         break;
1266                 }
1267                 case NODEBOX_WALLMOUNTED: {
1268                         // Add fix boxes
1269                         box_union->addInternalBox(nodebox.wall_top);
1270                         box_union->addInternalBox(nodebox.wall_bottom);
1271                         // Find maximal coordinate in the X-Z plane
1272                         f32 coords[] = {
1273                                 fabsf(nodebox.wall_side.MinEdge.X),
1274                                 fabsf(nodebox.wall_side.MinEdge.Z),
1275                                 fabsf(nodebox.wall_side.MaxEdge.X),
1276                                 fabsf(nodebox.wall_side.MaxEdge.Z) };
1277                         f32 max = 0;
1278                         for (float coord : coords) {
1279                                 if (max < coord) {
1280                                         max = coord;
1281                                 }
1282                         }
1283                         // Add the union of all possible rotated boxes
1284                         box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1285                         box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1286                         break;
1287                 }
1288                 case NODEBOX_CONNECTED: {
1289                         const auto &c = nodebox.getConnected();
1290                         // Add all possible connected boxes
1291                         boxVectorUnion(nodebox.fixed,         box_union);
1292                         boxVectorUnion(c.connect_top,         box_union);
1293                         boxVectorUnion(c.connect_bottom,      box_union);
1294                         boxVectorUnion(c.connect_front,       box_union);
1295                         boxVectorUnion(c.connect_left,        box_union);
1296                         boxVectorUnion(c.connect_back,        box_union);
1297                         boxVectorUnion(c.connect_right,       box_union);
1298                         boxVectorUnion(c.disconnected_top,    box_union);
1299                         boxVectorUnion(c.disconnected_bottom, box_union);
1300                         boxVectorUnion(c.disconnected_front,  box_union);
1301                         boxVectorUnion(c.disconnected_left,   box_union);
1302                         boxVectorUnion(c.disconnected_back,   box_union);
1303                         boxVectorUnion(c.disconnected_right,  box_union);
1304                         boxVectorUnion(c.disconnected,        box_union);
1305                         boxVectorUnion(c.disconnected_sides,  box_union);
1306                         break;
1307                 }
1308                 default: {
1309                         // NODEBOX_REGULAR
1310                         box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1311                         box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1312                 }
1313         }
1314 }
1315
1316
1317 inline void NodeDefManager::fixSelectionBoxIntUnion()
1318 {
1319         m_selection_box_int_union.MinEdge.X = floorf(
1320                 m_selection_box_union.MinEdge.X / BS + 0.5f);
1321         m_selection_box_int_union.MinEdge.Y = floorf(
1322                 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1323         m_selection_box_int_union.MinEdge.Z = floorf(
1324                 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1325         m_selection_box_int_union.MaxEdge.X = ceilf(
1326                 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1327         m_selection_box_int_union.MaxEdge.Y = ceilf(
1328                 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1329         m_selection_box_int_union.MaxEdge.Z = ceilf(
1330                 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1331 }
1332
1333
1334 void NodeDefManager::eraseIdFromGroups(content_t id)
1335 {
1336         // For all groups in m_group_to_items...
1337         for (auto iter_groups = m_group_to_items.begin();
1338                         iter_groups != m_group_to_items.end();) {
1339                 // Get the group items vector.
1340                 std::vector<content_t> &items = iter_groups->second;
1341
1342                 // Remove any occurence of the id in the group items vector.
1343                 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1344
1345                 // If group is empty, erase its vector from the map.
1346                 if (items.empty())
1347                         iter_groups = m_group_to_items.erase(iter_groups);
1348                 else
1349                         ++iter_groups;
1350         }
1351 }
1352
1353
1354 // IWritableNodeDefManager
1355 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1356 {
1357         // Pre-conditions
1358         assert(name != "");
1359         assert(name != "ignore");
1360         assert(name == def.name);
1361
1362         content_t id = CONTENT_IGNORE;
1363         if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1364                 // Get new id
1365                 id = allocateId();
1366                 if (id == CONTENT_IGNORE) {
1367                         warningstream << "NodeDefManager: Absolute "
1368                                 "limit reached" << std::endl;
1369                         return CONTENT_IGNORE;
1370                 }
1371                 assert(id != CONTENT_IGNORE);
1372                 addNameIdMapping(id, name);
1373         }
1374
1375         // If there is already ContentFeatures registered for this id, clear old groups
1376         if (id < m_content_features.size())
1377                 eraseIdFromGroups(id);
1378
1379         m_content_features[id] = def;
1380         verbosestream << "NodeDefManager: registering content id \"" << id
1381                 << "\": name=\"" << def.name << "\""<<std::endl;
1382
1383         getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1384         fixSelectionBoxIntUnion();
1385
1386         // Add this content to the list of all groups it belongs to
1387         for (const auto &group : def.groups) {
1388                 const std::string &group_name = group.first;
1389                 m_group_to_items[group_name].push_back(id);
1390         }
1391
1392         return id;
1393 }
1394
1395
1396 content_t NodeDefManager::allocateDummy(const std::string &name)
1397 {
1398         assert(name != "");     // Pre-condition
1399         ContentFeatures f;
1400         f.name = name;
1401         return set(name, f);
1402 }
1403
1404
1405 void NodeDefManager::removeNode(const std::string &name)
1406 {
1407         // Pre-condition
1408         assert(name != "");
1409
1410         // Erase name from name ID mapping
1411         content_t id = CONTENT_IGNORE;
1412         if (m_name_id_mapping.getId(name, id)) {
1413                 m_name_id_mapping.eraseName(name);
1414                 m_name_id_mapping_with_aliases.erase(name);
1415         }
1416
1417         eraseIdFromGroups(id);
1418 }
1419
1420
1421 void NodeDefManager::updateAliases(IItemDefManager *idef)
1422 {
1423         std::set<std::string> all;
1424         idef->getAll(all);
1425         m_name_id_mapping_with_aliases.clear();
1426         for (const std::string &name : all) {
1427                 const std::string &convert_to = idef->getAlias(name);
1428                 content_t id;
1429                 if (m_name_id_mapping.getId(convert_to, id)) {
1430                         m_name_id_mapping_with_aliases.insert(
1431                                 std::make_pair(name, id));
1432                 }
1433         }
1434 }
1435
1436 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1437 {
1438         infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1439                 "overrides to textures" << std::endl;
1440
1441         for (const TextureOverride& texture_override : overrides) {
1442                 content_t id;
1443                 if (!getId(texture_override.id, id))
1444                         continue; // Ignore unknown node
1445
1446                 ContentFeatures &nodedef = m_content_features[id];
1447
1448                 // Override tiles
1449                 if (texture_override.hasTarget(OverrideTarget::TOP))
1450                         nodedef.tiledef[0].name = texture_override.texture;
1451
1452                 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1453                         nodedef.tiledef[1].name = texture_override.texture;
1454
1455                 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1456                         nodedef.tiledef[2].name = texture_override.texture;
1457
1458                 if (texture_override.hasTarget(OverrideTarget::LEFT))
1459                         nodedef.tiledef[3].name = texture_override.texture;
1460
1461                 if (texture_override.hasTarget(OverrideTarget::BACK))
1462                         nodedef.tiledef[4].name = texture_override.texture;
1463
1464                 if (texture_override.hasTarget(OverrideTarget::FRONT))
1465                         nodedef.tiledef[5].name = texture_override.texture;
1466
1467
1468                 // Override special tiles, if applicable
1469                 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1470                         nodedef.tiledef_special[0].name = texture_override.texture;
1471
1472                 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1473                         nodedef.tiledef_special[1].name = texture_override.texture;
1474
1475                 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1476                         nodedef.tiledef_special[2].name = texture_override.texture;
1477
1478                 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1479                         nodedef.tiledef_special[3].name = texture_override.texture;
1480
1481                 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1482                         nodedef.tiledef_special[4].name = texture_override.texture;
1483
1484                 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1485                         nodedef.tiledef_special[5].name = texture_override.texture;
1486         }
1487 }
1488
1489 void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
1490 {
1491 #ifndef SERVER
1492         infostream << "NodeDefManager::updateTextures(): Updating "
1493                 "textures in node definitions" << std::endl;
1494
1495         Client *client = (Client *)gamedef;
1496         ITextureSource *tsrc = client->tsrc();
1497         IShaderSource *shdsrc = client->getShaderSource();
1498         auto smgr = client->getSceneManager();
1499         scene::IMeshManipulator *meshmanip = smgr->getMeshManipulator();
1500         TextureSettings tsettings;
1501         tsettings.readSettings();
1502
1503         u32 size = m_content_features.size();
1504
1505         for (u32 i = 0; i < size; i++) {
1506                 ContentFeatures *f = &(m_content_features[i]);
1507                 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1508                 client->showUpdateProgressTexture(progress_callback_args, i, size);
1509         }
1510 #endif
1511 }
1512
1513 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1514 {
1515         writeU8(os, 1); // version
1516         u16 count = 0;
1517         std::ostringstream os2(std::ios::binary);
1518         for (u32 i = 0; i < m_content_features.size(); i++) {
1519                 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1520                                 || i == CONTENT_UNKNOWN)
1521                         continue;
1522                 const ContentFeatures *f = &m_content_features[i];
1523                 if (f->name.empty())
1524                         continue;
1525                 writeU16(os2, i);
1526                 // Wrap it in a string to allow different lengths without
1527                 // strict version incompatibilities
1528                 std::ostringstream wrapper_os(std::ios::binary);
1529                 f->serialize(wrapper_os, protocol_version);
1530                 os2<<serializeString16(wrapper_os.str());
1531
1532                 // must not overflow
1533                 u16 next = count + 1;
1534                 FATAL_ERROR_IF(next < count, "Overflow");
1535                 count++;
1536         }
1537         writeU16(os, count);
1538         os << serializeString32(os2.str());
1539 }
1540
1541
1542 void NodeDefManager::deSerialize(std::istream &is)
1543 {
1544         clear();
1545         int version = readU8(is);
1546         if (version != 1)
1547                 throw SerializationError("unsupported NodeDefinitionManager version");
1548         u16 count = readU16(is);
1549         std::istringstream is2(deSerializeString32(is), std::ios::binary);
1550         ContentFeatures f;
1551         for (u16 n = 0; n < count; n++) {
1552                 u16 i = readU16(is2);
1553
1554                 // Read it from the string wrapper
1555                 std::string wrapper = deSerializeString16(is2);
1556                 std::istringstream wrapper_is(wrapper, std::ios::binary);
1557                 f.deSerialize(wrapper_is);
1558
1559                 // Check error conditions
1560                 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1561                         warningstream << "NodeDefManager::deSerialize(): "
1562                                 "not changing builtin node " << i << std::endl;
1563                         continue;
1564                 }
1565                 if (f.name.empty()) {
1566                         warningstream << "NodeDefManager::deSerialize(): "
1567                                 "received empty name" << std::endl;
1568                         continue;
1569                 }
1570
1571                 // Ignore aliases
1572                 u16 existing_id;
1573                 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1574                         warningstream << "NodeDefManager::deSerialize(): "
1575                                 "already defined with different ID: " << f.name << std::endl;
1576                         continue;
1577                 }
1578
1579                 // All is ok, add node definition with the requested ID
1580                 if (i >= m_content_features.size())
1581                         m_content_features.resize((u32)(i) + 1);
1582                 m_content_features[i] = f;
1583                 addNameIdMapping(i, f.name);
1584                 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1585
1586                 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1587                 fixSelectionBoxIntUnion();
1588         }
1589
1590         // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1591         // are not sent, resolve them client-side too.
1592         resolveCrossrefs();
1593 }
1594
1595
1596 void NodeDefManager::addNameIdMapping(content_t i, const std::string &name)
1597 {
1598         m_name_id_mapping.set(i, name);
1599         m_name_id_mapping_with_aliases.emplace(name, i);
1600 }
1601
1602
1603 NodeDefManager *createNodeDefManager()
1604 {
1605         return new NodeDefManager();
1606 }
1607
1608
1609 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1610 {
1611         nr->m_ndef = this;
1612         if (m_node_registration_complete)
1613                 nr->nodeResolveInternal();
1614         else
1615                 m_pending_resolve_callbacks.push_back(nr);
1616 }
1617
1618
1619 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1620 {
1621         size_t len = m_pending_resolve_callbacks.size();
1622         for (size_t i = 0; i != len; i++) {
1623                 if (nr != m_pending_resolve_callbacks[i])
1624                         continue;
1625
1626                 len--;
1627                 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1628                 m_pending_resolve_callbacks.resize(len);
1629                 return true;
1630         }
1631
1632         return false;
1633 }
1634
1635
1636 void NodeDefManager::runNodeResolveCallbacks()
1637 {
1638         for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1639                 NodeResolver *nr = m_pending_resolve_callbacks[i];
1640                 nr->nodeResolveInternal();
1641         }
1642
1643         m_pending_resolve_callbacks.clear();
1644 }
1645
1646
1647 void NodeDefManager::resetNodeResolveState()
1648 {
1649         m_node_registration_complete = false;
1650         m_pending_resolve_callbacks.clear();
1651 }
1652
1653 static void removeDupes(std::vector<content_t> &list)
1654 {
1655         std::sort(list.begin(), list.end());
1656         auto new_end = std::unique(list.begin(), list.end());
1657         list.erase(new_end, list.end());
1658 }
1659
1660 void NodeDefManager::resolveCrossrefs()
1661 {
1662         for (ContentFeatures &f : m_content_features) {
1663                 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1664                         f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1665                         f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1666                         continue;
1667                 }
1668                 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1669                         continue;
1670
1671                 for (const std::string &name : f.connects_to) {
1672                         getIds(name, f.connects_to_ids);
1673                 }
1674                 removeDupes(f.connects_to_ids);
1675         }
1676 }
1677
1678 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1679         u8 connect_face) const
1680 {
1681         const ContentFeatures &f1 = get(from);
1682
1683         if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1684                 return false;
1685
1686         // lookup target in connected set
1687         if (!CONTAINS(f1.connects_to_ids, to.param0))
1688                 return false;
1689
1690         const ContentFeatures &f2 = get(to);
1691
1692         if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1693                 // ignores actually looking if back connection exists
1694                 return CONTAINS(f2.connects_to_ids, from.param0);
1695
1696         // does to node declare usable faces?
1697         if (f2.connect_sides > 0) {
1698                 if ((f2.param_type_2 == CPT2_FACEDIR ||
1699                                 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1700                                 && (connect_face >= 4)) {
1701                         static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1702                                 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1703                                 0, // 4 - back
1704                                 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1705                                 0, // 8 - right
1706                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1707                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1708                                 0, // 16 - front
1709                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1710                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1711                                 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1712                                 };
1713                         return (f2.connect_sides
1714                                 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1715                 }
1716                 return (f2.connect_sides & connect_face);
1717         }
1718         // the target is just a regular node, so connect no matter back connection
1719         return true;
1720 }
1721
1722 ////
1723 //// NodeResolver
1724 ////
1725
1726 NodeResolver::NodeResolver()
1727 {
1728         reset();
1729 }
1730
1731
1732 NodeResolver::~NodeResolver()
1733 {
1734         if (!m_resolve_done && m_ndef)
1735                 m_ndef->cancelNodeResolveCallback(this);
1736 }
1737
1738
1739 void NodeResolver::cloneTo(NodeResolver *res) const
1740 {
1741         FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1742                 " after resolving has completed");
1743         /* We don't actually do anything significant. Since the node resolving has
1744          * already completed, the class that called us will already have the
1745          * resolved IDs in its data structures (which it copies on its own) */
1746         res->m_ndef = m_ndef;
1747         res->m_resolve_done = true;
1748 }
1749
1750
1751 void NodeResolver::nodeResolveInternal()
1752 {
1753         m_nodenames_idx   = 0;
1754         m_nnlistsizes_idx = 0;
1755
1756         resolveNodeNames();
1757         m_resolve_done = true;
1758
1759         m_nodenames.clear();
1760         m_nnlistsizes.clear();
1761 }
1762
1763
1764 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1765         const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1766 {
1767         if (m_nodenames_idx == m_nodenames.size()) {
1768                 *result_out = c_fallback;
1769                 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1770                 return false;
1771         }
1772
1773         content_t c;
1774         std::string name = m_nodenames[m_nodenames_idx++];
1775
1776         bool success = m_ndef->getId(name, c);
1777         if (!success && !node_alt.empty()) {
1778                 name = node_alt;
1779                 success = m_ndef->getId(name, c);
1780         }
1781
1782         if (!success) {
1783                 if (error_on_fallback)
1784                         errorstream << "NodeResolver: failed to resolve node name '" << name
1785                                 << "'." << std::endl;
1786                 c = c_fallback;
1787         }
1788
1789         *result_out = c;
1790         return success;
1791 }
1792
1793
1794 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1795         bool all_required, content_t c_fallback)
1796 {
1797         bool success = true;
1798
1799         if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1800                 errorstream << "NodeResolver: no more node lists" << std::endl;
1801                 return false;
1802         }
1803
1804         size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1805
1806         while (length--) {
1807                 if (m_nodenames_idx == m_nodenames.size()) {
1808                         errorstream << "NodeResolver: no more nodes in list" << std::endl;
1809                         return false;
1810                 }
1811
1812                 content_t c;
1813                 std::string &name = m_nodenames[m_nodenames_idx++];
1814
1815                 if (name.substr(0,6) != "group:") {
1816                         if (m_ndef->getId(name, c)) {
1817                                 result_out->push_back(c);
1818                         } else if (all_required) {
1819                                 errorstream << "NodeResolver: failed to resolve node name '"
1820                                         << name << "'." << std::endl;
1821                                 result_out->push_back(c_fallback);
1822                                 success = false;
1823                         }
1824                 } else {
1825                         m_ndef->getIds(name, *result_out);
1826                 }
1827         }
1828
1829         return success;
1830 }
1831
1832 void NodeResolver::reset(bool resolve_done)
1833 {
1834         m_nodenames.clear();
1835         m_nodenames_idx = 0;
1836         m_nnlistsizes.clear();
1837         m_nnlistsizes_idx = 0;
1838
1839         m_resolve_done = resolve_done;
1840
1841         m_nodenames.reserve(16);
1842         m_nnlistsizes.reserve(4);
1843 }