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.
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()
48 type = NODEBOX_REGULAR;
51 // default is sign/ladder-like
52 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
53 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
54 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
55 // no default for other parts
57 connect_bottom.clear();
58 connect_front.clear();
61 connect_right.clear();
64 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
75 writeU16(os, fixed.size());
76 for (const aabb3f &nodebox : fixed) {
77 writeV3F1000(os, nodebox.MinEdge);
78 writeV3F1000(os, nodebox.MaxEdge);
81 case NODEBOX_WALLMOUNTED:
84 writeV3F1000(os, wall_top.MinEdge);
85 writeV3F1000(os, wall_top.MaxEdge);
86 writeV3F1000(os, wall_bottom.MinEdge);
87 writeV3F1000(os, wall_bottom.MaxEdge);
88 writeV3F1000(os, wall_side.MinEdge);
89 writeV3F1000(os, wall_side.MaxEdge);
91 case NODEBOX_CONNECTED:
94 #define WRITEBOX(box) \
95 writeU16(os, (box).size()); \
96 for (const aabb3f &i: (box)) { \
97 writeV3F1000(os, i.MinEdge); \
98 writeV3F1000(os, i.MaxEdge); \
102 WRITEBOX(connect_top);
103 WRITEBOX(connect_bottom);
104 WRITEBOX(connect_front);
105 WRITEBOX(connect_left);
106 WRITEBOX(connect_back);
107 WRITEBOX(connect_right);
115 void NodeBox::deSerialize(std::istream &is)
117 int version = readU8(is);
119 throw SerializationError("unsupported NodeBox version");
123 type = (enum NodeBoxType)readU8(is);
125 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
127 u16 fixed_count = readU16(is);
131 box.MinEdge = readV3F1000(is);
132 box.MaxEdge = readV3F1000(is);
133 fixed.push_back(box);
136 else if(type == NODEBOX_WALLMOUNTED)
138 wall_top.MinEdge = readV3F1000(is);
139 wall_top.MaxEdge = readV3F1000(is);
140 wall_bottom.MinEdge = readV3F1000(is);
141 wall_bottom.MaxEdge = readV3F1000(is);
142 wall_side.MinEdge = readV3F1000(is);
143 wall_side.MaxEdge = readV3F1000(is);
145 else if (type == NODEBOX_CONNECTED)
147 #define READBOXES(box) { \
148 count = readU16(is); \
149 (box).reserve(count); \
151 v3f min = readV3F1000(is); \
152 v3f max = readV3F1000(is); \
153 (box).emplace_back(min, max); }; }
158 READBOXES(connect_top);
159 READBOXES(connect_bottom);
160 READBOXES(connect_front);
161 READBOXES(connect_left);
162 READBOXES(connect_back);
163 READBOXES(connect_right);
171 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
173 // protocol_version >= 36
175 writeU8(os, version);
177 os << serializeString(name);
178 animation.serialize(os, version);
179 writeU8(os, backface_culling);
180 writeU8(os, tileable_horizontal);
181 writeU8(os, tileable_vertical);
182 writeU8(os, has_color);
184 writeU8(os, color.getRed());
185 writeU8(os, color.getGreen());
186 writeU8(os, color.getBlue());
190 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
191 NodeDrawType drawtype)
193 int version = readU8(is);
194 name = deSerializeString(is);
195 animation.deSerialize(is, version);
196 backface_culling = readU8(is);
197 tileable_horizontal = readU8(is);
198 tileable_vertical = readU8(is);
199 has_color = readU8(is);
201 color.setRed(readU8(is));
202 color.setGreen(readU8(is));
203 color.setBlue(readU8(is));
209 SimpleSoundSpec serialization
212 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
213 std::ostream &os, u8 version)
215 os<<serializeString(ss.name);
216 writeF1000(os, ss.gain);
217 writeF1000(os, ss.pitch);
219 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss,
220 std::istream &is, u8 version)
222 ss.name = deSerializeString(is);
223 ss.gain = readF1000(is);
224 ss.pitch = readF1000(is);
227 void TextureSettings::readSettings()
229 connected_glass = g_settings->getBool("connected_glass");
230 opaque_water = g_settings->getBool("opaque_water");
231 bool enable_shaders = g_settings->getBool("enable_shaders");
232 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
233 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
234 bool smooth_lighting = g_settings->getBool("smooth_lighting");
235 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
236 enable_minimap = g_settings->getBool("enable_minimap");
237 std::string leaves_style_str = g_settings->get("leaves_style");
239 // Mesh cache is not supported in combination with smooth lighting
241 enable_mesh_cache = false;
243 use_normal_texture = enable_shaders &&
244 (enable_bumpmapping || enable_parallax_occlusion);
245 if (leaves_style_str == "fancy") {
246 leaves_style = LEAVES_FANCY;
247 } else if (leaves_style_str == "simple") {
248 leaves_style = LEAVES_SIMPLE;
250 leaves_style = LEAVES_OPAQUE;
258 ContentFeatures::ContentFeatures()
263 void ContentFeatures::reset()
270 visual_solidness = 0;
271 backface_culling = true;
274 has_on_construct = false;
275 has_on_destruct = false;
276 has_after_destruct = false;
280 NOTE: Most of this is always overridden by the default values given
285 // Unknown nodes can be dug
286 groups["dig_immediate"] = 2;
287 drawtype = NDT_NORMAL;
290 for (auto &i : mesh_ptr)
292 minimap_color = video::SColor(0, 0, 0, 0);
295 for (auto &i : tiledef)
297 for (auto &j : tiledef_special)
300 post_effect_color = video::SColor(0, 0, 0, 0);
301 param_type = CPT_NONE;
302 param_type_2 = CPT2_NONE;
303 is_ground_content = false;
304 light_propagates = false;
305 sunlight_propagates = false;
310 buildable_to = false;
312 rightclickable = true;
314 liquid_type = LIQUID_NONE;
315 liquid_alternative_flowing = "";
316 liquid_alternative_source = "";
317 liquid_viscosity = 0;
318 liquid_renewable = true;
319 liquid_range = LIQUID_LEVEL_MAX+1;
322 damage_per_second = 0;
323 node_box = NodeBox();
324 selection_box = NodeBox();
325 collision_box = NodeBox();
327 legacy_facedir_simple = false;
328 legacy_wallmounted = false;
329 sound_footstep = SimpleSoundSpec();
330 sound_dig = SimpleSoundSpec("__group");
331 sound_dug = SimpleSoundSpec();
333 connects_to_ids.clear();
335 color = video::SColor(0xFFFFFFFF);
338 node_dig_prediction = "air";
341 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
343 // protocol_version >= 36
345 writeU8(os, version);
348 os << serializeString(name);
349 writeU16(os, groups.size());
350 for (const auto &group : groups) {
351 os << serializeString(group.first);
352 writeS16(os, group.second);
354 writeU8(os, param_type);
355 writeU8(os, param_type_2);
358 writeU8(os, drawtype);
359 os << serializeString(mesh);
360 writeF1000(os, visual_scale);
362 for (const TileDef &td : tiledef)
363 td.serialize(os, protocol_version);
364 for (const TileDef &td : tiledef_overlay)
365 td.serialize(os, protocol_version);
366 writeU8(os, CF_SPECIAL_COUNT);
367 for (const TileDef &td : tiledef_special) {
368 td.serialize(os, protocol_version);
371 writeU8(os, color.getRed());
372 writeU8(os, color.getGreen());
373 writeU8(os, color.getBlue());
374 os << serializeString(palette_name);
376 writeU8(os, connect_sides);
377 writeU16(os, connects_to_ids.size());
378 for (u16 connects_to_id : connects_to_ids)
379 writeU16(os, connects_to_id);
380 writeU8(os, post_effect_color.getAlpha());
381 writeU8(os, post_effect_color.getRed());
382 writeU8(os, post_effect_color.getGreen());
383 writeU8(os, post_effect_color.getBlue());
384 writeU8(os, leveled);
387 writeU8(os, light_propagates);
388 writeU8(os, sunlight_propagates);
389 writeU8(os, light_source);
392 writeU8(os, is_ground_content);
395 writeU8(os, walkable);
396 writeU8(os, pointable);
397 writeU8(os, diggable);
398 writeU8(os, climbable);
399 writeU8(os, buildable_to);
400 writeU8(os, rightclickable);
401 writeU32(os, damage_per_second);
404 writeU8(os, liquid_type);
405 os << serializeString(liquid_alternative_flowing);
406 os << serializeString(liquid_alternative_source);
407 writeU8(os, liquid_viscosity);
408 writeU8(os, liquid_renewable);
409 writeU8(os, liquid_range);
410 writeU8(os, drowning);
411 writeU8(os, floodable);
414 node_box.serialize(os, protocol_version);
415 selection_box.serialize(os, protocol_version);
416 collision_box.serialize(os, protocol_version);
419 serializeSimpleSoundSpec(sound_footstep, os, version);
420 serializeSimpleSoundSpec(sound_dig, os, version);
421 serializeSimpleSoundSpec(sound_dug, os, version);
424 writeU8(os, legacy_facedir_simple);
425 writeU8(os, legacy_wallmounted);
427 os << serializeString(node_dig_prediction);
430 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
432 // alpha == 0 means that the node is using texture alpha
433 if (alpha == 0 || alpha == 255)
436 for (int i = 0; i < length; i++) {
437 if (tiles[i].name.empty())
440 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
441 tiles[i].name = s.str();
445 void ContentFeatures::deSerialize(std::istream &is)
448 int version = readU8(is);
450 throw SerializationError("unsupported ContentFeatures version");
453 name = deSerializeString(is);
455 u32 groups_size = readU16(is);
456 for (u32 i = 0; i < groups_size; i++) {
457 std::string name = deSerializeString(is);
458 int value = readS16(is);
459 groups[name] = value;
461 param_type = (enum ContentParamType) readU8(is);
462 param_type_2 = (enum ContentParamType2) readU8(is);
465 drawtype = (enum NodeDrawType) readU8(is);
466 mesh = deSerializeString(is);
467 visual_scale = readF1000(is);
469 throw SerializationError("unsupported tile count");
470 for (TileDef &td : tiledef)
471 td.deSerialize(is, version, drawtype);
472 for (TileDef &td : tiledef_overlay)
473 td.deSerialize(is, version, drawtype);
474 if (readU8(is) != CF_SPECIAL_COUNT)
475 throw SerializationError("unsupported CF_SPECIAL_COUNT");
476 for (TileDef &td : tiledef_special)
477 td.deSerialize(is, version, drawtype);
479 color.setRed(readU8(is));
480 color.setGreen(readU8(is));
481 color.setBlue(readU8(is));
482 palette_name = deSerializeString(is);
484 connect_sides = readU8(is);
485 u16 connects_to_size = readU16(is);
486 connects_to_ids.clear();
487 for (u16 i = 0; i < connects_to_size; i++)
488 connects_to_ids.insert(readU16(is));
489 post_effect_color.setAlpha(readU8(is));
490 post_effect_color.setRed(readU8(is));
491 post_effect_color.setGreen(readU8(is));
492 post_effect_color.setBlue(readU8(is));
493 leveled = readU8(is);
496 light_propagates = readU8(is);
497 sunlight_propagates = readU8(is);
498 light_source = readU8(is);
499 light_source = MYMIN(light_source, LIGHT_MAX);
502 is_ground_content = readU8(is);
505 walkable = readU8(is);
506 pointable = readU8(is);
507 diggable = readU8(is);
508 climbable = readU8(is);
509 buildable_to = readU8(is);
510 rightclickable = readU8(is);
511 damage_per_second = readU32(is);
514 liquid_type = (enum LiquidType) readU8(is);
515 liquid_alternative_flowing = deSerializeString(is);
516 liquid_alternative_source = deSerializeString(is);
517 liquid_viscosity = readU8(is);
518 liquid_renewable = readU8(is);
519 liquid_range = readU8(is);
520 drowning = readU8(is);
521 floodable = readU8(is);
524 node_box.deSerialize(is);
525 selection_box.deSerialize(is);
526 collision_box.deSerialize(is);
529 deSerializeSimpleSoundSpec(sound_footstep, is, version);
530 deSerializeSimpleSoundSpec(sound_dig, is, version);
531 deSerializeSimpleSoundSpec(sound_dug, is, version);
533 // read legacy properties
534 legacy_facedir_simple = readU8(is);
535 legacy_wallmounted = readU8(is);
538 node_dig_prediction = deSerializeString(is);
539 } catch(SerializationError &e) {};
543 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
544 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
545 bool backface_culling, u8 material_type)
547 tile->shader_id = shader_id;
548 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
549 tile->material_type = material_type;
551 // Normal texture and shader flags texture
552 if (use_normal_texture) {
553 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
555 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
558 tile->material_flags = 0;
559 if (backface_culling)
560 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
561 if (tiledef->animation.type != TAT_NONE)
562 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
563 if (tiledef->tileable_horizontal)
564 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
565 if (tiledef->tileable_vertical)
566 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
569 tile->has_color = tiledef->has_color;
570 if (tiledef->has_color)
571 tile->color = tiledef->color;
575 // Animation parameters
577 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
579 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
580 &frame_count, &frame_length_ms, NULL);
581 tile->animation_frame_count = frame_count;
582 tile->animation_frame_length_ms = frame_length_ms;
585 if (frame_count == 1) {
586 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
588 std::ostringstream os(std::ios::binary);
590 tile->frames = std::make_shared<std::vector<FrameSpec>>();
592 tile->frames->resize(frame_count);
594 for (int i = 0; i < frame_count; i++) {
600 tiledef->animation.getTextureModifer(os,
601 tile->texture->getOriginalSize(), i);
603 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
604 if (tile->normal_texture)
605 frame.normal_texture = tsrc->getNormalTexture(os.str());
606 frame.flags_texture = tile->flags_texture;
607 (*tile->frames)[i] = frame;
614 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
615 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
617 // minimap pixel color - the average color of a texture
618 if (tsettings.enable_minimap && !tiledef[0].name.empty())
619 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
621 // Figure out the actual tiles to use
623 for (u32 j = 0; j < 6; j++) {
624 tdef[j] = tiledef[j];
625 if (tdef[j].name.empty())
626 tdef[j].name = "unknown_node.png";
628 // also the overlay tiles
629 TileDef tdef_overlay[6];
630 for (u32 j = 0; j < 6; j++)
631 tdef_overlay[j] = tiledef_overlay[j];
632 // also the special tiles
633 TileDef tdef_spec[6];
634 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
635 tdef_spec[j] = tiledef_special[j];
637 bool is_liquid = false;
639 u8 material_type = (alpha == 255) ?
640 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
645 material_type = (alpha == 255) ?
646 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
653 assert(liquid_type == LIQUID_SOURCE);
654 if (tsettings.opaque_water)
659 case NDT_FLOWINGLIQUID:
660 assert(liquid_type == LIQUID_FLOWING);
662 if (tsettings.opaque_water)
668 visual_solidness = 1;
670 case NDT_GLASSLIKE_FRAMED:
672 visual_solidness = 1;
674 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
676 visual_solidness = 1;
677 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
681 visual_solidness = 1;
683 case NDT_ALLFACES_OPTIONAL:
684 if (tsettings.leaves_style == LEAVES_FANCY) {
685 drawtype = NDT_ALLFACES;
687 visual_solidness = 1;
688 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
689 for (u32 j = 0; j < 6; j++) {
690 if (!tdef_spec[j].name.empty())
691 tdef[j].name = tdef_spec[j].name;
693 drawtype = NDT_GLASSLIKE;
695 visual_solidness = 1;
697 drawtype = NDT_NORMAL;
699 for (TileDef &td : tdef)
700 td.name += std::string("^[noalpha");
703 material_type = TILE_MATERIAL_WAVING_LEAVES;
708 material_type = TILE_MATERIAL_WAVING_PLANTS;
717 material_type = TILE_MATERIAL_WAVING_PLANTS;
718 else if (waving == 2)
719 material_type = TILE_MATERIAL_WAVING_LEAVES;
727 case NDT_PLANTLIKE_ROOTED:
733 // Vertex alpha is no longer supported, correct if necessary.
734 correctAlpha(tdef, 6);
735 correctAlpha(tdef_overlay, 6);
736 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
737 material_type = (alpha == 255) ?
738 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
741 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
743 u8 overlay_material = material_type;
744 if (overlay_material == TILE_MATERIAL_OPAQUE)
745 overlay_material = TILE_MATERIAL_BASIC;
746 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
747 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
749 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
751 // Tiles (fill in f->tiles[])
752 for (u16 j = 0; j < 6; j++) {
753 fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader,
754 tsettings.use_normal_texture,
755 tdef[j].backface_culling, material_type);
756 if (!tdef_overlay[j].name.empty())
757 fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j],
758 overlay_shader, tsettings.use_normal_texture,
759 tdef[j].backface_culling, overlay_material);
762 u8 special_material = material_type;
763 if (drawtype == NDT_PLANTLIKE_ROOTED) {
765 special_material = TILE_MATERIAL_WAVING_PLANTS;
766 else if (waving == 2)
767 special_material = TILE_MATERIAL_WAVING_LEAVES;
769 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
771 // Special tiles (fill in f->special_tiles[])
772 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
773 fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j],
774 special_shader, tsettings.use_normal_texture,
775 tdef_spec[j].backface_culling, special_material);
778 if (param_type_2 == CPT2_COLOR ||
779 param_type_2 == CPT2_COLORED_FACEDIR ||
780 param_type_2 == CPT2_COLORED_WALLMOUNTED)
781 palette = tsrc->getPalette(palette_name);
783 if (drawtype == NDT_MESH && !mesh.empty()) {
785 // Read the mesh and apply scale
786 mesh_ptr[0] = client->getMesh(mesh);
788 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
789 scaleMesh(mesh_ptr[0], scale);
790 recalculateBoundingBox(mesh_ptr[0]);
791 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
793 } else if ((drawtype == NDT_NODEBOX) &&
794 ((node_box.type == NODEBOX_REGULAR) ||
795 (node_box.type == NODEBOX_FIXED)) &&
796 (!node_box.fixed.empty())) {
797 //Convert regular nodebox nodes to meshnodes
798 //Change the drawtype and apply scale
800 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
801 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
802 scaleMesh(mesh_ptr[0], scale);
803 recalculateBoundingBox(mesh_ptr[0]);
804 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
807 //Cache 6dfacedir and wallmounted rotated clones of meshes
808 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
809 (param_type_2 == CPT2_FACEDIR
810 || param_type_2 == CPT2_COLORED_FACEDIR)) {
811 for (u16 j = 1; j < 24; j++) {
812 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
813 rotateMeshBy6dFacedir(mesh_ptr[j], j);
814 recalculateBoundingBox(mesh_ptr[j]);
815 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
817 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
818 && (param_type_2 == CPT2_WALLMOUNTED ||
819 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
820 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
821 for (u16 j = 1; j < 6; j++) {
822 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
823 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
824 recalculateBoundingBox(mesh_ptr[j]);
825 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
827 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
828 recalculateBoundingBox(mesh_ptr[0]);
829 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
838 class CNodeDefManager: public IWritableNodeDefManager {
841 virtual ~CNodeDefManager();
844 inline virtual const ContentFeatures& get(content_t c) const;
845 inline virtual const ContentFeatures& get(const MapNode &n) const;
846 virtual bool getId(const std::string &name, content_t &result) const;
847 virtual content_t getId(const std::string &name) const;
848 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
849 virtual const ContentFeatures& get(const std::string &name) const;
850 content_t allocateId();
851 virtual content_t set(const std::string &name, const ContentFeatures &def);
852 virtual content_t allocateDummy(const std::string &name);
853 virtual void removeNode(const std::string &name);
854 virtual void updateAliases(IItemDefManager *idef);
855 virtual void applyTextureOverrides(const std::string &override_filepath);
856 virtual void updateTextures(IGameDef *gamedef,
857 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
858 void *progress_cbk_args);
859 void serialize(std::ostream &os, u16 protocol_version) const;
860 void deSerialize(std::istream &is);
862 inline virtual void setNodeRegistrationStatus(bool completed);
864 virtual void pendNodeResolve(NodeResolver *nr);
865 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
866 virtual void runNodeResolveCallbacks();
867 virtual void resetNodeResolveState();
868 virtual void mapNodeboxConnections();
869 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
870 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
872 return m_selection_box_int_union;
876 void addNameIdMapping(content_t i, std::string name);
878 * Recalculates m_selection_box_int_union based on
879 * m_selection_box_union.
881 void fixSelectionBoxIntUnion();
883 // Features indexed by id
884 std::vector<ContentFeatures> m_content_features;
886 // A mapping for fast converting back and forth between names and ids
887 NameIdMapping m_name_id_mapping;
889 // Like m_name_id_mapping, but only from names to ids, and includes
890 // item aliases too. Updated by updateAliases()
891 // Note: Not serialized.
893 std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases;
895 // A mapping from groups to a list of content_ts (and their levels)
896 // that belong to it. Necessary for a direct lookup in getIds().
897 // Note: Not serialized.
898 std::unordered_map<std::string, GroupItems> m_group_to_items;
900 // Next possibly free id
903 // NodeResolvers to callback once node registration has ended
904 std::vector<NodeResolver *> m_pending_resolve_callbacks;
906 // True when all nodes have been registered
907 bool m_node_registration_complete;
909 //! The union of all nodes' selection boxes.
910 aabb3f m_selection_box_union;
912 * The smallest box in node coordinates that
913 * contains all nodes' selection boxes.
915 core::aabbox3d<s16> m_selection_box_int_union;
919 CNodeDefManager::CNodeDefManager()
925 CNodeDefManager::~CNodeDefManager()
928 for (ContentFeatures &f : m_content_features) {
929 for (auto &j : f.mesh_ptr) {
938 void CNodeDefManager::clear()
940 m_content_features.clear();
941 m_name_id_mapping.clear();
942 m_name_id_mapping_with_aliases.clear();
943 m_group_to_items.clear();
945 m_selection_box_union.reset(0,0,0);
946 m_selection_box_int_union.reset(0,0,0);
948 resetNodeResolveState();
950 u32 initial_length = 0;
951 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
952 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
953 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
954 m_content_features.resize(initial_length);
956 // Set CONTENT_UNKNOWN
960 // Insert directly into containers
961 content_t c = CONTENT_UNKNOWN;
962 m_content_features[c] = f;
963 addNameIdMapping(c, f.name);
970 f.drawtype = NDT_AIRLIKE;
971 f.param_type = CPT_LIGHT;
972 f.light_propagates = true;
973 f.sunlight_propagates = true;
977 f.buildable_to = true;
979 f.is_ground_content = true;
980 // Insert directly into containers
981 content_t c = CONTENT_AIR;
982 m_content_features[c] = f;
983 addNameIdMapping(c, f.name);
986 // Set CONTENT_IGNORE
990 f.drawtype = NDT_AIRLIKE;
991 f.param_type = CPT_NONE;
992 f.light_propagates = false;
993 f.sunlight_propagates = false;
997 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
998 f.is_ground_content = true;
999 // Insert directly into containers
1000 content_t c = CONTENT_IGNORE;
1001 m_content_features[c] = f;
1002 addNameIdMapping(c, f.name);
1007 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1009 return c < m_content_features.size()
1010 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1014 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1016 return get(n.getContent());
1020 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1022 std::unordered_map<std::string, content_t>::const_iterator
1023 i = m_name_id_mapping_with_aliases.find(name);
1024 if(i == m_name_id_mapping_with_aliases.end())
1031 content_t CNodeDefManager::getId(const std::string &name) const
1033 content_t id = CONTENT_IGNORE;
1039 bool CNodeDefManager::getIds(const std::string &name,
1040 std::set<content_t> &result) const
1042 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1043 if (name.substr(0,6) != "group:") {
1044 content_t id = CONTENT_IGNORE;
1045 bool exists = getId(name, id);
1050 std::string group = name.substr(6);
1052 std::unordered_map<std::string, GroupItems>::const_iterator
1053 i = m_group_to_items.find(group);
1054 if (i == m_group_to_items.end())
1057 const GroupItems &items = i->second;
1058 for (const auto &item : items) {
1059 if (item.second != 0)
1060 result.insert(item.first);
1062 //printf("getIds: %dus\n", t.stop());
1067 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1069 content_t id = CONTENT_UNKNOWN;
1075 // returns CONTENT_IGNORE if no free ID found
1076 content_t CNodeDefManager::allocateId()
1078 for (content_t id = m_next_id;
1079 id >= m_next_id; // overflow?
1081 while (id >= m_content_features.size()) {
1082 m_content_features.emplace_back();
1084 const ContentFeatures &f = m_content_features[id];
1085 if (f.name.empty()) {
1090 // If we arrive here, an overflow occurred in id.
1091 // That means no ID was found
1092 return CONTENT_IGNORE;
1097 * Returns the smallest box that contains all boxes
1098 * in the vector. Box_union is expanded.
1099 * @param[in] boxes the vector containing the boxes
1100 * @param[in, out] box_union the union of the arguments
1102 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1104 for (const aabb3f &box : boxes) {
1105 box_union->addInternalBox(box);
1111 * Returns a box that contains the nodebox in every case.
1112 * The argument node_union is expanded.
1113 * @param[in] nodebox the nodebox to be measured
1114 * @param[in] features used to decide whether the nodebox
1116 * @param[in, out] box_union the union of the arguments
1118 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1121 switch(nodebox.type) {
1123 case NODEBOX_LEVELED: {
1125 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1126 boxVectorUnion(nodebox.fixed, &half_processed);
1127 // Set leveled boxes to maximal
1128 if (nodebox.type == NODEBOX_LEVELED) {
1129 half_processed.MaxEdge.Y = +BS / 2;
1131 if (features.param_type_2 == CPT2_FACEDIR ||
1132 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1133 // Get maximal coordinate
1135 fabsf(half_processed.MinEdge.X),
1136 fabsf(half_processed.MinEdge.Y),
1137 fabsf(half_processed.MinEdge.Z),
1138 fabsf(half_processed.MaxEdge.X),
1139 fabsf(half_processed.MaxEdge.Y),
1140 fabsf(half_processed.MaxEdge.Z) };
1142 for (float coord : coords) {
1147 // Add the union of all possible rotated boxes
1148 box_union->addInternalPoint(-max, -max, -max);
1149 box_union->addInternalPoint(+max, +max, +max);
1151 box_union->addInternalBox(half_processed);
1155 case NODEBOX_WALLMOUNTED: {
1157 box_union->addInternalBox(nodebox.wall_top);
1158 box_union->addInternalBox(nodebox.wall_bottom);
1159 // Find maximal coordinate in the X-Z plane
1161 fabsf(nodebox.wall_side.MinEdge.X),
1162 fabsf(nodebox.wall_side.MinEdge.Z),
1163 fabsf(nodebox.wall_side.MaxEdge.X),
1164 fabsf(nodebox.wall_side.MaxEdge.Z) };
1166 for (float coord : coords) {
1171 // Add the union of all possible rotated boxes
1172 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1173 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1176 case NODEBOX_CONNECTED: {
1177 // Add all possible connected boxes
1178 boxVectorUnion(nodebox.fixed, box_union);
1179 boxVectorUnion(nodebox.connect_top, box_union);
1180 boxVectorUnion(nodebox.connect_bottom, box_union);
1181 boxVectorUnion(nodebox.connect_front, box_union);
1182 boxVectorUnion(nodebox.connect_left, box_union);
1183 boxVectorUnion(nodebox.connect_back, box_union);
1184 boxVectorUnion(nodebox.connect_right, box_union);
1189 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1190 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1196 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1198 m_selection_box_int_union.MinEdge.X = floorf(
1199 m_selection_box_union.MinEdge.X / BS + 0.5f);
1200 m_selection_box_int_union.MinEdge.Y = floorf(
1201 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1202 m_selection_box_int_union.MinEdge.Z = floorf(
1203 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1204 m_selection_box_int_union.MaxEdge.X = ceilf(
1205 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1206 m_selection_box_int_union.MaxEdge.Y = ceilf(
1207 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1208 m_selection_box_int_union.MaxEdge.Z = ceilf(
1209 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1213 // IWritableNodeDefManager
1214 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1218 assert(name == def.name);
1220 // Don't allow redefining ignore (but allow air and unknown)
1221 if (name == "ignore") {
1222 warningstream << "NodeDefManager: Ignoring "
1223 "CONTENT_IGNORE redefinition"<<std::endl;
1224 return CONTENT_IGNORE;
1227 content_t id = CONTENT_IGNORE;
1228 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1231 if (id == CONTENT_IGNORE) {
1232 warningstream << "NodeDefManager: Absolute "
1233 "limit reached" << std::endl;
1234 return CONTENT_IGNORE;
1236 assert(id != CONTENT_IGNORE);
1237 addNameIdMapping(id, name);
1239 m_content_features[id] = def;
1240 verbosestream << "NodeDefManager: registering content id \"" << id
1241 << "\": name=\"" << def.name << "\""<<std::endl;
1243 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1244 fixSelectionBoxIntUnion();
1245 // Add this content to the list of all groups it belongs to
1246 // FIXME: This should remove a node from groups it no longer
1247 // belongs to when a node is re-registered
1248 for (const auto &group : def.groups) {
1249 const std::string &group_name = group.first;
1251 std::unordered_map<std::string, GroupItems>::iterator
1252 j = m_group_to_items.find(group_name);
1253 if (j == m_group_to_items.end()) {
1254 m_group_to_items[group_name].emplace_back(id, group.second);
1256 GroupItems &items = j->second;
1257 items.emplace_back(id, group.second);
1264 content_t CNodeDefManager::allocateDummy(const std::string &name)
1266 assert(name != ""); // Pre-condition
1269 return set(name, f);
1273 void CNodeDefManager::removeNode(const std::string &name)
1278 // Erase name from name ID mapping
1279 content_t id = CONTENT_IGNORE;
1280 if (m_name_id_mapping.getId(name, id)) {
1281 m_name_id_mapping.eraseName(name);
1282 m_name_id_mapping_with_aliases.erase(name);
1285 // Erase node content from all groups it belongs to
1286 for (std::unordered_map<std::string, GroupItems>::iterator iter_groups =
1287 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1288 GroupItems &items = iter_groups->second;
1289 for (GroupItems::iterator iter_groupitems = items.begin();
1290 iter_groupitems != items.end();) {
1291 if (iter_groupitems->first == id)
1292 items.erase(iter_groupitems++);
1297 // Check if group is empty
1299 m_group_to_items.erase(iter_groups++);
1306 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1308 std::set<std::string> all;
1310 m_name_id_mapping_with_aliases.clear();
1311 for (const std::string &name : all) {
1312 const std::string &convert_to = idef->getAlias(name);
1314 if (m_name_id_mapping.getId(convert_to, id)) {
1315 m_name_id_mapping_with_aliases.insert(
1316 std::make_pair(name, id));
1321 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1323 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1324 "overrides to textures from " << override_filepath << std::endl;
1326 std::ifstream infile(override_filepath.c_str());
1329 while (std::getline(infile, line)) {
1331 if (trim(line).empty())
1333 std::vector<std::string> splitted = str_split(line, ' ');
1334 if (splitted.size() != 3) {
1335 errorstream << override_filepath
1336 << ":" << line_c << " Could not apply texture override \""
1337 << line << "\": Syntax error" << std::endl;
1342 if (!getId(splitted[0], id))
1343 continue; // Ignore unknown node
1345 ContentFeatures &nodedef = m_content_features[id];
1347 if (splitted[1] == "top")
1348 nodedef.tiledef[0].name = splitted[2];
1349 else if (splitted[1] == "bottom")
1350 nodedef.tiledef[1].name = splitted[2];
1351 else if (splitted[1] == "right")
1352 nodedef.tiledef[2].name = splitted[2];
1353 else if (splitted[1] == "left")
1354 nodedef.tiledef[3].name = splitted[2];
1355 else if (splitted[1] == "back")
1356 nodedef.tiledef[4].name = splitted[2];
1357 else if (splitted[1] == "front")
1358 nodedef.tiledef[5].name = splitted[2];
1359 else if (splitted[1] == "all" || splitted[1] == "*")
1360 for (TileDef &i : nodedef.tiledef)
1361 i.name = splitted[2];
1362 else if (splitted[1] == "sides")
1363 for (int i = 2; i < 6; i++)
1364 nodedef.tiledef[i].name = splitted[2];
1366 errorstream << override_filepath
1367 << ":" << line_c << " Could not apply texture override \""
1368 << line << "\": Unknown node side \""
1369 << splitted[1] << "\"" << std::endl;
1375 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1376 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1377 void *progress_callback_args)
1380 infostream << "CNodeDefManager::updateTextures(): Updating "
1381 "textures in node definitions" << std::endl;
1383 Client *client = (Client *)gamedef;
1384 ITextureSource *tsrc = client->tsrc();
1385 IShaderSource *shdsrc = client->getShaderSource();
1386 scene::IMeshManipulator *meshmanip =
1387 RenderingEngine::get_scene_manager()->getMeshManipulator();
1388 TextureSettings tsettings;
1389 tsettings.readSettings();
1391 u32 size = m_content_features.size();
1393 for (u32 i = 0; i < size; i++) {
1394 ContentFeatures *f = &(m_content_features[i]);
1395 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1396 progress_callback(progress_callback_args, i, size);
1401 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1403 writeU8(os, 1); // version
1405 std::ostringstream os2(std::ios::binary);
1406 for (u32 i = 0; i < m_content_features.size(); i++) {
1407 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1408 || i == CONTENT_UNKNOWN)
1410 const ContentFeatures *f = &m_content_features[i];
1411 if (f->name.empty())
1414 // Wrap it in a string to allow different lengths without
1415 // strict version incompatibilities
1416 std::ostringstream wrapper_os(std::ios::binary);
1417 f->serialize(wrapper_os, protocol_version);
1418 os2<<serializeString(wrapper_os.str());
1420 // must not overflow
1421 u16 next = count + 1;
1422 FATAL_ERROR_IF(next < count, "Overflow");
1425 writeU16(os, count);
1426 os << serializeLongString(os2.str());
1430 void CNodeDefManager::deSerialize(std::istream &is)
1433 int version = readU8(is);
1435 throw SerializationError("unsupported NodeDefinitionManager version");
1436 u16 count = readU16(is);
1437 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1439 for (u16 n = 0; n < count; n++) {
1440 u16 i = readU16(is2);
1442 // Read it from the string wrapper
1443 std::string wrapper = deSerializeString(is2);
1444 std::istringstream wrapper_is(wrapper, std::ios::binary);
1445 f.deSerialize(wrapper_is);
1447 // Check error conditions
1448 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1449 warningstream << "NodeDefManager::deSerialize(): "
1450 "not changing builtin node " << i << std::endl;
1453 if (f.name.empty()) {
1454 warningstream << "NodeDefManager::deSerialize(): "
1455 "received empty name" << std::endl;
1461 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1462 warningstream << "NodeDefManager::deSerialize(): "
1463 "already defined with different ID: " << f.name << std::endl;
1467 // All is ok, add node definition with the requested ID
1468 if (i >= m_content_features.size())
1469 m_content_features.resize((u32)(i) + 1);
1470 m_content_features[i] = f;
1471 addNameIdMapping(i, f.name);
1472 verbosestream << "deserialized " << f.name << std::endl;
1474 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1475 fixSelectionBoxIntUnion();
1480 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1482 m_name_id_mapping.set(i, name);
1483 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1487 IWritableNodeDefManager *createNodeDefManager()
1489 return new CNodeDefManager();
1492 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1494 m_node_registration_complete = completed;
1498 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1501 if (m_node_registration_complete)
1502 nr->nodeResolveInternal();
1504 m_pending_resolve_callbacks.push_back(nr);
1508 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1510 size_t len = m_pending_resolve_callbacks.size();
1511 for (size_t i = 0; i != len; i++) {
1512 if (nr != m_pending_resolve_callbacks[i])
1516 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1517 m_pending_resolve_callbacks.resize(len);
1525 void CNodeDefManager::runNodeResolveCallbacks()
1527 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1528 NodeResolver *nr = m_pending_resolve_callbacks[i];
1529 nr->nodeResolveInternal();
1532 m_pending_resolve_callbacks.clear();
1536 void CNodeDefManager::resetNodeResolveState()
1538 m_node_registration_complete = false;
1539 m_pending_resolve_callbacks.clear();
1542 void CNodeDefManager::mapNodeboxConnections()
1544 for (ContentFeatures &f : m_content_features) {
1545 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1548 for (std::vector<std::string>::const_iterator it = f.connects_to.begin();
1549 it != f.connects_to.end(); ++it) {
1550 getIds(*it, f.connects_to_ids);
1555 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1557 const ContentFeatures &f1 = get(from);
1559 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1562 // lookup target in connected set
1563 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1566 const ContentFeatures &f2 = get(to);
1568 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1569 // ignores actually looking if back connection exists
1570 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1572 // does to node declare usable faces?
1573 if (f2.connect_sides > 0) {
1574 if ((f2.param_type_2 == CPT2_FACEDIR ||
1575 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1576 && (connect_face >= 4)) {
1577 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1578 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1580 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1582 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1583 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1585 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1586 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1587 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1589 return (f2.connect_sides
1590 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1592 return (f2.connect_sides & connect_face);
1594 // the target is just a regular node, so connect no matter back connection
1602 NodeResolver::NodeResolver()
1604 m_nodenames.reserve(16);
1605 m_nnlistsizes.reserve(4);
1609 NodeResolver::~NodeResolver()
1611 if (!m_resolve_done && m_ndef)
1612 m_ndef->cancelNodeResolveCallback(this);
1616 void NodeResolver::nodeResolveInternal()
1618 m_nodenames_idx = 0;
1619 m_nnlistsizes_idx = 0;
1622 m_resolve_done = true;
1624 m_nodenames.clear();
1625 m_nnlistsizes.clear();
1629 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1630 const std::string &node_alt, content_t c_fallback)
1632 if (m_nodenames_idx == m_nodenames.size()) {
1633 *result_out = c_fallback;
1634 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1639 std::string name = m_nodenames[m_nodenames_idx++];
1641 bool success = m_ndef->getId(name, c);
1642 if (!success && !node_alt.empty()) {
1644 success = m_ndef->getId(name, c);
1648 errorstream << "NodeResolver: failed to resolve node name '" << name
1649 << "'." << std::endl;
1658 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1659 bool all_required, content_t c_fallback)
1661 bool success = true;
1663 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1664 errorstream << "NodeResolver: no more node lists" << std::endl;
1668 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1671 if (m_nodenames_idx == m_nodenames.size()) {
1672 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1677 std::string &name = m_nodenames[m_nodenames_idx++];
1679 if (name.substr(0,6) != "group:") {
1680 if (m_ndef->getId(name, c)) {
1681 result_out->push_back(c);
1682 } else if (all_required) {
1683 errorstream << "NodeResolver: failed to resolve node name '"
1684 << name << "'." << std::endl;
1685 result_out->push_back(c_fallback);
1689 std::set<content_t> cids;
1690 std::set<content_t>::iterator it;
1691 m_ndef->getIds(name, cids);
1692 for (it = cids.begin(); it != cids.end(); ++it)
1693 result_out->push_back(*it);