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);
340 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
342 // protocol_version >= 36
344 writeU8(os, version);
347 os << serializeString(name);
348 writeU16(os, groups.size());
349 for (const auto &group : groups) {
350 os << serializeString(group.first);
351 writeS16(os, group.second);
353 writeU8(os, param_type);
354 writeU8(os, param_type_2);
357 writeU8(os, drawtype);
358 os << serializeString(mesh);
359 writeF1000(os, visual_scale);
361 for (const TileDef &td : tiledef)
362 td.serialize(os, protocol_version);
363 for (const TileDef &td : tiledef_overlay)
364 td.serialize(os, protocol_version);
365 writeU8(os, CF_SPECIAL_COUNT);
366 for (const TileDef &td : tiledef_special) {
367 td.serialize(os, protocol_version);
370 writeU8(os, color.getRed());
371 writeU8(os, color.getGreen());
372 writeU8(os, color.getBlue());
373 os << serializeString(palette_name);
375 writeU8(os, connect_sides);
376 writeU16(os, connects_to_ids.size());
377 for (u16 connects_to_id : connects_to_ids)
378 writeU16(os, connects_to_id);
379 writeU8(os, post_effect_color.getAlpha());
380 writeU8(os, post_effect_color.getRed());
381 writeU8(os, post_effect_color.getGreen());
382 writeU8(os, post_effect_color.getBlue());
383 writeU8(os, leveled);
386 writeU8(os, light_propagates);
387 writeU8(os, sunlight_propagates);
388 writeU8(os, light_source);
391 writeU8(os, is_ground_content);
394 writeU8(os, walkable);
395 writeU8(os, pointable);
396 writeU8(os, diggable);
397 writeU8(os, climbable);
398 writeU8(os, buildable_to);
399 writeU8(os, rightclickable);
400 writeU32(os, damage_per_second);
403 writeU8(os, liquid_type);
404 os << serializeString(liquid_alternative_flowing);
405 os << serializeString(liquid_alternative_source);
406 writeU8(os, liquid_viscosity);
407 writeU8(os, liquid_renewable);
408 writeU8(os, liquid_range);
409 writeU8(os, drowning);
410 writeU8(os, floodable);
413 node_box.serialize(os, protocol_version);
414 selection_box.serialize(os, protocol_version);
415 collision_box.serialize(os, protocol_version);
418 serializeSimpleSoundSpec(sound_footstep, os, version);
419 serializeSimpleSoundSpec(sound_dig, os, version);
420 serializeSimpleSoundSpec(sound_dug, os, version);
423 writeU8(os, legacy_facedir_simple);
424 writeU8(os, legacy_wallmounted);
427 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
429 // alpha == 0 means that the node is using texture alpha
430 if (alpha == 0 || alpha == 255)
433 for (int i = 0; i < length; i++) {
434 if (tiles[i].name.empty())
437 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
438 tiles[i].name = s.str();
442 void ContentFeatures::deSerialize(std::istream &is)
445 int version = readU8(is);
447 throw SerializationError("unsupported ContentFeatures version");
450 name = deSerializeString(is);
452 u32 groups_size = readU16(is);
453 for (u32 i = 0; i < groups_size; i++) {
454 std::string name = deSerializeString(is);
455 int value = readS16(is);
456 groups[name] = value;
458 param_type = (enum ContentParamType) readU8(is);
459 param_type_2 = (enum ContentParamType2) readU8(is);
462 drawtype = (enum NodeDrawType) readU8(is);
463 mesh = deSerializeString(is);
464 visual_scale = readF1000(is);
466 throw SerializationError("unsupported tile count");
467 for (TileDef &td : tiledef)
468 td.deSerialize(is, version, drawtype);
469 for (TileDef &td : tiledef_overlay)
470 td.deSerialize(is, version, drawtype);
471 if (readU8(is) != CF_SPECIAL_COUNT)
472 throw SerializationError("unsupported CF_SPECIAL_COUNT");
473 for (TileDef &td : tiledef_special)
474 td.deSerialize(is, version, drawtype);
476 color.setRed(readU8(is));
477 color.setGreen(readU8(is));
478 color.setBlue(readU8(is));
479 palette_name = deSerializeString(is);
481 connect_sides = readU8(is);
482 u16 connects_to_size = readU16(is);
483 connects_to_ids.clear();
484 for (u16 i = 0; i < connects_to_size; i++)
485 connects_to_ids.insert(readU16(is));
486 post_effect_color.setAlpha(readU8(is));
487 post_effect_color.setRed(readU8(is));
488 post_effect_color.setGreen(readU8(is));
489 post_effect_color.setBlue(readU8(is));
490 leveled = readU8(is);
493 light_propagates = readU8(is);
494 sunlight_propagates = readU8(is);
495 light_source = readU8(is);
496 light_source = MYMIN(light_source, LIGHT_MAX);
499 is_ground_content = readU8(is);
502 walkable = readU8(is);
503 pointable = readU8(is);
504 diggable = readU8(is);
505 climbable = readU8(is);
506 buildable_to = readU8(is);
507 rightclickable = readU8(is);
508 damage_per_second = readU32(is);
511 liquid_type = (enum LiquidType) readU8(is);
512 liquid_alternative_flowing = deSerializeString(is);
513 liquid_alternative_source = deSerializeString(is);
514 liquid_viscosity = readU8(is);
515 liquid_renewable = readU8(is);
516 liquid_range = readU8(is);
517 drowning = readU8(is);
518 floodable = readU8(is);
521 node_box.deSerialize(is);
522 selection_box.deSerialize(is);
523 collision_box.deSerialize(is);
526 deSerializeSimpleSoundSpec(sound_footstep, is, version);
527 deSerializeSimpleSoundSpec(sound_dig, is, version);
528 deSerializeSimpleSoundSpec(sound_dug, is, version);
530 // read legacy properties
531 legacy_facedir_simple = readU8(is);
532 legacy_wallmounted = readU8(is);
536 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
537 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
538 bool backface_culling, u8 material_type)
540 tile->shader_id = shader_id;
541 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
542 tile->material_type = material_type;
544 // Normal texture and shader flags texture
545 if (use_normal_texture) {
546 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
548 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
551 tile->material_flags = 0;
552 if (backface_culling)
553 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
554 if (tiledef->animation.type != TAT_NONE)
555 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
556 if (tiledef->tileable_horizontal)
557 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
558 if (tiledef->tileable_vertical)
559 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
562 tile->has_color = tiledef->has_color;
563 if (tiledef->has_color)
564 tile->color = tiledef->color;
568 // Animation parameters
570 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
572 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
573 &frame_count, &frame_length_ms, NULL);
574 tile->animation_frame_count = frame_count;
575 tile->animation_frame_length_ms = frame_length_ms;
578 if (frame_count == 1) {
579 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
581 std::ostringstream os(std::ios::binary);
583 tile->frames = std::make_shared<std::vector<FrameSpec>>();
585 tile->frames->resize(frame_count);
587 for (int i = 0; i < frame_count; i++) {
593 tiledef->animation.getTextureModifer(os,
594 tile->texture->getOriginalSize(), i);
596 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
597 if (tile->normal_texture)
598 frame.normal_texture = tsrc->getNormalTexture(os.str());
599 frame.flags_texture = tile->flags_texture;
600 (*tile->frames)[i] = frame;
607 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
608 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
610 // minimap pixel color - the average color of a texture
611 if (tsettings.enable_minimap && !tiledef[0].name.empty())
612 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
614 // Figure out the actual tiles to use
616 for (u32 j = 0; j < 6; j++) {
617 tdef[j] = tiledef[j];
618 if (tdef[j].name.empty())
619 tdef[j].name = "unknown_node.png";
621 // also the overlay tiles
622 TileDef tdef_overlay[6];
623 for (u32 j = 0; j < 6; j++)
624 tdef_overlay[j] = tiledef_overlay[j];
625 // also the special tiles
626 TileDef tdef_spec[6];
627 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
628 tdef_spec[j] = tiledef_special[j];
630 bool is_liquid = false;
632 u8 material_type = (alpha == 255) ?
633 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
638 material_type = (alpha == 255) ?
639 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
646 assert(liquid_type == LIQUID_SOURCE);
647 if (tsettings.opaque_water)
652 case NDT_FLOWINGLIQUID:
653 assert(liquid_type == LIQUID_FLOWING);
655 if (tsettings.opaque_water)
661 visual_solidness = 1;
663 case NDT_GLASSLIKE_FRAMED:
665 visual_solidness = 1;
667 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
669 visual_solidness = 1;
670 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
674 visual_solidness = 1;
676 case NDT_ALLFACES_OPTIONAL:
677 if (tsettings.leaves_style == LEAVES_FANCY) {
678 drawtype = NDT_ALLFACES;
680 visual_solidness = 1;
681 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
682 for (u32 j = 0; j < 6; j++) {
683 if (!tdef_spec[j].name.empty())
684 tdef[j].name = tdef_spec[j].name;
686 drawtype = NDT_GLASSLIKE;
688 visual_solidness = 1;
690 drawtype = NDT_NORMAL;
692 for (TileDef &td : tdef)
693 td.name += std::string("^[noalpha");
696 material_type = TILE_MATERIAL_WAVING_LEAVES;
701 material_type = TILE_MATERIAL_WAVING_PLANTS;
710 material_type = TILE_MATERIAL_WAVING_PLANTS;
711 else if (waving == 2)
712 material_type = TILE_MATERIAL_WAVING_LEAVES;
720 case NDT_PLANTLIKE_ROOTED:
726 // Vertex alpha is no longer supported, correct if necessary.
727 correctAlpha(tdef, 6);
728 correctAlpha(tdef_overlay, 6);
729 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
730 material_type = (alpha == 255) ?
731 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
734 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
736 u8 overlay_material = material_type;
737 if (overlay_material == TILE_MATERIAL_OPAQUE)
738 overlay_material = TILE_MATERIAL_BASIC;
739 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
740 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
742 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
744 // Tiles (fill in f->tiles[])
745 for (u16 j = 0; j < 6; j++) {
746 fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader,
747 tsettings.use_normal_texture,
748 tdef[j].backface_culling, material_type);
749 if (!tdef_overlay[j].name.empty())
750 fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j],
751 overlay_shader, tsettings.use_normal_texture,
752 tdef[j].backface_culling, overlay_material);
755 u8 special_material = material_type;
756 if (drawtype == NDT_PLANTLIKE_ROOTED) {
758 special_material = TILE_MATERIAL_WAVING_PLANTS;
759 else if (waving == 2)
760 special_material = TILE_MATERIAL_WAVING_LEAVES;
762 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
764 // Special tiles (fill in f->special_tiles[])
765 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
766 fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j],
767 special_shader, tsettings.use_normal_texture,
768 tdef_spec[j].backface_culling, special_material);
771 if (param_type_2 == CPT2_COLOR ||
772 param_type_2 == CPT2_COLORED_FACEDIR ||
773 param_type_2 == CPT2_COLORED_WALLMOUNTED)
774 palette = tsrc->getPalette(palette_name);
776 if (drawtype == NDT_MESH && !mesh.empty()) {
778 // Read the mesh and apply scale
779 mesh_ptr[0] = client->getMesh(mesh);
781 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
782 scaleMesh(mesh_ptr[0], scale);
783 recalculateBoundingBox(mesh_ptr[0]);
784 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
786 } else if ((drawtype == NDT_NODEBOX) &&
787 ((node_box.type == NODEBOX_REGULAR) ||
788 (node_box.type == NODEBOX_FIXED)) &&
789 (!node_box.fixed.empty())) {
790 //Convert regular nodebox nodes to meshnodes
791 //Change the drawtype and apply scale
793 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
794 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
795 scaleMesh(mesh_ptr[0], scale);
796 recalculateBoundingBox(mesh_ptr[0]);
797 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
800 //Cache 6dfacedir and wallmounted rotated clones of meshes
801 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
802 (param_type_2 == CPT2_FACEDIR
803 || param_type_2 == CPT2_COLORED_FACEDIR)) {
804 for (u16 j = 1; j < 24; j++) {
805 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
806 rotateMeshBy6dFacedir(mesh_ptr[j], j);
807 recalculateBoundingBox(mesh_ptr[j]);
808 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
810 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
811 && (param_type_2 == CPT2_WALLMOUNTED ||
812 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
813 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
814 for (u16 j = 1; j < 6; j++) {
815 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
816 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
817 recalculateBoundingBox(mesh_ptr[j]);
818 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
820 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
821 recalculateBoundingBox(mesh_ptr[0]);
822 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
831 class CNodeDefManager: public IWritableNodeDefManager {
834 virtual ~CNodeDefManager();
837 inline virtual const ContentFeatures& get(content_t c) const;
838 inline virtual const ContentFeatures& get(const MapNode &n) const;
839 virtual bool getId(const std::string &name, content_t &result) const;
840 virtual content_t getId(const std::string &name) const;
841 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
842 virtual const ContentFeatures& get(const std::string &name) const;
843 content_t allocateId();
844 virtual content_t set(const std::string &name, const ContentFeatures &def);
845 virtual content_t allocateDummy(const std::string &name);
846 virtual void removeNode(const std::string &name);
847 virtual void updateAliases(IItemDefManager *idef);
848 virtual void applyTextureOverrides(const std::string &override_filepath);
849 virtual void updateTextures(IGameDef *gamedef,
850 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
851 void *progress_cbk_args);
852 void serialize(std::ostream &os, u16 protocol_version) const;
853 void deSerialize(std::istream &is);
855 inline virtual void setNodeRegistrationStatus(bool completed);
857 virtual void pendNodeResolve(NodeResolver *nr);
858 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
859 virtual void runNodeResolveCallbacks();
860 virtual void resetNodeResolveState();
861 virtual void mapNodeboxConnections();
862 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
863 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
865 return m_selection_box_int_union;
869 void addNameIdMapping(content_t i, std::string name);
871 * Recalculates m_selection_box_int_union based on
872 * m_selection_box_union.
874 void fixSelectionBoxIntUnion();
876 // Features indexed by id
877 std::vector<ContentFeatures> m_content_features;
879 // A mapping for fast converting back and forth between names and ids
880 NameIdMapping m_name_id_mapping;
882 // Like m_name_id_mapping, but only from names to ids, and includes
883 // item aliases too. Updated by updateAliases()
884 // Note: Not serialized.
886 std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases;
888 // A mapping from groups to a list of content_ts (and their levels)
889 // that belong to it. Necessary for a direct lookup in getIds().
890 // Note: Not serialized.
891 std::unordered_map<std::string, GroupItems> m_group_to_items;
893 // Next possibly free id
896 // NodeResolvers to callback once node registration has ended
897 std::vector<NodeResolver *> m_pending_resolve_callbacks;
899 // True when all nodes have been registered
900 bool m_node_registration_complete;
902 //! The union of all nodes' selection boxes.
903 aabb3f m_selection_box_union;
905 * The smallest box in node coordinates that
906 * contains all nodes' selection boxes.
908 core::aabbox3d<s16> m_selection_box_int_union;
912 CNodeDefManager::CNodeDefManager()
918 CNodeDefManager::~CNodeDefManager()
921 for (ContentFeatures &f : m_content_features) {
922 for (auto &j : f.mesh_ptr) {
931 void CNodeDefManager::clear()
933 m_content_features.clear();
934 m_name_id_mapping.clear();
935 m_name_id_mapping_with_aliases.clear();
936 m_group_to_items.clear();
938 m_selection_box_union.reset(0,0,0);
939 m_selection_box_int_union.reset(0,0,0);
941 resetNodeResolveState();
943 u32 initial_length = 0;
944 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
945 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
946 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
947 m_content_features.resize(initial_length);
949 // Set CONTENT_UNKNOWN
953 // Insert directly into containers
954 content_t c = CONTENT_UNKNOWN;
955 m_content_features[c] = f;
956 addNameIdMapping(c, f.name);
963 f.drawtype = NDT_AIRLIKE;
964 f.param_type = CPT_LIGHT;
965 f.light_propagates = true;
966 f.sunlight_propagates = true;
970 f.buildable_to = true;
972 f.is_ground_content = true;
973 // Insert directly into containers
974 content_t c = CONTENT_AIR;
975 m_content_features[c] = f;
976 addNameIdMapping(c, f.name);
979 // Set CONTENT_IGNORE
983 f.drawtype = NDT_AIRLIKE;
984 f.param_type = CPT_NONE;
985 f.light_propagates = false;
986 f.sunlight_propagates = false;
990 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
991 f.is_ground_content = true;
992 // Insert directly into containers
993 content_t c = CONTENT_IGNORE;
994 m_content_features[c] = f;
995 addNameIdMapping(c, f.name);
1000 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1002 return c < m_content_features.size()
1003 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1007 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1009 return get(n.getContent());
1013 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1015 std::unordered_map<std::string, content_t>::const_iterator
1016 i = m_name_id_mapping_with_aliases.find(name);
1017 if(i == m_name_id_mapping_with_aliases.end())
1024 content_t CNodeDefManager::getId(const std::string &name) const
1026 content_t id = CONTENT_IGNORE;
1032 bool CNodeDefManager::getIds(const std::string &name,
1033 std::set<content_t> &result) const
1035 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1036 if (name.substr(0,6) != "group:") {
1037 content_t id = CONTENT_IGNORE;
1038 bool exists = getId(name, id);
1043 std::string group = name.substr(6);
1045 std::unordered_map<std::string, GroupItems>::const_iterator
1046 i = m_group_to_items.find(group);
1047 if (i == m_group_to_items.end())
1050 const GroupItems &items = i->second;
1051 for (const auto &item : items) {
1052 if (item.second != 0)
1053 result.insert(item.first);
1055 //printf("getIds: %dus\n", t.stop());
1060 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1062 content_t id = CONTENT_UNKNOWN;
1068 // returns CONTENT_IGNORE if no free ID found
1069 content_t CNodeDefManager::allocateId()
1071 for (content_t id = m_next_id;
1072 id >= m_next_id; // overflow?
1074 while (id >= m_content_features.size()) {
1075 m_content_features.emplace_back();
1077 const ContentFeatures &f = m_content_features[id];
1078 if (f.name.empty()) {
1083 // If we arrive here, an overflow occurred in id.
1084 // That means no ID was found
1085 return CONTENT_IGNORE;
1090 * Returns the smallest box that contains all boxes
1091 * in the vector. Box_union is expanded.
1092 * @param[in] boxes the vector containing the boxes
1093 * @param[in, out] box_union the union of the arguments
1095 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1097 for (const aabb3f &box : boxes) {
1098 box_union->addInternalBox(box);
1104 * Returns a box that contains the nodebox in every case.
1105 * The argument node_union is expanded.
1106 * @param[in] nodebox the nodebox to be measured
1107 * @param[in] features used to decide whether the nodebox
1109 * @param[in, out] box_union the union of the arguments
1111 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1114 switch(nodebox.type) {
1116 case NODEBOX_LEVELED: {
1118 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1119 boxVectorUnion(nodebox.fixed, &half_processed);
1120 // Set leveled boxes to maximal
1121 if (nodebox.type == NODEBOX_LEVELED) {
1122 half_processed.MaxEdge.Y = +BS / 2;
1124 if (features.param_type_2 == CPT2_FACEDIR ||
1125 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1126 // Get maximal coordinate
1128 fabsf(half_processed.MinEdge.X),
1129 fabsf(half_processed.MinEdge.Y),
1130 fabsf(half_processed.MinEdge.Z),
1131 fabsf(half_processed.MaxEdge.X),
1132 fabsf(half_processed.MaxEdge.Y),
1133 fabsf(half_processed.MaxEdge.Z) };
1135 for (float coord : coords) {
1140 // Add the union of all possible rotated boxes
1141 box_union->addInternalPoint(-max, -max, -max);
1142 box_union->addInternalPoint(+max, +max, +max);
1144 box_union->addInternalBox(half_processed);
1148 case NODEBOX_WALLMOUNTED: {
1150 box_union->addInternalBox(nodebox.wall_top);
1151 box_union->addInternalBox(nodebox.wall_bottom);
1152 // Find maximal coordinate in the X-Z plane
1154 fabsf(nodebox.wall_side.MinEdge.X),
1155 fabsf(nodebox.wall_side.MinEdge.Z),
1156 fabsf(nodebox.wall_side.MaxEdge.X),
1157 fabsf(nodebox.wall_side.MaxEdge.Z) };
1159 for (float coord : coords) {
1164 // Add the union of all possible rotated boxes
1165 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1166 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1169 case NODEBOX_CONNECTED: {
1170 // Add all possible connected boxes
1171 boxVectorUnion(nodebox.fixed, box_union);
1172 boxVectorUnion(nodebox.connect_top, box_union);
1173 boxVectorUnion(nodebox.connect_bottom, box_union);
1174 boxVectorUnion(nodebox.connect_front, box_union);
1175 boxVectorUnion(nodebox.connect_left, box_union);
1176 boxVectorUnion(nodebox.connect_back, box_union);
1177 boxVectorUnion(nodebox.connect_right, box_union);
1182 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1183 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1189 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1191 m_selection_box_int_union.MinEdge.X = floorf(
1192 m_selection_box_union.MinEdge.X / BS + 0.5f);
1193 m_selection_box_int_union.MinEdge.Y = floorf(
1194 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1195 m_selection_box_int_union.MinEdge.Z = floorf(
1196 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1197 m_selection_box_int_union.MaxEdge.X = ceilf(
1198 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1199 m_selection_box_int_union.MaxEdge.Y = ceilf(
1200 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1201 m_selection_box_int_union.MaxEdge.Z = ceilf(
1202 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1206 // IWritableNodeDefManager
1207 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1211 assert(name == def.name);
1213 // Don't allow redefining ignore (but allow air and unknown)
1214 if (name == "ignore") {
1215 warningstream << "NodeDefManager: Ignoring "
1216 "CONTENT_IGNORE redefinition"<<std::endl;
1217 return CONTENT_IGNORE;
1220 content_t id = CONTENT_IGNORE;
1221 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1224 if (id == CONTENT_IGNORE) {
1225 warningstream << "NodeDefManager: Absolute "
1226 "limit reached" << std::endl;
1227 return CONTENT_IGNORE;
1229 assert(id != CONTENT_IGNORE);
1230 addNameIdMapping(id, name);
1232 m_content_features[id] = def;
1233 verbosestream << "NodeDefManager: registering content id \"" << id
1234 << "\": name=\"" << def.name << "\""<<std::endl;
1236 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1237 fixSelectionBoxIntUnion();
1238 // Add this content to the list of all groups it belongs to
1239 // FIXME: This should remove a node from groups it no longer
1240 // belongs to when a node is re-registered
1241 for (const auto &group : def.groups) {
1242 const std::string &group_name = group.first;
1244 std::unordered_map<std::string, GroupItems>::iterator
1245 j = m_group_to_items.find(group_name);
1246 if (j == m_group_to_items.end()) {
1247 m_group_to_items[group_name].emplace_back(id, group.second);
1249 GroupItems &items = j->second;
1250 items.emplace_back(id, group.second);
1257 content_t CNodeDefManager::allocateDummy(const std::string &name)
1259 assert(name != ""); // Pre-condition
1262 return set(name, f);
1266 void CNodeDefManager::removeNode(const std::string &name)
1271 // Erase name from name ID mapping
1272 content_t id = CONTENT_IGNORE;
1273 if (m_name_id_mapping.getId(name, id)) {
1274 m_name_id_mapping.eraseName(name);
1275 m_name_id_mapping_with_aliases.erase(name);
1278 // Erase node content from all groups it belongs to
1279 for (std::unordered_map<std::string, GroupItems>::iterator iter_groups =
1280 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1281 GroupItems &items = iter_groups->second;
1282 for (GroupItems::iterator iter_groupitems = items.begin();
1283 iter_groupitems != items.end();) {
1284 if (iter_groupitems->first == id)
1285 items.erase(iter_groupitems++);
1290 // Check if group is empty
1292 m_group_to_items.erase(iter_groups++);
1299 void CNodeDefManager::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 CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1316 infostream << "CNodeDefManager::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 CNodeDefManager::updateTextures(IGameDef *gamedef,
1369 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1370 void *progress_callback_args)
1373 infostream << "CNodeDefManager::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 CNodeDefManager::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 CNodeDefManager::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 CNodeDefManager::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 IWritableNodeDefManager *createNodeDefManager()
1482 return new CNodeDefManager();
1485 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1487 m_node_registration_complete = completed;
1491 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1494 if (m_node_registration_complete)
1495 nr->nodeResolveInternal();
1497 m_pending_resolve_callbacks.push_back(nr);
1501 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1503 size_t len = m_pending_resolve_callbacks.size();
1504 for (size_t i = 0; i != len; i++) {
1505 if (nr != m_pending_resolve_callbacks[i])
1509 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1510 m_pending_resolve_callbacks.resize(len);
1518 void CNodeDefManager::runNodeResolveCallbacks()
1520 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1521 NodeResolver *nr = m_pending_resolve_callbacks[i];
1522 nr->nodeResolveInternal();
1525 m_pending_resolve_callbacks.clear();
1529 void CNodeDefManager::resetNodeResolveState()
1531 m_node_registration_complete = false;
1532 m_pending_resolve_callbacks.clear();
1535 void CNodeDefManager::mapNodeboxConnections()
1537 for (ContentFeatures &f : m_content_features) {
1538 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1541 for (std::vector<std::string>::const_iterator it = f.connects_to.begin();
1542 it != f.connects_to.end(); ++it) {
1543 getIds(*it, f.connects_to_ids);
1548 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1550 const ContentFeatures &f1 = get(from);
1552 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1555 // lookup target in connected set
1556 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1559 const ContentFeatures &f2 = get(to);
1561 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1562 // ignores actually looking if back connection exists
1563 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1565 // does to node declare usable faces?
1566 if (f2.connect_sides > 0) {
1567 if ((f2.param_type_2 == CPT2_FACEDIR ||
1568 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1569 && (connect_face >= 4)) {
1570 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1571 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1573 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1575 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1576 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1578 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1579 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1580 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1582 return (f2.connect_sides
1583 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1585 return (f2.connect_sides & connect_face);
1587 // the target is just a regular node, so connect no matter back connection
1595 NodeResolver::NodeResolver()
1597 m_nodenames.reserve(16);
1598 m_nnlistsizes.reserve(4);
1602 NodeResolver::~NodeResolver()
1604 if (!m_resolve_done && m_ndef)
1605 m_ndef->cancelNodeResolveCallback(this);
1609 void NodeResolver::nodeResolveInternal()
1611 m_nodenames_idx = 0;
1612 m_nnlistsizes_idx = 0;
1615 m_resolve_done = true;
1617 m_nodenames.clear();
1618 m_nnlistsizes.clear();
1622 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1623 const std::string &node_alt, content_t c_fallback)
1625 if (m_nodenames_idx == m_nodenames.size()) {
1626 *result_out = c_fallback;
1627 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1632 std::string name = m_nodenames[m_nodenames_idx++];
1634 bool success = m_ndef->getId(name, c);
1635 if (!success && !node_alt.empty()) {
1637 success = m_ndef->getId(name, c);
1641 errorstream << "NodeResolver: failed to resolve node name '" << name
1642 << "'." << std::endl;
1651 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1652 bool all_required, content_t c_fallback)
1654 bool success = true;
1656 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1657 errorstream << "NodeResolver: no more node lists" << std::endl;
1661 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1664 if (m_nodenames_idx == m_nodenames.size()) {
1665 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1670 std::string &name = m_nodenames[m_nodenames_idx++];
1672 if (name.substr(0,6) != "group:") {
1673 if (m_ndef->getId(name, c)) {
1674 result_out->push_back(c);
1675 } else if (all_required) {
1676 errorstream << "NodeResolver: failed to resolve node name '"
1677 << name << "'." << std::endl;
1678 result_out->push_back(c_fallback);
1682 std::set<content_t> cids;
1683 std::set<content_t>::iterator it;
1684 m_ndef->getIds(name, cids);
1685 for (it = cids.begin(); it != cids.end(); ++it)
1686 result_out->push_back(*it);