3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "client/mesh.h"
25 #include "client/shader.h"
26 #include "client/client.h"
27 #include "client/renderingengine.h"
28 #include "client/tile.h"
29 #include <IMeshManipulator.h>
33 #include "nameidmapping.h"
34 #include "util/numeric.h"
35 #include "util/serialize.h"
36 #include "exceptions.h"
40 #include <fstream> // Used in applyTextureOverrides()
50 type = NODEBOX_REGULAR;
53 // default is sign/ladder-like
54 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
55 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
56 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
57 // no default for other parts
59 connect_bottom.clear();
60 connect_front.clear();
63 connect_right.clear();
64 disconnected_top.clear();
65 disconnected_bottom.clear();
66 disconnected_front.clear();
67 disconnected_left.clear();
68 disconnected_back.clear();
69 disconnected_right.clear();
71 disconnected_sides.clear();
74 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
85 writeU16(os, fixed.size());
86 for (const aabb3f &nodebox : fixed) {
87 writeV3F32(os, nodebox.MinEdge);
88 writeV3F32(os, nodebox.MaxEdge);
91 case NODEBOX_WALLMOUNTED:
94 writeV3F32(os, wall_top.MinEdge);
95 writeV3F32(os, wall_top.MaxEdge);
96 writeV3F32(os, wall_bottom.MinEdge);
97 writeV3F32(os, wall_bottom.MaxEdge);
98 writeV3F32(os, wall_side.MinEdge);
99 writeV3F32(os, wall_side.MaxEdge);
101 case NODEBOX_CONNECTED:
104 #define WRITEBOX(box) \
105 writeU16(os, (box).size()); \
106 for (const aabb3f &i: (box)) { \
107 writeV3F32(os, i.MinEdge); \
108 writeV3F32(os, i.MaxEdge); \
112 WRITEBOX(connect_top);
113 WRITEBOX(connect_bottom);
114 WRITEBOX(connect_front);
115 WRITEBOX(connect_left);
116 WRITEBOX(connect_back);
117 WRITEBOX(connect_right);
118 WRITEBOX(disconnected_top);
119 WRITEBOX(disconnected_bottom);
120 WRITEBOX(disconnected_front);
121 WRITEBOX(disconnected_left);
122 WRITEBOX(disconnected_back);
123 WRITEBOX(disconnected_right);
124 WRITEBOX(disconnected);
125 WRITEBOX(disconnected_sides);
133 void NodeBox::deSerialize(std::istream &is)
135 int version = readU8(is);
137 throw SerializationError("unsupported NodeBox version");
141 type = (enum NodeBoxType)readU8(is);
143 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
145 u16 fixed_count = readU16(is);
149 box.MinEdge = readV3F32(is);
150 box.MaxEdge = readV3F32(is);
151 fixed.push_back(box);
154 else if(type == NODEBOX_WALLMOUNTED)
156 wall_top.MinEdge = readV3F32(is);
157 wall_top.MaxEdge = readV3F32(is);
158 wall_bottom.MinEdge = readV3F32(is);
159 wall_bottom.MaxEdge = readV3F32(is);
160 wall_side.MinEdge = readV3F32(is);
161 wall_side.MaxEdge = readV3F32(is);
163 else if (type == NODEBOX_CONNECTED)
165 #define READBOXES(box) { \
166 count = readU16(is); \
167 (box).reserve(count); \
169 v3f min = readV3F32(is); \
170 v3f max = readV3F32(is); \
171 (box).emplace_back(min, max); }; }
176 READBOXES(connect_top);
177 READBOXES(connect_bottom);
178 READBOXES(connect_front);
179 READBOXES(connect_left);
180 READBOXES(connect_back);
181 READBOXES(connect_right);
182 READBOXES(disconnected_top);
183 READBOXES(disconnected_bottom);
184 READBOXES(disconnected_front);
185 READBOXES(disconnected_left);
186 READBOXES(disconnected_back);
187 READBOXES(disconnected_right);
188 READBOXES(disconnected);
189 READBOXES(disconnected_sides);
197 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
198 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
199 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
200 #define TILE_FLAG_HAS_COLOR (1 << 3)
201 #define TILE_FLAG_HAS_SCALE (1 << 4)
202 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
204 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
206 // protocol_version >= 36
208 writeU8(os, version);
210 os << serializeString(name);
211 animation.serialize(os, version);
212 bool has_scale = scale > 0;
214 if (backface_culling)
215 flags |= TILE_FLAG_BACKFACE_CULLING;
216 if (tileable_horizontal)
217 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
218 if (tileable_vertical)
219 flags |= TILE_FLAG_TILEABLE_VERTICAL;
221 flags |= TILE_FLAG_HAS_COLOR;
223 flags |= TILE_FLAG_HAS_SCALE;
224 if (align_style != ALIGN_STYLE_NODE)
225 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
228 writeU8(os, color.getRed());
229 writeU8(os, color.getGreen());
230 writeU8(os, color.getBlue());
234 if (align_style != ALIGN_STYLE_NODE)
235 writeU8(os, align_style);
238 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
239 NodeDrawType drawtype)
241 int version = readU8(is);
243 throw SerializationError("unsupported TileDef version");
244 name = deSerializeString(is);
245 animation.deSerialize(is, version);
246 u16 flags = readU16(is);
247 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
248 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
249 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
250 has_color = flags & TILE_FLAG_HAS_COLOR;
251 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
252 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
254 color.setRed(readU8(is));
255 color.setGreen(readU8(is));
256 color.setBlue(readU8(is));
258 scale = has_scale ? readU8(is) : 0;
260 align_style = static_cast<AlignStyle>(readU8(is));
262 align_style = ALIGN_STYLE_NODE;
265 void TextureSettings::readSettings()
267 connected_glass = g_settings->getBool("connected_glass");
268 opaque_water = g_settings->getBool("opaque_water");
269 bool enable_shaders = g_settings->getBool("enable_shaders");
270 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
271 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
272 bool smooth_lighting = g_settings->getBool("smooth_lighting");
273 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
274 enable_minimap = g_settings->getBool("enable_minimap");
275 node_texture_size = g_settings->getU16("texture_min_size");
276 std::string leaves_style_str = g_settings->get("leaves_style");
277 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
278 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
280 // Mesh cache is not supported in combination with smooth lighting
282 enable_mesh_cache = false;
284 use_normal_texture = enable_shaders &&
285 (enable_bumpmapping || enable_parallax_occlusion);
286 if (leaves_style_str == "fancy") {
287 leaves_style = LEAVES_FANCY;
288 } else if (leaves_style_str == "simple") {
289 leaves_style = LEAVES_SIMPLE;
291 leaves_style = LEAVES_OPAQUE;
294 if (world_aligned_mode_str == "enable")
295 world_aligned_mode = WORLDALIGN_ENABLE;
296 else if (world_aligned_mode_str == "force_solid")
297 world_aligned_mode = WORLDALIGN_FORCE;
298 else if (world_aligned_mode_str == "force_nodebox")
299 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
301 world_aligned_mode = WORLDALIGN_DISABLE;
303 if (autoscale_mode_str == "enable")
304 autoscale_mode = AUTOSCALE_ENABLE;
305 else if (autoscale_mode_str == "force")
306 autoscale_mode = AUTOSCALE_FORCE;
308 autoscale_mode = AUTOSCALE_DISABLE;
315 ContentFeatures::ContentFeatures()
320 void ContentFeatures::reset()
327 visual_solidness = 0;
328 backface_culling = true;
331 has_on_construct = false;
332 has_on_destruct = false;
333 has_after_destruct = false;
337 NOTE: Most of this is always overridden by the default values given
342 // Unknown nodes can be dug
343 groups["dig_immediate"] = 2;
344 drawtype = NDT_NORMAL;
347 for (auto &i : mesh_ptr)
349 minimap_color = video::SColor(0, 0, 0, 0);
352 for (auto &i : tiledef)
354 for (auto &j : tiledef_special)
357 post_effect_color = video::SColor(0, 0, 0, 0);
358 param_type = CPT_NONE;
359 param_type_2 = CPT2_NONE;
360 is_ground_content = false;
361 light_propagates = false;
362 sunlight_propagates = false;
367 buildable_to = false;
369 rightclickable = true;
371 leveled_max = LEVELED_MAX;
372 liquid_type = LIQUID_NONE;
373 liquid_alternative_flowing = "";
374 liquid_alternative_flowing_id = CONTENT_IGNORE;
375 liquid_alternative_source = "";
376 liquid_alternative_source_id = CONTENT_IGNORE;
377 liquid_viscosity = 0;
378 liquid_renewable = true;
379 liquid_range = LIQUID_LEVEL_MAX+1;
382 damage_per_second = 0;
383 node_box = NodeBox();
384 selection_box = NodeBox();
385 collision_box = NodeBox();
387 legacy_facedir_simple = false;
388 legacy_wallmounted = false;
389 sound_footstep = SimpleSoundSpec();
390 sound_dig = SimpleSoundSpec("__group");
391 sound_dug = SimpleSoundSpec();
393 connects_to_ids.clear();
395 color = video::SColor(0xFFFFFFFF);
398 node_dig_prediction = "air";
401 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
403 const u8 version = CONTENTFEATURES_VERSION;
404 writeU8(os, version);
407 os << serializeString(name);
408 writeU16(os, groups.size());
409 for (const auto &group : groups) {
410 os << serializeString(group.first);
411 writeS16(os, group.second);
413 writeU8(os, param_type);
414 writeU8(os, param_type_2);
417 writeU8(os, drawtype);
418 os << serializeString(mesh);
419 writeF32(os, visual_scale);
421 for (const TileDef &td : tiledef)
422 td.serialize(os, protocol_version);
423 for (const TileDef &td : tiledef_overlay)
424 td.serialize(os, protocol_version);
425 writeU8(os, CF_SPECIAL_COUNT);
426 for (const TileDef &td : tiledef_special) {
427 td.serialize(os, protocol_version);
430 writeU8(os, color.getRed());
431 writeU8(os, color.getGreen());
432 writeU8(os, color.getBlue());
433 os << serializeString(palette_name);
435 writeU8(os, connect_sides);
436 writeU16(os, connects_to_ids.size());
437 for (u16 connects_to_id : connects_to_ids)
438 writeU16(os, connects_to_id);
439 writeARGB8(os, post_effect_color);
440 writeU8(os, leveled);
443 writeU8(os, light_propagates);
444 writeU8(os, sunlight_propagates);
445 writeU8(os, light_source);
448 writeU8(os, is_ground_content);
451 writeU8(os, walkable);
452 writeU8(os, pointable);
453 writeU8(os, diggable);
454 writeU8(os, climbable);
455 writeU8(os, buildable_to);
456 writeU8(os, rightclickable);
457 writeU32(os, damage_per_second);
460 writeU8(os, liquid_type);
461 os << serializeString(liquid_alternative_flowing);
462 os << serializeString(liquid_alternative_source);
463 writeU8(os, liquid_viscosity);
464 writeU8(os, liquid_renewable);
465 writeU8(os, liquid_range);
466 writeU8(os, drowning);
467 writeU8(os, floodable);
470 node_box.serialize(os, protocol_version);
471 selection_box.serialize(os, protocol_version);
472 collision_box.serialize(os, protocol_version);
475 sound_footstep.serialize(os, version);
476 sound_dig.serialize(os, version);
477 sound_dug.serialize(os, version);
480 writeU8(os, legacy_facedir_simple);
481 writeU8(os, legacy_wallmounted);
483 os << serializeString(node_dig_prediction);
484 writeU8(os, leveled_max);
487 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
489 // alpha == 0 means that the node is using texture alpha
490 if (alpha == 0 || alpha == 255)
493 for (int i = 0; i < length; i++) {
494 if (tiles[i].name.empty())
497 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
498 tiles[i].name = s.str();
502 void ContentFeatures::deSerialize(std::istream &is)
505 const u8 version = readU8(is);
506 if (version < CONTENTFEATURES_VERSION)
507 throw SerializationError("unsupported ContentFeatures version");
510 name = deSerializeString(is);
512 u32 groups_size = readU16(is);
513 for (u32 i = 0; i < groups_size; i++) {
514 std::string name = deSerializeString(is);
515 int value = readS16(is);
516 groups[name] = value;
518 param_type = (enum ContentParamType) readU8(is);
519 param_type_2 = (enum ContentParamType2) readU8(is);
522 drawtype = (enum NodeDrawType) readU8(is);
523 mesh = deSerializeString(is);
524 visual_scale = readF32(is);
526 throw SerializationError("unsupported tile count");
527 for (TileDef &td : tiledef)
528 td.deSerialize(is, version, drawtype);
529 for (TileDef &td : tiledef_overlay)
530 td.deSerialize(is, version, drawtype);
531 if (readU8(is) != CF_SPECIAL_COUNT)
532 throw SerializationError("unsupported CF_SPECIAL_COUNT");
533 for (TileDef &td : tiledef_special)
534 td.deSerialize(is, version, drawtype);
536 color.setRed(readU8(is));
537 color.setGreen(readU8(is));
538 color.setBlue(readU8(is));
539 palette_name = deSerializeString(is);
541 connect_sides = readU8(is);
542 u16 connects_to_size = readU16(is);
543 connects_to_ids.clear();
544 for (u16 i = 0; i < connects_to_size; i++)
545 connects_to_ids.push_back(readU16(is));
546 post_effect_color = readARGB8(is);
547 leveled = readU8(is);
550 light_propagates = readU8(is);
551 sunlight_propagates = readU8(is);
552 light_source = readU8(is);
553 light_source = MYMIN(light_source, LIGHT_MAX);
556 is_ground_content = readU8(is);
559 walkable = readU8(is);
560 pointable = readU8(is);
561 diggable = readU8(is);
562 climbable = readU8(is);
563 buildable_to = readU8(is);
564 rightclickable = readU8(is);
565 damage_per_second = readU32(is);
568 liquid_type = (enum LiquidType) readU8(is);
569 liquid_alternative_flowing = deSerializeString(is);
570 liquid_alternative_source = deSerializeString(is);
571 liquid_viscosity = readU8(is);
572 liquid_renewable = readU8(is);
573 liquid_range = readU8(is);
574 drowning = readU8(is);
575 floodable = readU8(is);
578 node_box.deSerialize(is);
579 selection_box.deSerialize(is);
580 collision_box.deSerialize(is);
583 sound_footstep.deSerialize(is, version);
584 sound_dig.deSerialize(is, version);
585 sound_dug.deSerialize(is, version);
587 // read legacy properties
588 legacy_facedir_simple = readU8(is);
589 legacy_wallmounted = readU8(is);
592 node_dig_prediction = deSerializeString(is);
593 u8 tmp_leveled_max = readU8(is);
594 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
595 throw SerializationError("");
596 leveled_max = tmp_leveled_max;
597 } catch(SerializationError &e) {};
601 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
602 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
603 u8 material_type, u32 shader_id, bool backface_culling,
604 const TextureSettings &tsettings)
606 layer->shader_id = shader_id;
607 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
608 layer->material_type = material_type;
610 bool has_scale = tiledef.scale > 0;
611 bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
612 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
613 if (use_autoscale && layer->texture) {
614 auto texture_size = layer->texture->getOriginalSize();
615 float base_size = tsettings.node_texture_size;
616 float size = std::fmin(texture_size.Width, texture_size.Height);
617 layer->scale = std::fmax(base_size, size) / base_size;
618 } else if (has_scale) {
619 layer->scale = tiledef.scale;
623 if (!tile.world_aligned)
626 // Normal texture and shader flags texture
627 if (tsettings.use_normal_texture) {
628 layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
630 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
633 layer->material_flags = 0;
634 if (backface_culling)
635 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
636 if (tiledef.animation.type != TAT_NONE)
637 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
638 if (tiledef.tileable_horizontal)
639 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
640 if (tiledef.tileable_vertical)
641 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
644 layer->has_color = tiledef.has_color;
645 if (tiledef.has_color)
646 layer->color = tiledef.color;
648 layer->color = color;
650 // Animation parameters
652 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
654 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
655 &frame_count, &frame_length_ms, NULL);
656 layer->animation_frame_count = frame_count;
657 layer->animation_frame_length_ms = frame_length_ms;
660 if (frame_count == 1) {
661 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
663 std::ostringstream os(std::ios::binary);
664 if (!layer->frames) {
665 layer->frames = std::make_shared<std::vector<FrameSpec>>();
667 layer->frames->resize(frame_count);
669 for (int i = 0; i < frame_count; i++) {
675 tiledef.animation.getTextureModifer(os,
676 layer->texture->getOriginalSize(), i);
678 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
679 if (layer->normal_texture)
680 frame.normal_texture = tsrc->getNormalTexture(os.str());
681 frame.flags_texture = layer->flags_texture;
682 (*layer->frames)[i] = frame;
689 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
691 if (style == ALIGN_STYLE_WORLD)
693 if (mode == WORLDALIGN_DISABLE)
695 if (style == ALIGN_STYLE_USER_DEFINED)
697 if (drawtype == NDT_NORMAL)
698 return mode >= WORLDALIGN_FORCE;
699 if (drawtype == NDT_NODEBOX)
700 return mode >= WORLDALIGN_FORCE_NODEBOX;
704 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
705 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
707 // minimap pixel color - the average color of a texture
708 if (tsettings.enable_minimap && !tiledef[0].name.empty())
709 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
711 // Figure out the actual tiles to use
713 for (u32 j = 0; j < 6; j++) {
714 tdef[j] = tiledef[j];
715 if (tdef[j].name.empty())
716 tdef[j].name = "unknown_node.png";
718 // also the overlay tiles
719 TileDef tdef_overlay[6];
720 for (u32 j = 0; j < 6; j++)
721 tdef_overlay[j] = tiledef_overlay[j];
722 // also the special tiles
723 TileDef tdef_spec[6];
724 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
725 tdef_spec[j] = tiledef_special[j];
727 bool is_liquid = false;
729 u8 material_type = (alpha == 255) ?
730 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
735 material_type = (alpha == 255) ?
736 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
743 assert(liquid_type == LIQUID_SOURCE);
744 if (tsettings.opaque_water)
749 case NDT_FLOWINGLIQUID:
750 assert(liquid_type == LIQUID_FLOWING);
752 if (tsettings.opaque_water)
758 visual_solidness = 1;
760 case NDT_GLASSLIKE_FRAMED:
762 visual_solidness = 1;
764 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
766 visual_solidness = 1;
767 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
771 visual_solidness = 1;
773 case NDT_ALLFACES_OPTIONAL:
774 if (tsettings.leaves_style == LEAVES_FANCY) {
775 drawtype = NDT_ALLFACES;
777 visual_solidness = 1;
778 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
779 for (u32 j = 0; j < 6; j++) {
780 if (!tdef_spec[j].name.empty())
781 tdef[j].name = tdef_spec[j].name;
783 drawtype = NDT_GLASSLIKE;
785 visual_solidness = 1;
787 drawtype = NDT_NORMAL;
789 for (TileDef &td : tdef)
790 td.name += std::string("^[noalpha");
793 material_type = TILE_MATERIAL_WAVING_LEAVES;
798 material_type = TILE_MATERIAL_WAVING_PLANTS;
807 material_type = TILE_MATERIAL_WAVING_PLANTS;
808 else if (waving == 2)
809 material_type = TILE_MATERIAL_WAVING_LEAVES;
810 else if (waving == 3)
811 material_type = TILE_MATERIAL_WAVING_LIQUID_BASIC;
819 case NDT_PLANTLIKE_ROOTED:
825 // Vertex alpha is no longer supported, correct if necessary.
826 correctAlpha(tdef, 6);
827 correctAlpha(tdef_overlay, 6);
828 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
831 material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
832 TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT;
834 material_type = (alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE :
835 TILE_MATERIAL_LIQUID_TRANSPARENT;
839 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
841 u8 overlay_material = material_type;
842 if (overlay_material == TILE_MATERIAL_OPAQUE)
843 overlay_material = TILE_MATERIAL_BASIC;
844 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
845 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
847 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
849 // Tiles (fill in f->tiles[])
850 for (u16 j = 0; j < 6; j++) {
851 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
852 tsettings.world_aligned_mode, drawtype);
853 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
854 color, material_type, tile_shader,
855 tdef[j].backface_culling, tsettings);
856 if (!tdef_overlay[j].name.empty())
857 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
858 color, overlay_material, overlay_shader,
859 tdef[j].backface_culling, tsettings);
862 u8 special_material = material_type;
863 if (drawtype == NDT_PLANTLIKE_ROOTED) {
865 special_material = TILE_MATERIAL_WAVING_PLANTS;
866 else if (waving == 2)
867 special_material = TILE_MATERIAL_WAVING_LEAVES;
869 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
871 // Special tiles (fill in f->special_tiles[])
872 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
873 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
874 color, special_material, special_shader,
875 tdef_spec[j].backface_culling, tsettings);
877 if (param_type_2 == CPT2_COLOR ||
878 param_type_2 == CPT2_COLORED_FACEDIR ||
879 param_type_2 == CPT2_COLORED_WALLMOUNTED)
880 palette = tsrc->getPalette(palette_name);
882 if (drawtype == NDT_MESH && !mesh.empty()) {
884 // Read the mesh and apply scale
885 mesh_ptr[0] = client->getMesh(mesh);
887 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
888 scaleMesh(mesh_ptr[0], scale);
889 recalculateBoundingBox(mesh_ptr[0]);
890 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
894 //Cache 6dfacedir and wallmounted rotated clones of meshes
895 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
896 (param_type_2 == CPT2_FACEDIR
897 || param_type_2 == CPT2_COLORED_FACEDIR)) {
898 for (u16 j = 1; j < 24; j++) {
899 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
900 rotateMeshBy6dFacedir(mesh_ptr[j], j);
901 recalculateBoundingBox(mesh_ptr[j]);
902 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
904 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
905 && (param_type_2 == CPT2_WALLMOUNTED ||
906 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
907 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
908 for (u16 j = 1; j < 6; j++) {
909 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
910 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
911 recalculateBoundingBox(mesh_ptr[j]);
912 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
914 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
915 recalculateBoundingBox(mesh_ptr[0]);
916 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
928 NodeDefManager::NodeDefManager()
934 NodeDefManager::~NodeDefManager()
937 for (ContentFeatures &f : m_content_features) {
938 for (auto &j : f.mesh_ptr) {
947 void NodeDefManager::clear()
949 m_content_features.clear();
950 m_name_id_mapping.clear();
951 m_name_id_mapping_with_aliases.clear();
952 m_group_to_items.clear();
954 m_selection_box_union.reset(0,0,0);
955 m_selection_box_int_union.reset(0,0,0);
957 resetNodeResolveState();
959 u32 initial_length = 0;
960 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
961 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
962 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
963 m_content_features.resize(initial_length);
965 // Set CONTENT_UNKNOWN
969 // Insert directly into containers
970 content_t c = CONTENT_UNKNOWN;
971 m_content_features[c] = f;
972 addNameIdMapping(c, f.name);
979 f.drawtype = NDT_AIRLIKE;
980 f.param_type = CPT_LIGHT;
981 f.light_propagates = true;
982 f.sunlight_propagates = true;
986 f.buildable_to = true;
988 f.is_ground_content = true;
989 // Insert directly into containers
990 content_t c = CONTENT_AIR;
991 m_content_features[c] = f;
992 addNameIdMapping(c, f.name);
995 // Set CONTENT_IGNORE
999 f.drawtype = NDT_AIRLIKE;
1000 f.param_type = CPT_NONE;
1001 f.light_propagates = false;
1002 f.sunlight_propagates = false;
1004 f.pointable = false;
1006 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1007 f.is_ground_content = true;
1008 // Insert directly into containers
1009 content_t c = CONTENT_IGNORE;
1010 m_content_features[c] = f;
1011 addNameIdMapping(c, f.name);
1016 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1018 std::unordered_map<std::string, content_t>::const_iterator
1019 i = m_name_id_mapping_with_aliases.find(name);
1020 if(i == m_name_id_mapping_with_aliases.end())
1027 content_t NodeDefManager::getId(const std::string &name) const
1029 content_t id = CONTENT_IGNORE;
1035 bool NodeDefManager::getIds(const std::string &name,
1036 std::vector<content_t> &result) const
1038 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1039 if (name.substr(0,6) != "group:") {
1040 content_t id = CONTENT_IGNORE;
1041 bool exists = getId(name, id);
1043 result.push_back(id);
1046 std::string group = name.substr(6);
1048 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1049 i = m_group_to_items.find(group);
1050 if (i == m_group_to_items.end())
1053 const std::vector<content_t> &items = i->second;
1054 result.insert(result.end(), items.begin(), items.end());
1055 //printf("getIds: %dus\n", t.stop());
1060 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1062 content_t id = CONTENT_UNKNOWN;
1068 // returns CONTENT_IGNORE if no free ID found
1069 content_t NodeDefManager::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);
1178 boxVectorUnion(nodebox.disconnected_top, box_union);
1179 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1180 boxVectorUnion(nodebox.disconnected_front, box_union);
1181 boxVectorUnion(nodebox.disconnected_left, box_union);
1182 boxVectorUnion(nodebox.disconnected_back, box_union);
1183 boxVectorUnion(nodebox.disconnected_right, box_union);
1184 boxVectorUnion(nodebox.disconnected, box_union);
1185 boxVectorUnion(nodebox.disconnected_sides, box_union);
1190 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1191 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1197 inline void NodeDefManager::fixSelectionBoxIntUnion()
1199 m_selection_box_int_union.MinEdge.X = floorf(
1200 m_selection_box_union.MinEdge.X / BS + 0.5f);
1201 m_selection_box_int_union.MinEdge.Y = floorf(
1202 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1203 m_selection_box_int_union.MinEdge.Z = floorf(
1204 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1205 m_selection_box_int_union.MaxEdge.X = ceilf(
1206 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1207 m_selection_box_int_union.MaxEdge.Y = ceilf(
1208 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1209 m_selection_box_int_union.MaxEdge.Z = ceilf(
1210 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1214 void NodeDefManager::eraseIdFromGroups(content_t id)
1216 // For all groups in m_group_to_items...
1217 for (auto iter_groups = m_group_to_items.begin();
1218 iter_groups != m_group_to_items.end();) {
1219 // Get the group items vector.
1220 std::vector<content_t> &items = iter_groups->second;
1222 // Remove any occurence of the id in the group items vector.
1223 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1225 // If group is empty, erase its vector from the map.
1227 iter_groups = m_group_to_items.erase(iter_groups);
1234 // IWritableNodeDefManager
1235 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1239 assert(name != "ignore");
1240 assert(name == def.name);
1242 content_t id = CONTENT_IGNORE;
1243 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1246 if (id == CONTENT_IGNORE) {
1247 warningstream << "NodeDefManager: Absolute "
1248 "limit reached" << std::endl;
1249 return CONTENT_IGNORE;
1251 assert(id != CONTENT_IGNORE);
1252 addNameIdMapping(id, name);
1255 // If there is already ContentFeatures registered for this id, clear old groups
1256 if (id < m_content_features.size())
1257 eraseIdFromGroups(id);
1259 m_content_features[id] = def;
1260 verbosestream << "NodeDefManager: registering content id \"" << id
1261 << "\": name=\"" << def.name << "\""<<std::endl;
1263 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1264 fixSelectionBoxIntUnion();
1266 // Add this content to the list of all groups it belongs to
1267 for (const auto &group : def.groups) {
1268 const std::string &group_name = group.first;
1269 m_group_to_items[group_name].push_back(id);
1276 content_t NodeDefManager::allocateDummy(const std::string &name)
1278 assert(name != ""); // Pre-condition
1281 return set(name, f);
1285 void NodeDefManager::removeNode(const std::string &name)
1290 // Erase name from name ID mapping
1291 content_t id = CONTENT_IGNORE;
1292 if (m_name_id_mapping.getId(name, id)) {
1293 m_name_id_mapping.eraseName(name);
1294 m_name_id_mapping_with_aliases.erase(name);
1297 eraseIdFromGroups(id);
1301 void NodeDefManager::updateAliases(IItemDefManager *idef)
1303 std::set<std::string> all;
1305 m_name_id_mapping_with_aliases.clear();
1306 for (const std::string &name : all) {
1307 const std::string &convert_to = idef->getAlias(name);
1309 if (m_name_id_mapping.getId(convert_to, id)) {
1310 m_name_id_mapping_with_aliases.insert(
1311 std::make_pair(name, id));
1316 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1318 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1319 "overrides to textures" << std::endl;
1321 for (const TextureOverride& texture_override : overrides) {
1323 if (!getId(texture_override.id, id))
1324 continue; // Ignore unknown node
1326 ContentFeatures &nodedef = m_content_features[id];
1328 if (texture_override.hasTarget(OverrideTarget::TOP))
1329 nodedef.tiledef[0].name = texture_override.texture;
1331 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1332 nodedef.tiledef[1].name = texture_override.texture;
1334 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1335 nodedef.tiledef[2].name = texture_override.texture;
1337 if (texture_override.hasTarget(OverrideTarget::LEFT))
1338 nodedef.tiledef[3].name = texture_override.texture;
1340 if (texture_override.hasTarget(OverrideTarget::BACK))
1341 nodedef.tiledef[4].name = texture_override.texture;
1343 if (texture_override.hasTarget(OverrideTarget::FRONT))
1344 nodedef.tiledef[5].name = texture_override.texture;
1348 void NodeDefManager::updateTextures(IGameDef *gamedef,
1349 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1350 void *progress_callback_args)
1353 infostream << "NodeDefManager::updateTextures(): Updating "
1354 "textures in node definitions" << std::endl;
1356 Client *client = (Client *)gamedef;
1357 ITextureSource *tsrc = client->tsrc();
1358 IShaderSource *shdsrc = client->getShaderSource();
1359 scene::IMeshManipulator *meshmanip =
1360 RenderingEngine::get_scene_manager()->getMeshManipulator();
1361 TextureSettings tsettings;
1362 tsettings.readSettings();
1364 u32 size = m_content_features.size();
1366 for (u32 i = 0; i < size; i++) {
1367 ContentFeatures *f = &(m_content_features[i]);
1368 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1369 progress_callback(progress_callback_args, i, size);
1374 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1376 writeU8(os, 1); // version
1378 std::ostringstream os2(std::ios::binary);
1379 for (u32 i = 0; i < m_content_features.size(); i++) {
1380 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1381 || i == CONTENT_UNKNOWN)
1383 const ContentFeatures *f = &m_content_features[i];
1384 if (f->name.empty())
1387 // Wrap it in a string to allow different lengths without
1388 // strict version incompatibilities
1389 std::ostringstream wrapper_os(std::ios::binary);
1390 f->serialize(wrapper_os, protocol_version);
1391 os2<<serializeString(wrapper_os.str());
1393 // must not overflow
1394 u16 next = count + 1;
1395 FATAL_ERROR_IF(next < count, "Overflow");
1398 writeU16(os, count);
1399 os << serializeLongString(os2.str());
1403 void NodeDefManager::deSerialize(std::istream &is)
1406 int version = readU8(is);
1408 throw SerializationError("unsupported NodeDefinitionManager version");
1409 u16 count = readU16(is);
1410 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1412 for (u16 n = 0; n < count; n++) {
1413 u16 i = readU16(is2);
1415 // Read it from the string wrapper
1416 std::string wrapper = deSerializeString(is2);
1417 std::istringstream wrapper_is(wrapper, std::ios::binary);
1418 f.deSerialize(wrapper_is);
1420 // Check error conditions
1421 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1422 warningstream << "NodeDefManager::deSerialize(): "
1423 "not changing builtin node " << i << std::endl;
1426 if (f.name.empty()) {
1427 warningstream << "NodeDefManager::deSerialize(): "
1428 "received empty name" << std::endl;
1434 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1435 warningstream << "NodeDefManager::deSerialize(): "
1436 "already defined with different ID: " << f.name << std::endl;
1440 // All is ok, add node definition with the requested ID
1441 if (i >= m_content_features.size())
1442 m_content_features.resize((u32)(i) + 1);
1443 m_content_features[i] = f;
1444 addNameIdMapping(i, f.name);
1445 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1447 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1448 fixSelectionBoxIntUnion();
1451 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1452 // are not sent, resolve them client-side too.
1457 void NodeDefManager::addNameIdMapping(content_t i, std::string name)
1459 m_name_id_mapping.set(i, name);
1460 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1464 NodeDefManager *createNodeDefManager()
1466 return new NodeDefManager();
1470 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1473 if (m_node_registration_complete)
1474 nr->nodeResolveInternal();
1476 m_pending_resolve_callbacks.push_back(nr);
1480 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1482 size_t len = m_pending_resolve_callbacks.size();
1483 for (size_t i = 0; i != len; i++) {
1484 if (nr != m_pending_resolve_callbacks[i])
1488 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1489 m_pending_resolve_callbacks.resize(len);
1497 void NodeDefManager::runNodeResolveCallbacks()
1499 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1500 NodeResolver *nr = m_pending_resolve_callbacks[i];
1501 nr->nodeResolveInternal();
1504 m_pending_resolve_callbacks.clear();
1508 void NodeDefManager::resetNodeResolveState()
1510 m_node_registration_complete = false;
1511 m_pending_resolve_callbacks.clear();
1514 static void removeDupes(std::vector<content_t> &list)
1516 std::sort(list.begin(), list.end());
1517 auto new_end = std::unique(list.begin(), list.end());
1518 list.erase(new_end, list.end());
1521 void NodeDefManager::resolveCrossrefs()
1523 for (ContentFeatures &f : m_content_features) {
1524 if (f.liquid_type != LIQUID_NONE) {
1525 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1526 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1529 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1532 for (const std::string &name : f.connects_to) {
1533 getIds(name, f.connects_to_ids);
1535 removeDupes(f.connects_to_ids);
1539 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1540 u8 connect_face) const
1542 const ContentFeatures &f1 = get(from);
1544 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1547 // lookup target in connected set
1548 if (!CONTAINS(f1.connects_to_ids, to.param0))
1551 const ContentFeatures &f2 = get(to);
1553 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1554 // ignores actually looking if back connection exists
1555 return CONTAINS(f2.connects_to_ids, from.param0);
1557 // does to node declare usable faces?
1558 if (f2.connect_sides > 0) {
1559 if ((f2.param_type_2 == CPT2_FACEDIR ||
1560 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1561 && (connect_face >= 4)) {
1562 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1563 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1565 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1567 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1568 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, 0, 0, 0, 0, 0,
1571 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1572 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1574 return (f2.connect_sides
1575 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1577 return (f2.connect_sides & connect_face);
1579 // the target is just a regular node, so connect no matter back connection
1587 NodeResolver::NodeResolver()
1589 m_nodenames.reserve(16);
1590 m_nnlistsizes.reserve(4);
1594 NodeResolver::~NodeResolver()
1596 if (!m_resolve_done && m_ndef)
1597 m_ndef->cancelNodeResolveCallback(this);
1601 void NodeResolver::cloneTo(NodeResolver *res) const
1603 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1604 " after resolving has completed");
1605 /* We don't actually do anything significant. Since the node resolving has
1606 * already completed, the class that called us will already have the
1607 * resolved IDs in its data structures (which it copies on its own) */
1608 res->m_ndef = m_ndef;
1609 res->m_resolve_done = true;
1613 void NodeResolver::nodeResolveInternal()
1615 m_nodenames_idx = 0;
1616 m_nnlistsizes_idx = 0;
1619 m_resolve_done = true;
1621 m_nodenames.clear();
1622 m_nnlistsizes.clear();
1626 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1627 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1629 if (m_nodenames_idx == m_nodenames.size()) {
1630 *result_out = c_fallback;
1631 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1636 std::string name = m_nodenames[m_nodenames_idx++];
1638 bool success = m_ndef->getId(name, c);
1639 if (!success && !node_alt.empty()) {
1641 success = m_ndef->getId(name, c);
1645 if (error_on_fallback)
1646 errorstream << "NodeResolver: failed to resolve node name '" << name
1647 << "'." << std::endl;
1656 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1657 bool all_required, content_t c_fallback)
1659 bool success = true;
1661 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1662 errorstream << "NodeResolver: no more node lists" << std::endl;
1666 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1669 if (m_nodenames_idx == m_nodenames.size()) {
1670 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1675 std::string &name = m_nodenames[m_nodenames_idx++];
1677 if (name.substr(0,6) != "group:") {
1678 if (m_ndef->getId(name, c)) {
1679 result_out->push_back(c);
1680 } else if (all_required) {
1681 errorstream << "NodeResolver: failed to resolve node name '"
1682 << name << "'." << std::endl;
1683 result_out->push_back(c_fallback);
1687 m_ndef->getIds(name, *result_out);