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 &d)
1364 ContentFeatures def = d;
1368 assert(name != "ignore");
1369 assert(name == def.name);
1371 content_t id = CONTENT_IGNORE;
1373 if (m_name_id_mapping.getId(name, id)) {
1375 ContentFeatures old_def = get(name);
1376 for (u32 j = 0; j < 6; j++)
1377 if (def.tiledef[j].name.empty())
1378 def.tiledef[j] = old_def.tiledef[j];
1379 for (u32 j = 0; j < 6; j++)
1380 if (def.tiledef_overlay[j].name.empty())
1381 def.tiledef_overlay[j] = old_def.tiledef_overlay[j];
1382 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
1383 if (def.tiledef_special[j].name.empty())
1384 def.tiledef_special[j] = old_def.tiledef_special[j];
1389 if (id == CONTENT_IGNORE) {
1390 warningstream << "NodeDefManager: Absolute "
1391 "limit reached" << std::endl;
1392 return CONTENT_IGNORE;
1394 assert(id != CONTENT_IGNORE);
1395 addNameIdMapping(id, name);
1398 // If there is already ContentFeatures registered for this id, clear old groups
1399 if (id < m_content_features.size())
1400 eraseIdFromGroups(id);
1402 m_content_features[id] = def;
1403 verbosestream << "NodeDefManager: registering content id \"" << id
1404 << "\": name=\"" << def.name << "\""<<std::endl;
1406 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1407 fixSelectionBoxIntUnion();
1409 // Add this content to the list of all groups it belongs to
1410 for (const auto &group : def.groups) {
1411 const std::string &group_name = group.first;
1412 m_group_to_items[group_name].push_back(id);
1419 content_t NodeDefManager::allocateDummy(const std::string &name)
1421 assert(name != ""); // Pre-condition
1424 return set(name, f);
1428 void NodeDefManager::removeNode(const std::string &name)
1433 // Erase name from name ID mapping
1434 content_t id = CONTENT_IGNORE;
1435 if (m_name_id_mapping.getId(name, id)) {
1436 m_name_id_mapping.eraseName(name);
1437 m_name_id_mapping_with_aliases.erase(name);
1440 eraseIdFromGroups(id);
1444 void NodeDefManager::updateAliases(IItemDefManager *idef)
1446 std::set<std::string> all;
1448 m_name_id_mapping_with_aliases.clear();
1449 for (const std::string &name : all) {
1450 const std::string &convert_to = idef->getAlias(name);
1452 if (m_name_id_mapping.getId(convert_to, id)) {
1453 m_name_id_mapping_with_aliases.insert(
1454 std::make_pair(name, id));
1459 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1461 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1462 "overrides to textures" << std::endl;
1464 for (const TextureOverride& texture_override : overrides) {
1466 if (!getId(texture_override.id, id))
1467 continue; // Ignore unknown node
1469 ContentFeatures &nodedef = m_content_features[id];
1472 if (texture_override.hasTarget(OverrideTarget::TOP))
1473 nodedef.tiledef[0].name = texture_override.texture;
1475 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1476 nodedef.tiledef[1].name = texture_override.texture;
1478 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1479 nodedef.tiledef[2].name = texture_override.texture;
1481 if (texture_override.hasTarget(OverrideTarget::LEFT))
1482 nodedef.tiledef[3].name = texture_override.texture;
1484 if (texture_override.hasTarget(OverrideTarget::BACK))
1485 nodedef.tiledef[4].name = texture_override.texture;
1487 if (texture_override.hasTarget(OverrideTarget::FRONT))
1488 nodedef.tiledef[5].name = texture_override.texture;
1491 // Override special tiles, if applicable
1492 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1493 nodedef.tiledef_special[0].name = texture_override.texture;
1495 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1496 nodedef.tiledef_special[1].name = texture_override.texture;
1498 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1499 nodedef.tiledef_special[2].name = texture_override.texture;
1501 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1502 nodedef.tiledef_special[3].name = texture_override.texture;
1504 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1505 nodedef.tiledef_special[4].name = texture_override.texture;
1507 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1508 nodedef.tiledef_special[5].name = texture_override.texture;
1512 void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
1515 infostream << "NodeDefManager::updateTextures(): Updating "
1516 "textures in node definitions" << std::endl;
1518 Client *client = (Client *)gamedef;
1519 ITextureSource *tsrc = client->tsrc();
1520 IShaderSource *shdsrc = client->getShaderSource();
1521 auto smgr = client->getSceneManager();
1522 scene::IMeshManipulator *meshmanip = smgr->getMeshManipulator();
1523 TextureSettings tsettings;
1524 tsettings.readSettings();
1526 u32 size = m_content_features.size();
1528 for (u32 i = 0; i < size; i++) {
1529 ContentFeatures *f = &(m_content_features[i]);
1530 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1531 client->showUpdateProgressTexture(progress_callback_args, i, size);
1536 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1538 writeU8(os, 1); // version
1540 std::ostringstream os2(std::ios::binary);
1541 for (u32 i = 0; i < m_content_features.size(); i++) {
1542 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1543 || i == CONTENT_UNKNOWN)
1545 const ContentFeatures *f = &m_content_features[i];
1546 if (f->name.empty())
1549 // Wrap it in a string to allow different lengths without
1550 // strict version incompatibilities
1551 std::ostringstream wrapper_os(std::ios::binary);
1552 f->serialize(wrapper_os, protocol_version);
1553 os2<<serializeString16(wrapper_os.str());
1555 // must not overflow
1556 u16 next = count + 1;
1557 FATAL_ERROR_IF(next < count, "Overflow");
1560 writeU16(os, count);
1561 os << serializeString32(os2.str());
1565 void NodeDefManager::deSerialize(std::istream &is)
1568 int version = readU8(is);
1570 throw SerializationError("unsupported NodeDefinitionManager version");
1571 u16 count = readU16(is);
1572 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1574 for (u16 n = 0; n < count; n++) {
1575 u16 i = readU16(is2);
1577 // Read it from the string wrapper
1578 std::string wrapper = deSerializeString16(is2);
1579 std::istringstream wrapper_is(wrapper, std::ios::binary);
1580 f.deSerialize(wrapper_is);
1582 // Check error conditions
1583 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1584 warningstream << "NodeDefManager::deSerialize(): "
1585 "not changing builtin node " << i << std::endl;
1588 if (f.name.empty()) {
1589 warningstream << "NodeDefManager::deSerialize(): "
1590 "received empty name" << std::endl;
1596 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1597 warningstream << "NodeDefManager::deSerialize(): "
1598 "already defined with different ID: " << f.name << std::endl;
1602 // All is ok, add node definition with the requested ID
1603 if (i >= m_content_features.size())
1604 m_content_features.resize((u32)(i) + 1);
1605 m_content_features[i] = f;
1606 addNameIdMapping(i, f.name);
1607 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1609 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1610 fixSelectionBoxIntUnion();
1613 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1614 // are not sent, resolve them client-side too.
1619 void NodeDefManager::addNameIdMapping(content_t i, const std::string &name)
1621 m_name_id_mapping.set(i, name);
1622 m_name_id_mapping_with_aliases.emplace(name, i);
1626 NodeDefManager *createNodeDefManager()
1628 return new NodeDefManager();
1632 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1635 if (m_node_registration_complete)
1636 nr->nodeResolveInternal();
1638 m_pending_resolve_callbacks.push_back(nr);
1642 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1644 size_t len = m_pending_resolve_callbacks.size();
1645 for (size_t i = 0; i != len; i++) {
1646 if (nr != m_pending_resolve_callbacks[i])
1650 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1651 m_pending_resolve_callbacks.resize(len);
1659 void NodeDefManager::runNodeResolveCallbacks()
1661 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1662 NodeResolver *nr = m_pending_resolve_callbacks[i];
1663 nr->nodeResolveInternal();
1666 m_pending_resolve_callbacks.clear();
1670 void NodeDefManager::resetNodeResolveState()
1672 m_node_registration_complete = false;
1673 m_pending_resolve_callbacks.clear();
1676 static void removeDupes(std::vector<content_t> &list)
1678 std::sort(list.begin(), list.end());
1679 auto new_end = std::unique(list.begin(), list.end());
1680 list.erase(new_end, list.end());
1683 void NodeDefManager::resolveCrossrefs()
1685 for (ContentFeatures &f : m_content_features) {
1686 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1687 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1688 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1691 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1694 for (const std::string &name : f.connects_to) {
1695 getIds(name, f.connects_to_ids);
1697 removeDupes(f.connects_to_ids);
1701 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1702 u8 connect_face) const
1704 const ContentFeatures &f1 = get(from);
1706 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1709 // lookup target in connected set
1710 if (!CONTAINS(f1.connects_to_ids, to.param0))
1713 const ContentFeatures &f2 = get(to);
1715 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1716 // ignores actually looking if back connection exists
1717 return CONTAINS(f2.connects_to_ids, from.param0);
1719 // does to node declare usable faces?
1720 if (f2.connect_sides > 0) {
1721 if ((f2.param_type_2 == CPT2_FACEDIR ||
1722 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1723 && (connect_face >= 4)) {
1724 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1725 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1727 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1729 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1730 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1732 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1733 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1734 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1736 return (f2.connect_sides
1737 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1739 return (f2.connect_sides & connect_face);
1741 // the target is just a regular node, so connect no matter back connection
1749 NodeResolver::NodeResolver()
1755 NodeResolver::~NodeResolver()
1757 if (!m_resolve_done && m_ndef)
1758 m_ndef->cancelNodeResolveCallback(this);
1762 void NodeResolver::cloneTo(NodeResolver *res) const
1764 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1765 " after resolving has completed");
1766 /* We don't actually do anything significant. Since the node resolving has
1767 * already completed, the class that called us will already have the
1768 * resolved IDs in its data structures (which it copies on its own) */
1769 res->m_ndef = m_ndef;
1770 res->m_resolve_done = true;
1774 void NodeResolver::nodeResolveInternal()
1776 m_nodenames_idx = 0;
1777 m_nnlistsizes_idx = 0;
1780 m_resolve_done = true;
1782 m_nodenames.clear();
1783 m_nnlistsizes.clear();
1787 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1788 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1790 if (m_nodenames_idx == m_nodenames.size()) {
1791 *result_out = c_fallback;
1792 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1797 std::string name = m_nodenames[m_nodenames_idx++];
1799 bool success = m_ndef->getId(name, c);
1800 if (!success && !node_alt.empty()) {
1802 success = m_ndef->getId(name, c);
1806 if (error_on_fallback)
1807 errorstream << "NodeResolver: failed to resolve node name '" << name
1808 << "'." << std::endl;
1817 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1818 bool all_required, content_t c_fallback)
1820 bool success = true;
1822 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1823 errorstream << "NodeResolver: no more node lists" << std::endl;
1827 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1830 if (m_nodenames_idx == m_nodenames.size()) {
1831 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1836 std::string &name = m_nodenames[m_nodenames_idx++];
1838 if (name.substr(0,6) != "group:") {
1839 if (m_ndef->getId(name, c)) {
1840 result_out->push_back(c);
1841 } else if (all_required) {
1842 errorstream << "NodeResolver: failed to resolve node name '"
1843 << name << "'." << std::endl;
1844 result_out->push_back(c_fallback);
1848 m_ndef->getIds(name, *result_out);
1855 void NodeResolver::reset(bool resolve_done)
1857 m_nodenames.clear();
1858 m_nodenames_idx = 0;
1859 m_nnlistsizes.clear();
1860 m_nnlistsizes_idx = 0;
1862 m_resolve_done = resolve_done;
1864 m_nodenames.reserve(16);
1865 m_nnlistsizes.reserve(4);