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/tile.h"
26 #include <IMeshManipulator.h>
30 #include "nameidmapping.h"
31 #include "util/numeric.h"
32 #include "util/serialize.h"
33 #include "exceptions.h"
37 #include <fstream> // Used in applyTextureOverrides()
45 type = NODEBOX_REGULAR;
48 // default is sign/ladder-like
49 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
50 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
51 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
52 // no default for other parts
54 connect_bottom.clear();
55 connect_front.clear();
58 connect_right.clear();
61 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
64 if (protocol_version >= 27)
66 else if (protocol_version >= 21)
74 writeU8(os, NODEBOX_FIXED);
78 writeU16(os, fixed.size());
79 for (std::vector<aabb3f>::const_iterator
81 i != fixed.end(); ++i)
83 writeV3F1000(os, i->MinEdge);
84 writeV3F1000(os, i->MaxEdge);
87 case NODEBOX_WALLMOUNTED:
90 writeV3F1000(os, wall_top.MinEdge);
91 writeV3F1000(os, wall_top.MaxEdge);
92 writeV3F1000(os, wall_bottom.MinEdge);
93 writeV3F1000(os, wall_bottom.MaxEdge);
94 writeV3F1000(os, wall_side.MinEdge);
95 writeV3F1000(os, wall_side.MaxEdge);
97 case NODEBOX_CONNECTED:
99 // send old clients nodes that can't be walked through
101 writeU8(os, NODEBOX_FIXED);
104 writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2));
105 writeV3F1000(os, v3f(BS/2, BS/2, BS/2));
109 #define WRITEBOX(box) do { \
110 writeU16(os, (box).size()); \
111 for (std::vector<aabb3f>::const_iterator \
113 i != (box).end(); ++i) { \
114 writeV3F1000(os, i->MinEdge); \
115 writeV3F1000(os, i->MaxEdge); \
119 WRITEBOX(connect_top);
120 WRITEBOX(connect_bottom);
121 WRITEBOX(connect_front);
122 WRITEBOX(connect_left);
123 WRITEBOX(connect_back);
124 WRITEBOX(connect_right);
133 void NodeBox::deSerialize(std::istream &is)
135 int version = readU8(is);
136 if (version < 1 || version > 3)
137 throw SerializationError("unsupported NodeBox version");
141 type = (enum NodeBoxType)readU8(is);
143 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
145 u16 fixed_count = readU16(is);
149 box.MinEdge = readV3F1000(is);
150 box.MaxEdge = readV3F1000(is);
151 fixed.push_back(box);
154 else if(type == NODEBOX_WALLMOUNTED)
156 wall_top.MinEdge = readV3F1000(is);
157 wall_top.MaxEdge = readV3F1000(is);
158 wall_bottom.MinEdge = readV3F1000(is);
159 wall_bottom.MaxEdge = readV3F1000(is);
160 wall_side.MinEdge = readV3F1000(is);
161 wall_side.MaxEdge = readV3F1000(is);
163 else if (type == NODEBOX_CONNECTED)
165 #define READBOXES(box) do { \
166 count = readU16(is); \
167 (box).reserve(count); \
169 v3f min = readV3F1000(is); \
170 v3f max = readV3F1000(is); \
171 (box).push_back(aabb3f(min, max)); }; } while (0)
176 READBOXES(connect_top);
177 READBOXES(connect_bottom);
178 READBOXES(connect_front);
179 READBOXES(connect_left);
180 READBOXES(connect_back);
181 READBOXES(connect_right);
189 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
191 if (protocol_version >= 26)
193 else if (protocol_version >= 17)
197 os<<serializeString(name);
198 writeU8(os, animation.type);
199 writeU16(os, animation.aspect_w);
200 writeU16(os, animation.aspect_h);
201 writeF1000(os, animation.length);
202 if (protocol_version >= 17)
203 writeU8(os, backface_culling);
204 if (protocol_version >= 26) {
205 writeU8(os, tileable_horizontal);
206 writeU8(os, tileable_vertical);
210 void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
212 int version = readU8(is);
213 name = deSerializeString(is);
214 animation.type = (TileAnimationType)readU8(is);
215 animation.aspect_w = readU16(is);
216 animation.aspect_h = readU16(is);
217 animation.length = readF1000(is);
219 backface_culling = readU8(is);
221 tileable_horizontal = readU8(is);
222 tileable_vertical = readU8(is);
225 if ((contenfeatures_version < 8) &&
226 ((drawtype == NDT_MESH) ||
227 (drawtype == NDT_FIRELIKE) ||
228 (drawtype == NDT_LIQUID) ||
229 (drawtype == NDT_PLANTLIKE)))
230 backface_culling = false;
235 SimpleSoundSpec serialization
238 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
241 os<<serializeString(ss.name);
242 writeF1000(os, ss.gain);
244 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
246 ss.name = deSerializeString(is);
247 ss.gain = readF1000(is);
250 void TextureSettings::readSettings()
252 connected_glass = g_settings->getBool("connected_glass");
253 opaque_water = g_settings->getBool("opaque_water");
254 bool enable_shaders = g_settings->getBool("enable_shaders");
255 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
256 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
257 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
258 enable_minimap = g_settings->getBool("enable_minimap");
259 std::string leaves_style_str = g_settings->get("leaves_style");
261 use_normal_texture = enable_shaders &&
262 (enable_bumpmapping || enable_parallax_occlusion);
263 if (leaves_style_str == "fancy") {
264 leaves_style = LEAVES_FANCY;
265 } else if (leaves_style_str == "simple") {
266 leaves_style = LEAVES_SIMPLE;
268 leaves_style = LEAVES_OPAQUE;
276 ContentFeatures::ContentFeatures()
281 ContentFeatures::~ContentFeatures()
285 void ContentFeatures::reset()
292 visual_solidness = 0;
293 backface_culling = true;
296 has_on_construct = false;
297 has_on_destruct = false;
298 has_after_destruct = false;
302 NOTE: Most of this is always overridden by the default values given
307 // Unknown nodes can be dug
308 groups["dig_immediate"] = 2;
309 drawtype = NDT_NORMAL;
312 for(u32 i = 0; i < 24; i++)
314 minimap_color = video::SColor(0, 0, 0, 0);
317 for(u32 i = 0; i < 6; i++)
318 tiledef[i] = TileDef();
319 for(u16 j = 0; j < CF_SPECIAL_COUNT; j++)
320 tiledef_special[j] = TileDef();
322 post_effect_color = video::SColor(0, 0, 0, 0);
323 param_type = CPT_NONE;
324 param_type_2 = CPT2_NONE;
325 is_ground_content = false;
326 light_propagates = false;
327 sunlight_propagates = false;
332 buildable_to = false;
334 rightclickable = true;
336 liquid_type = LIQUID_NONE;
337 liquid_alternative_flowing = "";
338 liquid_alternative_source = "";
339 liquid_viscosity = 0;
340 liquid_renewable = true;
341 liquid_range = LIQUID_LEVEL_MAX+1;
344 damage_per_second = 0;
345 node_box = NodeBox();
346 selection_box = NodeBox();
347 collision_box = NodeBox();
349 legacy_facedir_simple = false;
350 legacy_wallmounted = false;
351 sound_footstep = SimpleSoundSpec();
352 sound_dig = SimpleSoundSpec("__group");
353 sound_dug = SimpleSoundSpec();
355 connects_to_ids.clear();
359 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
361 if(protocol_version < 24){
362 serializeOld(os, protocol_version);
366 writeU8(os, protocol_version < 27 ? 7 : 8);
368 os<<serializeString(name);
369 writeU16(os, groups.size());
370 for(ItemGroupList::const_iterator
371 i = groups.begin(); i != groups.end(); ++i){
372 os<<serializeString(i->first);
373 writeS16(os, i->second);
375 writeU8(os, drawtype);
376 writeF1000(os, visual_scale);
378 for(u32 i = 0; i < 6; i++)
379 tiledef[i].serialize(os, protocol_version);
380 writeU8(os, CF_SPECIAL_COUNT);
381 for(u32 i = 0; i < CF_SPECIAL_COUNT; i++){
382 tiledef_special[i].serialize(os, protocol_version);
385 writeU8(os, post_effect_color.getAlpha());
386 writeU8(os, post_effect_color.getRed());
387 writeU8(os, post_effect_color.getGreen());
388 writeU8(os, post_effect_color.getBlue());
389 writeU8(os, param_type);
390 writeU8(os, param_type_2);
391 writeU8(os, is_ground_content);
392 writeU8(os, light_propagates);
393 writeU8(os, sunlight_propagates);
394 writeU8(os, walkable);
395 writeU8(os, pointable);
396 writeU8(os, diggable);
397 writeU8(os, climbable);
398 writeU8(os, buildable_to);
399 os<<serializeString(""); // legacy: used to be metadata_name
400 writeU8(os, liquid_type);
401 os<<serializeString(liquid_alternative_flowing);
402 os<<serializeString(liquid_alternative_source);
403 writeU8(os, liquid_viscosity);
404 writeU8(os, liquid_renewable);
405 writeU8(os, light_source);
406 writeU32(os, damage_per_second);
407 node_box.serialize(os, protocol_version);
408 selection_box.serialize(os, protocol_version);
409 writeU8(os, legacy_facedir_simple);
410 writeU8(os, legacy_wallmounted);
411 serializeSimpleSoundSpec(sound_footstep, os);
412 serializeSimpleSoundSpec(sound_dig, os);
413 serializeSimpleSoundSpec(sound_dug, os);
414 writeU8(os, rightclickable);
415 writeU8(os, drowning);
416 writeU8(os, leveled);
417 writeU8(os, liquid_range);
419 // Stuff below should be moved to correct place in a version that otherwise changes
420 // the protocol version
421 os<<serializeString(mesh);
422 collision_box.serialize(os, protocol_version);
423 writeU8(os, floodable);
424 writeU16(os, connects_to_ids.size());
425 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
426 i != connects_to_ids.end(); ++i)
428 writeU8(os, connect_sides);
431 void ContentFeatures::deSerialize(std::istream &is)
433 int version = readU8(is);
435 deSerializeOld(is, version);
437 } else if (version > 8) {
438 throw SerializationError("unsupported ContentFeatures version");
441 name = deSerializeString(is);
443 u32 groups_size = readU16(is);
444 for(u32 i = 0; i < groups_size; i++){
445 std::string name = deSerializeString(is);
446 int value = readS16(is);
447 groups[name] = value;
449 drawtype = (enum NodeDrawType)readU8(is);
451 visual_scale = readF1000(is);
453 throw SerializationError("unsupported tile count");
454 for(u32 i = 0; i < 6; i++)
455 tiledef[i].deSerialize(is, version, drawtype);
456 if(readU8(is) != CF_SPECIAL_COUNT)
457 throw SerializationError("unsupported CF_SPECIAL_COUNT");
458 for(u32 i = 0; i < CF_SPECIAL_COUNT; i++)
459 tiledef_special[i].deSerialize(is, version, drawtype);
461 post_effect_color.setAlpha(readU8(is));
462 post_effect_color.setRed(readU8(is));
463 post_effect_color.setGreen(readU8(is));
464 post_effect_color.setBlue(readU8(is));
465 param_type = (enum ContentParamType)readU8(is);
466 param_type_2 = (enum ContentParamType2)readU8(is);
467 is_ground_content = readU8(is);
468 light_propagates = readU8(is);
469 sunlight_propagates = readU8(is);
470 walkable = readU8(is);
471 pointable = readU8(is);
472 diggable = readU8(is);
473 climbable = readU8(is);
474 buildable_to = readU8(is);
475 deSerializeString(is); // legacy: used to be metadata_name
476 liquid_type = (enum LiquidType)readU8(is);
477 liquid_alternative_flowing = deSerializeString(is);
478 liquid_alternative_source = deSerializeString(is);
479 liquid_viscosity = readU8(is);
480 liquid_renewable = readU8(is);
481 light_source = readU8(is);
482 damage_per_second = readU32(is);
483 node_box.deSerialize(is);
484 selection_box.deSerialize(is);
485 legacy_facedir_simple = readU8(is);
486 legacy_wallmounted = readU8(is);
487 deSerializeSimpleSoundSpec(sound_footstep, is);
488 deSerializeSimpleSoundSpec(sound_dig, is);
489 deSerializeSimpleSoundSpec(sound_dug, is);
490 rightclickable = readU8(is);
491 drowning = readU8(is);
492 leveled = readU8(is);
493 liquid_range = readU8(is);
495 // If you add anything here, insert it primarily inside the try-catch
496 // block to not need to increase the version.
498 // Stuff below should be moved to correct place in a version that
499 // otherwise changes the protocol version
500 mesh = deSerializeString(is);
501 collision_box.deSerialize(is);
502 floodable = readU8(is);
503 u16 connects_to_size = readU16(is);
504 connects_to_ids.clear();
505 for (u16 i = 0; i < connects_to_size; i++)
506 connects_to_ids.insert(readU16(is));
507 connect_sides = readU8(is);
508 }catch(SerializationError &e) {};
512 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
513 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
514 bool backface_culling, u8 alpha, u8 material_type)
516 tile->shader_id = shader_id;
517 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
519 tile->material_type = material_type;
521 // Normal texture and shader flags texture
522 if (use_normal_texture) {
523 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
525 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
528 tile->material_flags = 0;
529 if (backface_culling)
530 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
531 if (tiledef->animation.type == TAT_VERTICAL_FRAMES)
532 tile->material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
533 if (tiledef->tileable_horizontal)
534 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
535 if (tiledef->tileable_vertical)
536 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
538 // Animation parameters
540 if (tile->material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) {
541 // Get texture size to determine frame count by aspect ratio
542 v2u32 size = tile->texture->getOriginalSize();
543 int frame_height = (float)size.X /
544 (float)tiledef->animation.aspect_w *
545 (float)tiledef->animation.aspect_h;
546 frame_count = size.Y / frame_height;
547 int frame_length_ms = 1000.0 * tiledef->animation.length / frame_count;
548 tile->animation_frame_count = frame_count;
549 tile->animation_frame_length_ms = frame_length_ms;
552 if (frame_count == 1) {
553 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
555 std::ostringstream os(std::ios::binary);
556 tile->frames.resize(frame_count);
558 for (int i = 0; i < frame_count; i++) {
563 os << tiledef->name << "^[verticalframe:"
564 << frame_count << ":" << i;
566 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
567 if (tile->normal_texture)
568 frame.normal_texture = tsrc->getNormalTexture(os.str());
569 frame.flags_texture = tile->flags_texture;
570 tile->frames[i] = frame;
577 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
578 scene::ISceneManager *smgr, scene::IMeshManipulator *meshmanip,
579 IGameDef *gamedef, const TextureSettings &tsettings)
581 // minimap pixel color - the average color of a texture
582 if (tsettings.enable_minimap && tiledef[0].name != "")
583 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
585 // Figure out the actual tiles to use
587 for (u32 j = 0; j < 6; j++) {
588 tdef[j] = tiledef[j];
589 if (tdef[j].name == "")
590 tdef[j].name = "unknown_node.png";
593 bool is_liquid = false;
594 bool is_water_surface = false;
596 u8 material_type = (alpha == 255) ?
597 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
608 assert(liquid_type == LIQUID_SOURCE);
609 if (tsettings.opaque_water)
614 case NDT_FLOWINGLIQUID:
615 assert(liquid_type == LIQUID_FLOWING);
617 if (tsettings.opaque_water)
623 visual_solidness = 1;
625 case NDT_GLASSLIKE_FRAMED:
627 visual_solidness = 1;
629 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
631 visual_solidness = 1;
632 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
636 visual_solidness = 1;
638 case NDT_ALLFACES_OPTIONAL:
639 if (tsettings.leaves_style == LEAVES_FANCY) {
640 drawtype = NDT_ALLFACES;
642 visual_solidness = 1;
643 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
644 for (u32 j = 0; j < 6; j++) {
645 if (tiledef_special[j].name != "")
646 tdef[j].name = tiledef_special[j].name;
648 drawtype = NDT_GLASSLIKE;
650 visual_solidness = 1;
652 drawtype = NDT_NORMAL;
654 for (u32 i = 0; i < 6; i++)
655 tdef[i].name += std::string("^[noalpha");
658 material_type = TILE_MATERIAL_WAVING_LEAVES;
663 material_type = TILE_MATERIAL_WAVING_PLANTS;
681 material_type = (alpha == 255) ?
682 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
683 if (name == "default:water_source")
684 is_water_surface = true;
688 for (u16 j = 0; j < 6; j++) {
689 tile_shader[j] = shdsrc->getShader("nodes_shader",
690 material_type, drawtype);
693 if (is_water_surface) {
694 tile_shader[0] = shdsrc->getShader("water_surface_shader",
695 material_type, drawtype);
698 // Tiles (fill in f->tiles[])
699 for (u16 j = 0; j < 6; j++) {
700 fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j],
701 tsettings.use_normal_texture,
702 tiledef[j].backface_culling, alpha, material_type);
705 // Special tiles (fill in f->special_tiles[])
706 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
707 fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j],
708 tile_shader[j], tsettings.use_normal_texture,
709 tiledef_special[j].backface_culling, alpha, material_type);
712 if ((drawtype == NDT_MESH) && (mesh != "")) {
714 // Read the mesh and apply scale
715 mesh_ptr[0] = gamedef->getMesh(mesh);
717 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
718 scaleMesh(mesh_ptr[0], scale);
719 recalculateBoundingBox(mesh_ptr[0]);
720 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
722 } else if ((drawtype == NDT_NODEBOX) &&
723 ((node_box.type == NODEBOX_REGULAR) ||
724 (node_box.type == NODEBOX_FIXED)) &&
725 (!node_box.fixed.empty())) {
726 //Convert regular nodebox nodes to meshnodes
727 //Change the drawtype and apply scale
729 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
730 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
731 scaleMesh(mesh_ptr[0], scale);
732 recalculateBoundingBox(mesh_ptr[0]);
733 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
736 //Cache 6dfacedir and wallmounted rotated clones of meshes
737 if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_FACEDIR)) {
738 for (u16 j = 1; j < 24; j++) {
739 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
740 rotateMeshBy6dFacedir(mesh_ptr[j], j);
741 recalculateBoundingBox(mesh_ptr[j]);
742 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
744 } else if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_WALLMOUNTED)) {
745 static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
746 for (u16 j = 1; j < 6; j++) {
747 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
748 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
749 recalculateBoundingBox(mesh_ptr[j]);
750 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
752 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
753 recalculateBoundingBox(mesh_ptr[0]);
754 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
763 class CNodeDefManager: public IWritableNodeDefManager {
766 virtual ~CNodeDefManager();
768 virtual IWritableNodeDefManager *clone();
769 inline virtual const ContentFeatures& get(content_t c) const;
770 inline virtual const ContentFeatures& get(const MapNode &n) const;
771 virtual bool getId(const std::string &name, content_t &result) const;
772 virtual content_t getId(const std::string &name) const;
773 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
774 virtual const ContentFeatures& get(const std::string &name) const;
775 content_t allocateId();
776 virtual content_t set(const std::string &name, const ContentFeatures &def);
777 virtual content_t allocateDummy(const std::string &name);
778 virtual void updateAliases(IItemDefManager *idef);
779 virtual void applyTextureOverrides(const std::string &override_filepath);
780 virtual void updateTextures(IGameDef *gamedef,
781 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
782 void *progress_cbk_args);
783 void serialize(std::ostream &os, u16 protocol_version) const;
784 void deSerialize(std::istream &is);
786 inline virtual bool getNodeRegistrationStatus() const;
787 inline virtual void setNodeRegistrationStatus(bool completed);
789 virtual void pendNodeResolve(NodeResolver *nr);
790 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
791 virtual void runNodeResolveCallbacks();
792 virtual void resetNodeResolveState();
793 virtual void mapNodeboxConnections();
794 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
797 void addNameIdMapping(content_t i, std::string name);
799 // Features indexed by id
800 std::vector<ContentFeatures> m_content_features;
802 // A mapping for fast converting back and forth between names and ids
803 NameIdMapping m_name_id_mapping;
805 // Like m_name_id_mapping, but only from names to ids, and includes
806 // item aliases too. Updated by updateAliases()
807 // Note: Not serialized.
809 std::map<std::string, content_t> m_name_id_mapping_with_aliases;
811 // A mapping from groups to a list of content_ts (and their levels)
812 // that belong to it. Necessary for a direct lookup in getIds().
813 // Note: Not serialized.
814 std::map<std::string, GroupItems> m_group_to_items;
816 // Next possibly free id
819 // NodeResolvers to callback once node registration has ended
820 std::vector<NodeResolver *> m_pending_resolve_callbacks;
822 // True when all nodes have been registered
823 bool m_node_registration_complete;
827 CNodeDefManager::CNodeDefManager()
833 CNodeDefManager::~CNodeDefManager()
836 for (u32 i = 0; i < m_content_features.size(); i++) {
837 ContentFeatures *f = &m_content_features[i];
838 for (u32 j = 0; j < 24; j++) {
840 f->mesh_ptr[j]->drop();
847 void CNodeDefManager::clear()
849 m_content_features.clear();
850 m_name_id_mapping.clear();
851 m_name_id_mapping_with_aliases.clear();
852 m_group_to_items.clear();
855 resetNodeResolveState();
857 u32 initial_length = 0;
858 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
859 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
860 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
861 m_content_features.resize(initial_length);
863 // Set CONTENT_UNKNOWN
867 // Insert directly into containers
868 content_t c = CONTENT_UNKNOWN;
869 m_content_features[c] = f;
870 addNameIdMapping(c, f.name);
877 f.drawtype = NDT_AIRLIKE;
878 f.param_type = CPT_LIGHT;
879 f.light_propagates = true;
880 f.sunlight_propagates = true;
884 f.buildable_to = true;
886 f.is_ground_content = true;
887 // Insert directly into containers
888 content_t c = CONTENT_AIR;
889 m_content_features[c] = f;
890 addNameIdMapping(c, f.name);
893 // Set CONTENT_IGNORE
897 f.drawtype = NDT_AIRLIKE;
898 f.param_type = CPT_NONE;
899 f.light_propagates = false;
900 f.sunlight_propagates = false;
904 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
905 f.is_ground_content = true;
906 // Insert directly into containers
907 content_t c = CONTENT_IGNORE;
908 m_content_features[c] = f;
909 addNameIdMapping(c, f.name);
914 IWritableNodeDefManager *CNodeDefManager::clone()
916 CNodeDefManager *mgr = new CNodeDefManager();
922 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
924 return c < m_content_features.size()
925 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
929 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
931 return get(n.getContent());
935 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
937 std::map<std::string, content_t>::const_iterator
938 i = m_name_id_mapping_with_aliases.find(name);
939 if(i == m_name_id_mapping_with_aliases.end())
946 content_t CNodeDefManager::getId(const std::string &name) const
948 content_t id = CONTENT_IGNORE;
954 bool CNodeDefManager::getIds(const std::string &name,
955 std::set<content_t> &result) const
957 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
958 if (name.substr(0,6) != "group:") {
959 content_t id = CONTENT_IGNORE;
960 bool exists = getId(name, id);
965 std::string group = name.substr(6);
967 std::map<std::string, GroupItems>::const_iterator
968 i = m_group_to_items.find(group);
969 if (i == m_group_to_items.end())
972 const GroupItems &items = i->second;
973 for (GroupItems::const_iterator j = items.begin();
974 j != items.end(); ++j) {
975 if ((*j).second != 0)
976 result.insert((*j).first);
978 //printf("getIds: %dus\n", t.stop());
983 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
985 content_t id = CONTENT_UNKNOWN;
991 // returns CONTENT_IGNORE if no free ID found
992 content_t CNodeDefManager::allocateId()
994 for (content_t id = m_next_id;
995 id >= m_next_id; // overflow?
997 while (id >= m_content_features.size()) {
998 m_content_features.push_back(ContentFeatures());
1000 const ContentFeatures &f = m_content_features[id];
1006 // If we arrive here, an overflow occurred in id.
1007 // That means no ID was found
1008 return CONTENT_IGNORE;
1012 // IWritableNodeDefManager
1013 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1017 assert(name == def.name);
1019 // Don't allow redefining ignore (but allow air and unknown)
1020 if (name == "ignore") {
1021 warningstream << "NodeDefManager: Ignoring "
1022 "CONTENT_IGNORE redefinition"<<std::endl;
1023 return CONTENT_IGNORE;
1026 content_t id = CONTENT_IGNORE;
1027 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1030 if (id == CONTENT_IGNORE) {
1031 warningstream << "NodeDefManager: Absolute "
1032 "limit reached" << std::endl;
1033 return CONTENT_IGNORE;
1035 assert(id != CONTENT_IGNORE);
1036 addNameIdMapping(id, name);
1038 m_content_features[id] = def;
1039 verbosestream << "NodeDefManager: registering content id \"" << id
1040 << "\": name=\"" << def.name << "\""<<std::endl;
1042 // Add this content to the list of all groups it belongs to
1043 // FIXME: This should remove a node from groups it no longer
1044 // belongs to when a node is re-registered
1045 for (ItemGroupList::const_iterator i = def.groups.begin();
1046 i != def.groups.end(); ++i) {
1047 std::string group_name = i->first;
1049 std::map<std::string, GroupItems>::iterator
1050 j = m_group_to_items.find(group_name);
1051 if (j == m_group_to_items.end()) {
1052 m_group_to_items[group_name].push_back(
1053 std::make_pair(id, i->second));
1055 GroupItems &items = j->second;
1056 items.push_back(std::make_pair(id, i->second));
1063 content_t CNodeDefManager::allocateDummy(const std::string &name)
1065 assert(name != ""); // Pre-condition
1068 return set(name, f);
1072 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1074 std::set<std::string> all = idef->getAll();
1075 m_name_id_mapping_with_aliases.clear();
1076 for (std::set<std::string>::iterator
1077 i = all.begin(); i != all.end(); ++i) {
1078 std::string name = *i;
1079 std::string convert_to = idef->getAlias(name);
1081 if (m_name_id_mapping.getId(convert_to, id)) {
1082 m_name_id_mapping_with_aliases.insert(
1083 std::make_pair(name, id));
1088 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1090 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1091 "overrides to textures from " << override_filepath << std::endl;
1093 std::ifstream infile(override_filepath.c_str());
1096 while (std::getline(infile, line)) {
1098 if (trim(line) == "")
1100 std::vector<std::string> splitted = str_split(line, ' ');
1101 if (splitted.size() != 3) {
1102 errorstream << override_filepath
1103 << ":" << line_c << " Could not apply texture override \""
1104 << line << "\": Syntax error" << std::endl;
1109 if (!getId(splitted[0], id)) {
1110 infostream << override_filepath
1111 << ":" << line_c << " Could not apply texture override \""
1112 << line << "\": Unknown node \""
1113 << splitted[0] << "\"" << std::endl;
1117 ContentFeatures &nodedef = m_content_features[id];
1119 if (splitted[1] == "top")
1120 nodedef.tiledef[0].name = splitted[2];
1121 else if (splitted[1] == "bottom")
1122 nodedef.tiledef[1].name = splitted[2];
1123 else if (splitted[1] == "right")
1124 nodedef.tiledef[2].name = splitted[2];
1125 else if (splitted[1] == "left")
1126 nodedef.tiledef[3].name = splitted[2];
1127 else if (splitted[1] == "back")
1128 nodedef.tiledef[4].name = splitted[2];
1129 else if (splitted[1] == "front")
1130 nodedef.tiledef[5].name = splitted[2];
1131 else if (splitted[1] == "all" || splitted[1] == "*")
1132 for (int i = 0; i < 6; i++)
1133 nodedef.tiledef[i].name = splitted[2];
1134 else if (splitted[1] == "sides")
1135 for (int i = 2; i < 6; i++)
1136 nodedef.tiledef[i].name = splitted[2];
1138 errorstream << override_filepath
1139 << ":" << line_c << " Could not apply texture override \""
1140 << line << "\": Unknown node side \""
1141 << splitted[1] << "\"" << std::endl;
1147 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1148 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1149 void *progress_callback_args)
1152 infostream << "CNodeDefManager::updateTextures(): Updating "
1153 "textures in node definitions" << std::endl;
1154 ITextureSource *tsrc = gamedef->tsrc();
1155 IShaderSource *shdsrc = gamedef->getShaderSource();
1156 scene::ISceneManager* smgr = gamedef->getSceneManager();
1157 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1158 TextureSettings tsettings;
1159 tsettings.readSettings();
1161 u32 size = m_content_features.size();
1163 for (u32 i = 0; i < size; i++) {
1164 m_content_features[i].updateTextures(tsrc, shdsrc, smgr, meshmanip, gamedef, tsettings);
1165 progress_callback(progress_callback_args, i, size);
1170 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1172 writeU8(os, 1); // version
1174 std::ostringstream os2(std::ios::binary);
1175 for (u32 i = 0; i < m_content_features.size(); i++) {
1176 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1177 || i == CONTENT_UNKNOWN)
1179 const ContentFeatures *f = &m_content_features[i];
1183 // Wrap it in a string to allow different lengths without
1184 // strict version incompatibilities
1185 std::ostringstream wrapper_os(std::ios::binary);
1186 f->serialize(wrapper_os, protocol_version);
1187 os2<<serializeString(wrapper_os.str());
1189 // must not overflow
1190 u16 next = count + 1;
1191 FATAL_ERROR_IF(next < count, "Overflow");
1194 writeU16(os, count);
1195 os << serializeLongString(os2.str());
1199 void CNodeDefManager::deSerialize(std::istream &is)
1202 int version = readU8(is);
1204 throw SerializationError("unsupported NodeDefinitionManager version");
1205 u16 count = readU16(is);
1206 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1208 for (u16 n = 0; n < count; n++) {
1209 u16 i = readU16(is2);
1211 // Read it from the string wrapper
1212 std::string wrapper = deSerializeString(is2);
1213 std::istringstream wrapper_is(wrapper, std::ios::binary);
1214 f.deSerialize(wrapper_is);
1216 // Check error conditions
1217 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1218 warningstream << "NodeDefManager::deSerialize(): "
1219 "not changing builtin node " << i << std::endl;
1223 warningstream << "NodeDefManager::deSerialize(): "
1224 "received empty name" << std::endl;
1230 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1231 warningstream << "NodeDefManager::deSerialize(): "
1232 "already defined with different ID: " << f.name << std::endl;
1236 // All is ok, add node definition with the requested ID
1237 if (i >= m_content_features.size())
1238 m_content_features.resize((u32)(i) + 1);
1239 m_content_features[i] = f;
1240 addNameIdMapping(i, f.name);
1241 verbosestream << "deserialized " << f.name << std::endl;
1246 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1248 m_name_id_mapping.set(i, name);
1249 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1253 IWritableNodeDefManager *createNodeDefManager()
1255 return new CNodeDefManager();
1259 //// Serialization of old ContentFeatures formats
1260 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1262 if (protocol_version == 13)
1264 writeU8(os, 5); // version
1265 os<<serializeString(name);
1266 writeU16(os, groups.size());
1267 for (ItemGroupList::const_iterator
1268 i = groups.begin(); i != groups.end(); ++i) {
1269 os<<serializeString(i->first);
1270 writeS16(os, i->second);
1272 writeU8(os, drawtype);
1273 writeF1000(os, visual_scale);
1275 for (u32 i = 0; i < 6; i++)
1276 tiledef[i].serialize(os, protocol_version);
1277 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1279 for (u32 i = 0; i < 2; i++)
1280 tiledef_special[i].serialize(os, protocol_version);
1282 writeU8(os, post_effect_color.getAlpha());
1283 writeU8(os, post_effect_color.getRed());
1284 writeU8(os, post_effect_color.getGreen());
1285 writeU8(os, post_effect_color.getBlue());
1286 writeU8(os, param_type);
1287 writeU8(os, param_type_2);
1288 writeU8(os, is_ground_content);
1289 writeU8(os, light_propagates);
1290 writeU8(os, sunlight_propagates);
1291 writeU8(os, walkable);
1292 writeU8(os, pointable);
1293 writeU8(os, diggable);
1294 writeU8(os, climbable);
1295 writeU8(os, buildable_to);
1296 os<<serializeString(""); // legacy: used to be metadata_name
1297 writeU8(os, liquid_type);
1298 os<<serializeString(liquid_alternative_flowing);
1299 os<<serializeString(liquid_alternative_source);
1300 writeU8(os, liquid_viscosity);
1301 writeU8(os, light_source);
1302 writeU32(os, damage_per_second);
1303 node_box.serialize(os, protocol_version);
1304 selection_box.serialize(os, protocol_version);
1305 writeU8(os, legacy_facedir_simple);
1306 writeU8(os, legacy_wallmounted);
1307 serializeSimpleSoundSpec(sound_footstep, os);
1308 serializeSimpleSoundSpec(sound_dig, os);
1309 serializeSimpleSoundSpec(sound_dug, os);
1311 else if (protocol_version > 13 && protocol_version < 24) {
1312 writeU8(os, 6); // version
1313 os<<serializeString(name);
1314 writeU16(os, groups.size());
1315 for (ItemGroupList::const_iterator
1316 i = groups.begin(); i != groups.end(); ++i) {
1317 os<<serializeString(i->first);
1318 writeS16(os, i->second);
1320 writeU8(os, drawtype);
1321 writeF1000(os, visual_scale);
1323 for (u32 i = 0; i < 6; i++)
1324 tiledef[i].serialize(os, protocol_version);
1325 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1327 for (u32 i = 0; i < 2; i++)
1328 tiledef_special[i].serialize(os, protocol_version);
1330 writeU8(os, post_effect_color.getAlpha());
1331 writeU8(os, post_effect_color.getRed());
1332 writeU8(os, post_effect_color.getGreen());
1333 writeU8(os, post_effect_color.getBlue());
1334 writeU8(os, param_type);
1335 writeU8(os, param_type_2);
1336 writeU8(os, is_ground_content);
1337 writeU8(os, light_propagates);
1338 writeU8(os, sunlight_propagates);
1339 writeU8(os, walkable);
1340 writeU8(os, pointable);
1341 writeU8(os, diggable);
1342 writeU8(os, climbable);
1343 writeU8(os, buildable_to);
1344 os<<serializeString(""); // legacy: used to be metadata_name
1345 writeU8(os, liquid_type);
1346 os<<serializeString(liquid_alternative_flowing);
1347 os<<serializeString(liquid_alternative_source);
1348 writeU8(os, liquid_viscosity);
1349 writeU8(os, liquid_renewable);
1350 writeU8(os, light_source);
1351 writeU32(os, damage_per_second);
1352 node_box.serialize(os, protocol_version);
1353 selection_box.serialize(os, protocol_version);
1354 writeU8(os, legacy_facedir_simple);
1355 writeU8(os, legacy_wallmounted);
1356 serializeSimpleSoundSpec(sound_footstep, os);
1357 serializeSimpleSoundSpec(sound_dig, os);
1358 serializeSimpleSoundSpec(sound_dug, os);
1359 writeU8(os, rightclickable);
1360 writeU8(os, drowning);
1361 writeU8(os, leveled);
1362 writeU8(os, liquid_range);
1364 throw SerializationError("ContentFeatures::serialize(): "
1365 "Unsupported version requested");
1368 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1370 if (version == 5) // In PROTOCOL_VERSION 13
1372 name = deSerializeString(is);
1374 u32 groups_size = readU16(is);
1375 for(u32 i=0; i<groups_size; i++){
1376 std::string name = deSerializeString(is);
1377 int value = readS16(is);
1378 groups[name] = value;
1380 drawtype = (enum NodeDrawType)readU8(is);
1382 visual_scale = readF1000(is);
1383 if (readU8(is) != 6)
1384 throw SerializationError("unsupported tile count");
1385 for (u32 i = 0; i < 6; i++)
1386 tiledef[i].deSerialize(is, version, drawtype);
1387 if (readU8(is) != CF_SPECIAL_COUNT)
1388 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1389 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1390 tiledef_special[i].deSerialize(is, version, drawtype);
1392 post_effect_color.setAlpha(readU8(is));
1393 post_effect_color.setRed(readU8(is));
1394 post_effect_color.setGreen(readU8(is));
1395 post_effect_color.setBlue(readU8(is));
1396 param_type = (enum ContentParamType)readU8(is);
1397 param_type_2 = (enum ContentParamType2)readU8(is);
1398 is_ground_content = readU8(is);
1399 light_propagates = readU8(is);
1400 sunlight_propagates = readU8(is);
1401 walkable = readU8(is);
1402 pointable = readU8(is);
1403 diggable = readU8(is);
1404 climbable = readU8(is);
1405 buildable_to = readU8(is);
1406 deSerializeString(is); // legacy: used to be metadata_name
1407 liquid_type = (enum LiquidType)readU8(is);
1408 liquid_alternative_flowing = deSerializeString(is);
1409 liquid_alternative_source = deSerializeString(is);
1410 liquid_viscosity = readU8(is);
1411 light_source = readU8(is);
1412 damage_per_second = readU32(is);
1413 node_box.deSerialize(is);
1414 selection_box.deSerialize(is);
1415 legacy_facedir_simple = readU8(is);
1416 legacy_wallmounted = readU8(is);
1417 deSerializeSimpleSoundSpec(sound_footstep, is);
1418 deSerializeSimpleSoundSpec(sound_dig, is);
1419 deSerializeSimpleSoundSpec(sound_dug, is);
1420 } else if (version == 6) {
1421 name = deSerializeString(is);
1423 u32 groups_size = readU16(is);
1424 for (u32 i = 0; i < groups_size; i++) {
1425 std::string name = deSerializeString(is);
1426 int value = readS16(is);
1427 groups[name] = value;
1429 drawtype = (enum NodeDrawType)readU8(is);
1430 visual_scale = readF1000(is);
1431 if (readU8(is) != 6)
1432 throw SerializationError("unsupported tile count");
1433 for (u32 i = 0; i < 6; i++)
1434 tiledef[i].deSerialize(is, version, drawtype);
1435 // CF_SPECIAL_COUNT in version 6 = 2
1436 if (readU8(is) != 2)
1437 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1438 for (u32 i = 0; i < 2; i++)
1439 tiledef_special[i].deSerialize(is, version, drawtype);
1441 post_effect_color.setAlpha(readU8(is));
1442 post_effect_color.setRed(readU8(is));
1443 post_effect_color.setGreen(readU8(is));
1444 post_effect_color.setBlue(readU8(is));
1445 param_type = (enum ContentParamType)readU8(is);
1446 param_type_2 = (enum ContentParamType2)readU8(is);
1447 is_ground_content = readU8(is);
1448 light_propagates = readU8(is);
1449 sunlight_propagates = readU8(is);
1450 walkable = readU8(is);
1451 pointable = readU8(is);
1452 diggable = readU8(is);
1453 climbable = readU8(is);
1454 buildable_to = readU8(is);
1455 deSerializeString(is); // legacy: used to be metadata_name
1456 liquid_type = (enum LiquidType)readU8(is);
1457 liquid_alternative_flowing = deSerializeString(is);
1458 liquid_alternative_source = deSerializeString(is);
1459 liquid_viscosity = readU8(is);
1460 liquid_renewable = readU8(is);
1461 light_source = readU8(is);
1462 damage_per_second = readU32(is);
1463 node_box.deSerialize(is);
1464 selection_box.deSerialize(is);
1465 legacy_facedir_simple = readU8(is);
1466 legacy_wallmounted = readU8(is);
1467 deSerializeSimpleSoundSpec(sound_footstep, is);
1468 deSerializeSimpleSoundSpec(sound_dig, is);
1469 deSerializeSimpleSoundSpec(sound_dug, is);
1470 rightclickable = readU8(is);
1471 drowning = readU8(is);
1472 leveled = readU8(is);
1473 liquid_range = readU8(is);
1475 throw SerializationError("unsupported ContentFeatures version");
1480 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1482 return m_node_registration_complete;
1486 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1488 m_node_registration_complete = completed;
1492 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1495 if (m_node_registration_complete)
1496 nr->nodeResolveInternal();
1498 m_pending_resolve_callbacks.push_back(nr);
1502 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1504 size_t len = m_pending_resolve_callbacks.size();
1505 for (size_t i = 0; i != len; i++) {
1506 if (nr != m_pending_resolve_callbacks[i])
1510 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1511 m_pending_resolve_callbacks.resize(len);
1519 void CNodeDefManager::runNodeResolveCallbacks()
1521 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1522 NodeResolver *nr = m_pending_resolve_callbacks[i];
1523 nr->nodeResolveInternal();
1526 m_pending_resolve_callbacks.clear();
1530 void CNodeDefManager::resetNodeResolveState()
1532 m_node_registration_complete = false;
1533 m_pending_resolve_callbacks.clear();
1536 void CNodeDefManager::mapNodeboxConnections()
1538 for (u32 i = 0; i < m_content_features.size(); i++) {
1539 ContentFeatures *f = &m_content_features[i];
1540 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1542 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1543 it != f->connects_to.end(); ++it) {
1544 getIds(*it, f->connects_to_ids);
1549 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1551 const ContentFeatures &f1 = get(from);
1553 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1556 // lookup target in connected set
1557 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1560 const ContentFeatures &f2 = get(to);
1562 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1563 // ignores actually looking if back connection exists
1564 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1566 // does to node declare usable faces?
1567 if (f2.connect_sides > 0) {
1568 if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) {
1569 static const u8 rot[33 * 4] = {
1570 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1571 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back
1572 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right
1573 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1574 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front
1575 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1576 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1577 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1578 32, 16, 8, 4 // 32 - left
1580 return (f2.connect_sides & rot[(connect_face * 4) + to.param2]);
1582 return (f2.connect_sides & connect_face);
1584 // the target is just a regular node, so connect no matter back connection
1592 NodeResolver::NodeResolver()
1595 m_nodenames_idx = 0;
1596 m_nnlistsizes_idx = 0;
1597 m_resolve_done = false;
1599 m_nodenames.reserve(16);
1600 m_nnlistsizes.reserve(4);
1604 NodeResolver::~NodeResolver()
1606 if (!m_resolve_done && m_ndef)
1607 m_ndef->cancelNodeResolveCallback(this);
1611 void NodeResolver::nodeResolveInternal()
1613 m_nodenames_idx = 0;
1614 m_nnlistsizes_idx = 0;
1617 m_resolve_done = true;
1619 m_nodenames.clear();
1620 m_nnlistsizes.clear();
1624 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1625 const std::string &node_alt, content_t c_fallback)
1627 if (m_nodenames_idx == m_nodenames.size()) {
1628 *result_out = c_fallback;
1629 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1634 std::string name = m_nodenames[m_nodenames_idx++];
1636 bool success = m_ndef->getId(name, c);
1637 if (!success && node_alt != "") {
1639 success = m_ndef->getId(name, c);
1643 errorstream << "NodeResolver: failed to resolve node name '" << name
1644 << "'." << std::endl;
1653 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1654 bool all_required, content_t c_fallback)
1656 bool success = true;
1658 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1659 errorstream << "NodeResolver: no more node lists" << std::endl;
1663 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1666 if (m_nodenames_idx == m_nodenames.size()) {
1667 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1672 std::string &name = m_nodenames[m_nodenames_idx++];
1674 if (name.substr(0,6) != "group:") {
1675 if (m_ndef->getId(name, c)) {
1676 result_out->push_back(c);
1677 } else if (all_required) {
1678 errorstream << "NodeResolver: failed to resolve node name '"
1679 << name << "'." << std::endl;
1680 result_out->push_back(c_fallback);
1684 std::set<content_t> cids;
1685 std::set<content_t>::iterator it;
1686 m_ndef->getIds(name, cids);
1687 for (it = cids.begin(); it != cids.end(); ++it)
1688 result_out->push_back(*it);