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