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