3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
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>
33 #include "nameidmapping.h"
34 #include "util/numeric.h"
35 #include "util/serialize.h"
36 #include "util/string.h"
37 #include "exceptions.h"
41 #include <fstream> // Used in applyTextureOverrides()
51 type = NODEBOX_REGULAR;
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
60 connect_bottom.clear();
61 connect_front.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();
72 disconnected_sides.clear();
75 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
86 writeU16(os, fixed.size());
87 for (const aabb3f &nodebox : fixed) {
88 writeV3F32(os, nodebox.MinEdge);
89 writeV3F32(os, nodebox.MaxEdge);
92 case NODEBOX_WALLMOUNTED:
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);
102 case NODEBOX_CONNECTED:
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); \
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);
134 void NodeBox::deSerialize(std::istream &is)
136 int version = readU8(is);
138 throw SerializationError("unsupported NodeBox version");
142 type = (enum NodeBoxType)readU8(is);
144 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
146 u16 fixed_count = readU16(is);
150 box.MinEdge = readV3F32(is);
151 box.MaxEdge = readV3F32(is);
152 fixed.push_back(box);
155 else if(type == NODEBOX_WALLMOUNTED)
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);
164 else if (type == NODEBOX_CONNECTED)
166 #define READBOXES(box) { \
167 count = readU16(is); \
168 (box).reserve(count); \
170 v3f min = readV3F32(is); \
171 v3f max = readV3F32(is); \
172 (box).emplace_back(min, max); }; }
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);
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)
205 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
207 // protocol_version >= 36
209 writeU8(os, version);
211 if (protocol_version > 39) {
212 os << serializeString16(name);
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;
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:");
229 os << serializeString16(name);
231 os << serializeString16("blank.png^" + name);
233 animation.serialize(os, version);
234 bool has_scale = scale > 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;
243 flags |= TILE_FLAG_HAS_COLOR;
245 flags |= TILE_FLAG_HAS_SCALE;
246 if (align_style != ALIGN_STYLE_NODE)
247 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
250 writeU8(os, color.getRed());
251 writeU8(os, color.getGreen());
252 writeU8(os, color.getBlue());
256 if (align_style != ALIGN_STYLE_NODE)
257 writeU8(os, align_style);
260 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
261 NodeDrawType drawtype)
263 int version = readU8(is);
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;
276 color.setRed(readU8(is));
277 color.setGreen(readU8(is));
278 color.setBlue(readU8(is));
280 scale = has_scale ? readU8(is) : 0;
282 align_style = static_cast<AlignStyle>(readU8(is));
284 align_style = ALIGN_STYLE_NODE;
287 void TextureSettings::readSettings()
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");
299 // Mesh cache is not supported in combination with smooth lighting
301 enable_mesh_cache = false;
303 if (leaves_style_str == "fancy") {
304 leaves_style = LEAVES_FANCY;
305 } else if (leaves_style_str == "simple") {
306 leaves_style = LEAVES_SIMPLE;
308 leaves_style = LEAVES_OPAQUE;
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;
318 world_aligned_mode = WORLDALIGN_DISABLE;
320 if (autoscale_mode_str == "enable")
321 autoscale_mode = AUTOSCALE_ENABLE;
322 else if (autoscale_mode_str == "force")
323 autoscale_mode = AUTOSCALE_FORCE;
325 autoscale_mode = AUTOSCALE_DISABLE;
332 ContentFeatures::ContentFeatures()
337 ContentFeatures::~ContentFeatures()
340 for (u16 j = 0; j < 6; j++) {
341 delete tiles[j].layers[0].frames;
342 delete tiles[j].layers[1].frames;
344 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
345 delete special_tiles[j].layers[0].frames;
349 void ContentFeatures::reset()
356 visual_solidness = 0;
357 backface_culling = true;
360 has_on_construct = false;
361 has_on_destruct = false;
362 has_after_destruct = false;
366 NOTE: Most of this is always overridden by the default values given
371 // Unknown nodes can be dug
372 groups["dig_immediate"] = 2;
373 drawtype = NDT_NORMAL;
376 for (auto &i : mesh_ptr)
378 minimap_color = video::SColor(0, 0, 0, 0);
381 for (auto &i : tiledef)
383 for (auto &j : tiledef_special)
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;
396 buildable_to = false;
398 rightclickable = true;
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;
411 damage_per_second = 0;
412 node_box = NodeBox();
413 selection_box = NodeBox();
414 collision_box = NodeBox();
416 legacy_facedir_simple = false;
417 legacy_wallmounted = false;
418 sound_footstep = SimpleSoundSpec();
419 sound_dig = SimpleSoundSpec("__group");
420 sound_dug = SimpleSoundSpec();
422 connects_to_ids.clear();
424 color = video::SColor(0xFFFFFFFF);
427 node_dig_prediction = "air";
429 liquid_move_physics = false;
432 void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
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
438 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
441 case NDT_FLOWINGLIQUID:
442 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
445 alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
450 u8 ContentFeatures::getAlphaForLegacy() const
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;
457 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
459 const u8 version = CONTENTFEATURES_VERSION;
460 writeU8(os, version);
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));
471 writeS16(os, group.second);
474 writeU8(os, param_type);
475 writeU8(os, param_type_2);
478 writeU8(os, drawtype);
479 os << serializeString16(mesh);
480 writeF32(os, visual_scale);
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);
490 writeU8(os, getAlphaForLegacy());
491 writeU8(os, color.getRed());
492 writeU8(os, color.getGreen());
493 writeU8(os, color.getBlue());
494 os << serializeString16(palette_name);
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);
504 writeU8(os, light_propagates);
505 writeU8(os, sunlight_propagates);
506 writeU8(os, light_source);
509 writeU8(os, is_ground_content);
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);
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;
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);
540 node_box.serialize(os, protocol_version);
541 selection_box.serialize(os, protocol_version);
542 collision_box.serialize(os, protocol_version);
545 sound_footstep.serialize(os, version);
546 sound_dig.serialize(os, version);
547 sound_dug.serialize(os, version);
550 writeU8(os, legacy_facedir_simple);
551 writeU8(os, legacy_wallmounted);
554 os << serializeString16(node_dig_prediction);
555 writeU8(os, leveled_max);
557 writeU8(os, move_resistance);
558 writeU8(os, liquid_move_physics);
561 void ContentFeatures::deSerialize(std::istream &is)
564 const u8 version = readU8(is);
565 if (version < CONTENTFEATURES_VERSION)
566 throw SerializationError("unsupported ContentFeatures version");
569 name = deSerializeString16(is);
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;
577 param_type = (enum ContentParamType) readU8(is);
578 param_type_2 = (enum ContentParamType2) readU8(is);
581 drawtype = (enum NodeDrawType) readU8(is);
582 mesh = deSerializeString16(is);
583 visual_scale = readF32(is);
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);
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);
609 light_propagates = readU8(is);
610 sunlight_propagates = readU8(is);
611 light_source = readU8(is);
612 light_source = MYMIN(light_source, LIGHT_MAX);
615 is_ground_content = readU8(is);
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);
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);
639 node_box.deSerialize(is);
640 selection_box.deSerialize(is);
641 collision_box.deSerialize(is);
644 sound_footstep.deSerialize(is, version);
645 sound_dig.deSerialize(is, version);
646 sound_dug.deSerialize(is, version);
648 // read legacy properties
649 legacy_facedir_simple = readU8(is);
650 legacy_wallmounted = readU8(is);
653 node_dig_prediction = deSerializeString16(is);
656 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
657 throw SerializationError("");
662 throw SerializationError("");
663 alpha = static_cast<enum AlphaMode>(tmp);
667 throw SerializationError("");
668 move_resistance = tmp;
672 throw SerializationError("");
673 liquid_move_physics = tmp;
674 } catch(SerializationError &e) {};
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)
683 layer->shader_id = shader_id;
684 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
685 layer->material_type = material_type;
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;
700 if (!tile.world_aligned)
703 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
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;
717 layer->has_color = tiledef.has_color;
718 if (tiledef.has_color)
719 layer->color = tiledef.color;
721 layer->color = color;
723 // Animation parameters
725 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
726 assert(layer->texture);
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;
734 if (frame_count == 1) {
735 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
737 assert(layer->texture);
739 layer->frames = new std::vector<FrameSpec>();
740 layer->frames->resize(frame_count);
742 std::ostringstream os(std::ios::binary);
743 for (int i = 0; i < frame_count; i++) {
748 tiledef.animation.getTextureModifer(os,
749 layer->texture->getOriginalSize(), i);
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;
760 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
762 video::IVideoDriver *driver = RenderingEngine::get_video_driver();
763 static thread_local bool long_warning_printed = false;
764 std::set<std::string> seen;
766 for (int i = 0; i < length; i++) {
767 if (seen.find(tiles[i].name) != seen.end())
769 seen.insert(tiles[i].name);
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());
777 core::dimension2d<u32> dim = image->getDimension();
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) {
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."
802 long_warning_printed = true;
809 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
811 if (style == ALIGN_STYLE_WORLD)
813 if (mode == WORLDALIGN_DISABLE)
815 if (style == ALIGN_STYLE_USER_DEFINED)
817 if (drawtype == NDT_NORMAL)
818 return mode >= WORLDALIGN_FORCE;
819 if (drawtype == NDT_NODEBOX)
820 return mode >= WORLDALIGN_FORCE_NODEBOX;
824 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
825 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
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);
831 // Figure out the actual tiles to use
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;
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];
850 bool is_liquid = false;
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;
857 MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
858 TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
859 TILE_MATERIAL_ALPHA);
870 if (tsettings.opaque_water)
871 alpha = ALPHAMODE_OPAQUE;
875 case NDT_FLOWINGLIQUID:
877 if (tsettings.opaque_water)
878 alpha = ALPHAMODE_OPAQUE;
883 visual_solidness = 1;
885 case NDT_GLASSLIKE_FRAMED:
887 visual_solidness = 1;
889 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
891 visual_solidness = 1;
892 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
896 visual_solidness = 1;
898 case NDT_ALLFACES_OPTIONAL:
899 if (tsettings.leaves_style == LEAVES_FANCY) {
900 drawtype = NDT_ALLFACES;
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;
908 drawtype = NDT_GLASSLIKE;
910 visual_solidness = 1;
912 drawtype = NDT_NORMAL;
914 for (TileDef &td : tdef)
915 td.name += std::string("^[noalpha");
918 material_type = TILE_MATERIAL_WAVING_LEAVES;
923 material_type = TILE_MATERIAL_WAVING_PLANTS;
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);
947 case NDT_PLANTLIKE_ROOTED:
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);
958 material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
959 TILE_MATERIAL_LIQUID_TRANSPARENT;
963 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
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;
971 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
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);
986 MaterialType special_material = material_type;
987 if (drawtype == NDT_PLANTLIKE_ROOTED) {
989 special_material = TILE_MATERIAL_WAVING_PLANTS;
990 else if (waving == 2)
991 special_material = TILE_MATERIAL_WAVING_LEAVES;
993 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
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);
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);
1007 if (drawtype == NDT_MESH && !mesh.empty()) {
1008 // Meshnode drawtype
1009 // Read the mesh and apply scale
1010 mesh_ptr[0] = client->getMesh(mesh);
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);
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);
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);
1039 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
1040 recalculateBoundingBox(mesh_ptr[0]);
1041 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1053 NodeDefManager::NodeDefManager()
1059 NodeDefManager::~NodeDefManager()
1062 for (ContentFeatures &f : m_content_features) {
1063 for (auto &j : f.mesh_ptr) {
1072 void NodeDefManager::clear()
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();
1079 m_selection_box_union.reset(0,0,0);
1080 m_selection_box_int_union.reset(0,0,0);
1082 resetNodeResolveState();
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);
1090 // Set CONTENT_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);
1108 f.drawtype = NDT_AIRLIKE;
1109 f.param_type = CPT_LIGHT;
1110 f.light_propagates = true;
1111 f.sunlight_propagates = true;
1113 f.pointable = false;
1115 f.buildable_to = 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);
1124 // Set CONTENT_IGNORE
1128 f.drawtype = NDT_AIRLIKE;
1129 f.param_type = CPT_NONE;
1130 f.light_propagates = false;
1131 f.sunlight_propagates = false;
1133 f.pointable = 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);
1145 bool NodeDefManager::getId(const std::string &name, content_t &result) const
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())
1156 content_t NodeDefManager::getId(const std::string &name) const
1158 content_t id = CONTENT_IGNORE;
1164 bool NodeDefManager::getIds(const std::string &name,
1165 std::vector<content_t> &result) const
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);
1172 result.push_back(id);
1175 std::string group = name.substr(6);
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())
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());
1189 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1191 content_t id = CONTENT_UNKNOWN;
1197 // returns CONTENT_IGNORE if no free ID found
1198 content_t NodeDefManager::allocateId()
1200 for (content_t id = m_next_id;
1201 id >= m_next_id; // overflow?
1203 while (id >= m_content_features.size()) {
1204 m_content_features.emplace_back();
1206 const ContentFeatures &f = m_content_features[id];
1207 if (f.name.empty()) {
1212 // If we arrive here, an overflow occurred in id.
1213 // That means no ID was found
1214 return CONTENT_IGNORE;
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
1224 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1226 for (const aabb3f &box : boxes) {
1227 box_union->addInternalBox(box);
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
1238 * @param[in, out] box_union the union of the arguments
1240 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1243 switch(nodebox.type) {
1245 case NODEBOX_LEVELED: {
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;
1253 if (features.param_type_2 == CPT2_FACEDIR ||
1254 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1255 // Get maximal coordinate
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) };
1264 for (float coord : coords) {
1269 // Add the union of all possible rotated boxes
1270 box_union->addInternalPoint(-max, -max, -max);
1271 box_union->addInternalPoint(+max, +max, +max);
1273 box_union->addInternalBox(half_processed);
1277 case NODEBOX_WALLMOUNTED: {
1279 box_union->addInternalBox(nodebox.wall_top);
1280 box_union->addInternalBox(nodebox.wall_bottom);
1281 // Find maximal coordinate in the X-Z plane
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) };
1288 for (float coord : coords) {
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);
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);
1319 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1320 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1326 inline void NodeDefManager::fixSelectionBoxIntUnion()
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);
1343 void NodeDefManager::eraseIdFromGroups(content_t id)
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;
1351 // Remove any occurence of the id in the group items vector.
1352 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1354 // If group is empty, erase its vector from the map.
1356 iter_groups = m_group_to_items.erase(iter_groups);
1363 // IWritableNodeDefManager
1364 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1368 assert(name != "ignore");
1369 assert(name == def.name);
1371 content_t id = CONTENT_IGNORE;
1372 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1375 if (id == CONTENT_IGNORE) {
1376 warningstream << "NodeDefManager: Absolute "
1377 "limit reached" << std::endl;
1378 return CONTENT_IGNORE;
1380 assert(id != CONTENT_IGNORE);
1381 addNameIdMapping(id, name);
1384 // If there is already ContentFeatures registered for this id, clear old groups
1385 if (id < m_content_features.size())
1386 eraseIdFromGroups(id);
1388 m_content_features[id] = def;
1389 verbosestream << "NodeDefManager: registering content id \"" << id
1390 << "\": name=\"" << def.name << "\""<<std::endl;
1392 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1393 fixSelectionBoxIntUnion();
1395 // Add this content to the list of all groups it belongs to
1396 for (const auto &group : def.groups) {
1397 const std::string &group_name = group.first;
1398 m_group_to_items[group_name].push_back(id);
1405 content_t NodeDefManager::allocateDummy(const std::string &name)
1407 assert(name != ""); // Pre-condition
1410 return set(name, f);
1414 void NodeDefManager::removeNode(const std::string &name)
1419 // Erase name from name ID mapping
1420 content_t id = CONTENT_IGNORE;
1421 if (m_name_id_mapping.getId(name, id)) {
1422 m_name_id_mapping.eraseName(name);
1423 m_name_id_mapping_with_aliases.erase(name);
1426 eraseIdFromGroups(id);
1430 void NodeDefManager::updateAliases(IItemDefManager *idef)
1432 std::set<std::string> all;
1434 m_name_id_mapping_with_aliases.clear();
1435 for (const std::string &name : all) {
1436 const std::string &convert_to = idef->getAlias(name);
1438 if (m_name_id_mapping.getId(convert_to, id)) {
1439 m_name_id_mapping_with_aliases.insert(
1440 std::make_pair(name, id));
1445 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1447 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1448 "overrides to textures" << std::endl;
1450 for (const TextureOverride& texture_override : overrides) {
1452 if (!getId(texture_override.id, id))
1453 continue; // Ignore unknown node
1455 ContentFeatures &nodedef = m_content_features[id];
1458 if (texture_override.hasTarget(OverrideTarget::TOP))
1459 nodedef.tiledef[0].name = texture_override.texture;
1461 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1462 nodedef.tiledef[1].name = texture_override.texture;
1464 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1465 nodedef.tiledef[2].name = texture_override.texture;
1467 if (texture_override.hasTarget(OverrideTarget::LEFT))
1468 nodedef.tiledef[3].name = texture_override.texture;
1470 if (texture_override.hasTarget(OverrideTarget::BACK))
1471 nodedef.tiledef[4].name = texture_override.texture;
1473 if (texture_override.hasTarget(OverrideTarget::FRONT))
1474 nodedef.tiledef[5].name = texture_override.texture;
1477 // Override special tiles, if applicable
1478 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1479 nodedef.tiledef_special[0].name = texture_override.texture;
1481 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1482 nodedef.tiledef_special[1].name = texture_override.texture;
1484 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1485 nodedef.tiledef_special[2].name = texture_override.texture;
1487 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1488 nodedef.tiledef_special[3].name = texture_override.texture;
1490 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1491 nodedef.tiledef_special[4].name = texture_override.texture;
1493 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1494 nodedef.tiledef_special[5].name = texture_override.texture;
1498 void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
1501 infostream << "NodeDefManager::updateTextures(): Updating "
1502 "textures in node definitions" << std::endl;
1504 Client *client = (Client *)gamedef;
1505 ITextureSource *tsrc = client->tsrc();
1506 IShaderSource *shdsrc = client->getShaderSource();
1507 auto smgr = client->getSceneManager();
1508 scene::IMeshManipulator *meshmanip = smgr->getMeshManipulator();
1509 TextureSettings tsettings;
1510 tsettings.readSettings();
1512 u32 size = m_content_features.size();
1514 for (u32 i = 0; i < size; i++) {
1515 ContentFeatures *f = &(m_content_features[i]);
1516 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1517 client->showUpdateProgressTexture(progress_callback_args, i, size);
1522 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1524 writeU8(os, 1); // version
1526 std::ostringstream os2(std::ios::binary);
1527 for (u32 i = 0; i < m_content_features.size(); i++) {
1528 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1529 || i == CONTENT_UNKNOWN)
1531 const ContentFeatures *f = &m_content_features[i];
1532 if (f->name.empty())
1535 // Wrap it in a string to allow different lengths without
1536 // strict version incompatibilities
1537 std::ostringstream wrapper_os(std::ios::binary);
1538 f->serialize(wrapper_os, protocol_version);
1539 os2<<serializeString16(wrapper_os.str());
1541 // must not overflow
1542 u16 next = count + 1;
1543 FATAL_ERROR_IF(next < count, "Overflow");
1546 writeU16(os, count);
1547 os << serializeString32(os2.str());
1551 void NodeDefManager::deSerialize(std::istream &is)
1554 int version = readU8(is);
1556 throw SerializationError("unsupported NodeDefinitionManager version");
1557 u16 count = readU16(is);
1558 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1560 for (u16 n = 0; n < count; n++) {
1561 u16 i = readU16(is2);
1563 // Read it from the string wrapper
1564 std::string wrapper = deSerializeString16(is2);
1565 std::istringstream wrapper_is(wrapper, std::ios::binary);
1566 f.deSerialize(wrapper_is);
1568 // Check error conditions
1569 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1570 warningstream << "NodeDefManager::deSerialize(): "
1571 "not changing builtin node " << i << std::endl;
1574 if (f.name.empty()) {
1575 warningstream << "NodeDefManager::deSerialize(): "
1576 "received empty name" << std::endl;
1582 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1583 warningstream << "NodeDefManager::deSerialize(): "
1584 "already defined with different ID: " << f.name << std::endl;
1588 // All is ok, add node definition with the requested ID
1589 if (i >= m_content_features.size())
1590 m_content_features.resize((u32)(i) + 1);
1591 m_content_features[i] = f;
1592 addNameIdMapping(i, f.name);
1593 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1595 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1596 fixSelectionBoxIntUnion();
1599 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1600 // are not sent, resolve them client-side too.
1605 void NodeDefManager::addNameIdMapping(content_t i, const std::string &name)
1607 m_name_id_mapping.set(i, name);
1608 m_name_id_mapping_with_aliases.emplace(name, i);
1612 NodeDefManager *createNodeDefManager()
1614 return new NodeDefManager();
1618 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1621 if (m_node_registration_complete)
1622 nr->nodeResolveInternal();
1624 m_pending_resolve_callbacks.push_back(nr);
1628 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1630 size_t len = m_pending_resolve_callbacks.size();
1631 for (size_t i = 0; i != len; i++) {
1632 if (nr != m_pending_resolve_callbacks[i])
1636 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1637 m_pending_resolve_callbacks.resize(len);
1645 void NodeDefManager::runNodeResolveCallbacks()
1647 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1648 NodeResolver *nr = m_pending_resolve_callbacks[i];
1649 nr->nodeResolveInternal();
1652 m_pending_resolve_callbacks.clear();
1656 void NodeDefManager::resetNodeResolveState()
1658 m_node_registration_complete = false;
1659 m_pending_resolve_callbacks.clear();
1662 static void removeDupes(std::vector<content_t> &list)
1664 std::sort(list.begin(), list.end());
1665 auto new_end = std::unique(list.begin(), list.end());
1666 list.erase(new_end, list.end());
1669 void NodeDefManager::resolveCrossrefs()
1671 for (ContentFeatures &f : m_content_features) {
1672 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1673 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1674 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1677 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1680 for (const std::string &name : f.connects_to) {
1681 getIds(name, f.connects_to_ids);
1683 removeDupes(f.connects_to_ids);
1687 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1688 u8 connect_face) const
1690 const ContentFeatures &f1 = get(from);
1692 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1695 // lookup target in connected set
1696 if (!CONTAINS(f1.connects_to_ids, to.param0))
1699 const ContentFeatures &f2 = get(to);
1701 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1702 // ignores actually looking if back connection exists
1703 return CONTAINS(f2.connects_to_ids, from.param0);
1705 // does to node declare usable faces?
1706 if (f2.connect_sides > 0) {
1707 if ((f2.param_type_2 == CPT2_FACEDIR ||
1708 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1709 && (connect_face >= 4)) {
1710 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1711 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1713 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1715 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1716 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1718 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1719 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1720 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1722 return (f2.connect_sides
1723 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1725 return (f2.connect_sides & connect_face);
1727 // the target is just a regular node, so connect no matter back connection
1735 NodeResolver::NodeResolver()
1741 NodeResolver::~NodeResolver()
1743 if (!m_resolve_done && m_ndef)
1744 m_ndef->cancelNodeResolveCallback(this);
1748 void NodeResolver::cloneTo(NodeResolver *res) const
1750 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1751 " after resolving has completed");
1752 /* We don't actually do anything significant. Since the node resolving has
1753 * already completed, the class that called us will already have the
1754 * resolved IDs in its data structures (which it copies on its own) */
1755 res->m_ndef = m_ndef;
1756 res->m_resolve_done = true;
1760 void NodeResolver::nodeResolveInternal()
1762 m_nodenames_idx = 0;
1763 m_nnlistsizes_idx = 0;
1766 m_resolve_done = true;
1768 m_nodenames.clear();
1769 m_nnlistsizes.clear();
1773 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1774 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1776 if (m_nodenames_idx == m_nodenames.size()) {
1777 *result_out = c_fallback;
1778 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1783 std::string name = m_nodenames[m_nodenames_idx++];
1785 bool success = m_ndef->getId(name, c);
1786 if (!success && !node_alt.empty()) {
1788 success = m_ndef->getId(name, c);
1792 if (error_on_fallback)
1793 errorstream << "NodeResolver: failed to resolve node name '" << name
1794 << "'." << std::endl;
1803 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1804 bool all_required, content_t c_fallback)
1806 bool success = true;
1808 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1809 errorstream << "NodeResolver: no more node lists" << std::endl;
1813 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1816 if (m_nodenames_idx == m_nodenames.size()) {
1817 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1822 std::string &name = m_nodenames[m_nodenames_idx++];
1824 if (name.substr(0,6) != "group:") {
1825 if (m_ndef->getId(name, c)) {
1826 result_out->push_back(c);
1827 } else if (all_required) {
1828 errorstream << "NodeResolver: failed to resolve node name '"
1829 << name << "'." << std::endl;
1830 result_out->push_back(c_fallback);
1834 m_ndef->getIds(name, *result_out);
1841 void NodeResolver::reset(bool resolve_done)
1843 m_nodenames.clear();
1844 m_nodenames_idx = 0;
1845 m_nnlistsizes.clear();
1846 m_nnlistsizes_idx = 0;
1848 m_resolve_done = resolve_done;
1850 m_nodenames.reserve(16);
1851 m_nnlistsizes.reserve(4);