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
62 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
73 writeU16(os, fixed.size());
74 for (const aabb3f &nodebox : fixed) {
75 writeV3F32(os, nodebox.MinEdge);
76 writeV3F32(os, nodebox.MaxEdge);
79 case NODEBOX_WALLMOUNTED:
82 writeV3F32(os, wall_top.MinEdge);
83 writeV3F32(os, wall_top.MaxEdge);
84 writeV3F32(os, wall_bottom.MinEdge);
85 writeV3F32(os, wall_bottom.MaxEdge);
86 writeV3F32(os, wall_side.MinEdge);
87 writeV3F32(os, wall_side.MaxEdge);
89 case NODEBOX_CONNECTED: {
92 #define WRITEBOX(box) \
93 writeU16(os, (box).size()); \
94 for (const aabb3f &i: (box)) { \
95 writeV3F32(os, i.MinEdge); \
96 writeV3F32(os, i.MaxEdge); \
99 const auto &c = getConnected();
102 WRITEBOX(c.connect_top);
103 WRITEBOX(c.connect_bottom);
104 WRITEBOX(c.connect_front);
105 WRITEBOX(c.connect_left);
106 WRITEBOX(c.connect_back);
107 WRITEBOX(c.connect_right);
108 WRITEBOX(c.disconnected_top);
109 WRITEBOX(c.disconnected_bottom);
110 WRITEBOX(c.disconnected_front);
111 WRITEBOX(c.disconnected_left);
112 WRITEBOX(c.disconnected_back);
113 WRITEBOX(c.disconnected_right);
114 WRITEBOX(c.disconnected);
115 WRITEBOX(c.disconnected_sides);
124 void NodeBox::deSerialize(std::istream &is)
126 int version = readU8(is);
128 throw SerializationError("unsupported NodeBox version");
132 type = (enum NodeBoxType)readU8(is);
134 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
136 u16 fixed_count = readU16(is);
140 box.MinEdge = readV3F32(is);
141 box.MaxEdge = readV3F32(is);
142 fixed.push_back(box);
145 else if(type == NODEBOX_WALLMOUNTED)
147 wall_top.MinEdge = readV3F32(is);
148 wall_top.MaxEdge = readV3F32(is);
149 wall_bottom.MinEdge = readV3F32(is);
150 wall_bottom.MaxEdge = readV3F32(is);
151 wall_side.MinEdge = readV3F32(is);
152 wall_side.MaxEdge = readV3F32(is);
154 else if (type == NODEBOX_CONNECTED)
156 #define READBOXES(box) { \
157 count = readU16(is); \
158 (box).reserve(count); \
160 v3f min = readV3F32(is); \
161 v3f max = readV3F32(is); \
162 (box).emplace_back(min, max); }; }
166 auto &c = getConnected();
169 READBOXES(c.connect_top);
170 READBOXES(c.connect_bottom);
171 READBOXES(c.connect_front);
172 READBOXES(c.connect_left);
173 READBOXES(c.connect_back);
174 READBOXES(c.connect_right);
175 READBOXES(c.disconnected_top);
176 READBOXES(c.disconnected_bottom);
177 READBOXES(c.disconnected_front);
178 READBOXES(c.disconnected_left);
179 READBOXES(c.disconnected_back);
180 READBOXES(c.disconnected_right);
181 READBOXES(c.disconnected);
182 READBOXES(c.disconnected_sides);
190 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
191 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
192 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
193 #define TILE_FLAG_HAS_COLOR (1 << 3)
194 #define TILE_FLAG_HAS_SCALE (1 << 4)
195 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
197 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
199 // protocol_version >= 36
201 writeU8(os, version);
203 if (protocol_version > 39) {
204 os << serializeString16(name);
206 // Before f018737, TextureSource::getTextureAverageColor did not handle
207 // missing textures. "[png" can be used as base texture, but is not known
208 // on older clients. Hence use "blank.png" to avoid this problem.
209 // To be forward-compatible with future base textures/modifiers,
210 // we apply the same prefix to any texture beginning with [,
211 // except for the ones that are supported on older clients.
212 bool pass_through = true;
214 if (!name.empty() && name[0] == '[') {
215 pass_through = str_starts_with(name, "[combine:") ||
216 str_starts_with(name, "[inventorycube{") ||
217 str_starts_with(name, "[lowpart:");
221 os << serializeString16(name);
223 os << serializeString16("blank.png^" + name);
225 animation.serialize(os, version);
226 bool has_scale = scale > 0;
228 if (backface_culling)
229 flags |= TILE_FLAG_BACKFACE_CULLING;
230 if (tileable_horizontal)
231 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
232 if (tileable_vertical)
233 flags |= TILE_FLAG_TILEABLE_VERTICAL;
235 flags |= TILE_FLAG_HAS_COLOR;
237 flags |= TILE_FLAG_HAS_SCALE;
238 if (align_style != ALIGN_STYLE_NODE)
239 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
242 writeU8(os, color.getRed());
243 writeU8(os, color.getGreen());
244 writeU8(os, color.getBlue());
248 if (align_style != ALIGN_STYLE_NODE)
249 writeU8(os, align_style);
252 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
253 NodeDrawType drawtype)
255 int version = readU8(is);
257 throw SerializationError("unsupported TileDef version");
258 name = deSerializeString16(is);
259 animation.deSerialize(is, version);
260 u16 flags = readU16(is);
261 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
262 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
263 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
264 has_color = flags & TILE_FLAG_HAS_COLOR;
265 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
266 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
268 color.setRed(readU8(is));
269 color.setGreen(readU8(is));
270 color.setBlue(readU8(is));
272 scale = has_scale ? readU8(is) : 0;
274 align_style = static_cast<AlignStyle>(readU8(is));
276 align_style = ALIGN_STYLE_NODE;
279 void TextureSettings::readSettings()
281 connected_glass = g_settings->getBool("connected_glass");
282 opaque_water = g_settings->getBool("opaque_water");
283 bool smooth_lighting = g_settings->getBool("smooth_lighting");
284 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
285 enable_minimap = g_settings->getBool("enable_minimap");
286 node_texture_size = g_settings->getU16("texture_min_size");
287 std::string leaves_style_str = g_settings->get("leaves_style");
288 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
289 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
291 // Mesh cache is not supported in combination with smooth lighting
293 enable_mesh_cache = false;
295 if (leaves_style_str == "fancy") {
296 leaves_style = LEAVES_FANCY;
297 } else if (leaves_style_str == "simple") {
298 leaves_style = LEAVES_SIMPLE;
300 leaves_style = LEAVES_OPAQUE;
303 if (world_aligned_mode_str == "enable")
304 world_aligned_mode = WORLDALIGN_ENABLE;
305 else if (world_aligned_mode_str == "force_solid")
306 world_aligned_mode = WORLDALIGN_FORCE;
307 else if (world_aligned_mode_str == "force_nodebox")
308 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
310 world_aligned_mode = WORLDALIGN_DISABLE;
312 if (autoscale_mode_str == "enable")
313 autoscale_mode = AUTOSCALE_ENABLE;
314 else if (autoscale_mode_str == "force")
315 autoscale_mode = AUTOSCALE_FORCE;
317 autoscale_mode = AUTOSCALE_DISABLE;
324 ContentFeatures::ContentFeatures()
329 ContentFeatures::~ContentFeatures()
332 for (u16 j = 0; j < 6; j++) {
333 delete tiles[j].layers[0].frames;
334 delete tiles[j].layers[1].frames;
336 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
337 delete special_tiles[j].layers[0].frames;
341 void ContentFeatures::reset()
348 visual_solidness = 0;
349 backface_culling = true;
352 has_on_construct = false;
353 has_on_destruct = false;
354 has_after_destruct = false;
358 NOTE: Most of this is always overridden by the default values given
363 // Unknown nodes can be dug
364 groups["dig_immediate"] = 2;
365 drawtype = NDT_NORMAL;
368 for (auto &i : mesh_ptr)
370 minimap_color = video::SColor(0, 0, 0, 0);
373 for (auto &i : tiledef)
375 for (auto &j : tiledef_special)
377 alpha = ALPHAMODE_OPAQUE;
378 post_effect_color = video::SColor(0, 0, 0, 0);
379 param_type = CPT_NONE;
380 param_type_2 = CPT2_NONE;
381 is_ground_content = false;
382 light_propagates = false;
383 sunlight_propagates = false;
388 buildable_to = false;
390 rightclickable = true;
392 leveled_max = LEVELED_MAX;
393 liquid_type = LIQUID_NONE;
394 liquid_alternative_flowing = "";
395 liquid_alternative_flowing_id = CONTENT_IGNORE;
396 liquid_alternative_source = "";
397 liquid_alternative_source_id = CONTENT_IGNORE;
398 liquid_viscosity = 0;
399 liquid_renewable = true;
400 liquid_range = LIQUID_LEVEL_MAX+1;
403 damage_per_second = 0;
405 selection_box.reset();
406 collision_box.reset();
408 legacy_facedir_simple = false;
409 legacy_wallmounted = false;
410 sound_footstep = SimpleSoundSpec();
411 sound_dig = SimpleSoundSpec("__group");
412 sound_dug = SimpleSoundSpec();
414 connects_to_ids.clear();
416 color = video::SColor(0xFFFFFFFF);
419 node_dig_prediction = "air";
421 liquid_move_physics = false;
424 void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
426 // No special handling for nodebox/mesh here as it doesn't make sense to
427 // throw warnings when the server is too old to support the "correct" way
430 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
433 case NDT_FLOWINGLIQUID:
434 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
437 alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
442 u8 ContentFeatures::getAlphaForLegacy() const
444 // This is so simple only because 255 and 0 mean wildly different things
445 // depending on drawtype...
446 return alpha == ALPHAMODE_OPAQUE ? 255 : 0;
449 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
451 const u8 version = CONTENTFEATURES_VERSION;
452 writeU8(os, version);
455 os << serializeString16(name);
456 writeU16(os, groups.size());
457 for (const auto &group : groups) {
458 os << serializeString16(group.first);
459 if (protocol_version < 41 && group.first.compare("bouncy") == 0) {
460 // Old clients may choke on negative bouncy value
461 writeS16(os, abs(group.second));
463 writeS16(os, group.second);
466 writeU8(os, param_type);
467 writeU8(os, param_type_2);
470 writeU8(os, drawtype);
471 os << serializeString16(mesh);
472 writeF32(os, visual_scale);
474 for (const TileDef &td : tiledef)
475 td.serialize(os, protocol_version);
476 for (const TileDef &td : tiledef_overlay)
477 td.serialize(os, protocol_version);
478 writeU8(os, CF_SPECIAL_COUNT);
479 for (const TileDef &td : tiledef_special) {
480 td.serialize(os, protocol_version);
482 writeU8(os, getAlphaForLegacy());
483 writeU8(os, color.getRed());
484 writeU8(os, color.getGreen());
485 writeU8(os, color.getBlue());
486 os << serializeString16(palette_name);
488 writeU8(os, connect_sides);
489 writeU16(os, connects_to_ids.size());
490 for (u16 connects_to_id : connects_to_ids)
491 writeU16(os, connects_to_id);
492 writeARGB8(os, post_effect_color);
493 writeU8(os, leveled);
496 writeU8(os, light_propagates);
497 writeU8(os, sunlight_propagates);
498 writeU8(os, light_source);
501 writeU8(os, is_ground_content);
504 writeU8(os, walkable);
505 writeU8(os, pointable);
506 writeU8(os, diggable);
507 writeU8(os, climbable);
508 writeU8(os, buildable_to);
509 writeU8(os, rightclickable);
510 writeU32(os, damage_per_second);
513 LiquidType liquid_type_bc = liquid_type;
514 if (protocol_version <= 39) {
515 // Since commit 7f25823, liquid drawtypes can be used even with LIQUID_NONE
516 // solution: force liquid type accordingly to accepted values
517 if (drawtype == NDT_LIQUID)
518 liquid_type_bc = LIQUID_SOURCE;
519 else if (drawtype == NDT_FLOWINGLIQUID)
520 liquid_type_bc = LIQUID_FLOWING;
522 writeU8(os, liquid_type_bc);
523 os << serializeString16(liquid_alternative_flowing);
524 os << serializeString16(liquid_alternative_source);
525 writeU8(os, liquid_viscosity);
526 writeU8(os, liquid_renewable);
527 writeU8(os, liquid_range);
528 writeU8(os, drowning);
529 writeU8(os, floodable);
532 node_box.serialize(os, protocol_version);
533 selection_box.serialize(os, protocol_version);
534 collision_box.serialize(os, protocol_version);
537 sound_footstep.serialize(os, version);
538 sound_dig.serialize(os, version);
539 sound_dug.serialize(os, version);
542 writeU8(os, legacy_facedir_simple);
543 writeU8(os, legacy_wallmounted);
546 os << serializeString16(node_dig_prediction);
547 writeU8(os, leveled_max);
549 writeU8(os, move_resistance);
550 writeU8(os, liquid_move_physics);
553 void ContentFeatures::deSerialize(std::istream &is)
556 const u8 version = readU8(is);
557 if (version < CONTENTFEATURES_VERSION)
558 throw SerializationError("unsupported ContentFeatures version");
561 name = deSerializeString16(is);
563 u32 groups_size = readU16(is);
564 for (u32 i = 0; i < groups_size; i++) {
565 std::string name = deSerializeString16(is);
566 int value = readS16(is);
567 groups[name] = value;
569 param_type = (enum ContentParamType) readU8(is);
570 param_type_2 = (enum ContentParamType2) readU8(is);
573 drawtype = (enum NodeDrawType) readU8(is);
574 mesh = deSerializeString16(is);
575 visual_scale = readF32(is);
577 throw SerializationError("unsupported tile count");
578 for (TileDef &td : tiledef)
579 td.deSerialize(is, version, drawtype);
580 for (TileDef &td : tiledef_overlay)
581 td.deSerialize(is, version, drawtype);
582 if (readU8(is) != CF_SPECIAL_COUNT)
583 throw SerializationError("unsupported CF_SPECIAL_COUNT");
584 for (TileDef &td : tiledef_special)
585 td.deSerialize(is, version, drawtype);
586 setAlphaFromLegacy(readU8(is));
587 color.setRed(readU8(is));
588 color.setGreen(readU8(is));
589 color.setBlue(readU8(is));
590 palette_name = deSerializeString16(is);
592 connect_sides = readU8(is);
593 u16 connects_to_size = readU16(is);
594 connects_to_ids.clear();
595 for (u16 i = 0; i < connects_to_size; i++)
596 connects_to_ids.push_back(readU16(is));
597 post_effect_color = readARGB8(is);
598 leveled = readU8(is);
601 light_propagates = readU8(is);
602 sunlight_propagates = readU8(is);
603 light_source = readU8(is);
604 light_source = MYMIN(light_source, LIGHT_MAX);
607 is_ground_content = readU8(is);
610 walkable = readU8(is);
611 pointable = readU8(is);
612 diggable = readU8(is);
613 climbable = readU8(is);
614 buildable_to = readU8(is);
615 rightclickable = readU8(is);
616 damage_per_second = readU32(is);
619 liquid_type = (enum LiquidType) readU8(is);
620 liquid_move_physics = liquid_type != LIQUID_NONE;
621 liquid_alternative_flowing = deSerializeString16(is);
622 liquid_alternative_source = deSerializeString16(is);
623 liquid_viscosity = readU8(is);
624 move_resistance = liquid_viscosity; // set default move_resistance
625 liquid_renewable = readU8(is);
626 liquid_range = readU8(is);
627 drowning = readU8(is);
628 floodable = readU8(is);
631 node_box.deSerialize(is);
632 selection_box.deSerialize(is);
633 collision_box.deSerialize(is);
636 sound_footstep.deSerialize(is, version);
637 sound_dig.deSerialize(is, version);
638 sound_dug.deSerialize(is, version);
640 // read legacy properties
641 legacy_facedir_simple = readU8(is);
642 legacy_wallmounted = readU8(is);
645 node_dig_prediction = deSerializeString16(is);
648 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
649 throw SerializationError("");
654 throw SerializationError("");
655 alpha = static_cast<enum AlphaMode>(tmp);
659 throw SerializationError("");
660 move_resistance = tmp;
664 throw SerializationError("");
665 liquid_move_physics = tmp;
666 } catch(SerializationError &e) {};
670 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
671 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
672 u8 material_type, u32 shader_id, bool backface_culling,
673 const TextureSettings &tsettings)
675 layer->shader_id = shader_id;
676 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
677 layer->material_type = material_type;
679 bool has_scale = tiledef.scale > 0;
680 bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
681 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
682 if (use_autoscale && layer->texture) {
683 auto texture_size = layer->texture->getOriginalSize();
684 float base_size = tsettings.node_texture_size;
685 float size = std::fmin(texture_size.Width, texture_size.Height);
686 layer->scale = std::fmax(base_size, size) / base_size;
687 } else if (has_scale) {
688 layer->scale = tiledef.scale;
692 if (!tile.world_aligned)
695 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
698 layer->material_flags = 0;
699 if (backface_culling)
700 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
701 if (tiledef.animation.type != TAT_NONE)
702 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
703 if (tiledef.tileable_horizontal)
704 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
705 if (tiledef.tileable_vertical)
706 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
709 layer->has_color = tiledef.has_color;
710 if (tiledef.has_color)
711 layer->color = tiledef.color;
713 layer->color = color;
715 // Animation parameters
717 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
718 assert(layer->texture);
720 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
721 &frame_count, &frame_length_ms, NULL);
722 layer->animation_frame_count = frame_count;
723 layer->animation_frame_length_ms = frame_length_ms;
726 if (frame_count == 1) {
727 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
729 assert(layer->texture);
731 layer->frames = new std::vector<FrameSpec>();
732 layer->frames->resize(frame_count);
734 std::ostringstream os(std::ios::binary);
735 for (int i = 0; i < frame_count; i++) {
740 tiledef.animation.getTextureModifer(os,
741 layer->texture->getOriginalSize(), i);
743 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
744 if (layer->normal_texture)
745 frame.normal_texture = tsrc->getNormalTexture(os.str());
746 frame.flags_texture = layer->flags_texture;
747 (*layer->frames)[i] = frame;
752 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
754 video::IVideoDriver *driver = RenderingEngine::get_video_driver();
755 static thread_local bool long_warning_printed = false;
756 std::set<std::string> seen;
758 for (int i = 0; i < length; i++) {
759 if (seen.find(tiles[i].name) != seen.end())
761 seen.insert(tiles[i].name);
763 // Load the texture and see if there's any transparent pixels
764 video::ITexture *texture = tsrc->getTexture(tiles[i].name);
765 video::IImage *image = driver->createImage(texture,
766 core::position2d<s32>(0, 0), texture->getOriginalSize());
769 core::dimension2d<u32> dim = image->getDimension();
771 for (u16 x = 0; x < dim.Width; x++) {
772 for (u16 y = 0; y < dim.Height; y++) {
773 if (image->getPixel(x, y).getAlpha() < 255) {
784 warningstream << "Texture \"" << tiles[i].name << "\" of "
785 << name << " has transparency, assuming "
786 "use_texture_alpha = \"clip\"." << std::endl;
787 if (!long_warning_printed) {
788 warningstream << " This warning can be a false-positive if "
789 "unused pixels in the texture are transparent. However if "
790 "it is meant to be transparent, you *MUST* update the "
791 "nodedef and set use_texture_alpha = \"clip\"! This "
792 "compatibility code will be removed in a few releases."
794 long_warning_printed = true;
801 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
803 if (style == ALIGN_STYLE_WORLD)
805 if (mode == WORLDALIGN_DISABLE)
807 if (style == ALIGN_STYLE_USER_DEFINED)
809 if (drawtype == NDT_NORMAL)
810 return mode >= WORLDALIGN_FORCE;
811 if (drawtype == NDT_NODEBOX)
812 return mode >= WORLDALIGN_FORCE_NODEBOX;
816 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
817 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
819 // minimap pixel color - the average color of a texture
820 if (tsettings.enable_minimap && !tiledef[0].name.empty())
821 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
823 // Figure out the actual tiles to use
825 for (u32 j = 0; j < 6; j++) {
826 tdef[j] = tiledef[j];
827 if (tdef[j].name.empty()) {
828 tdef[j].name = "no_texture.png";
829 tdef[j].backface_culling = false;
832 // also the overlay tiles
833 TileDef tdef_overlay[6];
834 for (u32 j = 0; j < 6; j++)
835 tdef_overlay[j] = tiledef_overlay[j];
836 // also the special tiles
837 TileDef tdef_spec[6];
838 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++) {
839 tdef_spec[j] = tiledef_special[j];
842 bool is_liquid = false;
844 if (alpha == ALPHAMODE_LEGACY_COMPAT) {
845 // Before working with the alpha mode, resolve any legacy kludges
846 alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
849 MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
850 TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
851 TILE_MATERIAL_ALPHA);
862 if (tsettings.opaque_water)
863 alpha = ALPHAMODE_OPAQUE;
867 case NDT_FLOWINGLIQUID:
869 if (tsettings.opaque_water)
870 alpha = ALPHAMODE_OPAQUE;
875 visual_solidness = 1;
877 case NDT_GLASSLIKE_FRAMED:
879 visual_solidness = 1;
881 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
883 visual_solidness = 1;
884 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
888 visual_solidness = 1;
890 case NDT_ALLFACES_OPTIONAL:
891 if (tsettings.leaves_style == LEAVES_FANCY) {
892 drawtype = NDT_ALLFACES;
894 visual_solidness = 1;
895 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
896 for (u32 j = 0; j < 6; j++) {
897 if (!tdef_spec[j].name.empty())
898 tdef[j].name = tdef_spec[j].name;
900 drawtype = NDT_GLASSLIKE;
902 visual_solidness = 1;
905 // waving nodes must make faces so there are no gaps
906 drawtype = NDT_ALLFACES;
908 visual_solidness = 1;
910 drawtype = NDT_NORMAL;
913 for (TileDef &td : tdef)
914 td.name += std::string("^[noalpha");
917 material_type = TILE_MATERIAL_WAVING_LEAVES;
922 material_type = TILE_MATERIAL_WAVING_PLANTS;
931 material_type = TILE_MATERIAL_WAVING_PLANTS;
932 } else if (waving == 2) {
933 material_type = TILE_MATERIAL_WAVING_LEAVES;
934 } else if (waving == 3) {
935 material_type = alpha == ALPHAMODE_OPAQUE ?
936 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
937 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
946 case NDT_PLANTLIKE_ROOTED:
953 material_type = alpha == ALPHAMODE_OPAQUE ?
954 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
955 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
957 material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
958 TILE_MATERIAL_LIQUID_TRANSPARENT;
962 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
964 MaterialType overlay_material = material_type;
965 if (overlay_material == TILE_MATERIAL_OPAQUE)
966 overlay_material = TILE_MATERIAL_BASIC;
967 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
968 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
970 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
972 // Tiles (fill in f->tiles[])
973 for (u16 j = 0; j < 6; j++) {
974 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
975 tsettings.world_aligned_mode, drawtype);
976 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
977 color, material_type, tile_shader,
978 tdef[j].backface_culling, tsettings);
979 if (!tdef_overlay[j].name.empty())
980 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
981 color, overlay_material, overlay_shader,
982 tdef[j].backface_culling, tsettings);
985 MaterialType special_material = material_type;
986 if (drawtype == NDT_PLANTLIKE_ROOTED) {
988 special_material = TILE_MATERIAL_WAVING_PLANTS;
989 else if (waving == 2)
990 special_material = TILE_MATERIAL_WAVING_LEAVES;
992 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
994 // Special tiles (fill in f->special_tiles[])
995 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
996 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
997 color, special_material, special_shader,
998 tdef_spec[j].backface_culling, tsettings);
1000 if (param_type_2 == CPT2_COLOR ||
1001 param_type_2 == CPT2_COLORED_FACEDIR ||
1002 param_type_2 == CPT2_COLORED_WALLMOUNTED ||
1003 param_type_2 == CPT2_COLORED_DEGROTATE)
1004 palette = tsrc->getPalette(palette_name);
1006 if (drawtype == NDT_MESH && !mesh.empty()) {
1007 // Meshnode drawtype
1008 // Read the mesh and apply scale
1009 mesh_ptr[0] = client->getMesh(mesh);
1011 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
1012 scaleMesh(mesh_ptr[0], scale);
1013 recalculateBoundingBox(mesh_ptr[0]);
1014 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1018 //Cache 6dfacedir and wallmounted rotated clones of meshes
1019 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
1020 (param_type_2 == CPT2_FACEDIR
1021 || param_type_2 == CPT2_COLORED_FACEDIR)) {
1022 for (u16 j = 1; j < 24; j++) {
1023 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
1024 rotateMeshBy6dFacedir(mesh_ptr[j], j);
1025 recalculateBoundingBox(mesh_ptr[j]);
1026 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1028 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
1029 && (param_type_2 == CPT2_WALLMOUNTED ||
1030 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
1031 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
1032 for (u16 j = 1; j < 6; j++) {
1033 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
1034 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
1035 recalculateBoundingBox(mesh_ptr[j]);
1036 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1038 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
1039 recalculateBoundingBox(mesh_ptr[0]);
1040 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1052 NodeDefManager::NodeDefManager()
1058 NodeDefManager::~NodeDefManager()
1061 for (ContentFeatures &f : m_content_features) {
1062 for (auto &j : f.mesh_ptr) {
1071 void NodeDefManager::clear()
1073 m_content_features.clear();
1074 m_name_id_mapping.clear();
1075 m_name_id_mapping_with_aliases.clear();
1076 m_group_to_items.clear();
1078 m_selection_box_union.reset(0,0,0);
1079 m_selection_box_int_union.reset(0,0,0);
1081 resetNodeResolveState();
1083 u32 initial_length = 0;
1084 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1085 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1086 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1087 m_content_features.resize(initial_length);
1089 // Set CONTENT_UNKNOWN
1093 for (int t = 0; t < 6; t++)
1094 f.tiledef[t].name = "unknown_node.png";
1095 // Insert directly into containers
1096 content_t c = CONTENT_UNKNOWN;
1097 m_content_features[c] = f;
1098 addNameIdMapping(c, f.name);
1105 f.drawtype = NDT_AIRLIKE;
1106 f.param_type = CPT_LIGHT;
1107 f.light_propagates = true;
1108 f.sunlight_propagates = true;
1110 f.pointable = false;
1112 f.buildable_to = true;
1114 f.is_ground_content = true;
1115 // Insert directly into containers
1116 content_t c = CONTENT_AIR;
1117 m_content_features[c] = f;
1118 addNameIdMapping(c, f.name);
1121 // Set CONTENT_IGNORE
1125 f.drawtype = NDT_AIRLIKE;
1126 f.param_type = CPT_NONE;
1127 f.light_propagates = false;
1128 f.sunlight_propagates = false;
1130 f.pointable = false;
1132 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1133 f.is_ground_content = true;
1134 // Insert directly into containers
1135 content_t c = CONTENT_IGNORE;
1136 m_content_features[c] = f;
1137 addNameIdMapping(c, f.name);
1142 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1144 std::unordered_map<std::string, content_t>::const_iterator
1145 i = m_name_id_mapping_with_aliases.find(name);
1146 if(i == m_name_id_mapping_with_aliases.end())
1153 content_t NodeDefManager::getId(const std::string &name) const
1155 content_t id = CONTENT_IGNORE;
1161 bool NodeDefManager::getIds(const std::string &name,
1162 std::vector<content_t> &result) const
1164 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1165 if (name.substr(0,6) != "group:") {
1166 content_t id = CONTENT_IGNORE;
1167 bool exists = getId(name, id);
1169 result.push_back(id);
1172 std::string group = name.substr(6);
1174 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1175 i = m_group_to_items.find(group);
1176 if (i == m_group_to_items.end())
1179 const std::vector<content_t> &items = i->second;
1180 result.insert(result.end(), items.begin(), items.end());
1181 //printf("getIds: %dus\n", t.stop());
1186 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1188 content_t id = CONTENT_UNKNOWN;
1194 // returns CONTENT_IGNORE if no free ID found
1195 content_t NodeDefManager::allocateId()
1197 for (content_t id = m_next_id;
1198 id >= m_next_id; // overflow?
1200 while (id >= m_content_features.size()) {
1201 m_content_features.emplace_back();
1203 const ContentFeatures &f = m_content_features[id];
1204 if (f.name.empty()) {
1209 // If we arrive here, an overflow occurred in id.
1210 // That means no ID was found
1211 return CONTENT_IGNORE;
1216 * Returns the smallest box that contains all boxes
1217 * in the vector. Box_union is expanded.
1218 * @param[in] boxes the vector containing the boxes
1219 * @param[in, out] box_union the union of the arguments
1221 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1223 for (const aabb3f &box : boxes) {
1224 box_union->addInternalBox(box);
1230 * Returns a box that contains the nodebox in every case.
1231 * The argument node_union is expanded.
1232 * @param[in] nodebox the nodebox to be measured
1233 * @param[in] features used to decide whether the nodebox
1235 * @param[in, out] box_union the union of the arguments
1237 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1240 switch(nodebox.type) {
1242 case NODEBOX_LEVELED: {
1244 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1245 boxVectorUnion(nodebox.fixed, &half_processed);
1246 // Set leveled boxes to maximal
1247 if (nodebox.type == NODEBOX_LEVELED) {
1248 half_processed.MaxEdge.Y = +BS / 2;
1250 if (features.param_type_2 == CPT2_FACEDIR ||
1251 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1252 // Get maximal coordinate
1254 fabsf(half_processed.MinEdge.X),
1255 fabsf(half_processed.MinEdge.Y),
1256 fabsf(half_processed.MinEdge.Z),
1257 fabsf(half_processed.MaxEdge.X),
1258 fabsf(half_processed.MaxEdge.Y),
1259 fabsf(half_processed.MaxEdge.Z) };
1261 for (float coord : coords) {
1266 // Add the union of all possible rotated boxes
1267 box_union->addInternalPoint(-max, -max, -max);
1268 box_union->addInternalPoint(+max, +max, +max);
1270 box_union->addInternalBox(half_processed);
1274 case NODEBOX_WALLMOUNTED: {
1276 box_union->addInternalBox(nodebox.wall_top);
1277 box_union->addInternalBox(nodebox.wall_bottom);
1278 // Find maximal coordinate in the X-Z plane
1280 fabsf(nodebox.wall_side.MinEdge.X),
1281 fabsf(nodebox.wall_side.MinEdge.Z),
1282 fabsf(nodebox.wall_side.MaxEdge.X),
1283 fabsf(nodebox.wall_side.MaxEdge.Z) };
1285 for (float coord : coords) {
1290 // Add the union of all possible rotated boxes
1291 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1292 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1295 case NODEBOX_CONNECTED: {
1296 const auto &c = nodebox.getConnected();
1297 // Add all possible connected boxes
1298 boxVectorUnion(nodebox.fixed, box_union);
1299 boxVectorUnion(c.connect_top, box_union);
1300 boxVectorUnion(c.connect_bottom, box_union);
1301 boxVectorUnion(c.connect_front, box_union);
1302 boxVectorUnion(c.connect_left, box_union);
1303 boxVectorUnion(c.connect_back, box_union);
1304 boxVectorUnion(c.connect_right, box_union);
1305 boxVectorUnion(c.disconnected_top, box_union);
1306 boxVectorUnion(c.disconnected_bottom, box_union);
1307 boxVectorUnion(c.disconnected_front, box_union);
1308 boxVectorUnion(c.disconnected_left, box_union);
1309 boxVectorUnion(c.disconnected_back, box_union);
1310 boxVectorUnion(c.disconnected_right, box_union);
1311 boxVectorUnion(c.disconnected, box_union);
1312 boxVectorUnion(c.disconnected_sides, box_union);
1317 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1318 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1324 inline void NodeDefManager::fixSelectionBoxIntUnion()
1326 m_selection_box_int_union.MinEdge.X = floorf(
1327 m_selection_box_union.MinEdge.X / BS + 0.5f);
1328 m_selection_box_int_union.MinEdge.Y = floorf(
1329 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1330 m_selection_box_int_union.MinEdge.Z = floorf(
1331 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1332 m_selection_box_int_union.MaxEdge.X = ceilf(
1333 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1334 m_selection_box_int_union.MaxEdge.Y = ceilf(
1335 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1336 m_selection_box_int_union.MaxEdge.Z = ceilf(
1337 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1341 void NodeDefManager::eraseIdFromGroups(content_t id)
1343 // For all groups in m_group_to_items...
1344 for (auto iter_groups = m_group_to_items.begin();
1345 iter_groups != m_group_to_items.end();) {
1346 // Get the group items vector.
1347 std::vector<content_t> &items = iter_groups->second;
1349 // Remove any occurence of the id in the group items vector.
1350 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1352 // If group is empty, erase its vector from the map.
1354 iter_groups = m_group_to_items.erase(iter_groups);
1361 // IWritableNodeDefManager
1362 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1366 assert(name != "ignore");
1367 assert(name == def.name);
1369 content_t id = CONTENT_IGNORE;
1370 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1373 if (id == CONTENT_IGNORE) {
1374 warningstream << "NodeDefManager: Absolute "
1375 "limit reached" << std::endl;
1376 return CONTENT_IGNORE;
1378 assert(id != CONTENT_IGNORE);
1379 addNameIdMapping(id, name);
1382 // If there is already ContentFeatures registered for this id, clear old groups
1383 if (id < m_content_features.size())
1384 eraseIdFromGroups(id);
1386 m_content_features[id] = def;
1387 verbosestream << "NodeDefManager: registering content id \"" << id
1388 << "\": name=\"" << def.name << "\""<<std::endl;
1390 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1391 fixSelectionBoxIntUnion();
1393 // Add this content to the list of all groups it belongs to
1394 for (const auto &group : def.groups) {
1395 const std::string &group_name = group.first;
1396 m_group_to_items[group_name].push_back(id);
1403 content_t NodeDefManager::allocateDummy(const std::string &name)
1405 assert(name != ""); // Pre-condition
1408 return set(name, f);
1412 void NodeDefManager::removeNode(const std::string &name)
1417 // Erase name from name ID mapping
1418 content_t id = CONTENT_IGNORE;
1419 if (m_name_id_mapping.getId(name, id)) {
1420 m_name_id_mapping.eraseName(name);
1421 m_name_id_mapping_with_aliases.erase(name);
1424 eraseIdFromGroups(id);
1428 void NodeDefManager::updateAliases(IItemDefManager *idef)
1430 std::set<std::string> all;
1432 m_name_id_mapping_with_aliases.clear();
1433 for (const std::string &name : all) {
1434 const std::string &convert_to = idef->getAlias(name);
1436 if (m_name_id_mapping.getId(convert_to, id)) {
1437 m_name_id_mapping_with_aliases.insert(
1438 std::make_pair(name, id));
1443 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1445 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1446 "overrides to textures" << std::endl;
1448 for (const TextureOverride& texture_override : overrides) {
1450 if (!getId(texture_override.id, id))
1451 continue; // Ignore unknown node
1453 ContentFeatures &nodedef = m_content_features[id];
1456 if (texture_override.hasTarget(OverrideTarget::TOP))
1457 nodedef.tiledef[0].name = texture_override.texture;
1459 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1460 nodedef.tiledef[1].name = texture_override.texture;
1462 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1463 nodedef.tiledef[2].name = texture_override.texture;
1465 if (texture_override.hasTarget(OverrideTarget::LEFT))
1466 nodedef.tiledef[3].name = texture_override.texture;
1468 if (texture_override.hasTarget(OverrideTarget::BACK))
1469 nodedef.tiledef[4].name = texture_override.texture;
1471 if (texture_override.hasTarget(OverrideTarget::FRONT))
1472 nodedef.tiledef[5].name = texture_override.texture;
1475 // Override special tiles, if applicable
1476 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1477 nodedef.tiledef_special[0].name = texture_override.texture;
1479 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1480 nodedef.tiledef_special[1].name = texture_override.texture;
1482 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1483 nodedef.tiledef_special[2].name = texture_override.texture;
1485 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1486 nodedef.tiledef_special[3].name = texture_override.texture;
1488 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1489 nodedef.tiledef_special[4].name = texture_override.texture;
1491 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1492 nodedef.tiledef_special[5].name = texture_override.texture;
1496 void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
1499 infostream << "NodeDefManager::updateTextures(): Updating "
1500 "textures in node definitions" << std::endl;
1502 Client *client = (Client *)gamedef;
1503 ITextureSource *tsrc = client->tsrc();
1504 IShaderSource *shdsrc = client->getShaderSource();
1505 auto smgr = client->getSceneManager();
1506 scene::IMeshManipulator *meshmanip = smgr->getMeshManipulator();
1507 TextureSettings tsettings;
1508 tsettings.readSettings();
1510 u32 size = m_content_features.size();
1512 for (u32 i = 0; i < size; i++) {
1513 ContentFeatures *f = &(m_content_features[i]);
1514 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1515 client->showUpdateProgressTexture(progress_callback_args, i, size);
1520 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1522 writeU8(os, 1); // version
1524 std::ostringstream os2(std::ios::binary);
1525 for (u32 i = 0; i < m_content_features.size(); i++) {
1526 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1527 || i == CONTENT_UNKNOWN)
1529 const ContentFeatures *f = &m_content_features[i];
1530 if (f->name.empty())
1533 // Wrap it in a string to allow different lengths without
1534 // strict version incompatibilities
1535 std::ostringstream wrapper_os(std::ios::binary);
1536 f->serialize(wrapper_os, protocol_version);
1537 os2<<serializeString16(wrapper_os.str());
1539 // must not overflow
1540 u16 next = count + 1;
1541 FATAL_ERROR_IF(next < count, "Overflow");
1544 writeU16(os, count);
1545 os << serializeString32(os2.str());
1549 void NodeDefManager::deSerialize(std::istream &is)
1552 int version = readU8(is);
1554 throw SerializationError("unsupported NodeDefinitionManager version");
1555 u16 count = readU16(is);
1556 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1558 for (u16 n = 0; n < count; n++) {
1559 u16 i = readU16(is2);
1561 // Read it from the string wrapper
1562 std::string wrapper = deSerializeString16(is2);
1563 std::istringstream wrapper_is(wrapper, std::ios::binary);
1564 f.deSerialize(wrapper_is);
1566 // Check error conditions
1567 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1568 warningstream << "NodeDefManager::deSerialize(): "
1569 "not changing builtin node " << i << std::endl;
1572 if (f.name.empty()) {
1573 warningstream << "NodeDefManager::deSerialize(): "
1574 "received empty name" << std::endl;
1580 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1581 warningstream << "NodeDefManager::deSerialize(): "
1582 "already defined with different ID: " << f.name << std::endl;
1586 // All is ok, add node definition with the requested ID
1587 if (i >= m_content_features.size())
1588 m_content_features.resize((u32)(i) + 1);
1589 m_content_features[i] = f;
1590 addNameIdMapping(i, f.name);
1591 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1593 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1594 fixSelectionBoxIntUnion();
1597 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1598 // are not sent, resolve them client-side too.
1603 void NodeDefManager::addNameIdMapping(content_t i, const std::string &name)
1605 m_name_id_mapping.set(i, name);
1606 m_name_id_mapping_with_aliases.emplace(name, i);
1610 NodeDefManager *createNodeDefManager()
1612 return new NodeDefManager();
1616 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1619 if (m_node_registration_complete)
1620 nr->nodeResolveInternal();
1622 m_pending_resolve_callbacks.push_back(nr);
1626 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1628 size_t len = m_pending_resolve_callbacks.size();
1629 for (size_t i = 0; i != len; i++) {
1630 if (nr != m_pending_resolve_callbacks[i])
1634 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1635 m_pending_resolve_callbacks.resize(len);
1643 void NodeDefManager::runNodeResolveCallbacks()
1645 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1646 NodeResolver *nr = m_pending_resolve_callbacks[i];
1647 nr->nodeResolveInternal();
1650 m_pending_resolve_callbacks.clear();
1654 void NodeDefManager::resetNodeResolveState()
1656 m_node_registration_complete = false;
1657 m_pending_resolve_callbacks.clear();
1660 static void removeDupes(std::vector<content_t> &list)
1662 std::sort(list.begin(), list.end());
1663 auto new_end = std::unique(list.begin(), list.end());
1664 list.erase(new_end, list.end());
1667 void NodeDefManager::resolveCrossrefs()
1669 for (ContentFeatures &f : m_content_features) {
1670 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1671 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1672 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1675 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1678 for (const std::string &name : f.connects_to) {
1679 getIds(name, f.connects_to_ids);
1681 removeDupes(f.connects_to_ids);
1685 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1686 u8 connect_face) const
1688 const ContentFeatures &f1 = get(from);
1690 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1693 // lookup target in connected set
1694 if (!CONTAINS(f1.connects_to_ids, to.param0))
1697 const ContentFeatures &f2 = get(to);
1699 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1700 // ignores actually looking if back connection exists
1701 return CONTAINS(f2.connects_to_ids, from.param0);
1703 // does to node declare usable faces?
1704 if (f2.connect_sides > 0) {
1705 if ((f2.param_type_2 == CPT2_FACEDIR ||
1706 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1707 && (connect_face >= 4)) {
1708 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1709 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1711 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1713 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1714 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1716 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1717 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1718 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1720 return (f2.connect_sides
1721 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1723 return (f2.connect_sides & connect_face);
1725 // the target is just a regular node, so connect no matter back connection
1733 NodeResolver::NodeResolver()
1739 NodeResolver::~NodeResolver()
1741 if (!m_resolve_done && m_ndef)
1742 m_ndef->cancelNodeResolveCallback(this);
1746 void NodeResolver::cloneTo(NodeResolver *res) const
1748 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1749 " after resolving has completed");
1750 /* We don't actually do anything significant. Since the node resolving has
1751 * already completed, the class that called us will already have the
1752 * resolved IDs in its data structures (which it copies on its own) */
1753 res->m_ndef = m_ndef;
1754 res->m_resolve_done = true;
1758 void NodeResolver::nodeResolveInternal()
1760 m_nodenames_idx = 0;
1761 m_nnlistsizes_idx = 0;
1764 m_resolve_done = true;
1766 m_nodenames.clear();
1767 m_nnlistsizes.clear();
1771 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1772 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1774 if (m_nodenames_idx == m_nodenames.size()) {
1775 *result_out = c_fallback;
1776 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1781 std::string name = m_nodenames[m_nodenames_idx++];
1783 bool success = m_ndef->getId(name, c);
1784 if (!success && !node_alt.empty()) {
1786 success = m_ndef->getId(name, c);
1790 if (error_on_fallback)
1791 errorstream << "NodeResolver: failed to resolve node name '" << name
1792 << "'." << std::endl;
1801 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1802 bool all_required, content_t c_fallback)
1804 bool success = true;
1806 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1807 errorstream << "NodeResolver: no more node lists" << std::endl;
1811 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1814 if (m_nodenames_idx == m_nodenames.size()) {
1815 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1820 std::string &name = m_nodenames[m_nodenames_idx++];
1822 if (name.substr(0,6) != "group:") {
1823 if (m_ndef->getId(name, c)) {
1824 result_out->push_back(c);
1825 } else if (all_required) {
1826 errorstream << "NodeResolver: failed to resolve node name '"
1827 << name << "'." << std::endl;
1828 result_out->push_back(c_fallback);
1832 m_ndef->getIds(name, *result_out);
1839 void NodeResolver::reset(bool resolve_done)
1841 m_nodenames.clear();
1842 m_nodenames_idx = 0;
1843 m_nnlistsizes.clear();
1844 m_nnlistsizes_idx = 0;
1846 m_resolve_done = resolve_done;
1848 m_nodenames.reserve(16);
1849 m_nnlistsizes.reserve(4);