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 "exceptions.h"
40 #include <fstream> // Used in applyTextureOverrides()
50 type = NODEBOX_REGULAR;
53 // default is sign/ladder-like
54 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
55 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
56 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
57 // no default for other parts
59 connect_bottom.clear();
60 connect_front.clear();
63 connect_right.clear();
64 disconnected_top.clear();
65 disconnected_bottom.clear();
66 disconnected_front.clear();
67 disconnected_left.clear();
68 disconnected_back.clear();
69 disconnected_right.clear();
71 disconnected_sides.clear();
74 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
85 writeU16(os, fixed.size());
86 for (const aabb3f &nodebox : fixed) {
87 writeV3F32(os, nodebox.MinEdge);
88 writeV3F32(os, nodebox.MaxEdge);
91 case NODEBOX_WALLMOUNTED:
94 writeV3F32(os, wall_top.MinEdge);
95 writeV3F32(os, wall_top.MaxEdge);
96 writeV3F32(os, wall_bottom.MinEdge);
97 writeV3F32(os, wall_bottom.MaxEdge);
98 writeV3F32(os, wall_side.MinEdge);
99 writeV3F32(os, wall_side.MaxEdge);
101 case NODEBOX_CONNECTED:
104 #define WRITEBOX(box) \
105 writeU16(os, (box).size()); \
106 for (const aabb3f &i: (box)) { \
107 writeV3F32(os, i.MinEdge); \
108 writeV3F32(os, i.MaxEdge); \
112 WRITEBOX(connect_top);
113 WRITEBOX(connect_bottom);
114 WRITEBOX(connect_front);
115 WRITEBOX(connect_left);
116 WRITEBOX(connect_back);
117 WRITEBOX(connect_right);
118 WRITEBOX(disconnected_top);
119 WRITEBOX(disconnected_bottom);
120 WRITEBOX(disconnected_front);
121 WRITEBOX(disconnected_left);
122 WRITEBOX(disconnected_back);
123 WRITEBOX(disconnected_right);
124 WRITEBOX(disconnected);
125 WRITEBOX(disconnected_sides);
133 void NodeBox::deSerialize(std::istream &is)
135 int version = readU8(is);
137 throw SerializationError("unsupported NodeBox version");
141 type = (enum NodeBoxType)readU8(is);
143 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
145 u16 fixed_count = readU16(is);
149 box.MinEdge = readV3F32(is);
150 box.MaxEdge = readV3F32(is);
151 fixed.push_back(box);
154 else if(type == NODEBOX_WALLMOUNTED)
156 wall_top.MinEdge = readV3F32(is);
157 wall_top.MaxEdge = readV3F32(is);
158 wall_bottom.MinEdge = readV3F32(is);
159 wall_bottom.MaxEdge = readV3F32(is);
160 wall_side.MinEdge = readV3F32(is);
161 wall_side.MaxEdge = readV3F32(is);
163 else if (type == NODEBOX_CONNECTED)
165 #define READBOXES(box) { \
166 count = readU16(is); \
167 (box).reserve(count); \
169 v3f min = readV3F32(is); \
170 v3f max = readV3F32(is); \
171 (box).emplace_back(min, max); }; }
176 READBOXES(connect_top);
177 READBOXES(connect_bottom);
178 READBOXES(connect_front);
179 READBOXES(connect_left);
180 READBOXES(connect_back);
181 READBOXES(connect_right);
182 READBOXES(disconnected_top);
183 READBOXES(disconnected_bottom);
184 READBOXES(disconnected_front);
185 READBOXES(disconnected_left);
186 READBOXES(disconnected_back);
187 READBOXES(disconnected_right);
188 READBOXES(disconnected);
189 READBOXES(disconnected_sides);
197 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
198 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
199 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
200 #define TILE_FLAG_HAS_COLOR (1 << 3)
201 #define TILE_FLAG_HAS_SCALE (1 << 4)
202 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
204 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
206 // protocol_version >= 36
208 writeU8(os, version);
210 if (protocol_version > 39) {
211 os << serializeString16(name);
213 // Before f018737, TextureSource::getTextureAverageColor did not handle
214 // missing textures. "[png" can be used as base texture, but is not known
215 // on older clients. Hence use "blank.png" to avoid this problem.
216 if (!name.empty() && name[0] == '[')
217 os << serializeString16("blank.png^" + name);
219 os << serializeString16(name);
221 animation.serialize(os, version);
222 bool has_scale = scale > 0;
224 if (backface_culling)
225 flags |= TILE_FLAG_BACKFACE_CULLING;
226 if (tileable_horizontal)
227 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
228 if (tileable_vertical)
229 flags |= TILE_FLAG_TILEABLE_VERTICAL;
231 flags |= TILE_FLAG_HAS_COLOR;
233 flags |= TILE_FLAG_HAS_SCALE;
234 if (align_style != ALIGN_STYLE_NODE)
235 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
238 writeU8(os, color.getRed());
239 writeU8(os, color.getGreen());
240 writeU8(os, color.getBlue());
244 if (align_style != ALIGN_STYLE_NODE)
245 writeU8(os, align_style);
248 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
249 NodeDrawType drawtype)
251 int version = readU8(is);
253 throw SerializationError("unsupported TileDef version");
254 name = deSerializeString16(is);
255 animation.deSerialize(is, version);
256 u16 flags = readU16(is);
257 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
258 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
259 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
260 has_color = flags & TILE_FLAG_HAS_COLOR;
261 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
262 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
264 color.setRed(readU8(is));
265 color.setGreen(readU8(is));
266 color.setBlue(readU8(is));
268 scale = has_scale ? readU8(is) : 0;
270 align_style = static_cast<AlignStyle>(readU8(is));
272 align_style = ALIGN_STYLE_NODE;
275 void TextureSettings::readSettings()
277 connected_glass = g_settings->getBool("connected_glass");
278 opaque_water = g_settings->getBool("opaque_water");
279 bool smooth_lighting = g_settings->getBool("smooth_lighting");
280 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
281 enable_minimap = g_settings->getBool("enable_minimap");
282 node_texture_size = g_settings->getU16("texture_min_size");
283 std::string leaves_style_str = g_settings->get("leaves_style");
284 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
285 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
287 // Mesh cache is not supported in combination with smooth lighting
289 enable_mesh_cache = false;
291 if (leaves_style_str == "fancy") {
292 leaves_style = LEAVES_FANCY;
293 } else if (leaves_style_str == "simple") {
294 leaves_style = LEAVES_SIMPLE;
296 leaves_style = LEAVES_OPAQUE;
299 if (world_aligned_mode_str == "enable")
300 world_aligned_mode = WORLDALIGN_ENABLE;
301 else if (world_aligned_mode_str == "force_solid")
302 world_aligned_mode = WORLDALIGN_FORCE;
303 else if (world_aligned_mode_str == "force_nodebox")
304 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
306 world_aligned_mode = WORLDALIGN_DISABLE;
308 if (autoscale_mode_str == "enable")
309 autoscale_mode = AUTOSCALE_ENABLE;
310 else if (autoscale_mode_str == "force")
311 autoscale_mode = AUTOSCALE_FORCE;
313 autoscale_mode = AUTOSCALE_DISABLE;
320 ContentFeatures::ContentFeatures()
325 ContentFeatures::~ContentFeatures()
328 for (u16 j = 0; j < 6; j++) {
329 delete tiles[j].layers[0].frames;
330 delete tiles[j].layers[1].frames;
332 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
333 delete special_tiles[j].layers[0].frames;
337 void ContentFeatures::reset()
344 visual_solidness = 0;
345 backface_culling = true;
348 has_on_construct = false;
349 has_on_destruct = false;
350 has_after_destruct = false;
354 NOTE: Most of this is always overridden by the default values given
359 // Unknown nodes can be dug
360 groups["dig_immediate"] = 2;
361 drawtype = NDT_NORMAL;
364 for (auto &i : mesh_ptr)
366 minimap_color = video::SColor(0, 0, 0, 0);
369 for (auto &i : tiledef)
371 for (auto &j : tiledef_special)
373 alpha = ALPHAMODE_OPAQUE;
374 post_effect_color = video::SColor(0, 0, 0, 0);
375 param_type = CPT_NONE;
376 param_type_2 = CPT2_NONE;
377 is_ground_content = false;
378 light_propagates = false;
379 sunlight_propagates = false;
384 buildable_to = false;
386 rightclickable = true;
388 leveled_max = LEVELED_MAX;
389 liquid_type = LIQUID_NONE;
390 liquid_alternative_flowing = "";
391 liquid_alternative_flowing_id = CONTENT_IGNORE;
392 liquid_alternative_source = "";
393 liquid_alternative_source_id = CONTENT_IGNORE;
394 liquid_viscosity = 0;
395 liquid_renewable = true;
396 liquid_range = LIQUID_LEVEL_MAX+1;
399 damage_per_second = 0;
400 node_box = NodeBox();
401 selection_box = NodeBox();
402 collision_box = NodeBox();
404 legacy_facedir_simple = false;
405 legacy_wallmounted = false;
406 sound_footstep = SimpleSoundSpec();
407 sound_dig = SimpleSoundSpec("__group");
408 sound_dug = SimpleSoundSpec();
410 connects_to_ids.clear();
412 color = video::SColor(0xFFFFFFFF);
415 node_dig_prediction = "air";
417 liquid_move_physics = false;
420 void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
422 // No special handling for nodebox/mesh here as it doesn't make sense to
423 // throw warnings when the server is too old to support the "correct" way
426 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
429 case NDT_FLOWINGLIQUID:
430 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
433 alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
438 u8 ContentFeatures::getAlphaForLegacy() const
440 // This is so simple only because 255 and 0 mean wildly different things
441 // depending on drawtype...
442 return alpha == ALPHAMODE_OPAQUE ? 255 : 0;
445 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
447 const u8 version = CONTENTFEATURES_VERSION;
448 writeU8(os, version);
451 os << serializeString16(name);
452 writeU16(os, groups.size());
453 for (const auto &group : groups) {
454 os << serializeString16(group.first);
455 if (protocol_version < 41 && group.first.compare("bouncy") == 0) {
456 // Old clients may choke on negative bouncy value
457 writeS16(os, abs(group.second));
459 writeS16(os, group.second);
462 writeU8(os, param_type);
463 writeU8(os, param_type_2);
466 writeU8(os, drawtype);
467 os << serializeString16(mesh);
468 writeF32(os, visual_scale);
470 for (const TileDef &td : tiledef)
471 td.serialize(os, protocol_version);
472 for (const TileDef &td : tiledef_overlay)
473 td.serialize(os, protocol_version);
474 writeU8(os, CF_SPECIAL_COUNT);
475 for (const TileDef &td : tiledef_special) {
476 td.serialize(os, protocol_version);
478 writeU8(os, getAlphaForLegacy());
479 writeU8(os, color.getRed());
480 writeU8(os, color.getGreen());
481 writeU8(os, color.getBlue());
482 os << serializeString16(palette_name);
484 writeU8(os, connect_sides);
485 writeU16(os, connects_to_ids.size());
486 for (u16 connects_to_id : connects_to_ids)
487 writeU16(os, connects_to_id);
488 writeARGB8(os, post_effect_color);
489 writeU8(os, leveled);
492 writeU8(os, light_propagates);
493 writeU8(os, sunlight_propagates);
494 writeU8(os, light_source);
497 writeU8(os, is_ground_content);
500 writeU8(os, walkable);
501 writeU8(os, pointable);
502 writeU8(os, diggable);
503 writeU8(os, climbable);
504 writeU8(os, buildable_to);
505 writeU8(os, rightclickable);
506 writeU32(os, damage_per_second);
509 LiquidType liquid_type_bc = liquid_type;
510 if (protocol_version <= 39) {
511 // Since commit 7f25823, liquid drawtypes can be used even with LIQUID_NONE
512 // solution: force liquid type accordingly to accepted values
513 if (drawtype == NDT_LIQUID)
514 liquid_type_bc = LIQUID_SOURCE;
515 else if (drawtype == NDT_FLOWINGLIQUID)
516 liquid_type_bc = LIQUID_FLOWING;
518 writeU8(os, liquid_type_bc);
519 os << serializeString16(liquid_alternative_flowing);
520 os << serializeString16(liquid_alternative_source);
521 writeU8(os, liquid_viscosity);
522 writeU8(os, liquid_renewable);
523 writeU8(os, liquid_range);
524 writeU8(os, drowning);
525 writeU8(os, floodable);
528 node_box.serialize(os, protocol_version);
529 selection_box.serialize(os, protocol_version);
530 collision_box.serialize(os, protocol_version);
533 sound_footstep.serialize(os, version);
534 sound_dig.serialize(os, version);
535 sound_dug.serialize(os, version);
538 writeU8(os, legacy_facedir_simple);
539 writeU8(os, legacy_wallmounted);
542 os << serializeString16(node_dig_prediction);
543 writeU8(os, leveled_max);
545 writeU8(os, move_resistance);
546 writeU8(os, liquid_move_physics);
549 void ContentFeatures::deSerialize(std::istream &is)
552 const u8 version = readU8(is);
553 if (version < CONTENTFEATURES_VERSION)
554 throw SerializationError("unsupported ContentFeatures version");
557 name = deSerializeString16(is);
559 u32 groups_size = readU16(is);
560 for (u32 i = 0; i < groups_size; i++) {
561 std::string name = deSerializeString16(is);
562 int value = readS16(is);
563 groups[name] = value;
565 param_type = (enum ContentParamType) readU8(is);
566 param_type_2 = (enum ContentParamType2) readU8(is);
569 drawtype = (enum NodeDrawType) readU8(is);
570 mesh = deSerializeString16(is);
571 visual_scale = readF32(is);
573 throw SerializationError("unsupported tile count");
574 for (TileDef &td : tiledef)
575 td.deSerialize(is, version, drawtype);
576 for (TileDef &td : tiledef_overlay)
577 td.deSerialize(is, version, drawtype);
578 if (readU8(is) != CF_SPECIAL_COUNT)
579 throw SerializationError("unsupported CF_SPECIAL_COUNT");
580 for (TileDef &td : tiledef_special)
581 td.deSerialize(is, version, drawtype);
582 setAlphaFromLegacy(readU8(is));
583 color.setRed(readU8(is));
584 color.setGreen(readU8(is));
585 color.setBlue(readU8(is));
586 palette_name = deSerializeString16(is);
588 connect_sides = readU8(is);
589 u16 connects_to_size = readU16(is);
590 connects_to_ids.clear();
591 for (u16 i = 0; i < connects_to_size; i++)
592 connects_to_ids.push_back(readU16(is));
593 post_effect_color = readARGB8(is);
594 leveled = readU8(is);
597 light_propagates = readU8(is);
598 sunlight_propagates = readU8(is);
599 light_source = readU8(is);
600 light_source = MYMIN(light_source, LIGHT_MAX);
603 is_ground_content = readU8(is);
606 walkable = readU8(is);
607 pointable = readU8(is);
608 diggable = readU8(is);
609 climbable = readU8(is);
610 buildable_to = readU8(is);
611 rightclickable = readU8(is);
612 damage_per_second = readU32(is);
615 liquid_type = (enum LiquidType) readU8(is);
616 liquid_move_physics = liquid_type != LIQUID_NONE;
617 liquid_alternative_flowing = deSerializeString16(is);
618 liquid_alternative_source = deSerializeString16(is);
619 liquid_viscosity = readU8(is);
620 move_resistance = liquid_viscosity; // set default move_resistance
621 liquid_renewable = readU8(is);
622 liquid_range = readU8(is);
623 drowning = readU8(is);
624 floodable = readU8(is);
627 node_box.deSerialize(is);
628 selection_box.deSerialize(is);
629 collision_box.deSerialize(is);
632 sound_footstep.deSerialize(is, version);
633 sound_dig.deSerialize(is, version);
634 sound_dug.deSerialize(is, version);
636 // read legacy properties
637 legacy_facedir_simple = readU8(is);
638 legacy_wallmounted = readU8(is);
641 node_dig_prediction = deSerializeString16(is);
644 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
645 throw SerializationError("");
650 throw SerializationError("");
651 alpha = static_cast<enum AlphaMode>(tmp);
655 throw SerializationError("");
656 move_resistance = tmp;
660 throw SerializationError("");
661 liquid_move_physics = tmp;
662 } catch(SerializationError &e) {};
666 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
667 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
668 u8 material_type, u32 shader_id, bool backface_culling,
669 const TextureSettings &tsettings)
671 layer->shader_id = shader_id;
672 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
673 layer->material_type = material_type;
675 bool has_scale = tiledef.scale > 0;
676 bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
677 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
678 if (use_autoscale && layer->texture) {
679 auto texture_size = layer->texture->getOriginalSize();
680 float base_size = tsettings.node_texture_size;
681 float size = std::fmin(texture_size.Width, texture_size.Height);
682 layer->scale = std::fmax(base_size, size) / base_size;
683 } else if (has_scale) {
684 layer->scale = tiledef.scale;
688 if (!tile.world_aligned)
691 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
694 layer->material_flags = 0;
695 if (backface_culling)
696 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
697 if (tiledef.animation.type != TAT_NONE)
698 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
699 if (tiledef.tileable_horizontal)
700 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
701 if (tiledef.tileable_vertical)
702 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
705 layer->has_color = tiledef.has_color;
706 if (tiledef.has_color)
707 layer->color = tiledef.color;
709 layer->color = color;
711 // Animation parameters
713 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
714 assert(layer->texture);
716 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
717 &frame_count, &frame_length_ms, NULL);
718 layer->animation_frame_count = frame_count;
719 layer->animation_frame_length_ms = frame_length_ms;
722 if (frame_count == 1) {
723 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
725 assert(layer->texture);
727 layer->frames = new std::vector<FrameSpec>();
728 layer->frames->resize(frame_count);
730 std::ostringstream os(std::ios::binary);
731 for (int i = 0; i < frame_count; i++) {
736 tiledef.animation.getTextureModifer(os,
737 layer->texture->getOriginalSize(), i);
739 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
740 if (layer->normal_texture)
741 frame.normal_texture = tsrc->getNormalTexture(os.str());
742 frame.flags_texture = layer->flags_texture;
743 (*layer->frames)[i] = frame;
748 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
750 video::IVideoDriver *driver = RenderingEngine::get_video_driver();
751 static thread_local bool long_warning_printed = false;
752 std::set<std::string> seen;
754 for (int i = 0; i < length; i++) {
755 if (seen.find(tiles[i].name) != seen.end())
757 seen.insert(tiles[i].name);
759 // Load the texture and see if there's any transparent pixels
760 video::ITexture *texture = tsrc->getTexture(tiles[i].name);
761 video::IImage *image = driver->createImage(texture,
762 core::position2d<s32>(0, 0), texture->getOriginalSize());
765 core::dimension2d<u32> dim = image->getDimension();
767 for (u16 x = 0; x < dim.Width; x++) {
768 for (u16 y = 0; y < dim.Height; y++) {
769 if (image->getPixel(x, y).getAlpha() < 255) {
780 warningstream << "Texture \"" << tiles[i].name << "\" of "
781 << name << " has transparency, assuming "
782 "use_texture_alpha = \"clip\"." << std::endl;
783 if (!long_warning_printed) {
784 warningstream << " This warning can be a false-positive if "
785 "unused pixels in the texture are transparent. However if "
786 "it is meant to be transparent, you *MUST* update the "
787 "nodedef and set use_texture_alpha = \"clip\"! This "
788 "compatibility code will be removed in a few releases."
790 long_warning_printed = true;
797 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
799 if (style == ALIGN_STYLE_WORLD)
801 if (mode == WORLDALIGN_DISABLE)
803 if (style == ALIGN_STYLE_USER_DEFINED)
805 if (drawtype == NDT_NORMAL)
806 return mode >= WORLDALIGN_FORCE;
807 if (drawtype == NDT_NODEBOX)
808 return mode >= WORLDALIGN_FORCE_NODEBOX;
812 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
813 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
815 // minimap pixel color - the average color of a texture
816 if (tsettings.enable_minimap && !tiledef[0].name.empty())
817 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
819 // Figure out the actual tiles to use
821 for (u32 j = 0; j < 6; j++) {
822 tdef[j] = tiledef[j];
823 if (tdef[j].name.empty()) {
824 tdef[j].name = "no_texture.png";
825 tdef[j].backface_culling = false;
828 // also the overlay tiles
829 TileDef tdef_overlay[6];
830 for (u32 j = 0; j < 6; j++)
831 tdef_overlay[j] = tiledef_overlay[j];
832 // also the special tiles
833 TileDef tdef_spec[6];
834 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++) {
835 tdef_spec[j] = tiledef_special[j];
838 bool is_liquid = false;
840 if (alpha == ALPHAMODE_LEGACY_COMPAT) {
841 // Before working with the alpha mode, resolve any legacy kludges
842 alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
845 MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
846 TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
847 TILE_MATERIAL_ALPHA);
858 if (tsettings.opaque_water)
859 alpha = ALPHAMODE_OPAQUE;
863 case NDT_FLOWINGLIQUID:
865 if (tsettings.opaque_water)
866 alpha = ALPHAMODE_OPAQUE;
871 visual_solidness = 1;
873 case NDT_GLASSLIKE_FRAMED:
875 visual_solidness = 1;
877 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
879 visual_solidness = 1;
880 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
884 visual_solidness = 1;
886 case NDT_ALLFACES_OPTIONAL:
887 if (tsettings.leaves_style == LEAVES_FANCY) {
888 drawtype = NDT_ALLFACES;
890 visual_solidness = 1;
891 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
892 for (u32 j = 0; j < 6; j++) {
893 if (!tdef_spec[j].name.empty())
894 tdef[j].name = tdef_spec[j].name;
896 drawtype = NDT_GLASSLIKE;
898 visual_solidness = 1;
900 drawtype = NDT_NORMAL;
902 for (TileDef &td : tdef)
903 td.name += std::string("^[noalpha");
906 material_type = TILE_MATERIAL_WAVING_LEAVES;
911 material_type = TILE_MATERIAL_WAVING_PLANTS;
920 material_type = TILE_MATERIAL_WAVING_PLANTS;
921 } else if (waving == 2) {
922 material_type = TILE_MATERIAL_WAVING_LEAVES;
923 } else if (waving == 3) {
924 material_type = alpha == ALPHAMODE_OPAQUE ?
925 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
926 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
935 case NDT_PLANTLIKE_ROOTED:
942 material_type = alpha == ALPHAMODE_OPAQUE ?
943 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
944 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
946 material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
947 TILE_MATERIAL_LIQUID_TRANSPARENT;
951 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
953 MaterialType overlay_material = material_type;
954 if (overlay_material == TILE_MATERIAL_OPAQUE)
955 overlay_material = TILE_MATERIAL_BASIC;
956 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
957 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
959 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
961 // Tiles (fill in f->tiles[])
962 for (u16 j = 0; j < 6; j++) {
963 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
964 tsettings.world_aligned_mode, drawtype);
965 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
966 color, material_type, tile_shader,
967 tdef[j].backface_culling, tsettings);
968 if (!tdef_overlay[j].name.empty())
969 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
970 color, overlay_material, overlay_shader,
971 tdef[j].backface_culling, tsettings);
974 MaterialType special_material = material_type;
975 if (drawtype == NDT_PLANTLIKE_ROOTED) {
977 special_material = TILE_MATERIAL_WAVING_PLANTS;
978 else if (waving == 2)
979 special_material = TILE_MATERIAL_WAVING_LEAVES;
981 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
983 // Special tiles (fill in f->special_tiles[])
984 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
985 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
986 color, special_material, special_shader,
987 tdef_spec[j].backface_culling, tsettings);
989 if (param_type_2 == CPT2_COLOR ||
990 param_type_2 == CPT2_COLORED_FACEDIR ||
991 param_type_2 == CPT2_COLORED_WALLMOUNTED ||
992 param_type_2 == CPT2_COLORED_DEGROTATE)
993 palette = tsrc->getPalette(palette_name);
995 if (drawtype == NDT_MESH && !mesh.empty()) {
997 // Read the mesh and apply scale
998 mesh_ptr[0] = client->getMesh(mesh);
1000 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
1001 scaleMesh(mesh_ptr[0], scale);
1002 recalculateBoundingBox(mesh_ptr[0]);
1003 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1007 //Cache 6dfacedir and wallmounted rotated clones of meshes
1008 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
1009 (param_type_2 == CPT2_FACEDIR
1010 || param_type_2 == CPT2_COLORED_FACEDIR)) {
1011 for (u16 j = 1; j < 24; j++) {
1012 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
1013 rotateMeshBy6dFacedir(mesh_ptr[j], j);
1014 recalculateBoundingBox(mesh_ptr[j]);
1015 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1017 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
1018 && (param_type_2 == CPT2_WALLMOUNTED ||
1019 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
1020 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
1021 for (u16 j = 1; j < 6; j++) {
1022 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
1023 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
1024 recalculateBoundingBox(mesh_ptr[j]);
1025 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1027 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
1028 recalculateBoundingBox(mesh_ptr[0]);
1029 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1041 NodeDefManager::NodeDefManager()
1047 NodeDefManager::~NodeDefManager()
1050 for (ContentFeatures &f : m_content_features) {
1051 for (auto &j : f.mesh_ptr) {
1060 void NodeDefManager::clear()
1062 m_content_features.clear();
1063 m_name_id_mapping.clear();
1064 m_name_id_mapping_with_aliases.clear();
1065 m_group_to_items.clear();
1067 m_selection_box_union.reset(0,0,0);
1068 m_selection_box_int_union.reset(0,0,0);
1070 resetNodeResolveState();
1072 u32 initial_length = 0;
1073 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1074 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1075 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1076 m_content_features.resize(initial_length);
1078 // Set CONTENT_UNKNOWN
1082 TileDef unknownTile;
1083 unknownTile.name = "unknown_node.png";
1084 for (int t = 0; t < 6; t++)
1085 f.tiledef[t] = unknownTile;
1086 // Insert directly into containers
1087 content_t c = CONTENT_UNKNOWN;
1088 m_content_features[c] = f;
1089 addNameIdMapping(c, f.name);
1096 f.drawtype = NDT_AIRLIKE;
1097 f.param_type = CPT_LIGHT;
1098 f.light_propagates = true;
1099 f.sunlight_propagates = true;
1101 f.pointable = false;
1103 f.buildable_to = true;
1105 f.is_ground_content = true;
1106 // Insert directly into containers
1107 content_t c = CONTENT_AIR;
1108 m_content_features[c] = f;
1109 addNameIdMapping(c, f.name);
1112 // Set CONTENT_IGNORE
1116 f.drawtype = NDT_AIRLIKE;
1117 f.param_type = CPT_NONE;
1118 f.light_propagates = false;
1119 f.sunlight_propagates = false;
1121 f.pointable = false;
1123 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1124 f.is_ground_content = true;
1125 // Insert directly into containers
1126 content_t c = CONTENT_IGNORE;
1127 m_content_features[c] = f;
1128 addNameIdMapping(c, f.name);
1133 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1135 std::unordered_map<std::string, content_t>::const_iterator
1136 i = m_name_id_mapping_with_aliases.find(name);
1137 if(i == m_name_id_mapping_with_aliases.end())
1144 content_t NodeDefManager::getId(const std::string &name) const
1146 content_t id = CONTENT_IGNORE;
1152 bool NodeDefManager::getIds(const std::string &name,
1153 std::vector<content_t> &result) const
1155 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1156 if (name.substr(0,6) != "group:") {
1157 content_t id = CONTENT_IGNORE;
1158 bool exists = getId(name, id);
1160 result.push_back(id);
1163 std::string group = name.substr(6);
1165 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1166 i = m_group_to_items.find(group);
1167 if (i == m_group_to_items.end())
1170 const std::vector<content_t> &items = i->second;
1171 result.insert(result.end(), items.begin(), items.end());
1172 //printf("getIds: %dus\n", t.stop());
1177 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1179 content_t id = CONTENT_UNKNOWN;
1185 // returns CONTENT_IGNORE if no free ID found
1186 content_t NodeDefManager::allocateId()
1188 for (content_t id = m_next_id;
1189 id >= m_next_id; // overflow?
1191 while (id >= m_content_features.size()) {
1192 m_content_features.emplace_back();
1194 const ContentFeatures &f = m_content_features[id];
1195 if (f.name.empty()) {
1200 // If we arrive here, an overflow occurred in id.
1201 // That means no ID was found
1202 return CONTENT_IGNORE;
1207 * Returns the smallest box that contains all boxes
1208 * in the vector. Box_union is expanded.
1209 * @param[in] boxes the vector containing the boxes
1210 * @param[in, out] box_union the union of the arguments
1212 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1214 for (const aabb3f &box : boxes) {
1215 box_union->addInternalBox(box);
1221 * Returns a box that contains the nodebox in every case.
1222 * The argument node_union is expanded.
1223 * @param[in] nodebox the nodebox to be measured
1224 * @param[in] features used to decide whether the nodebox
1226 * @param[in, out] box_union the union of the arguments
1228 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1231 switch(nodebox.type) {
1233 case NODEBOX_LEVELED: {
1235 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1236 boxVectorUnion(nodebox.fixed, &half_processed);
1237 // Set leveled boxes to maximal
1238 if (nodebox.type == NODEBOX_LEVELED) {
1239 half_processed.MaxEdge.Y = +BS / 2;
1241 if (features.param_type_2 == CPT2_FACEDIR ||
1242 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1243 // Get maximal coordinate
1245 fabsf(half_processed.MinEdge.X),
1246 fabsf(half_processed.MinEdge.Y),
1247 fabsf(half_processed.MinEdge.Z),
1248 fabsf(half_processed.MaxEdge.X),
1249 fabsf(half_processed.MaxEdge.Y),
1250 fabsf(half_processed.MaxEdge.Z) };
1252 for (float coord : coords) {
1257 // Add the union of all possible rotated boxes
1258 box_union->addInternalPoint(-max, -max, -max);
1259 box_union->addInternalPoint(+max, +max, +max);
1261 box_union->addInternalBox(half_processed);
1265 case NODEBOX_WALLMOUNTED: {
1267 box_union->addInternalBox(nodebox.wall_top);
1268 box_union->addInternalBox(nodebox.wall_bottom);
1269 // Find maximal coordinate in the X-Z plane
1271 fabsf(nodebox.wall_side.MinEdge.X),
1272 fabsf(nodebox.wall_side.MinEdge.Z),
1273 fabsf(nodebox.wall_side.MaxEdge.X),
1274 fabsf(nodebox.wall_side.MaxEdge.Z) };
1276 for (float coord : coords) {
1281 // Add the union of all possible rotated boxes
1282 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1283 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1286 case NODEBOX_CONNECTED: {
1287 // Add all possible connected boxes
1288 boxVectorUnion(nodebox.fixed, box_union);
1289 boxVectorUnion(nodebox.connect_top, box_union);
1290 boxVectorUnion(nodebox.connect_bottom, box_union);
1291 boxVectorUnion(nodebox.connect_front, box_union);
1292 boxVectorUnion(nodebox.connect_left, box_union);
1293 boxVectorUnion(nodebox.connect_back, box_union);
1294 boxVectorUnion(nodebox.connect_right, box_union);
1295 boxVectorUnion(nodebox.disconnected_top, box_union);
1296 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1297 boxVectorUnion(nodebox.disconnected_front, box_union);
1298 boxVectorUnion(nodebox.disconnected_left, box_union);
1299 boxVectorUnion(nodebox.disconnected_back, box_union);
1300 boxVectorUnion(nodebox.disconnected_right, box_union);
1301 boxVectorUnion(nodebox.disconnected, box_union);
1302 boxVectorUnion(nodebox.disconnected_sides, box_union);
1307 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1308 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1314 inline void NodeDefManager::fixSelectionBoxIntUnion()
1316 m_selection_box_int_union.MinEdge.X = floorf(
1317 m_selection_box_union.MinEdge.X / BS + 0.5f);
1318 m_selection_box_int_union.MinEdge.Y = floorf(
1319 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1320 m_selection_box_int_union.MinEdge.Z = floorf(
1321 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1322 m_selection_box_int_union.MaxEdge.X = ceilf(
1323 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1324 m_selection_box_int_union.MaxEdge.Y = ceilf(
1325 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1326 m_selection_box_int_union.MaxEdge.Z = ceilf(
1327 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1331 void NodeDefManager::eraseIdFromGroups(content_t id)
1333 // For all groups in m_group_to_items...
1334 for (auto iter_groups = m_group_to_items.begin();
1335 iter_groups != m_group_to_items.end();) {
1336 // Get the group items vector.
1337 std::vector<content_t> &items = iter_groups->second;
1339 // Remove any occurence of the id in the group items vector.
1340 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1342 // If group is empty, erase its vector from the map.
1344 iter_groups = m_group_to_items.erase(iter_groups);
1351 // IWritableNodeDefManager
1352 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1356 assert(name != "ignore");
1357 assert(name == def.name);
1359 content_t id = CONTENT_IGNORE;
1360 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1363 if (id == CONTENT_IGNORE) {
1364 warningstream << "NodeDefManager: Absolute "
1365 "limit reached" << std::endl;
1366 return CONTENT_IGNORE;
1368 assert(id != CONTENT_IGNORE);
1369 addNameIdMapping(id, name);
1372 // If there is already ContentFeatures registered for this id, clear old groups
1373 if (id < m_content_features.size())
1374 eraseIdFromGroups(id);
1376 m_content_features[id] = def;
1377 verbosestream << "NodeDefManager: registering content id \"" << id
1378 << "\": name=\"" << def.name << "\""<<std::endl;
1380 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1381 fixSelectionBoxIntUnion();
1383 // Add this content to the list of all groups it belongs to
1384 for (const auto &group : def.groups) {
1385 const std::string &group_name = group.first;
1386 m_group_to_items[group_name].push_back(id);
1393 content_t NodeDefManager::allocateDummy(const std::string &name)
1395 assert(name != ""); // Pre-condition
1398 return set(name, f);
1402 void NodeDefManager::removeNode(const std::string &name)
1407 // Erase name from name ID mapping
1408 content_t id = CONTENT_IGNORE;
1409 if (m_name_id_mapping.getId(name, id)) {
1410 m_name_id_mapping.eraseName(name);
1411 m_name_id_mapping_with_aliases.erase(name);
1414 eraseIdFromGroups(id);
1418 void NodeDefManager::updateAliases(IItemDefManager *idef)
1420 std::set<std::string> all;
1422 m_name_id_mapping_with_aliases.clear();
1423 for (const std::string &name : all) {
1424 const std::string &convert_to = idef->getAlias(name);
1426 if (m_name_id_mapping.getId(convert_to, id)) {
1427 m_name_id_mapping_with_aliases.insert(
1428 std::make_pair(name, id));
1433 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1435 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1436 "overrides to textures" << std::endl;
1438 for (const TextureOverride& texture_override : overrides) {
1440 if (!getId(texture_override.id, id))
1441 continue; // Ignore unknown node
1443 ContentFeatures &nodedef = m_content_features[id];
1446 if (texture_override.hasTarget(OverrideTarget::TOP))
1447 nodedef.tiledef[0].name = texture_override.texture;
1449 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1450 nodedef.tiledef[1].name = texture_override.texture;
1452 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1453 nodedef.tiledef[2].name = texture_override.texture;
1455 if (texture_override.hasTarget(OverrideTarget::LEFT))
1456 nodedef.tiledef[3].name = texture_override.texture;
1458 if (texture_override.hasTarget(OverrideTarget::BACK))
1459 nodedef.tiledef[4].name = texture_override.texture;
1461 if (texture_override.hasTarget(OverrideTarget::FRONT))
1462 nodedef.tiledef[5].name = texture_override.texture;
1465 // Override special tiles, if applicable
1466 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1467 nodedef.tiledef_special[0].name = texture_override.texture;
1469 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1470 nodedef.tiledef_special[1].name = texture_override.texture;
1472 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1473 nodedef.tiledef_special[2].name = texture_override.texture;
1475 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1476 nodedef.tiledef_special[3].name = texture_override.texture;
1478 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1479 nodedef.tiledef_special[4].name = texture_override.texture;
1481 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1482 nodedef.tiledef_special[5].name = texture_override.texture;
1486 void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
1489 infostream << "NodeDefManager::updateTextures(): Updating "
1490 "textures in node definitions" << std::endl;
1492 Client *client = (Client *)gamedef;
1493 ITextureSource *tsrc = client->tsrc();
1494 IShaderSource *shdsrc = client->getShaderSource();
1495 auto smgr = client->getSceneManager();
1496 scene::IMeshManipulator *meshmanip = smgr->getMeshManipulator();
1497 TextureSettings tsettings;
1498 tsettings.readSettings();
1500 u32 size = m_content_features.size();
1502 for (u32 i = 0; i < size; i++) {
1503 ContentFeatures *f = &(m_content_features[i]);
1504 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1505 client->showUpdateProgressTexture(progress_callback_args, i, size);
1510 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1512 writeU8(os, 1); // version
1514 std::ostringstream os2(std::ios::binary);
1515 for (u32 i = 0; i < m_content_features.size(); i++) {
1516 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1517 || i == CONTENT_UNKNOWN)
1519 const ContentFeatures *f = &m_content_features[i];
1520 if (f->name.empty())
1523 // Wrap it in a string to allow different lengths without
1524 // strict version incompatibilities
1525 std::ostringstream wrapper_os(std::ios::binary);
1526 f->serialize(wrapper_os, protocol_version);
1527 os2<<serializeString16(wrapper_os.str());
1529 // must not overflow
1530 u16 next = count + 1;
1531 FATAL_ERROR_IF(next < count, "Overflow");
1534 writeU16(os, count);
1535 os << serializeString32(os2.str());
1539 void NodeDefManager::deSerialize(std::istream &is)
1542 int version = readU8(is);
1544 throw SerializationError("unsupported NodeDefinitionManager version");
1545 u16 count = readU16(is);
1546 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1548 for (u16 n = 0; n < count; n++) {
1549 u16 i = readU16(is2);
1551 // Read it from the string wrapper
1552 std::string wrapper = deSerializeString16(is2);
1553 std::istringstream wrapper_is(wrapper, std::ios::binary);
1554 f.deSerialize(wrapper_is);
1556 // Check error conditions
1557 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1558 warningstream << "NodeDefManager::deSerialize(): "
1559 "not changing builtin node " << i << std::endl;
1562 if (f.name.empty()) {
1563 warningstream << "NodeDefManager::deSerialize(): "
1564 "received empty name" << std::endl;
1570 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1571 warningstream << "NodeDefManager::deSerialize(): "
1572 "already defined with different ID: " << f.name << std::endl;
1576 // All is ok, add node definition with the requested ID
1577 if (i >= m_content_features.size())
1578 m_content_features.resize((u32)(i) + 1);
1579 m_content_features[i] = f;
1580 addNameIdMapping(i, f.name);
1581 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1583 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1584 fixSelectionBoxIntUnion();
1587 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1588 // are not sent, resolve them client-side too.
1593 void NodeDefManager::addNameIdMapping(content_t i, const std::string &name)
1595 m_name_id_mapping.set(i, name);
1596 m_name_id_mapping_with_aliases.emplace(name, i);
1600 NodeDefManager *createNodeDefManager()
1602 return new NodeDefManager();
1606 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1609 if (m_node_registration_complete)
1610 nr->nodeResolveInternal();
1612 m_pending_resolve_callbacks.push_back(nr);
1616 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1618 size_t len = m_pending_resolve_callbacks.size();
1619 for (size_t i = 0; i != len; i++) {
1620 if (nr != m_pending_resolve_callbacks[i])
1624 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1625 m_pending_resolve_callbacks.resize(len);
1633 void NodeDefManager::runNodeResolveCallbacks()
1635 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1636 NodeResolver *nr = m_pending_resolve_callbacks[i];
1637 nr->nodeResolveInternal();
1640 m_pending_resolve_callbacks.clear();
1644 void NodeDefManager::resetNodeResolveState()
1646 m_node_registration_complete = false;
1647 m_pending_resolve_callbacks.clear();
1650 static void removeDupes(std::vector<content_t> &list)
1652 std::sort(list.begin(), list.end());
1653 auto new_end = std::unique(list.begin(), list.end());
1654 list.erase(new_end, list.end());
1657 void NodeDefManager::resolveCrossrefs()
1659 for (ContentFeatures &f : m_content_features) {
1660 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1661 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1662 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1665 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1668 for (const std::string &name : f.connects_to) {
1669 getIds(name, f.connects_to_ids);
1671 removeDupes(f.connects_to_ids);
1675 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1676 u8 connect_face) const
1678 const ContentFeatures &f1 = get(from);
1680 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1683 // lookup target in connected set
1684 if (!CONTAINS(f1.connects_to_ids, to.param0))
1687 const ContentFeatures &f2 = get(to);
1689 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1690 // ignores actually looking if back connection exists
1691 return CONTAINS(f2.connects_to_ids, from.param0);
1693 // does to node declare usable faces?
1694 if (f2.connect_sides > 0) {
1695 if ((f2.param_type_2 == CPT2_FACEDIR ||
1696 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1697 && (connect_face >= 4)) {
1698 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1699 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1701 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1703 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1704 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1706 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1707 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1708 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1710 return (f2.connect_sides
1711 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1713 return (f2.connect_sides & connect_face);
1715 // the target is just a regular node, so connect no matter back connection
1723 NodeResolver::NodeResolver()
1729 NodeResolver::~NodeResolver()
1731 if (!m_resolve_done && m_ndef)
1732 m_ndef->cancelNodeResolveCallback(this);
1736 void NodeResolver::cloneTo(NodeResolver *res) const
1738 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1739 " after resolving has completed");
1740 /* We don't actually do anything significant. Since the node resolving has
1741 * already completed, the class that called us will already have the
1742 * resolved IDs in its data structures (which it copies on its own) */
1743 res->m_ndef = m_ndef;
1744 res->m_resolve_done = true;
1748 void NodeResolver::nodeResolveInternal()
1750 m_nodenames_idx = 0;
1751 m_nnlistsizes_idx = 0;
1754 m_resolve_done = true;
1756 m_nodenames.clear();
1757 m_nnlistsizes.clear();
1761 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1762 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1764 if (m_nodenames_idx == m_nodenames.size()) {
1765 *result_out = c_fallback;
1766 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1771 std::string name = m_nodenames[m_nodenames_idx++];
1773 bool success = m_ndef->getId(name, c);
1774 if (!success && !node_alt.empty()) {
1776 success = m_ndef->getId(name, c);
1780 if (error_on_fallback)
1781 errorstream << "NodeResolver: failed to resolve node name '" << name
1782 << "'." << std::endl;
1791 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1792 bool all_required, content_t c_fallback)
1794 bool success = true;
1796 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1797 errorstream << "NodeResolver: no more node lists" << std::endl;
1801 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1804 if (m_nodenames_idx == m_nodenames.size()) {
1805 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1810 std::string &name = m_nodenames[m_nodenames_idx++];
1812 if (name.substr(0,6) != "group:") {
1813 if (m_ndef->getId(name, c)) {
1814 result_out->push_back(c);
1815 } else if (all_required) {
1816 errorstream << "NodeResolver: failed to resolve node name '"
1817 << name << "'." << std::endl;
1818 result_out->push_back(c_fallback);
1822 m_ndef->getIds(name, *result_out);
1829 void NodeResolver::reset(bool resolve_done)
1831 m_nodenames.clear();
1832 m_nodenames_idx = 0;
1833 m_nnlistsizes.clear();
1834 m_nnlistsizes_idx = 0;
1836 m_resolve_done = resolve_done;
1838 m_nodenames.reserve(16);
1839 m_nnlistsizes.reserve(4);