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 writeV3F1000(os, nodebox.MinEdge);
88 writeV3F1000(os, nodebox.MaxEdge);
91 case NODEBOX_WALLMOUNTED:
94 writeV3F1000(os, wall_top.MinEdge);
95 writeV3F1000(os, wall_top.MaxEdge);
96 writeV3F1000(os, wall_bottom.MinEdge);
97 writeV3F1000(os, wall_bottom.MaxEdge);
98 writeV3F1000(os, wall_side.MinEdge);
99 writeV3F1000(os, wall_side.MaxEdge);
101 case NODEBOX_CONNECTED:
104 #define WRITEBOX(box) \
105 writeU16(os, (box).size()); \
106 for (const aabb3f &i: (box)) { \
107 writeV3F1000(os, i.MinEdge); \
108 writeV3F1000(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 = readV3F1000(is);
150 box.MaxEdge = readV3F1000(is);
151 fixed.push_back(box);
154 else if(type == NODEBOX_WALLMOUNTED)
156 wall_top.MinEdge = readV3F1000(is);
157 wall_top.MaxEdge = readV3F1000(is);
158 wall_bottom.MinEdge = readV3F1000(is);
159 wall_bottom.MaxEdge = readV3F1000(is);
160 wall_side.MinEdge = readV3F1000(is);
161 wall_side.MaxEdge = readV3F1000(is);
163 else if (type == NODEBOX_CONNECTED)
165 #define READBOXES(box) { \
166 count = readU16(is); \
167 (box).reserve(count); \
169 v3f min = readV3F1000(is); \
170 v3f max = readV3F1000(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);
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);
199 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
200 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
201 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
202 #define TILE_FLAG_HAS_COLOR (1 << 3)
203 #define TILE_FLAG_HAS_SCALE (1 << 4)
204 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
206 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
208 // protocol_version >= 36
210 writeU8(os, version);
212 os << serializeString(name);
213 animation.serialize(os, version);
214 bool has_scale = scale > 0;
216 if (backface_culling)
217 flags |= TILE_FLAG_BACKFACE_CULLING;
218 if (tileable_horizontal)
219 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
220 if (tileable_vertical)
221 flags |= TILE_FLAG_TILEABLE_VERTICAL;
223 flags |= TILE_FLAG_HAS_COLOR;
225 flags |= TILE_FLAG_HAS_SCALE;
226 if (align_style != ALIGN_STYLE_NODE)
227 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
230 writeU8(os, color.getRed());
231 writeU8(os, color.getGreen());
232 writeU8(os, color.getBlue());
236 if (align_style != ALIGN_STYLE_NODE)
237 writeU8(os, align_style);
240 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
241 NodeDrawType drawtype)
243 int version = readU8(is);
245 throw SerializationError("unsupported TileDef version");
246 name = deSerializeString(is);
247 animation.deSerialize(is, version);
248 u16 flags = readU16(is);
249 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
250 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
251 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
252 has_color = flags & TILE_FLAG_HAS_COLOR;
253 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
254 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
256 color.setRed(readU8(is));
257 color.setGreen(readU8(is));
258 color.setBlue(readU8(is));
260 scale = has_scale ? readU8(is) : 0;
262 align_style = static_cast<AlignStyle>(readU8(is));
264 align_style = ALIGN_STYLE_NODE;
269 SimpleSoundSpec serialization
272 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
273 std::ostream &os, u8 version)
275 os<<serializeString(ss.name);
276 writeF1000(os, ss.gain);
277 writeF1000(os, ss.pitch);
279 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss,
280 std::istream &is, u8 version)
282 ss.name = deSerializeString(is);
283 ss.gain = readF1000(is);
284 ss.pitch = readF1000(is);
287 void TextureSettings::readSettings()
289 connected_glass = g_settings->getBool("connected_glass");
290 opaque_water = g_settings->getBool("opaque_water");
291 bool enable_shaders = g_settings->getBool("enable_shaders");
292 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
293 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
294 bool smooth_lighting = g_settings->getBool("smooth_lighting");
295 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
296 enable_minimap = g_settings->getBool("enable_minimap");
297 node_texture_size = g_settings->getU16("texture_min_size");
298 std::string leaves_style_str = g_settings->get("leaves_style");
299 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
300 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
302 // Mesh cache is not supported in combination with smooth lighting
304 enable_mesh_cache = false;
306 use_normal_texture = enable_shaders &&
307 (enable_bumpmapping || enable_parallax_occlusion);
308 if (leaves_style_str == "fancy") {
309 leaves_style = LEAVES_FANCY;
310 } else if (leaves_style_str == "simple") {
311 leaves_style = LEAVES_SIMPLE;
313 leaves_style = LEAVES_OPAQUE;
316 if (world_aligned_mode_str == "enable")
317 world_aligned_mode = WORLDALIGN_ENABLE;
318 else if (world_aligned_mode_str == "force_solid")
319 world_aligned_mode = WORLDALIGN_FORCE;
320 else if (world_aligned_mode_str == "force_nodebox")
321 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
323 world_aligned_mode = WORLDALIGN_DISABLE;
325 if (autoscale_mode_str == "enable")
326 autoscale_mode = AUTOSCALE_ENABLE;
327 else if (autoscale_mode_str == "force")
328 autoscale_mode = AUTOSCALE_FORCE;
330 autoscale_mode = AUTOSCALE_DISABLE;
337 ContentFeatures::ContentFeatures()
342 void ContentFeatures::reset()
349 visual_solidness = 0;
350 backface_culling = true;
353 has_on_construct = false;
354 has_on_destruct = false;
355 has_after_destruct = false;
359 NOTE: Most of this is always overridden by the default values given
364 // Unknown nodes can be dug
365 groups["dig_immediate"] = 2;
366 drawtype = NDT_NORMAL;
369 for (auto &i : mesh_ptr)
371 minimap_color = video::SColor(0, 0, 0, 0);
374 for (auto &i : tiledef)
376 for (auto &j : tiledef_special)
379 post_effect_color = video::SColor(0, 0, 0, 0);
380 param_type = CPT_NONE;
381 param_type_2 = CPT2_NONE;
382 is_ground_content = false;
383 light_propagates = false;
384 sunlight_propagates = false;
389 buildable_to = false;
391 rightclickable = true;
393 liquid_type = LIQUID_NONE;
394 liquid_alternative_flowing = "";
395 liquid_alternative_source = "";
396 liquid_viscosity = 0;
397 liquid_renewable = true;
398 liquid_range = LIQUID_LEVEL_MAX+1;
401 damage_per_second = 0;
402 node_box = NodeBox();
403 selection_box = NodeBox();
404 collision_box = NodeBox();
406 legacy_facedir_simple = false;
407 legacy_wallmounted = false;
408 sound_footstep = SimpleSoundSpec();
409 sound_dig = SimpleSoundSpec("__group");
410 sound_dug = SimpleSoundSpec();
412 connects_to_ids.clear();
414 color = video::SColor(0xFFFFFFFF);
417 node_dig_prediction = "air";
420 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
422 // protocol_version >= 36
424 writeU8(os, version);
427 os << serializeString(name);
428 writeU16(os, groups.size());
429 for (const auto &group : groups) {
430 os << serializeString(group.first);
431 writeS16(os, group.second);
433 writeU8(os, param_type);
434 writeU8(os, param_type_2);
437 writeU8(os, drawtype);
438 os << serializeString(mesh);
439 writeF1000(os, visual_scale);
441 for (const TileDef &td : tiledef)
442 td.serialize(os, protocol_version);
443 for (const TileDef &td : tiledef_overlay)
444 td.serialize(os, protocol_version);
445 writeU8(os, CF_SPECIAL_COUNT);
446 for (const TileDef &td : tiledef_special) {
447 td.serialize(os, protocol_version);
450 writeU8(os, color.getRed());
451 writeU8(os, color.getGreen());
452 writeU8(os, color.getBlue());
453 os << serializeString(palette_name);
455 writeU8(os, connect_sides);
456 writeU16(os, connects_to_ids.size());
457 for (u16 connects_to_id : connects_to_ids)
458 writeU16(os, connects_to_id);
459 writeU8(os, post_effect_color.getAlpha());
460 writeU8(os, post_effect_color.getRed());
461 writeU8(os, post_effect_color.getGreen());
462 writeU8(os, post_effect_color.getBlue());
463 writeU8(os, leveled);
466 writeU8(os, light_propagates);
467 writeU8(os, sunlight_propagates);
468 writeU8(os, light_source);
471 writeU8(os, is_ground_content);
474 writeU8(os, walkable);
475 writeU8(os, pointable);
476 writeU8(os, diggable);
477 writeU8(os, climbable);
478 writeU8(os, buildable_to);
479 writeU8(os, rightclickable);
480 writeU32(os, damage_per_second);
483 writeU8(os, liquid_type);
484 os << serializeString(liquid_alternative_flowing);
485 os << serializeString(liquid_alternative_source);
486 writeU8(os, liquid_viscosity);
487 writeU8(os, liquid_renewable);
488 writeU8(os, liquid_range);
489 writeU8(os, drowning);
490 writeU8(os, floodable);
493 node_box.serialize(os, protocol_version);
494 selection_box.serialize(os, protocol_version);
495 collision_box.serialize(os, protocol_version);
498 serializeSimpleSoundSpec(sound_footstep, os, version);
499 serializeSimpleSoundSpec(sound_dig, os, version);
500 serializeSimpleSoundSpec(sound_dug, os, version);
503 writeU8(os, legacy_facedir_simple);
504 writeU8(os, legacy_wallmounted);
506 os << serializeString(node_dig_prediction);
509 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
511 // alpha == 0 means that the node is using texture alpha
512 if (alpha == 0 || alpha == 255)
515 for (int i = 0; i < length; i++) {
516 if (tiles[i].name.empty())
519 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
520 tiles[i].name = s.str();
524 void ContentFeatures::deSerialize(std::istream &is)
527 int version = readU8(is);
529 throw SerializationError("unsupported ContentFeatures version");
532 name = deSerializeString(is);
534 u32 groups_size = readU16(is);
535 for (u32 i = 0; i < groups_size; i++) {
536 std::string name = deSerializeString(is);
537 int value = readS16(is);
538 groups[name] = value;
540 param_type = (enum ContentParamType) readU8(is);
541 param_type_2 = (enum ContentParamType2) readU8(is);
544 drawtype = (enum NodeDrawType) readU8(is);
545 mesh = deSerializeString(is);
546 visual_scale = readF1000(is);
548 throw SerializationError("unsupported tile count");
549 for (TileDef &td : tiledef)
550 td.deSerialize(is, version, drawtype);
551 for (TileDef &td : tiledef_overlay)
552 td.deSerialize(is, version, drawtype);
553 if (readU8(is) != CF_SPECIAL_COUNT)
554 throw SerializationError("unsupported CF_SPECIAL_COUNT");
555 for (TileDef &td : tiledef_special)
556 td.deSerialize(is, version, drawtype);
558 color.setRed(readU8(is));
559 color.setGreen(readU8(is));
560 color.setBlue(readU8(is));
561 palette_name = deSerializeString(is);
563 connect_sides = readU8(is);
564 u16 connects_to_size = readU16(is);
565 connects_to_ids.clear();
566 for (u16 i = 0; i < connects_to_size; i++)
567 connects_to_ids.push_back(readU16(is));
568 post_effect_color.setAlpha(readU8(is));
569 post_effect_color.setRed(readU8(is));
570 post_effect_color.setGreen(readU8(is));
571 post_effect_color.setBlue(readU8(is));
572 leveled = readU8(is);
575 light_propagates = readU8(is);
576 sunlight_propagates = readU8(is);
577 light_source = readU8(is);
578 light_source = MYMIN(light_source, LIGHT_MAX);
581 is_ground_content = readU8(is);
584 walkable = readU8(is);
585 pointable = readU8(is);
586 diggable = readU8(is);
587 climbable = readU8(is);
588 buildable_to = readU8(is);
589 rightclickable = readU8(is);
590 damage_per_second = readU32(is);
593 liquid_type = (enum LiquidType) readU8(is);
594 liquid_alternative_flowing = deSerializeString(is);
595 liquid_alternative_source = deSerializeString(is);
596 liquid_viscosity = readU8(is);
597 liquid_renewable = readU8(is);
598 liquid_range = readU8(is);
599 drowning = readU8(is);
600 floodable = readU8(is);
603 node_box.deSerialize(is);
604 selection_box.deSerialize(is);
605 collision_box.deSerialize(is);
608 deSerializeSimpleSoundSpec(sound_footstep, is, version);
609 deSerializeSimpleSoundSpec(sound_dig, is, version);
610 deSerializeSimpleSoundSpec(sound_dug, is, version);
612 // read legacy properties
613 legacy_facedir_simple = readU8(is);
614 legacy_wallmounted = readU8(is);
617 node_dig_prediction = deSerializeString(is);
618 } catch(SerializationError &e) {};
622 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
623 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
624 u8 material_type, u32 shader_id, bool backface_culling,
625 const TextureSettings &tsettings)
627 layer->shader_id = shader_id;
628 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
629 layer->material_type = material_type;
631 bool has_scale = tiledef.scale > 0;
632 if (((tsettings.autoscale_mode == AUTOSCALE_ENABLE) && !has_scale) ||
633 (tsettings.autoscale_mode == AUTOSCALE_FORCE)) {
634 auto texture_size = layer->texture->getOriginalSize();
635 float base_size = tsettings.node_texture_size;
636 float size = std::fmin(texture_size.Width, texture_size.Height);
637 layer->scale = std::fmax(base_size, size) / base_size;
638 } else if (has_scale) {
639 layer->scale = tiledef.scale;
643 if (!tile.world_aligned)
646 // Normal texture and shader flags texture
647 if (tsettings.use_normal_texture) {
648 layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
650 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
653 layer->material_flags = 0;
654 if (backface_culling)
655 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
656 if (tiledef.animation.type != TAT_NONE)
657 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
658 if (tiledef.tileable_horizontal)
659 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
660 if (tiledef.tileable_vertical)
661 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
664 layer->has_color = tiledef.has_color;
665 if (tiledef.has_color)
666 layer->color = tiledef.color;
668 layer->color = color;
670 // Animation parameters
672 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
674 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
675 &frame_count, &frame_length_ms, NULL);
676 layer->animation_frame_count = frame_count;
677 layer->animation_frame_length_ms = frame_length_ms;
680 if (frame_count == 1) {
681 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
683 std::ostringstream os(std::ios::binary);
684 if (!layer->frames) {
685 layer->frames = std::make_shared<std::vector<FrameSpec>>();
687 layer->frames->resize(frame_count);
689 for (int i = 0; i < frame_count; i++) {
695 tiledef.animation.getTextureModifer(os,
696 layer->texture->getOriginalSize(), i);
698 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
699 if (layer->normal_texture)
700 frame.normal_texture = tsrc->getNormalTexture(os.str());
701 frame.flags_texture = layer->flags_texture;
702 (*layer->frames)[i] = frame;
709 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
711 if (style == ALIGN_STYLE_WORLD)
713 if (mode == WORLDALIGN_DISABLE)
715 if (style == ALIGN_STYLE_USER_DEFINED)
717 if (drawtype == NDT_NORMAL)
718 return mode >= WORLDALIGN_FORCE;
719 if (drawtype == NDT_NODEBOX)
720 return mode >= WORLDALIGN_FORCE_NODEBOX;
724 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
725 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
727 // minimap pixel color - the average color of a texture
728 if (tsettings.enable_minimap && !tiledef[0].name.empty())
729 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
731 // Figure out the actual tiles to use
733 for (u32 j = 0; j < 6; j++) {
734 tdef[j] = tiledef[j];
735 if (tdef[j].name.empty())
736 tdef[j].name = "unknown_node.png";
738 // also the overlay tiles
739 TileDef tdef_overlay[6];
740 for (u32 j = 0; j < 6; j++)
741 tdef_overlay[j] = tiledef_overlay[j];
742 // also the special tiles
743 TileDef tdef_spec[6];
744 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
745 tdef_spec[j] = tiledef_special[j];
747 bool is_liquid = false;
749 u8 material_type = (alpha == 255) ?
750 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
755 material_type = (alpha == 255) ?
756 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
763 assert(liquid_type == LIQUID_SOURCE);
764 if (tsettings.opaque_water)
769 case NDT_FLOWINGLIQUID:
770 assert(liquid_type == LIQUID_FLOWING);
772 if (tsettings.opaque_water)
778 visual_solidness = 1;
780 case NDT_GLASSLIKE_FRAMED:
782 visual_solidness = 1;
784 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
786 visual_solidness = 1;
787 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
791 visual_solidness = 1;
793 case NDT_ALLFACES_OPTIONAL:
794 if (tsettings.leaves_style == LEAVES_FANCY) {
795 drawtype = NDT_ALLFACES;
797 visual_solidness = 1;
798 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
799 for (u32 j = 0; j < 6; j++) {
800 if (!tdef_spec[j].name.empty())
801 tdef[j].name = tdef_spec[j].name;
803 drawtype = NDT_GLASSLIKE;
805 visual_solidness = 1;
807 drawtype = NDT_NORMAL;
809 for (TileDef &td : tdef)
810 td.name += std::string("^[noalpha");
813 material_type = TILE_MATERIAL_WAVING_LEAVES;
818 material_type = TILE_MATERIAL_WAVING_PLANTS;
827 material_type = TILE_MATERIAL_WAVING_PLANTS;
828 else if (waving == 2)
829 material_type = TILE_MATERIAL_WAVING_LEAVES;
837 case NDT_PLANTLIKE_ROOTED:
843 // Vertex alpha is no longer supported, correct if necessary.
844 correctAlpha(tdef, 6);
845 correctAlpha(tdef_overlay, 6);
846 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
847 material_type = (alpha == 255) ?
848 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
851 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
853 u8 overlay_material = material_type;
854 if (overlay_material == TILE_MATERIAL_OPAQUE)
855 overlay_material = TILE_MATERIAL_BASIC;
856 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
857 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
859 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
861 // Tiles (fill in f->tiles[])
862 for (u16 j = 0; j < 6; j++) {
863 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
864 tsettings.world_aligned_mode, drawtype);
865 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
866 color, material_type, tile_shader,
867 tdef[j].backface_culling, tsettings);
868 if (!tdef_overlay[j].name.empty())
869 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
870 color, overlay_material, overlay_shader,
871 tdef[j].backface_culling, tsettings);
874 u8 special_material = material_type;
875 if (drawtype == NDT_PLANTLIKE_ROOTED) {
877 special_material = TILE_MATERIAL_WAVING_PLANTS;
878 else if (waving == 2)
879 special_material = TILE_MATERIAL_WAVING_LEAVES;
881 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
883 // Special tiles (fill in f->special_tiles[])
884 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
885 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
886 color, special_material, special_shader,
887 tdef_spec[j].backface_culling, tsettings);
889 if (param_type_2 == CPT2_COLOR ||
890 param_type_2 == CPT2_COLORED_FACEDIR ||
891 param_type_2 == CPT2_COLORED_WALLMOUNTED)
892 palette = tsrc->getPalette(palette_name);
894 if (drawtype == NDT_MESH && !mesh.empty()) {
896 // Read the mesh and apply scale
897 mesh_ptr[0] = client->getMesh(mesh);
899 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
900 scaleMesh(mesh_ptr[0], scale);
901 recalculateBoundingBox(mesh_ptr[0]);
902 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
906 //Cache 6dfacedir and wallmounted rotated clones of meshes
907 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
908 (param_type_2 == CPT2_FACEDIR
909 || param_type_2 == CPT2_COLORED_FACEDIR)) {
910 for (u16 j = 1; j < 24; j++) {
911 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
912 rotateMeshBy6dFacedir(mesh_ptr[j], j);
913 recalculateBoundingBox(mesh_ptr[j]);
914 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
916 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
917 && (param_type_2 == CPT2_WALLMOUNTED ||
918 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
919 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
920 for (u16 j = 1; j < 6; j++) {
921 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
922 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
923 recalculateBoundingBox(mesh_ptr[j]);
924 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
926 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
927 recalculateBoundingBox(mesh_ptr[0]);
928 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
940 NodeDefManager::NodeDefManager()
946 NodeDefManager::~NodeDefManager()
949 for (ContentFeatures &f : m_content_features) {
950 for (auto &j : f.mesh_ptr) {
959 void NodeDefManager::clear()
961 m_content_features.clear();
962 m_name_id_mapping.clear();
963 m_name_id_mapping_with_aliases.clear();
964 m_group_to_items.clear();
966 m_selection_box_union.reset(0,0,0);
967 m_selection_box_int_union.reset(0,0,0);
969 resetNodeResolveState();
971 u32 initial_length = 0;
972 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
973 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
974 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
975 m_content_features.resize(initial_length);
977 // Set CONTENT_UNKNOWN
981 // Insert directly into containers
982 content_t c = CONTENT_UNKNOWN;
983 m_content_features[c] = f;
984 addNameIdMapping(c, f.name);
991 f.drawtype = NDT_AIRLIKE;
992 f.param_type = CPT_LIGHT;
993 f.light_propagates = true;
994 f.sunlight_propagates = true;
998 f.buildable_to = true;
1000 f.is_ground_content = true;
1001 // Insert directly into containers
1002 content_t c = CONTENT_AIR;
1003 m_content_features[c] = f;
1004 addNameIdMapping(c, f.name);
1007 // Set CONTENT_IGNORE
1011 f.drawtype = NDT_AIRLIKE;
1012 f.param_type = CPT_NONE;
1013 f.light_propagates = false;
1014 f.sunlight_propagates = false;
1016 f.pointable = false;
1018 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1019 f.is_ground_content = true;
1020 // Insert directly into containers
1021 content_t c = CONTENT_IGNORE;
1022 m_content_features[c] = f;
1023 addNameIdMapping(c, f.name);
1028 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1030 std::unordered_map<std::string, content_t>::const_iterator
1031 i = m_name_id_mapping_with_aliases.find(name);
1032 if(i == m_name_id_mapping_with_aliases.end())
1039 content_t NodeDefManager::getId(const std::string &name) const
1041 content_t id = CONTENT_IGNORE;
1047 bool NodeDefManager::getIds(const std::string &name,
1048 std::vector<content_t> &result) const
1050 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1051 if (name.substr(0,6) != "group:") {
1052 content_t id = CONTENT_IGNORE;
1053 bool exists = getId(name, id);
1055 result.push_back(id);
1058 std::string group = name.substr(6);
1060 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1061 i = m_group_to_items.find(group);
1062 if (i == m_group_to_items.end())
1065 const std::vector<content_t> &items = i->second;
1066 result.insert(result.end(), items.begin(), items.end());
1067 //printf("getIds: %dus\n", t.stop());
1072 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1074 content_t id = CONTENT_UNKNOWN;
1080 // returns CONTENT_IGNORE if no free ID found
1081 content_t NodeDefManager::allocateId()
1083 for (content_t id = m_next_id;
1084 id >= m_next_id; // overflow?
1086 while (id >= m_content_features.size()) {
1087 m_content_features.emplace_back();
1089 const ContentFeatures &f = m_content_features[id];
1090 if (f.name.empty()) {
1095 // If we arrive here, an overflow occurred in id.
1096 // That means no ID was found
1097 return CONTENT_IGNORE;
1102 * Returns the smallest box that contains all boxes
1103 * in the vector. Box_union is expanded.
1104 * @param[in] boxes the vector containing the boxes
1105 * @param[in, out] box_union the union of the arguments
1107 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1109 for (const aabb3f &box : boxes) {
1110 box_union->addInternalBox(box);
1116 * Returns a box that contains the nodebox in every case.
1117 * The argument node_union is expanded.
1118 * @param[in] nodebox the nodebox to be measured
1119 * @param[in] features used to decide whether the nodebox
1121 * @param[in, out] box_union the union of the arguments
1123 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1126 switch(nodebox.type) {
1128 case NODEBOX_LEVELED: {
1130 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1131 boxVectorUnion(nodebox.fixed, &half_processed);
1132 // Set leveled boxes to maximal
1133 if (nodebox.type == NODEBOX_LEVELED) {
1134 half_processed.MaxEdge.Y = +BS / 2;
1136 if (features.param_type_2 == CPT2_FACEDIR ||
1137 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1138 // Get maximal coordinate
1140 fabsf(half_processed.MinEdge.X),
1141 fabsf(half_processed.MinEdge.Y),
1142 fabsf(half_processed.MinEdge.Z),
1143 fabsf(half_processed.MaxEdge.X),
1144 fabsf(half_processed.MaxEdge.Y),
1145 fabsf(half_processed.MaxEdge.Z) };
1147 for (float coord : coords) {
1152 // Add the union of all possible rotated boxes
1153 box_union->addInternalPoint(-max, -max, -max);
1154 box_union->addInternalPoint(+max, +max, +max);
1156 box_union->addInternalBox(half_processed);
1160 case NODEBOX_WALLMOUNTED: {
1162 box_union->addInternalBox(nodebox.wall_top);
1163 box_union->addInternalBox(nodebox.wall_bottom);
1164 // Find maximal coordinate in the X-Z plane
1166 fabsf(nodebox.wall_side.MinEdge.X),
1167 fabsf(nodebox.wall_side.MinEdge.Z),
1168 fabsf(nodebox.wall_side.MaxEdge.X),
1169 fabsf(nodebox.wall_side.MaxEdge.Z) };
1171 for (float coord : coords) {
1176 // Add the union of all possible rotated boxes
1177 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1178 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1181 case NODEBOX_CONNECTED: {
1182 // Add all possible connected boxes
1183 boxVectorUnion(nodebox.fixed, box_union);
1184 boxVectorUnion(nodebox.connect_top, box_union);
1185 boxVectorUnion(nodebox.connect_bottom, box_union);
1186 boxVectorUnion(nodebox.connect_front, box_union);
1187 boxVectorUnion(nodebox.connect_left, box_union);
1188 boxVectorUnion(nodebox.connect_back, box_union);
1189 boxVectorUnion(nodebox.connect_right, box_union);
1190 boxVectorUnion(nodebox.disconnected_top, box_union);
1191 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1192 boxVectorUnion(nodebox.disconnected_front, box_union);
1193 boxVectorUnion(nodebox.disconnected_left, box_union);
1194 boxVectorUnion(nodebox.disconnected_back, box_union);
1195 boxVectorUnion(nodebox.disconnected_right, box_union);
1196 boxVectorUnion(nodebox.disconnected, box_union);
1197 boxVectorUnion(nodebox.disconnected_sides, box_union);
1202 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1203 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1209 inline void NodeDefManager::fixSelectionBoxIntUnion()
1211 m_selection_box_int_union.MinEdge.X = floorf(
1212 m_selection_box_union.MinEdge.X / BS + 0.5f);
1213 m_selection_box_int_union.MinEdge.Y = floorf(
1214 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1215 m_selection_box_int_union.MinEdge.Z = floorf(
1216 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1217 m_selection_box_int_union.MaxEdge.X = ceilf(
1218 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1219 m_selection_box_int_union.MaxEdge.Y = ceilf(
1220 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1221 m_selection_box_int_union.MaxEdge.Z = ceilf(
1222 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1226 // IWritableNodeDefManager
1227 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1231 assert(name != "ignore");
1232 assert(name == def.name);
1234 content_t id = CONTENT_IGNORE;
1235 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1238 if (id == CONTENT_IGNORE) {
1239 warningstream << "NodeDefManager: Absolute "
1240 "limit reached" << std::endl;
1241 return CONTENT_IGNORE;
1243 assert(id != CONTENT_IGNORE);
1244 addNameIdMapping(id, name);
1246 m_content_features[id] = def;
1247 verbosestream << "NodeDefManager: registering content id \"" << id
1248 << "\": name=\"" << def.name << "\""<<std::endl;
1250 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1251 fixSelectionBoxIntUnion();
1252 // Add this content to the list of all groups it belongs to
1253 // FIXME: This should remove a node from groups it no longer
1254 // belongs to when a node is re-registered
1255 for (const auto &group : def.groups) {
1256 const std::string &group_name = group.first;
1257 m_group_to_items[group_name].push_back(id);
1263 content_t NodeDefManager::allocateDummy(const std::string &name)
1265 assert(name != ""); // Pre-condition
1268 return set(name, f);
1272 void NodeDefManager::removeNode(const std::string &name)
1277 // Erase name from name ID mapping
1278 content_t id = CONTENT_IGNORE;
1279 if (m_name_id_mapping.getId(name, id)) {
1280 m_name_id_mapping.eraseName(name);
1281 m_name_id_mapping_with_aliases.erase(name);
1284 // Erase node content from all groups it belongs to
1285 for (std::unordered_map<std::string, std::vector<content_t>>::iterator iter_groups =
1286 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1287 std::vector<content_t> &items = iter_groups->second;
1288 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1290 // Check if group is empty
1292 m_group_to_items.erase(iter_groups++);
1299 void NodeDefManager::updateAliases(IItemDefManager *idef)
1301 std::set<std::string> all;
1303 m_name_id_mapping_with_aliases.clear();
1304 for (const std::string &name : all) {
1305 const std::string &convert_to = idef->getAlias(name);
1307 if (m_name_id_mapping.getId(convert_to, id)) {
1308 m_name_id_mapping_with_aliases.insert(
1309 std::make_pair(name, id));
1314 void NodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1316 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1317 "overrides to textures from " << override_filepath << std::endl;
1319 std::ifstream infile(override_filepath.c_str());
1322 while (std::getline(infile, line)) {
1324 if (trim(line).empty())
1326 std::vector<std::string> splitted = str_split(line, ' ');
1327 if (splitted.size() != 3) {
1328 errorstream << override_filepath
1329 << ":" << line_c << " Could not apply texture override \""
1330 << line << "\": Syntax error" << std::endl;
1335 if (!getId(splitted[0], id))
1336 continue; // Ignore unknown node
1338 ContentFeatures &nodedef = m_content_features[id];
1340 if (splitted[1] == "top")
1341 nodedef.tiledef[0].name = splitted[2];
1342 else if (splitted[1] == "bottom")
1343 nodedef.tiledef[1].name = splitted[2];
1344 else if (splitted[1] == "right")
1345 nodedef.tiledef[2].name = splitted[2];
1346 else if (splitted[1] == "left")
1347 nodedef.tiledef[3].name = splitted[2];
1348 else if (splitted[1] == "back")
1349 nodedef.tiledef[4].name = splitted[2];
1350 else if (splitted[1] == "front")
1351 nodedef.tiledef[5].name = splitted[2];
1352 else if (splitted[1] == "all" || splitted[1] == "*")
1353 for (TileDef &i : nodedef.tiledef)
1354 i.name = splitted[2];
1355 else if (splitted[1] == "sides")
1356 for (int i = 2; i < 6; i++)
1357 nodedef.tiledef[i].name = splitted[2];
1359 errorstream << override_filepath
1360 << ":" << line_c << " Could not apply texture override \""
1361 << line << "\": Unknown node side \""
1362 << splitted[1] << "\"" << std::endl;
1368 void NodeDefManager::updateTextures(IGameDef *gamedef,
1369 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1370 void *progress_callback_args)
1373 infostream << "NodeDefManager::updateTextures(): Updating "
1374 "textures in node definitions" << std::endl;
1376 Client *client = (Client *)gamedef;
1377 ITextureSource *tsrc = client->tsrc();
1378 IShaderSource *shdsrc = client->getShaderSource();
1379 scene::IMeshManipulator *meshmanip =
1380 RenderingEngine::get_scene_manager()->getMeshManipulator();
1381 TextureSettings tsettings;
1382 tsettings.readSettings();
1384 u32 size = m_content_features.size();
1386 for (u32 i = 0; i < size; i++) {
1387 ContentFeatures *f = &(m_content_features[i]);
1388 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1389 progress_callback(progress_callback_args, i, size);
1394 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1396 writeU8(os, 1); // version
1398 std::ostringstream os2(std::ios::binary);
1399 for (u32 i = 0; i < m_content_features.size(); i++) {
1400 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1401 || i == CONTENT_UNKNOWN)
1403 const ContentFeatures *f = &m_content_features[i];
1404 if (f->name.empty())
1407 // Wrap it in a string to allow different lengths without
1408 // strict version incompatibilities
1409 std::ostringstream wrapper_os(std::ios::binary);
1410 f->serialize(wrapper_os, protocol_version);
1411 os2<<serializeString(wrapper_os.str());
1413 // must not overflow
1414 u16 next = count + 1;
1415 FATAL_ERROR_IF(next < count, "Overflow");
1418 writeU16(os, count);
1419 os << serializeLongString(os2.str());
1423 void NodeDefManager::deSerialize(std::istream &is)
1426 int version = readU8(is);
1428 throw SerializationError("unsupported NodeDefinitionManager version");
1429 u16 count = readU16(is);
1430 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1432 for (u16 n = 0; n < count; n++) {
1433 u16 i = readU16(is2);
1435 // Read it from the string wrapper
1436 std::string wrapper = deSerializeString(is2);
1437 std::istringstream wrapper_is(wrapper, std::ios::binary);
1438 f.deSerialize(wrapper_is);
1440 // Check error conditions
1441 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1442 warningstream << "NodeDefManager::deSerialize(): "
1443 "not changing builtin node " << i << std::endl;
1446 if (f.name.empty()) {
1447 warningstream << "NodeDefManager::deSerialize(): "
1448 "received empty name" << std::endl;
1454 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1455 warningstream << "NodeDefManager::deSerialize(): "
1456 "already defined with different ID: " << f.name << std::endl;
1460 // All is ok, add node definition with the requested ID
1461 if (i >= m_content_features.size())
1462 m_content_features.resize((u32)(i) + 1);
1463 m_content_features[i] = f;
1464 addNameIdMapping(i, f.name);
1465 verbosestream << "deserialized " << f.name << std::endl;
1467 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1468 fixSelectionBoxIntUnion();
1473 void NodeDefManager::addNameIdMapping(content_t i, std::string name)
1475 m_name_id_mapping.set(i, name);
1476 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1480 NodeDefManager *createNodeDefManager()
1482 return new NodeDefManager();
1486 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1489 if (m_node_registration_complete)
1490 nr->nodeResolveInternal();
1492 m_pending_resolve_callbacks.push_back(nr);
1496 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1498 size_t len = m_pending_resolve_callbacks.size();
1499 for (size_t i = 0; i != len; i++) {
1500 if (nr != m_pending_resolve_callbacks[i])
1504 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1505 m_pending_resolve_callbacks.resize(len);
1513 void NodeDefManager::runNodeResolveCallbacks()
1515 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1516 NodeResolver *nr = m_pending_resolve_callbacks[i];
1517 nr->nodeResolveInternal();
1520 m_pending_resolve_callbacks.clear();
1524 void NodeDefManager::resetNodeResolveState()
1526 m_node_registration_complete = false;
1527 m_pending_resolve_callbacks.clear();
1530 void NodeDefManager::mapNodeboxConnections()
1532 for (ContentFeatures &f : m_content_features) {
1533 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1536 for (const std::string &name : f.connects_to) {
1537 getIds(name, f.connects_to_ids);
1542 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1543 u8 connect_face) const
1545 const ContentFeatures &f1 = get(from);
1547 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1550 // lookup target in connected set
1551 if (!CONTAINS(f1.connects_to_ids, to.param0))
1554 const ContentFeatures &f2 = get(to);
1556 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1557 // ignores actually looking if back connection exists
1558 return CONTAINS(f2.connects_to_ids, from.param0);
1560 // does to node declare usable faces?
1561 if (f2.connect_sides > 0) {
1562 if ((f2.param_type_2 == CPT2_FACEDIR ||
1563 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1564 && (connect_face >= 4)) {
1565 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1566 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1568 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1570 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1571 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1573 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1574 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1575 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1577 return (f2.connect_sides
1578 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1580 return (f2.connect_sides & connect_face);
1582 // the target is just a regular node, so connect no matter back connection
1590 NodeResolver::NodeResolver()
1592 m_nodenames.reserve(16);
1593 m_nnlistsizes.reserve(4);
1597 NodeResolver::~NodeResolver()
1599 if (!m_resolve_done && m_ndef)
1600 m_ndef->cancelNodeResolveCallback(this);
1604 void NodeResolver::nodeResolveInternal()
1606 m_nodenames_idx = 0;
1607 m_nnlistsizes_idx = 0;
1610 m_resolve_done = true;
1612 m_nodenames.clear();
1613 m_nnlistsizes.clear();
1617 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1618 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1620 if (m_nodenames_idx == m_nodenames.size()) {
1621 *result_out = c_fallback;
1622 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1627 std::string name = m_nodenames[m_nodenames_idx++];
1629 bool success = m_ndef->getId(name, c);
1630 if (!success && !node_alt.empty()) {
1632 success = m_ndef->getId(name, c);
1636 if (error_on_fallback)
1637 errorstream << "NodeResolver: failed to resolve node name '" << name
1638 << "'." << std::endl;
1647 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1648 bool all_required, content_t c_fallback)
1650 bool success = true;
1652 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1653 errorstream << "NodeResolver: no more node lists" << std::endl;
1657 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1660 if (m_nodenames_idx == m_nodenames.size()) {
1661 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1666 std::string &name = m_nodenames[m_nodenames_idx++];
1668 if (name.substr(0,6) != "group:") {
1669 if (m_ndef->getId(name, c)) {
1670 result_out->push_back(c);
1671 } else if (all_required) {
1672 errorstream << "NodeResolver: failed to resolve node name '"
1673 << name << "'." << std::endl;
1674 result_out->push_back(c_fallback);
1678 m_ndef->getIds(name, *result_out);