3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "client/mesh.h"
25 #include "client/shader.h"
26 #include "client/client.h"
27 #include "client/renderingengine.h"
28 #include "client/tile.h"
29 #include <IMeshManipulator.h>
33 #include "nameidmapping.h"
34 #include "util/numeric.h"
35 #include "util/serialize.h"
36 #include "util/string.h"
37 #include "exceptions.h"
41 #include <fstream> // Used in applyTextureOverrides()
51 type = NODEBOX_REGULAR;
54 // default is sign/ladder-like
55 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
56 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
57 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
58 // no default for other parts
60 connect_bottom.clear();
61 connect_front.clear();
64 connect_right.clear();
65 disconnected_top.clear();
66 disconnected_bottom.clear();
67 disconnected_front.clear();
68 disconnected_left.clear();
69 disconnected_back.clear();
70 disconnected_right.clear();
72 disconnected_sides.clear();
75 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
86 writeU16(os, fixed.size());
87 for (const aabb3f &nodebox : fixed) {
88 writeV3F32(os, nodebox.MinEdge);
89 writeV3F32(os, nodebox.MaxEdge);
92 case NODEBOX_WALLMOUNTED:
95 writeV3F32(os, wall_top.MinEdge);
96 writeV3F32(os, wall_top.MaxEdge);
97 writeV3F32(os, wall_bottom.MinEdge);
98 writeV3F32(os, wall_bottom.MaxEdge);
99 writeV3F32(os, wall_side.MinEdge);
100 writeV3F32(os, wall_side.MaxEdge);
102 case NODEBOX_CONNECTED:
105 #define WRITEBOX(box) \
106 writeU16(os, (box).size()); \
107 for (const aabb3f &i: (box)) { \
108 writeV3F32(os, i.MinEdge); \
109 writeV3F32(os, i.MaxEdge); \
113 WRITEBOX(connect_top);
114 WRITEBOX(connect_bottom);
115 WRITEBOX(connect_front);
116 WRITEBOX(connect_left);
117 WRITEBOX(connect_back);
118 WRITEBOX(connect_right);
119 WRITEBOX(disconnected_top);
120 WRITEBOX(disconnected_bottom);
121 WRITEBOX(disconnected_front);
122 WRITEBOX(disconnected_left);
123 WRITEBOX(disconnected_back);
124 WRITEBOX(disconnected_right);
125 WRITEBOX(disconnected);
126 WRITEBOX(disconnected_sides);
134 void NodeBox::deSerialize(std::istream &is)
136 int version = readU8(is);
138 throw SerializationError("unsupported NodeBox version");
142 type = (enum NodeBoxType)readU8(is);
144 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
146 u16 fixed_count = readU16(is);
150 box.MinEdge = readV3F32(is);
151 box.MaxEdge = readV3F32(is);
152 fixed.push_back(box);
155 else if(type == NODEBOX_WALLMOUNTED)
157 wall_top.MinEdge = readV3F32(is);
158 wall_top.MaxEdge = readV3F32(is);
159 wall_bottom.MinEdge = readV3F32(is);
160 wall_bottom.MaxEdge = readV3F32(is);
161 wall_side.MinEdge = readV3F32(is);
162 wall_side.MaxEdge = readV3F32(is);
164 else if (type == NODEBOX_CONNECTED)
166 #define READBOXES(box) { \
167 count = readU16(is); \
168 (box).reserve(count); \
170 v3f min = readV3F32(is); \
171 v3f max = readV3F32(is); \
172 (box).emplace_back(min, max); }; }
177 READBOXES(connect_top);
178 READBOXES(connect_bottom);
179 READBOXES(connect_front);
180 READBOXES(connect_left);
181 READBOXES(connect_back);
182 READBOXES(connect_right);
183 READBOXES(disconnected_top);
184 READBOXES(disconnected_bottom);
185 READBOXES(disconnected_front);
186 READBOXES(disconnected_left);
187 READBOXES(disconnected_back);
188 READBOXES(disconnected_right);
189 READBOXES(disconnected);
190 READBOXES(disconnected_sides);
198 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
199 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
200 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
201 #define TILE_FLAG_HAS_COLOR (1 << 3)
202 #define TILE_FLAG_HAS_SCALE (1 << 4)
203 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
205 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
207 // protocol_version >= 36
209 writeU8(os, version);
211 if (protocol_version > 39) {
212 os << serializeString16(name);
214 // Before f018737, TextureSource::getTextureAverageColor did not handle
215 // missing textures. "[png" can be used as base texture, but is not known
216 // on older clients. Hence use "blank.png" to avoid this problem.
217 // To be forward-compatible with future base textures/modifiers,
218 // we apply the same prefix to any texture beginning with [,
219 // except for the ones that are supported on older clients.
220 bool pass_through = true;
222 if (!name.empty() && name[0] == '[') {
223 pass_through = str_starts_with(name, "[combine:") ||
224 str_starts_with(name, "[inventorycube{") ||
225 str_starts_with(name, "[lowpart:");
229 os << serializeString16(name);
231 os << serializeString16("blank.png^" + name);
233 animation.serialize(os, version);
234 bool has_scale = scale > 0;
236 if (backface_culling)
237 flags |= TILE_FLAG_BACKFACE_CULLING;
238 if (tileable_horizontal)
239 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
240 if (tileable_vertical)
241 flags |= TILE_FLAG_TILEABLE_VERTICAL;
243 flags |= TILE_FLAG_HAS_COLOR;
245 flags |= TILE_FLAG_HAS_SCALE;
246 if (align_style != ALIGN_STYLE_NODE)
247 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
250 writeU8(os, color.getRed());
251 writeU8(os, color.getGreen());
252 writeU8(os, color.getBlue());
256 if (align_style != ALIGN_STYLE_NODE)
257 writeU8(os, align_style);
260 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
261 NodeDrawType drawtype)
263 int version = readU8(is);
265 throw SerializationError("unsupported TileDef version");
266 name = deSerializeString16(is);
267 animation.deSerialize(is, version);
268 u16 flags = readU16(is);
269 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
270 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
271 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
272 has_color = flags & TILE_FLAG_HAS_COLOR;
273 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
274 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
276 color.setRed(readU8(is));
277 color.setGreen(readU8(is));
278 color.setBlue(readU8(is));
280 scale = has_scale ? readU8(is) : 0;
282 align_style = static_cast<AlignStyle>(readU8(is));
284 align_style = ALIGN_STYLE_NODE;
287 void TextureSettings::readSettings()
289 connected_glass = g_settings->getBool("connected_glass");
290 opaque_water = g_settings->getBool("opaque_water");
291 bool smooth_lighting = g_settings->getBool("smooth_lighting");
292 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
293 enable_minimap = g_settings->getBool("enable_minimap");
294 node_texture_size = g_settings->getU16("texture_min_size");
295 std::string leaves_style_str = g_settings->get("leaves_style");
296 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
297 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
299 // Mesh cache is not supported in combination with smooth lighting
301 enable_mesh_cache = false;
303 if (leaves_style_str == "fancy") {
304 leaves_style = LEAVES_FANCY;
305 } else if (leaves_style_str == "simple") {
306 leaves_style = LEAVES_SIMPLE;
308 leaves_style = LEAVES_OPAQUE;
311 if (world_aligned_mode_str == "enable")
312 world_aligned_mode = WORLDALIGN_ENABLE;
313 else if (world_aligned_mode_str == "force_solid")
314 world_aligned_mode = WORLDALIGN_FORCE;
315 else if (world_aligned_mode_str == "force_nodebox")
316 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
318 world_aligned_mode = WORLDALIGN_DISABLE;
320 if (autoscale_mode_str == "enable")
321 autoscale_mode = AUTOSCALE_ENABLE;
322 else if (autoscale_mode_str == "force")
323 autoscale_mode = AUTOSCALE_FORCE;
325 autoscale_mode = AUTOSCALE_DISABLE;
332 ContentFeatures::ContentFeatures()
337 ContentFeatures::~ContentFeatures()
340 for (u16 j = 0; j < 6; j++) {
341 delete tiles[j].layers[0].frames;
342 delete tiles[j].layers[1].frames;
344 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
345 delete special_tiles[j].layers[0].frames;
349 void ContentFeatures::reset()
356 visual_solidness = 0;
357 backface_culling = true;
360 has_on_construct = false;
361 has_on_destruct = false;
362 has_after_destruct = false;
366 NOTE: Most of this is always overridden by the default values given
371 // Unknown nodes can be dug
372 groups["dig_immediate"] = 2;
373 drawtype = NDT_NORMAL;
376 for (auto &i : mesh_ptr)
378 minimap_color = video::SColor(0, 0, 0, 0);
381 for (auto &i : tiledef)
383 for (auto &j : tiledef_special)
385 alpha = ALPHAMODE_OPAQUE;
386 post_effect_color = video::SColor(0, 0, 0, 0);
387 param_type = CPT_NONE;
388 param_type_2 = CPT2_NONE;
389 is_ground_content = false;
390 light_propagates = false;
391 sunlight_propagates = false;
396 buildable_to = false;
398 rightclickable = true;
400 leveled_max = LEVELED_MAX;
401 liquid_type = LIQUID_NONE;
402 liquid_alternative_flowing = "";
403 liquid_alternative_flowing_id = CONTENT_IGNORE;
404 liquid_alternative_source = "";
405 liquid_alternative_source_id = CONTENT_IGNORE;
406 liquid_viscosity = 0;
407 liquid_renewable = true;
408 liquid_range = LIQUID_LEVEL_MAX+1;
411 damage_per_second = 0;
412 node_box = NodeBox();
413 selection_box = NodeBox();
414 collision_box = NodeBox();
416 legacy_facedir_simple = false;
417 legacy_wallmounted = false;
418 sound_footstep = SimpleSoundSpec();
419 sound_dig = SimpleSoundSpec("__group");
420 sound_dug = SimpleSoundSpec();
422 connects_to_ids.clear();
424 color = video::SColor(0xFFFFFFFF);
427 node_dig_prediction = "air";
429 liquid_move_physics = false;
432 void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
434 // No special handling for nodebox/mesh here as it doesn't make sense to
435 // throw warnings when the server is too old to support the "correct" way
438 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
441 case NDT_FLOWINGLIQUID:
442 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
445 alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
450 u8 ContentFeatures::getAlphaForLegacy() const
452 // This is so simple only because 255 and 0 mean wildly different things
453 // depending on drawtype...
454 return alpha == ALPHAMODE_OPAQUE ? 255 : 0;
457 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
459 const u8 version = CONTENTFEATURES_VERSION;
460 writeU8(os, version);
463 os << serializeString16(name);
464 writeU16(os, groups.size());
465 for (const auto &group : groups) {
466 os << serializeString16(group.first);
467 if (protocol_version < 41 && group.first.compare("bouncy") == 0) {
468 // Old clients may choke on negative bouncy value
469 writeS16(os, abs(group.second));
471 writeS16(os, group.second);
474 writeU8(os, param_type);
475 writeU8(os, param_type_2);
478 writeU8(os, drawtype);
479 os << serializeString16(mesh);
480 writeF32(os, visual_scale);
482 for (const TileDef &td : tiledef)
483 td.serialize(os, protocol_version);
484 for (const TileDef &td : tiledef_overlay)
485 td.serialize(os, protocol_version);
486 writeU8(os, CF_SPECIAL_COUNT);
487 for (const TileDef &td : tiledef_special) {
488 td.serialize(os, protocol_version);
490 writeU8(os, getAlphaForLegacy());
491 writeU8(os, color.getRed());
492 writeU8(os, color.getGreen());
493 writeU8(os, color.getBlue());
494 os << serializeString16(palette_name);
496 writeU8(os, connect_sides);
497 writeU16(os, connects_to_ids.size());
498 for (u16 connects_to_id : connects_to_ids)
499 writeU16(os, connects_to_id);
500 writeARGB8(os, post_effect_color);
501 writeU8(os, leveled);
504 writeU8(os, light_propagates);
505 writeU8(os, sunlight_propagates);
506 writeU8(os, light_source);
509 writeU8(os, is_ground_content);
512 writeU8(os, walkable);
513 writeU8(os, pointable);
514 writeU8(os, diggable);
515 writeU8(os, climbable);
516 writeU8(os, buildable_to);
517 writeU8(os, rightclickable);
518 writeU32(os, damage_per_second);
521 LiquidType liquid_type_bc = liquid_type;
522 if (protocol_version <= 39) {
523 // Since commit 7f25823, liquid drawtypes can be used even with LIQUID_NONE
524 // solution: force liquid type accordingly to accepted values
525 if (drawtype == NDT_LIQUID)
526 liquid_type_bc = LIQUID_SOURCE;
527 else if (drawtype == NDT_FLOWINGLIQUID)
528 liquid_type_bc = LIQUID_FLOWING;
530 writeU8(os, liquid_type_bc);
531 os << serializeString16(liquid_alternative_flowing);
532 os << serializeString16(liquid_alternative_source);
533 writeU8(os, liquid_viscosity);
534 writeU8(os, liquid_renewable);
535 writeU8(os, liquid_range);
536 writeU8(os, drowning);
537 writeU8(os, floodable);
540 node_box.serialize(os, protocol_version);
541 selection_box.serialize(os, protocol_version);
542 collision_box.serialize(os, protocol_version);
545 sound_footstep.serialize(os, version);
546 sound_dig.serialize(os, version);
547 sound_dug.serialize(os, version);
550 writeU8(os, legacy_facedir_simple);
551 writeU8(os, legacy_wallmounted);
554 os << serializeString16(node_dig_prediction);
555 writeU8(os, leveled_max);
557 writeU8(os, move_resistance);
558 writeU8(os, liquid_move_physics);
561 void ContentFeatures::deSerialize(std::istream &is)
564 const u8 version = readU8(is);
565 if (version < CONTENTFEATURES_VERSION)
566 throw SerializationError("unsupported ContentFeatures version");
569 name = deSerializeString16(is);
571 u32 groups_size = readU16(is);
572 for (u32 i = 0; i < groups_size; i++) {
573 std::string name = deSerializeString16(is);
574 int value = readS16(is);
575 groups[name] = value;
577 param_type = (enum ContentParamType) readU8(is);
578 param_type_2 = (enum ContentParamType2) readU8(is);
581 drawtype = (enum NodeDrawType) readU8(is);
582 mesh = deSerializeString16(is);
583 visual_scale = readF32(is);
585 throw SerializationError("unsupported tile count");
586 for (TileDef &td : tiledef)
587 td.deSerialize(is, version, drawtype);
588 for (TileDef &td : tiledef_overlay)
589 td.deSerialize(is, version, drawtype);
590 if (readU8(is) != CF_SPECIAL_COUNT)
591 throw SerializationError("unsupported CF_SPECIAL_COUNT");
592 for (TileDef &td : tiledef_special)
593 td.deSerialize(is, version, drawtype);
594 setAlphaFromLegacy(readU8(is));
595 color.setRed(readU8(is));
596 color.setGreen(readU8(is));
597 color.setBlue(readU8(is));
598 palette_name = deSerializeString16(is);
600 connect_sides = readU8(is);
601 u16 connects_to_size = readU16(is);
602 connects_to_ids.clear();
603 for (u16 i = 0; i < connects_to_size; i++)
604 connects_to_ids.push_back(readU16(is));
605 post_effect_color = readARGB8(is);
606 leveled = readU8(is);
609 light_propagates = readU8(is);
610 sunlight_propagates = readU8(is);
611 light_source = readU8(is);
612 light_source = MYMIN(light_source, LIGHT_MAX);
615 is_ground_content = readU8(is);
618 walkable = readU8(is);
619 pointable = readU8(is);
620 diggable = readU8(is);
621 climbable = readU8(is);
622 buildable_to = readU8(is);
623 rightclickable = readU8(is);
624 damage_per_second = readU32(is);
627 liquid_type = (enum LiquidType) readU8(is);
628 liquid_move_physics = liquid_type != LIQUID_NONE;
629 liquid_alternative_flowing = deSerializeString16(is);
630 liquid_alternative_source = deSerializeString16(is);
631 liquid_viscosity = readU8(is);
632 move_resistance = liquid_viscosity; // set default move_resistance
633 liquid_renewable = readU8(is);
634 liquid_range = readU8(is);
635 drowning = readU8(is);
636 floodable = readU8(is);
639 node_box.deSerialize(is);
640 selection_box.deSerialize(is);
641 collision_box.deSerialize(is);
644 sound_footstep.deSerialize(is, version);
645 sound_dig.deSerialize(is, version);
646 sound_dug.deSerialize(is, version);
648 // read legacy properties
649 legacy_facedir_simple = readU8(is);
650 legacy_wallmounted = readU8(is);
653 node_dig_prediction = deSerializeString16(is);
656 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
657 throw SerializationError("");
662 throw SerializationError("");
663 alpha = static_cast<enum AlphaMode>(tmp);
667 throw SerializationError("");
668 move_resistance = tmp;
672 throw SerializationError("");
673 liquid_move_physics = tmp;
674 } catch(SerializationError &e) {};
678 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
679 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
680 u8 material_type, u32 shader_id, bool backface_culling,
681 const TextureSettings &tsettings)
683 layer->shader_id = shader_id;
684 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
685 layer->material_type = material_type;
687 bool has_scale = tiledef.scale > 0;
688 bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
689 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
690 if (use_autoscale && layer->texture) {
691 auto texture_size = layer->texture->getOriginalSize();
692 float base_size = tsettings.node_texture_size;
693 float size = std::fmin(texture_size.Width, texture_size.Height);
694 layer->scale = std::fmax(base_size, size) / base_size;
695 } else if (has_scale) {
696 layer->scale = tiledef.scale;
700 if (!tile.world_aligned)
703 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
706 layer->material_flags = 0;
707 if (backface_culling)
708 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
709 if (tiledef.animation.type != TAT_NONE)
710 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
711 if (tiledef.tileable_horizontal)
712 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
713 if (tiledef.tileable_vertical)
714 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
717 layer->has_color = tiledef.has_color;
718 if (tiledef.has_color)
719 layer->color = tiledef.color;
721 layer->color = color;
723 // Animation parameters
725 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
726 assert(layer->texture);
728 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
729 &frame_count, &frame_length_ms, NULL);
730 layer->animation_frame_count = frame_count;
731 layer->animation_frame_length_ms = frame_length_ms;
734 if (frame_count == 1) {
735 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
737 assert(layer->texture);
739 layer->frames = new std::vector<FrameSpec>();
740 layer->frames->resize(frame_count);
742 std::ostringstream os(std::ios::binary);
743 for (int i = 0; i < frame_count; i++) {
748 tiledef.animation.getTextureModifer(os,
749 layer->texture->getOriginalSize(), i);
751 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
752 if (layer->normal_texture)
753 frame.normal_texture = tsrc->getNormalTexture(os.str());
754 frame.flags_texture = layer->flags_texture;
755 (*layer->frames)[i] = frame;
760 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
762 video::IVideoDriver *driver = RenderingEngine::get_video_driver();
763 static thread_local bool long_warning_printed = false;
764 std::set<std::string> seen;
766 for (int i = 0; i < length; i++) {
767 if (seen.find(tiles[i].name) != seen.end())
769 seen.insert(tiles[i].name);
771 // Load the texture and see if there's any transparent pixels
772 video::ITexture *texture = tsrc->getTexture(tiles[i].name);
773 video::IImage *image = driver->createImage(texture,
774 core::position2d<s32>(0, 0), texture->getOriginalSize());
777 core::dimension2d<u32> dim = image->getDimension();
779 for (u16 x = 0; x < dim.Width; x++) {
780 for (u16 y = 0; y < dim.Height; y++) {
781 if (image->getPixel(x, y).getAlpha() < 255) {
792 warningstream << "Texture \"" << tiles[i].name << "\" of "
793 << name << " has transparency, assuming "
794 "use_texture_alpha = \"clip\"." << std::endl;
795 if (!long_warning_printed) {
796 warningstream << " This warning can be a false-positive if "
797 "unused pixels in the texture are transparent. However if "
798 "it is meant to be transparent, you *MUST* update the "
799 "nodedef and set use_texture_alpha = \"clip\"! This "
800 "compatibility code will be removed in a few releases."
802 long_warning_printed = true;
809 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
811 if (style == ALIGN_STYLE_WORLD)
813 if (mode == WORLDALIGN_DISABLE)
815 if (style == ALIGN_STYLE_USER_DEFINED)
817 if (drawtype == NDT_NORMAL)
818 return mode >= WORLDALIGN_FORCE;
819 if (drawtype == NDT_NODEBOX)
820 return mode >= WORLDALIGN_FORCE_NODEBOX;
824 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
825 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
827 // minimap pixel color - the average color of a texture
828 if (tsettings.enable_minimap && !tiledef[0].name.empty())
829 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
831 // Figure out the actual tiles to use
833 for (u32 j = 0; j < 6; j++) {
834 tdef[j] = tiledef[j];
835 if (tdef[j].name.empty()) {
836 tdef[j].name = "no_texture.png";
837 tdef[j].backface_culling = false;
840 // also the overlay tiles
841 TileDef tdef_overlay[6];
842 for (u32 j = 0; j < 6; j++)
843 tdef_overlay[j] = tiledef_overlay[j];
844 // also the special tiles
845 TileDef tdef_spec[6];
846 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++) {
847 tdef_spec[j] = tiledef_special[j];
850 bool is_liquid = false;
852 if (alpha == ALPHAMODE_LEGACY_COMPAT) {
853 // Before working with the alpha mode, resolve any legacy kludges
854 alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
857 MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
858 TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
859 TILE_MATERIAL_ALPHA);
870 if (tsettings.opaque_water)
871 alpha = ALPHAMODE_OPAQUE;
875 case NDT_FLOWINGLIQUID:
877 if (tsettings.opaque_water)
878 alpha = ALPHAMODE_OPAQUE;
883 visual_solidness = 1;
885 case NDT_GLASSLIKE_FRAMED:
887 visual_solidness = 1;
889 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
891 visual_solidness = 1;
892 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
896 visual_solidness = 1;
898 case NDT_ALLFACES_OPTIONAL:
899 if (tsettings.leaves_style == LEAVES_FANCY) {
900 drawtype = NDT_ALLFACES;
902 visual_solidness = 1;
903 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
904 for (u32 j = 0; j < 6; j++) {
905 if (!tdef_spec[j].name.empty())
906 tdef[j].name = tdef_spec[j].name;
908 drawtype = NDT_GLASSLIKE;
910 visual_solidness = 1;
912 drawtype = NDT_NORMAL;
914 for (TileDef &td : tdef)
915 td.name += std::string("^[noalpha");
918 material_type = TILE_MATERIAL_WAVING_LEAVES;
923 material_type = TILE_MATERIAL_WAVING_PLANTS;
932 material_type = TILE_MATERIAL_WAVING_PLANTS;
933 } else if (waving == 2) {
934 material_type = TILE_MATERIAL_WAVING_LEAVES;
935 } else if (waving == 3) {
936 material_type = alpha == ALPHAMODE_OPAQUE ?
937 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
938 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
947 case NDT_PLANTLIKE_ROOTED:
954 material_type = alpha == ALPHAMODE_OPAQUE ?
955 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
956 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
958 material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
959 TILE_MATERIAL_LIQUID_TRANSPARENT;
963 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
965 MaterialType overlay_material = material_type;
966 if (overlay_material == TILE_MATERIAL_OPAQUE)
967 overlay_material = TILE_MATERIAL_BASIC;
968 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
969 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
971 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
973 // Tiles (fill in f->tiles[])
974 for (u16 j = 0; j < 6; j++) {
975 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
976 tsettings.world_aligned_mode, drawtype);
977 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
978 color, material_type, tile_shader,
979 tdef[j].backface_culling, tsettings);
980 if (!tdef_overlay[j].name.empty())
981 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
982 color, overlay_material, overlay_shader,
983 tdef[j].backface_culling, tsettings);
986 MaterialType special_material = material_type;
987 if (drawtype == NDT_PLANTLIKE_ROOTED) {
989 special_material = TILE_MATERIAL_WAVING_PLANTS;
990 else if (waving == 2)
991 special_material = TILE_MATERIAL_WAVING_LEAVES;
993 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
995 // Special tiles (fill in f->special_tiles[])
996 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
997 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
998 color, special_material, special_shader,
999 tdef_spec[j].backface_culling, tsettings);
1001 if (param_type_2 == CPT2_COLOR ||
1002 param_type_2 == CPT2_COLORED_FACEDIR ||
1003 param_type_2 == CPT2_COLORED_WALLMOUNTED ||
1004 param_type_2 == CPT2_COLORED_DEGROTATE)
1005 palette = tsrc->getPalette(palette_name);
1007 if (drawtype == NDT_MESH && !mesh.empty()) {
1008 // Meshnode drawtype
1009 // Read the mesh and apply scale
1010 mesh_ptr[0] = client->getMesh(mesh);
1012 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
1013 scaleMesh(mesh_ptr[0], scale);
1014 recalculateBoundingBox(mesh_ptr[0]);
1015 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1019 //Cache 6dfacedir and wallmounted rotated clones of meshes
1020 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
1021 (param_type_2 == CPT2_FACEDIR
1022 || param_type_2 == CPT2_COLORED_FACEDIR)) {
1023 for (u16 j = 1; j < 24; j++) {
1024 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
1025 rotateMeshBy6dFacedir(mesh_ptr[j], j);
1026 recalculateBoundingBox(mesh_ptr[j]);
1027 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1029 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
1030 && (param_type_2 == CPT2_WALLMOUNTED ||
1031 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
1032 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
1033 for (u16 j = 1; j < 6; j++) {
1034 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
1035 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
1036 recalculateBoundingBox(mesh_ptr[j]);
1037 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1039 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
1040 recalculateBoundingBox(mesh_ptr[0]);
1041 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1053 NodeDefManager::NodeDefManager()
1059 NodeDefManager::~NodeDefManager()
1062 for (ContentFeatures &f : m_content_features) {
1063 for (auto &j : f.mesh_ptr) {
1072 void NodeDefManager::clear()
1074 m_content_features.clear();
1075 m_name_id_mapping.clear();
1076 m_name_id_mapping_with_aliases.clear();
1077 m_group_to_items.clear();
1079 m_selection_box_union.reset(0,0,0);
1080 m_selection_box_int_union.reset(0,0,0);
1082 resetNodeResolveState();
1084 u32 initial_length = 0;
1085 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1086 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1087 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1088 m_content_features.resize(initial_length);
1090 // Set CONTENT_UNKNOWN
1094 TileDef unknownTile;
1095 unknownTile.name = "unknown_node.png";
1096 for (int t = 0; t < 6; t++)
1097 f.tiledef[t] = unknownTile;
1098 // Insert directly into containers
1099 content_t c = CONTENT_UNKNOWN;
1100 m_content_features[c] = f;
1101 addNameIdMapping(c, f.name);
1108 f.drawtype = NDT_AIRLIKE;
1109 f.param_type = CPT_LIGHT;
1110 f.light_propagates = true;
1111 f.sunlight_propagates = true;
1113 f.pointable = false;
1115 f.buildable_to = true;
1117 f.is_ground_content = true;
1118 // Insert directly into containers
1119 content_t c = CONTENT_AIR;
1120 m_content_features[c] = f;
1121 addNameIdMapping(c, f.name);
1124 // Set CONTENT_IGNORE
1128 f.drawtype = NDT_AIRLIKE;
1129 f.param_type = CPT_NONE;
1130 f.light_propagates = false;
1131 f.sunlight_propagates = false;
1133 f.pointable = false;
1135 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1136 f.is_ground_content = true;
1137 // Insert directly into containers
1138 content_t c = CONTENT_IGNORE;
1139 m_content_features[c] = f;
1140 addNameIdMapping(c, f.name);
1145 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1147 std::unordered_map<std::string, content_t>::const_iterator
1148 i = m_name_id_mapping_with_aliases.find(name);
1149 if(i == m_name_id_mapping_with_aliases.end())
1156 content_t NodeDefManager::getId(const std::string &name) const
1158 content_t id = CONTENT_IGNORE;
1164 bool NodeDefManager::getIds(const std::string &name,
1165 std::vector<content_t> &result) const
1167 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1168 if (name.substr(0,6) != "group:") {
1169 content_t id = CONTENT_IGNORE;
1170 bool exists = getId(name, id);
1172 result.push_back(id);
1175 std::string group = name.substr(6);
1177 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1178 i = m_group_to_items.find(group);
1179 if (i == m_group_to_items.end())
1182 const std::vector<content_t> &items = i->second;
1183 result.insert(result.end(), items.begin(), items.end());
1184 //printf("getIds: %dus\n", t.stop());
1189 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1191 content_t id = CONTENT_UNKNOWN;
1197 // returns CONTENT_IGNORE if no free ID found
1198 content_t NodeDefManager::allocateId()
1200 for (content_t id = m_next_id;
1201 id >= m_next_id; // overflow?
1203 while (id >= m_content_features.size()) {
1204 m_content_features.emplace_back();
1206 const ContentFeatures &f = m_content_features[id];
1207 if (f.name.empty()) {
1212 // If we arrive here, an overflow occurred in id.
1213 // That means no ID was found
1214 return CONTENT_IGNORE;
1219 * Returns the smallest box that contains all boxes
1220 * in the vector. Box_union is expanded.
1221 * @param[in] boxes the vector containing the boxes
1222 * @param[in, out] box_union the union of the arguments
1224 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1226 for (const aabb3f &box : boxes) {
1227 box_union->addInternalBox(box);
1233 * Returns a box that contains the nodebox in every case.
1234 * The argument node_union is expanded.
1235 * @param[in] nodebox the nodebox to be measured
1236 * @param[in] features used to decide whether the nodebox
1238 * @param[in, out] box_union the union of the arguments
1240 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1243 switch(nodebox.type) {
1245 case NODEBOX_LEVELED: {
1247 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1248 boxVectorUnion(nodebox.fixed, &half_processed);
1249 // Set leveled boxes to maximal
1250 if (nodebox.type == NODEBOX_LEVELED) {
1251 half_processed.MaxEdge.Y = +BS / 2;
1253 if (features.param_type_2 == CPT2_FACEDIR ||
1254 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1255 // Get maximal coordinate
1257 fabsf(half_processed.MinEdge.X),
1258 fabsf(half_processed.MinEdge.Y),
1259 fabsf(half_processed.MinEdge.Z),
1260 fabsf(half_processed.MaxEdge.X),
1261 fabsf(half_processed.MaxEdge.Y),
1262 fabsf(half_processed.MaxEdge.Z) };
1264 for (float coord : coords) {
1269 // Add the union of all possible rotated boxes
1270 box_union->addInternalPoint(-max, -max, -max);
1271 box_union->addInternalPoint(+max, +max, +max);
1273 box_union->addInternalBox(half_processed);
1277 case NODEBOX_WALLMOUNTED: {
1279 box_union->addInternalBox(nodebox.wall_top);
1280 box_union->addInternalBox(nodebox.wall_bottom);
1281 // Find maximal coordinate in the X-Z plane
1283 fabsf(nodebox.wall_side.MinEdge.X),
1284 fabsf(nodebox.wall_side.MinEdge.Z),
1285 fabsf(nodebox.wall_side.MaxEdge.X),
1286 fabsf(nodebox.wall_side.MaxEdge.Z) };
1288 for (float coord : coords) {
1293 // Add the union of all possible rotated boxes
1294 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1295 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1298 case NODEBOX_CONNECTED: {
1299 // Add all possible connected boxes
1300 boxVectorUnion(nodebox.fixed, box_union);
1301 boxVectorUnion(nodebox.connect_top, box_union);
1302 boxVectorUnion(nodebox.connect_bottom, box_union);
1303 boxVectorUnion(nodebox.connect_front, box_union);
1304 boxVectorUnion(nodebox.connect_left, box_union);
1305 boxVectorUnion(nodebox.connect_back, box_union);
1306 boxVectorUnion(nodebox.connect_right, box_union);
1307 boxVectorUnion(nodebox.disconnected_top, box_union);
1308 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1309 boxVectorUnion(nodebox.disconnected_front, box_union);
1310 boxVectorUnion(nodebox.disconnected_left, box_union);
1311 boxVectorUnion(nodebox.disconnected_back, box_union);
1312 boxVectorUnion(nodebox.disconnected_right, box_union);
1313 boxVectorUnion(nodebox.disconnected, box_union);
1314 boxVectorUnion(nodebox.disconnected_sides, box_union);
1319 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1320 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1326 inline void NodeDefManager::fixSelectionBoxIntUnion()
1328 m_selection_box_int_union.MinEdge.X = floorf(
1329 m_selection_box_union.MinEdge.X / BS + 0.5f);
1330 m_selection_box_int_union.MinEdge.Y = floorf(
1331 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1332 m_selection_box_int_union.MinEdge.Z = floorf(
1333 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1334 m_selection_box_int_union.MaxEdge.X = ceilf(
1335 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1336 m_selection_box_int_union.MaxEdge.Y = ceilf(
1337 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1338 m_selection_box_int_union.MaxEdge.Z = ceilf(
1339 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1343 void NodeDefManager::eraseIdFromGroups(content_t id)
1345 // For all groups in m_group_to_items...
1346 for (auto iter_groups = m_group_to_items.begin();
1347 iter_groups != m_group_to_items.end();) {
1348 // Get the group items vector.
1349 std::vector<content_t> &items = iter_groups->second;
1351 // Remove any occurence of the id in the group items vector.
1352 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1354 // If group is empty, erase its vector from the map.
1356 iter_groups = m_group_to_items.erase(iter_groups);
1363 // IWritableNodeDefManager
1364 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &d)
1366 ContentFeatures def = d;
1370 assert(name != "ignore");
1371 assert(name == def.name);
1373 content_t id = CONTENT_IGNORE;
1375 if (m_name_id_mapping.getId(name, id)) {
1377 ContentFeatures old_def = get(name);
1378 for (u32 j = 0; j < 6; j++)
1379 if (def.tiledef[j].name.empty())
1380 def.tiledef[j] = old_def.tiledef[j];
1381 for (u32 j = 0; j < 6; j++)
1382 if (def.tiledef_overlay[j].name.empty())
1383 def.tiledef_overlay[j] = old_def.tiledef_overlay[j];
1384 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
1385 if (def.tiledef_special[j].name.empty())
1386 def.tiledef_special[j] = old_def.tiledef_special[j];
1391 if (id == CONTENT_IGNORE) {
1392 warningstream << "NodeDefManager: Absolute "
1393 "limit reached" << std::endl;
1394 return CONTENT_IGNORE;
1396 assert(id != CONTENT_IGNORE);
1397 addNameIdMapping(id, name);
1400 // If there is already ContentFeatures registered for this id, clear old groups
1401 if (id < m_content_features.size())
1402 eraseIdFromGroups(id);
1404 m_content_features[id] = def;
1405 verbosestream << "NodeDefManager: registering content id \"" << id
1406 << "\": name=\"" << def.name << "\""<<std::endl;
1408 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1409 fixSelectionBoxIntUnion();
1411 // Add this content to the list of all groups it belongs to
1412 for (const auto &group : def.groups) {
1413 const std::string &group_name = group.first;
1414 m_group_to_items[group_name].push_back(id);
1421 content_t NodeDefManager::allocateDummy(const std::string &name)
1423 assert(name != ""); // Pre-condition
1426 return set(name, f);
1430 void NodeDefManager::removeNode(const std::string &name)
1435 // Erase name from name ID mapping
1436 content_t id = CONTENT_IGNORE;
1437 if (m_name_id_mapping.getId(name, id)) {
1438 m_name_id_mapping.eraseName(name);
1439 m_name_id_mapping_with_aliases.erase(name);
1442 eraseIdFromGroups(id);
1446 void NodeDefManager::updateAliases(IItemDefManager *idef)
1448 std::set<std::string> all;
1450 m_name_id_mapping_with_aliases.clear();
1451 for (const std::string &name : all) {
1452 const std::string &convert_to = idef->getAlias(name);
1454 if (m_name_id_mapping.getId(convert_to, id)) {
1455 m_name_id_mapping_with_aliases.insert(
1456 std::make_pair(name, id));
1461 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1463 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1464 "overrides to textures" << std::endl;
1466 for (const TextureOverride& texture_override : overrides) {
1468 if (!getId(texture_override.id, id))
1469 continue; // Ignore unknown node
1471 ContentFeatures &nodedef = m_content_features[id];
1474 if (texture_override.hasTarget(OverrideTarget::TOP))
1475 nodedef.tiledef[0].name = texture_override.texture;
1477 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1478 nodedef.tiledef[1].name = texture_override.texture;
1480 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1481 nodedef.tiledef[2].name = texture_override.texture;
1483 if (texture_override.hasTarget(OverrideTarget::LEFT))
1484 nodedef.tiledef[3].name = texture_override.texture;
1486 if (texture_override.hasTarget(OverrideTarget::BACK))
1487 nodedef.tiledef[4].name = texture_override.texture;
1489 if (texture_override.hasTarget(OverrideTarget::FRONT))
1490 nodedef.tiledef[5].name = texture_override.texture;
1493 // Override special tiles, if applicable
1494 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1495 nodedef.tiledef_special[0].name = texture_override.texture;
1497 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1498 nodedef.tiledef_special[1].name = texture_override.texture;
1500 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1501 nodedef.tiledef_special[2].name = texture_override.texture;
1503 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1504 nodedef.tiledef_special[3].name = texture_override.texture;
1506 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1507 nodedef.tiledef_special[4].name = texture_override.texture;
1509 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1510 nodedef.tiledef_special[5].name = texture_override.texture;
1514 void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
1517 infostream << "NodeDefManager::updateTextures(): Updating "
1518 "textures in node definitions" << std::endl;
1520 Client *client = (Client *)gamedef;
1521 ITextureSource *tsrc = client->tsrc();
1522 IShaderSource *shdsrc = client->getShaderSource();
1523 auto smgr = client->getSceneManager();
1524 scene::IMeshManipulator *meshmanip = smgr->getMeshManipulator();
1525 TextureSettings tsettings;
1526 tsettings.readSettings();
1528 u32 size = m_content_features.size();
1530 for (u32 i = 0; i < size; i++) {
1531 ContentFeatures *f = &(m_content_features[i]);
1532 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1533 client->showUpdateProgressTexture(progress_callback_args, i, size);
1538 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1540 writeU8(os, 1); // version
1542 std::ostringstream os2(std::ios::binary);
1543 for (u32 i = 0; i < m_content_features.size(); i++) {
1544 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1545 || i == CONTENT_UNKNOWN)
1547 const ContentFeatures *f = &m_content_features[i];
1548 if (f->name.empty())
1551 // Wrap it in a string to allow different lengths without
1552 // strict version incompatibilities
1553 std::ostringstream wrapper_os(std::ios::binary);
1554 f->serialize(wrapper_os, protocol_version);
1555 os2<<serializeString16(wrapper_os.str());
1557 // must not overflow
1558 u16 next = count + 1;
1559 FATAL_ERROR_IF(next < count, "Overflow");
1562 writeU16(os, count);
1563 os << serializeString32(os2.str());
1567 void NodeDefManager::deSerialize(std::istream &is)
1570 int version = readU8(is);
1572 throw SerializationError("unsupported NodeDefinitionManager version");
1573 u16 count = readU16(is);
1574 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1576 for (u16 n = 0; n < count; n++) {
1577 u16 i = readU16(is2);
1579 // Read it from the string wrapper
1580 std::string wrapper = deSerializeString16(is2);
1581 std::istringstream wrapper_is(wrapper, std::ios::binary);
1582 f.deSerialize(wrapper_is);
1584 // Check error conditions
1585 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1586 warningstream << "NodeDefManager::deSerialize(): "
1587 "not changing builtin node " << i << std::endl;
1590 if (f.name.empty()) {
1591 warningstream << "NodeDefManager::deSerialize(): "
1592 "received empty name" << std::endl;
1598 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1599 warningstream << "NodeDefManager::deSerialize(): "
1600 "already defined with different ID: " << f.name << std::endl;
1604 // All is ok, add node definition with the requested ID
1605 if (i >= m_content_features.size())
1606 m_content_features.resize((u32)(i) + 1);
1607 m_content_features[i] = f;
1608 addNameIdMapping(i, f.name);
1609 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1611 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1612 fixSelectionBoxIntUnion();
1615 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1616 // are not sent, resolve them client-side too.
1621 void NodeDefManager::addNameIdMapping(content_t i, const std::string &name)
1623 m_name_id_mapping.set(i, name);
1624 m_name_id_mapping_with_aliases.emplace(name, i);
1628 NodeDefManager *createNodeDefManager()
1630 return new NodeDefManager();
1634 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1637 if (m_node_registration_complete)
1638 nr->nodeResolveInternal();
1640 m_pending_resolve_callbacks.push_back(nr);
1644 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1646 size_t len = m_pending_resolve_callbacks.size();
1647 for (size_t i = 0; i != len; i++) {
1648 if (nr != m_pending_resolve_callbacks[i])
1652 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1653 m_pending_resolve_callbacks.resize(len);
1661 void NodeDefManager::runNodeResolveCallbacks()
1663 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1664 NodeResolver *nr = m_pending_resolve_callbacks[i];
1665 nr->nodeResolveInternal();
1668 m_pending_resolve_callbacks.clear();
1672 void NodeDefManager::resetNodeResolveState()
1674 m_node_registration_complete = false;
1675 m_pending_resolve_callbacks.clear();
1678 static void removeDupes(std::vector<content_t> &list)
1680 std::sort(list.begin(), list.end());
1681 auto new_end = std::unique(list.begin(), list.end());
1682 list.erase(new_end, list.end());
1685 void NodeDefManager::resolveCrossrefs()
1687 for (ContentFeatures &f : m_content_features) {
1688 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1689 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1690 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1693 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1696 for (const std::string &name : f.connects_to) {
1697 getIds(name, f.connects_to_ids);
1699 removeDupes(f.connects_to_ids);
1703 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1704 u8 connect_face) const
1706 const ContentFeatures &f1 = get(from);
1708 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1711 // lookup target in connected set
1712 if (!CONTAINS(f1.connects_to_ids, to.param0))
1715 const ContentFeatures &f2 = get(to);
1717 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1718 // ignores actually looking if back connection exists
1719 return CONTAINS(f2.connects_to_ids, from.param0);
1721 // does to node declare usable faces?
1722 if (f2.connect_sides > 0) {
1723 if ((f2.param_type_2 == CPT2_FACEDIR ||
1724 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1725 && (connect_face >= 4)) {
1726 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1727 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1729 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1731 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1732 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1734 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1735 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1736 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1738 return (f2.connect_sides
1739 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1741 return (f2.connect_sides & connect_face);
1743 // the target is just a regular node, so connect no matter back connection
1751 NodeResolver::NodeResolver()
1757 NodeResolver::~NodeResolver()
1759 if (!m_resolve_done && m_ndef)
1760 m_ndef->cancelNodeResolveCallback(this);
1764 void NodeResolver::cloneTo(NodeResolver *res) const
1766 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1767 " after resolving has completed");
1768 /* We don't actually do anything significant. Since the node resolving has
1769 * already completed, the class that called us will already have the
1770 * resolved IDs in its data structures (which it copies on its own) */
1771 res->m_ndef = m_ndef;
1772 res->m_resolve_done = true;
1776 void NodeResolver::nodeResolveInternal()
1778 m_nodenames_idx = 0;
1779 m_nnlistsizes_idx = 0;
1782 m_resolve_done = true;
1784 m_nodenames.clear();
1785 m_nnlistsizes.clear();
1789 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1790 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1792 if (m_nodenames_idx == m_nodenames.size()) {
1793 *result_out = c_fallback;
1794 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1799 std::string name = m_nodenames[m_nodenames_idx++];
1801 bool success = m_ndef->getId(name, c);
1802 if (!success && !node_alt.empty()) {
1804 success = m_ndef->getId(name, c);
1808 if (error_on_fallback)
1809 errorstream << "NodeResolver: failed to resolve node name '" << name
1810 << "'." << std::endl;
1819 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1820 bool all_required, content_t c_fallback)
1822 bool success = true;
1824 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1825 errorstream << "NodeResolver: no more node lists" << std::endl;
1829 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1832 if (m_nodenames_idx == m_nodenames.size()) {
1833 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1838 std::string &name = m_nodenames[m_nodenames_idx++];
1840 if (name.substr(0,6) != "group:") {
1841 if (m_ndef->getId(name, c)) {
1842 result_out->push_back(c);
1843 } else if (all_required) {
1844 errorstream << "NodeResolver: failed to resolve node name '"
1845 << name << "'." << std::endl;
1846 result_out->push_back(c_fallback);
1850 m_ndef->getIds(name, *result_out);
1857 void NodeResolver::reset(bool resolve_done)
1859 m_nodenames.clear();
1860 m_nodenames_idx = 0;
1861 m_nnlistsizes.clear();
1862 m_nnlistsizes_idx = 0;
1864 m_resolve_done = resolve_done;
1866 m_nodenames.reserve(16);
1867 m_nnlistsizes.reserve(4);