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 if ((protocol_version < 28) && (param_type_2 == CPT2_MESHOPTIONS))
391 writeU8(os, CPT2_NONE);
393 writeU8(os, param_type_2);
394 writeU8(os, is_ground_content);
395 writeU8(os, light_propagates);
396 writeU8(os, sunlight_propagates);
397 writeU8(os, walkable);
398 writeU8(os, pointable);
399 writeU8(os, diggable);
400 writeU8(os, climbable);
401 writeU8(os, buildable_to);
402 os<<serializeString(""); // legacy: used to be metadata_name
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, light_source);
409 writeU32(os, damage_per_second);
410 node_box.serialize(os, protocol_version);
411 selection_box.serialize(os, protocol_version);
412 writeU8(os, legacy_facedir_simple);
413 writeU8(os, legacy_wallmounted);
414 serializeSimpleSoundSpec(sound_footstep, os);
415 serializeSimpleSoundSpec(sound_dig, os);
416 serializeSimpleSoundSpec(sound_dug, os);
417 writeU8(os, rightclickable);
418 writeU8(os, drowning);
419 writeU8(os, leveled);
420 writeU8(os, liquid_range);
422 // Stuff below should be moved to correct place in a version that otherwise changes
423 // the protocol version
424 os<<serializeString(mesh);
425 collision_box.serialize(os, protocol_version);
426 writeU8(os, floodable);
427 writeU16(os, connects_to_ids.size());
428 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
429 i != connects_to_ids.end(); ++i)
431 writeU8(os, connect_sides);
434 void ContentFeatures::deSerialize(std::istream &is)
436 int version = readU8(is);
438 deSerializeOld(is, version);
440 } else if (version > 8) {
441 throw SerializationError("unsupported ContentFeatures version");
444 name = deSerializeString(is);
446 u32 groups_size = readU16(is);
447 for(u32 i = 0; i < groups_size; i++){
448 std::string name = deSerializeString(is);
449 int value = readS16(is);
450 groups[name] = value;
452 drawtype = (enum NodeDrawType)readU8(is);
454 visual_scale = readF1000(is);
456 throw SerializationError("unsupported tile count");
457 for(u32 i = 0; i < 6; i++)
458 tiledef[i].deSerialize(is, version, drawtype);
459 if(readU8(is) != CF_SPECIAL_COUNT)
460 throw SerializationError("unsupported CF_SPECIAL_COUNT");
461 for(u32 i = 0; i < CF_SPECIAL_COUNT; i++)
462 tiledef_special[i].deSerialize(is, version, drawtype);
464 post_effect_color.setAlpha(readU8(is));
465 post_effect_color.setRed(readU8(is));
466 post_effect_color.setGreen(readU8(is));
467 post_effect_color.setBlue(readU8(is));
468 param_type = (enum ContentParamType)readU8(is);
469 param_type_2 = (enum ContentParamType2)readU8(is);
470 is_ground_content = readU8(is);
471 light_propagates = readU8(is);
472 sunlight_propagates = readU8(is);
473 walkable = readU8(is);
474 pointable = readU8(is);
475 diggable = readU8(is);
476 climbable = readU8(is);
477 buildable_to = readU8(is);
478 deSerializeString(is); // legacy: used to be metadata_name
479 liquid_type = (enum LiquidType)readU8(is);
480 liquid_alternative_flowing = deSerializeString(is);
481 liquid_alternative_source = deSerializeString(is);
482 liquid_viscosity = readU8(is);
483 liquid_renewable = readU8(is);
484 light_source = readU8(is);
485 damage_per_second = readU32(is);
486 node_box.deSerialize(is);
487 selection_box.deSerialize(is);
488 legacy_facedir_simple = readU8(is);
489 legacy_wallmounted = readU8(is);
490 deSerializeSimpleSoundSpec(sound_footstep, is);
491 deSerializeSimpleSoundSpec(sound_dig, is);
492 deSerializeSimpleSoundSpec(sound_dug, is);
493 rightclickable = readU8(is);
494 drowning = readU8(is);
495 leveled = readU8(is);
496 liquid_range = readU8(is);
498 // If you add anything here, insert it primarily inside the try-catch
499 // block to not need to increase the version.
501 // Stuff below should be moved to correct place in a version that
502 // otherwise changes the protocol version
503 mesh = deSerializeString(is);
504 collision_box.deSerialize(is);
505 floodable = readU8(is);
506 u16 connects_to_size = readU16(is);
507 connects_to_ids.clear();
508 for (u16 i = 0; i < connects_to_size; i++)
509 connects_to_ids.insert(readU16(is));
510 connect_sides = readU8(is);
511 }catch(SerializationError &e) {};
515 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
516 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
517 bool backface_culling, u8 alpha, u8 material_type)
519 tile->shader_id = shader_id;
520 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
522 tile->material_type = material_type;
524 // Normal texture and shader flags texture
525 if (use_normal_texture) {
526 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
528 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
531 tile->material_flags = 0;
532 if (backface_culling)
533 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
534 if (tiledef->animation.type == TAT_VERTICAL_FRAMES)
535 tile->material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
536 if (tiledef->tileable_horizontal)
537 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
538 if (tiledef->tileable_vertical)
539 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
541 // Animation parameters
543 if (tile->material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) {
544 // Get texture size to determine frame count by aspect ratio
545 v2u32 size = tile->texture->getOriginalSize();
546 int frame_height = (float)size.X /
547 (float)tiledef->animation.aspect_w *
548 (float)tiledef->animation.aspect_h;
549 frame_count = size.Y / frame_height;
550 int frame_length_ms = 1000.0 * tiledef->animation.length / frame_count;
551 tile->animation_frame_count = frame_count;
552 tile->animation_frame_length_ms = frame_length_ms;
555 if (frame_count == 1) {
556 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
558 std::ostringstream os(std::ios::binary);
559 tile->frames.resize(frame_count);
561 for (int i = 0; i < frame_count; i++) {
566 os << tiledef->name << "^[verticalframe:"
567 << frame_count << ":" << i;
569 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
570 if (tile->normal_texture)
571 frame.normal_texture = tsrc->getNormalTexture(os.str());
572 frame.flags_texture = tile->flags_texture;
573 tile->frames[i] = frame;
580 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
581 scene::ISceneManager *smgr, scene::IMeshManipulator *meshmanip,
582 IGameDef *gamedef, const TextureSettings &tsettings)
584 // minimap pixel color - the average color of a texture
585 if (tsettings.enable_minimap && tiledef[0].name != "")
586 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
588 // Figure out the actual tiles to use
590 for (u32 j = 0; j < 6; j++) {
591 tdef[j] = tiledef[j];
592 if (tdef[j].name == "")
593 tdef[j].name = "unknown_node.png";
596 bool is_liquid = false;
597 bool is_water_surface = false;
599 u8 material_type = (alpha == 255) ?
600 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
611 assert(liquid_type == LIQUID_SOURCE);
612 if (tsettings.opaque_water)
617 case NDT_FLOWINGLIQUID:
618 assert(liquid_type == LIQUID_FLOWING);
620 if (tsettings.opaque_water)
626 visual_solidness = 1;
628 case NDT_GLASSLIKE_FRAMED:
630 visual_solidness = 1;
632 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
634 visual_solidness = 1;
635 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
639 visual_solidness = 1;
641 case NDT_ALLFACES_OPTIONAL:
642 if (tsettings.leaves_style == LEAVES_FANCY) {
643 drawtype = NDT_ALLFACES;
645 visual_solidness = 1;
646 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
647 for (u32 j = 0; j < 6; j++) {
648 if (tiledef_special[j].name != "")
649 tdef[j].name = tiledef_special[j].name;
651 drawtype = NDT_GLASSLIKE;
653 visual_solidness = 1;
655 drawtype = NDT_NORMAL;
657 for (u32 i = 0; i < 6; i++)
658 tdef[i].name += std::string("^[noalpha");
661 material_type = TILE_MATERIAL_WAVING_LEAVES;
666 material_type = TILE_MATERIAL_WAVING_PLANTS;
684 material_type = (alpha == 255) ?
685 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
686 if (name == "default:water_source")
687 is_water_surface = true;
691 for (u16 j = 0; j < 6; j++) {
692 tile_shader[j] = shdsrc->getShader("nodes_shader",
693 material_type, drawtype);
696 if (is_water_surface) {
697 tile_shader[0] = shdsrc->getShader("water_surface_shader",
698 material_type, drawtype);
701 // Tiles (fill in f->tiles[])
702 for (u16 j = 0; j < 6; j++) {
703 fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j],
704 tsettings.use_normal_texture,
705 tiledef[j].backface_culling, alpha, material_type);
708 // Special tiles (fill in f->special_tiles[])
709 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
710 fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j],
711 tile_shader[j], tsettings.use_normal_texture,
712 tiledef_special[j].backface_culling, alpha, material_type);
715 if ((drawtype == NDT_MESH) && (mesh != "")) {
717 // Read the mesh and apply scale
718 mesh_ptr[0] = gamedef->getMesh(mesh);
720 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
721 scaleMesh(mesh_ptr[0], scale);
722 recalculateBoundingBox(mesh_ptr[0]);
723 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
725 } else if ((drawtype == NDT_NODEBOX) &&
726 ((node_box.type == NODEBOX_REGULAR) ||
727 (node_box.type == NODEBOX_FIXED)) &&
728 (!node_box.fixed.empty())) {
729 //Convert regular nodebox nodes to meshnodes
730 //Change the drawtype and apply scale
732 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
733 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
734 scaleMesh(mesh_ptr[0], scale);
735 recalculateBoundingBox(mesh_ptr[0]);
736 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
739 //Cache 6dfacedir and wallmounted rotated clones of meshes
740 if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_FACEDIR)) {
741 for (u16 j = 1; j < 24; j++) {
742 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
743 rotateMeshBy6dFacedir(mesh_ptr[j], j);
744 recalculateBoundingBox(mesh_ptr[j]);
745 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
747 } else if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_WALLMOUNTED)) {
748 static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
749 for (u16 j = 1; j < 6; j++) {
750 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
751 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
752 recalculateBoundingBox(mesh_ptr[j]);
753 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
755 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
756 recalculateBoundingBox(mesh_ptr[0]);
757 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
766 class CNodeDefManager: public IWritableNodeDefManager {
769 virtual ~CNodeDefManager();
771 virtual IWritableNodeDefManager *clone();
772 inline virtual const ContentFeatures& get(content_t c) const;
773 inline virtual const ContentFeatures& get(const MapNode &n) const;
774 virtual bool getId(const std::string &name, content_t &result) const;
775 virtual content_t getId(const std::string &name) const;
776 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
777 virtual const ContentFeatures& get(const std::string &name) const;
778 content_t allocateId();
779 virtual content_t set(const std::string &name, const ContentFeatures &def);
780 virtual content_t allocateDummy(const std::string &name);
781 virtual void updateAliases(IItemDefManager *idef);
782 virtual void applyTextureOverrides(const std::string &override_filepath);
783 virtual void updateTextures(IGameDef *gamedef,
784 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
785 void *progress_cbk_args);
786 void serialize(std::ostream &os, u16 protocol_version) const;
787 void deSerialize(std::istream &is);
789 inline virtual bool getNodeRegistrationStatus() const;
790 inline virtual void setNodeRegistrationStatus(bool completed);
792 virtual void pendNodeResolve(NodeResolver *nr);
793 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
794 virtual void runNodeResolveCallbacks();
795 virtual void resetNodeResolveState();
796 virtual void mapNodeboxConnections();
797 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
800 void addNameIdMapping(content_t i, std::string name);
802 // Features indexed by id
803 std::vector<ContentFeatures> m_content_features;
805 // A mapping for fast converting back and forth between names and ids
806 NameIdMapping m_name_id_mapping;
808 // Like m_name_id_mapping, but only from names to ids, and includes
809 // item aliases too. Updated by updateAliases()
810 // Note: Not serialized.
812 std::map<std::string, content_t> m_name_id_mapping_with_aliases;
814 // A mapping from groups to a list of content_ts (and their levels)
815 // that belong to it. Necessary for a direct lookup in getIds().
816 // Note: Not serialized.
817 std::map<std::string, GroupItems> m_group_to_items;
819 // Next possibly free id
822 // NodeResolvers to callback once node registration has ended
823 std::vector<NodeResolver *> m_pending_resolve_callbacks;
825 // True when all nodes have been registered
826 bool m_node_registration_complete;
830 CNodeDefManager::CNodeDefManager()
836 CNodeDefManager::~CNodeDefManager()
839 for (u32 i = 0; i < m_content_features.size(); i++) {
840 ContentFeatures *f = &m_content_features[i];
841 for (u32 j = 0; j < 24; j++) {
843 f->mesh_ptr[j]->drop();
850 void CNodeDefManager::clear()
852 m_content_features.clear();
853 m_name_id_mapping.clear();
854 m_name_id_mapping_with_aliases.clear();
855 m_group_to_items.clear();
858 resetNodeResolveState();
860 u32 initial_length = 0;
861 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
862 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
863 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
864 m_content_features.resize(initial_length);
866 // Set CONTENT_UNKNOWN
870 // Insert directly into containers
871 content_t c = CONTENT_UNKNOWN;
872 m_content_features[c] = f;
873 addNameIdMapping(c, f.name);
880 f.drawtype = NDT_AIRLIKE;
881 f.param_type = CPT_LIGHT;
882 f.light_propagates = true;
883 f.sunlight_propagates = true;
887 f.buildable_to = true;
889 f.is_ground_content = true;
890 // Insert directly into containers
891 content_t c = CONTENT_AIR;
892 m_content_features[c] = f;
893 addNameIdMapping(c, f.name);
896 // Set CONTENT_IGNORE
900 f.drawtype = NDT_AIRLIKE;
901 f.param_type = CPT_NONE;
902 f.light_propagates = false;
903 f.sunlight_propagates = false;
907 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
908 f.is_ground_content = true;
909 // Insert directly into containers
910 content_t c = CONTENT_IGNORE;
911 m_content_features[c] = f;
912 addNameIdMapping(c, f.name);
917 IWritableNodeDefManager *CNodeDefManager::clone()
919 CNodeDefManager *mgr = new CNodeDefManager();
925 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
927 return c < m_content_features.size()
928 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
932 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
934 return get(n.getContent());
938 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
940 std::map<std::string, content_t>::const_iterator
941 i = m_name_id_mapping_with_aliases.find(name);
942 if(i == m_name_id_mapping_with_aliases.end())
949 content_t CNodeDefManager::getId(const std::string &name) const
951 content_t id = CONTENT_IGNORE;
957 bool CNodeDefManager::getIds(const std::string &name,
958 std::set<content_t> &result) const
960 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
961 if (name.substr(0,6) != "group:") {
962 content_t id = CONTENT_IGNORE;
963 bool exists = getId(name, id);
968 std::string group = name.substr(6);
970 std::map<std::string, GroupItems>::const_iterator
971 i = m_group_to_items.find(group);
972 if (i == m_group_to_items.end())
975 const GroupItems &items = i->second;
976 for (GroupItems::const_iterator j = items.begin();
977 j != items.end(); ++j) {
978 if ((*j).second != 0)
979 result.insert((*j).first);
981 //printf("getIds: %dus\n", t.stop());
986 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
988 content_t id = CONTENT_UNKNOWN;
994 // returns CONTENT_IGNORE if no free ID found
995 content_t CNodeDefManager::allocateId()
997 for (content_t id = m_next_id;
998 id >= m_next_id; // overflow?
1000 while (id >= m_content_features.size()) {
1001 m_content_features.push_back(ContentFeatures());
1003 const ContentFeatures &f = m_content_features[id];
1009 // If we arrive here, an overflow occurred in id.
1010 // That means no ID was found
1011 return CONTENT_IGNORE;
1015 // IWritableNodeDefManager
1016 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1020 assert(name == def.name);
1022 // Don't allow redefining ignore (but allow air and unknown)
1023 if (name == "ignore") {
1024 warningstream << "NodeDefManager: Ignoring "
1025 "CONTENT_IGNORE redefinition"<<std::endl;
1026 return CONTENT_IGNORE;
1029 content_t id = CONTENT_IGNORE;
1030 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1033 if (id == CONTENT_IGNORE) {
1034 warningstream << "NodeDefManager: Absolute "
1035 "limit reached" << std::endl;
1036 return CONTENT_IGNORE;
1038 assert(id != CONTENT_IGNORE);
1039 addNameIdMapping(id, name);
1041 m_content_features[id] = def;
1042 verbosestream << "NodeDefManager: registering content id \"" << id
1043 << "\": name=\"" << def.name << "\""<<std::endl;
1045 // Add this content to the list of all groups it belongs to
1046 // FIXME: This should remove a node from groups it no longer
1047 // belongs to when a node is re-registered
1048 for (ItemGroupList::const_iterator i = def.groups.begin();
1049 i != def.groups.end(); ++i) {
1050 std::string group_name = i->first;
1052 std::map<std::string, GroupItems>::iterator
1053 j = m_group_to_items.find(group_name);
1054 if (j == m_group_to_items.end()) {
1055 m_group_to_items[group_name].push_back(
1056 std::make_pair(id, i->second));
1058 GroupItems &items = j->second;
1059 items.push_back(std::make_pair(id, i->second));
1066 content_t CNodeDefManager::allocateDummy(const std::string &name)
1068 assert(name != ""); // Pre-condition
1071 return set(name, f);
1075 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1077 std::set<std::string> all = idef->getAll();
1078 m_name_id_mapping_with_aliases.clear();
1079 for (std::set<std::string>::iterator
1080 i = all.begin(); i != all.end(); ++i) {
1081 std::string name = *i;
1082 std::string convert_to = idef->getAlias(name);
1084 if (m_name_id_mapping.getId(convert_to, id)) {
1085 m_name_id_mapping_with_aliases.insert(
1086 std::make_pair(name, id));
1091 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1093 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1094 "overrides to textures from " << override_filepath << std::endl;
1096 std::ifstream infile(override_filepath.c_str());
1099 while (std::getline(infile, line)) {
1101 if (trim(line) == "")
1103 std::vector<std::string> splitted = str_split(line, ' ');
1104 if (splitted.size() != 3) {
1105 errorstream << override_filepath
1106 << ":" << line_c << " Could not apply texture override \""
1107 << line << "\": Syntax error" << std::endl;
1112 if (!getId(splitted[0], id)) {
1113 infostream << override_filepath
1114 << ":" << line_c << " Could not apply texture override \""
1115 << line << "\": Unknown node \""
1116 << splitted[0] << "\"" << std::endl;
1120 ContentFeatures &nodedef = m_content_features[id];
1122 if (splitted[1] == "top")
1123 nodedef.tiledef[0].name = splitted[2];
1124 else if (splitted[1] == "bottom")
1125 nodedef.tiledef[1].name = splitted[2];
1126 else if (splitted[1] == "right")
1127 nodedef.tiledef[2].name = splitted[2];
1128 else if (splitted[1] == "left")
1129 nodedef.tiledef[3].name = splitted[2];
1130 else if (splitted[1] == "back")
1131 nodedef.tiledef[4].name = splitted[2];
1132 else if (splitted[1] == "front")
1133 nodedef.tiledef[5].name = splitted[2];
1134 else if (splitted[1] == "all" || splitted[1] == "*")
1135 for (int i = 0; i < 6; i++)
1136 nodedef.tiledef[i].name = splitted[2];
1137 else if (splitted[1] == "sides")
1138 for (int i = 2; i < 6; i++)
1139 nodedef.tiledef[i].name = splitted[2];
1141 errorstream << override_filepath
1142 << ":" << line_c << " Could not apply texture override \""
1143 << line << "\": Unknown node side \""
1144 << splitted[1] << "\"" << std::endl;
1150 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1151 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1152 void *progress_callback_args)
1155 infostream << "CNodeDefManager::updateTextures(): Updating "
1156 "textures in node definitions" << std::endl;
1157 ITextureSource *tsrc = gamedef->tsrc();
1158 IShaderSource *shdsrc = gamedef->getShaderSource();
1159 scene::ISceneManager* smgr = gamedef->getSceneManager();
1160 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1161 TextureSettings tsettings;
1162 tsettings.readSettings();
1164 u32 size = m_content_features.size();
1166 for (u32 i = 0; i < size; i++) {
1167 m_content_features[i].updateTextures(tsrc, shdsrc, smgr, meshmanip, gamedef, tsettings);
1168 progress_callback(progress_callback_args, i, size);
1173 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1175 writeU8(os, 1); // version
1177 std::ostringstream os2(std::ios::binary);
1178 for (u32 i = 0; i < m_content_features.size(); i++) {
1179 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1180 || i == CONTENT_UNKNOWN)
1182 const ContentFeatures *f = &m_content_features[i];
1186 // Wrap it in a string to allow different lengths without
1187 // strict version incompatibilities
1188 std::ostringstream wrapper_os(std::ios::binary);
1189 f->serialize(wrapper_os, protocol_version);
1190 os2<<serializeString(wrapper_os.str());
1192 // must not overflow
1193 u16 next = count + 1;
1194 FATAL_ERROR_IF(next < count, "Overflow");
1197 writeU16(os, count);
1198 os << serializeLongString(os2.str());
1202 void CNodeDefManager::deSerialize(std::istream &is)
1205 int version = readU8(is);
1207 throw SerializationError("unsupported NodeDefinitionManager version");
1208 u16 count = readU16(is);
1209 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1211 for (u16 n = 0; n < count; n++) {
1212 u16 i = readU16(is2);
1214 // Read it from the string wrapper
1215 std::string wrapper = deSerializeString(is2);
1216 std::istringstream wrapper_is(wrapper, std::ios::binary);
1217 f.deSerialize(wrapper_is);
1219 // Check error conditions
1220 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1221 warningstream << "NodeDefManager::deSerialize(): "
1222 "not changing builtin node " << i << std::endl;
1226 warningstream << "NodeDefManager::deSerialize(): "
1227 "received empty name" << std::endl;
1233 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1234 warningstream << "NodeDefManager::deSerialize(): "
1235 "already defined with different ID: " << f.name << std::endl;
1239 // All is ok, add node definition with the requested ID
1240 if (i >= m_content_features.size())
1241 m_content_features.resize((u32)(i) + 1);
1242 m_content_features[i] = f;
1243 addNameIdMapping(i, f.name);
1244 verbosestream << "deserialized " << f.name << std::endl;
1249 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1251 m_name_id_mapping.set(i, name);
1252 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1256 IWritableNodeDefManager *createNodeDefManager()
1258 return new CNodeDefManager();
1262 //// Serialization of old ContentFeatures formats
1263 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1265 if (protocol_version == 13)
1267 writeU8(os, 5); // version
1268 os<<serializeString(name);
1269 writeU16(os, groups.size());
1270 for (ItemGroupList::const_iterator
1271 i = groups.begin(); i != groups.end(); ++i) {
1272 os<<serializeString(i->first);
1273 writeS16(os, i->second);
1275 writeU8(os, drawtype);
1276 writeF1000(os, visual_scale);
1278 for (u32 i = 0; i < 6; i++)
1279 tiledef[i].serialize(os, protocol_version);
1280 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1282 for (u32 i = 0; i < 2; i++)
1283 tiledef_special[i].serialize(os, protocol_version);
1285 writeU8(os, post_effect_color.getAlpha());
1286 writeU8(os, post_effect_color.getRed());
1287 writeU8(os, post_effect_color.getGreen());
1288 writeU8(os, post_effect_color.getBlue());
1289 writeU8(os, param_type);
1290 writeU8(os, param_type_2);
1291 writeU8(os, is_ground_content);
1292 writeU8(os, light_propagates);
1293 writeU8(os, sunlight_propagates);
1294 writeU8(os, walkable);
1295 writeU8(os, pointable);
1296 writeU8(os, diggable);
1297 writeU8(os, climbable);
1298 writeU8(os, buildable_to);
1299 os<<serializeString(""); // legacy: used to be metadata_name
1300 writeU8(os, liquid_type);
1301 os<<serializeString(liquid_alternative_flowing);
1302 os<<serializeString(liquid_alternative_source);
1303 writeU8(os, liquid_viscosity);
1304 writeU8(os, light_source);
1305 writeU32(os, damage_per_second);
1306 node_box.serialize(os, protocol_version);
1307 selection_box.serialize(os, protocol_version);
1308 writeU8(os, legacy_facedir_simple);
1309 writeU8(os, legacy_wallmounted);
1310 serializeSimpleSoundSpec(sound_footstep, os);
1311 serializeSimpleSoundSpec(sound_dig, os);
1312 serializeSimpleSoundSpec(sound_dug, os);
1314 else if (protocol_version > 13 && protocol_version < 24) {
1315 writeU8(os, 6); // version
1316 os<<serializeString(name);
1317 writeU16(os, groups.size());
1318 for (ItemGroupList::const_iterator
1319 i = groups.begin(); i != groups.end(); ++i) {
1320 os<<serializeString(i->first);
1321 writeS16(os, i->second);
1323 writeU8(os, drawtype);
1324 writeF1000(os, visual_scale);
1326 for (u32 i = 0; i < 6; i++)
1327 tiledef[i].serialize(os, protocol_version);
1328 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1330 for (u32 i = 0; i < 2; i++)
1331 tiledef_special[i].serialize(os, protocol_version);
1333 writeU8(os, post_effect_color.getAlpha());
1334 writeU8(os, post_effect_color.getRed());
1335 writeU8(os, post_effect_color.getGreen());
1336 writeU8(os, post_effect_color.getBlue());
1337 writeU8(os, param_type);
1338 writeU8(os, param_type_2);
1339 writeU8(os, is_ground_content);
1340 writeU8(os, light_propagates);
1341 writeU8(os, sunlight_propagates);
1342 writeU8(os, walkable);
1343 writeU8(os, pointable);
1344 writeU8(os, diggable);
1345 writeU8(os, climbable);
1346 writeU8(os, buildable_to);
1347 os<<serializeString(""); // legacy: used to be metadata_name
1348 writeU8(os, liquid_type);
1349 os<<serializeString(liquid_alternative_flowing);
1350 os<<serializeString(liquid_alternative_source);
1351 writeU8(os, liquid_viscosity);
1352 writeU8(os, liquid_renewable);
1353 writeU8(os, light_source);
1354 writeU32(os, damage_per_second);
1355 node_box.serialize(os, protocol_version);
1356 selection_box.serialize(os, protocol_version);
1357 writeU8(os, legacy_facedir_simple);
1358 writeU8(os, legacy_wallmounted);
1359 serializeSimpleSoundSpec(sound_footstep, os);
1360 serializeSimpleSoundSpec(sound_dig, os);
1361 serializeSimpleSoundSpec(sound_dug, os);
1362 writeU8(os, rightclickable);
1363 writeU8(os, drowning);
1364 writeU8(os, leveled);
1365 writeU8(os, liquid_range);
1367 throw SerializationError("ContentFeatures::serialize(): "
1368 "Unsupported version requested");
1371 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1373 if (version == 5) // In PROTOCOL_VERSION 13
1375 name = deSerializeString(is);
1377 u32 groups_size = readU16(is);
1378 for(u32 i=0; i<groups_size; i++){
1379 std::string name = deSerializeString(is);
1380 int value = readS16(is);
1381 groups[name] = value;
1383 drawtype = (enum NodeDrawType)readU8(is);
1385 visual_scale = readF1000(is);
1386 if (readU8(is) != 6)
1387 throw SerializationError("unsupported tile count");
1388 for (u32 i = 0; i < 6; i++)
1389 tiledef[i].deSerialize(is, version, drawtype);
1390 if (readU8(is) != CF_SPECIAL_COUNT)
1391 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1392 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1393 tiledef_special[i].deSerialize(is, version, drawtype);
1395 post_effect_color.setAlpha(readU8(is));
1396 post_effect_color.setRed(readU8(is));
1397 post_effect_color.setGreen(readU8(is));
1398 post_effect_color.setBlue(readU8(is));
1399 param_type = (enum ContentParamType)readU8(is);
1400 param_type_2 = (enum ContentParamType2)readU8(is);
1401 is_ground_content = readU8(is);
1402 light_propagates = readU8(is);
1403 sunlight_propagates = readU8(is);
1404 walkable = readU8(is);
1405 pointable = readU8(is);
1406 diggable = readU8(is);
1407 climbable = readU8(is);
1408 buildable_to = readU8(is);
1409 deSerializeString(is); // legacy: used to be metadata_name
1410 liquid_type = (enum LiquidType)readU8(is);
1411 liquid_alternative_flowing = deSerializeString(is);
1412 liquid_alternative_source = deSerializeString(is);
1413 liquid_viscosity = readU8(is);
1414 light_source = readU8(is);
1415 damage_per_second = readU32(is);
1416 node_box.deSerialize(is);
1417 selection_box.deSerialize(is);
1418 legacy_facedir_simple = readU8(is);
1419 legacy_wallmounted = readU8(is);
1420 deSerializeSimpleSoundSpec(sound_footstep, is);
1421 deSerializeSimpleSoundSpec(sound_dig, is);
1422 deSerializeSimpleSoundSpec(sound_dug, is);
1423 } else if (version == 6) {
1424 name = deSerializeString(is);
1426 u32 groups_size = readU16(is);
1427 for (u32 i = 0; i < groups_size; i++) {
1428 std::string name = deSerializeString(is);
1429 int value = readS16(is);
1430 groups[name] = value;
1432 drawtype = (enum NodeDrawType)readU8(is);
1433 visual_scale = readF1000(is);
1434 if (readU8(is) != 6)
1435 throw SerializationError("unsupported tile count");
1436 for (u32 i = 0; i < 6; i++)
1437 tiledef[i].deSerialize(is, version, drawtype);
1438 // CF_SPECIAL_COUNT in version 6 = 2
1439 if (readU8(is) != 2)
1440 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1441 for (u32 i = 0; i < 2; i++)
1442 tiledef_special[i].deSerialize(is, version, drawtype);
1444 post_effect_color.setAlpha(readU8(is));
1445 post_effect_color.setRed(readU8(is));
1446 post_effect_color.setGreen(readU8(is));
1447 post_effect_color.setBlue(readU8(is));
1448 param_type = (enum ContentParamType)readU8(is);
1449 param_type_2 = (enum ContentParamType2)readU8(is);
1450 is_ground_content = readU8(is);
1451 light_propagates = readU8(is);
1452 sunlight_propagates = readU8(is);
1453 walkable = readU8(is);
1454 pointable = readU8(is);
1455 diggable = readU8(is);
1456 climbable = readU8(is);
1457 buildable_to = readU8(is);
1458 deSerializeString(is); // legacy: used to be metadata_name
1459 liquid_type = (enum LiquidType)readU8(is);
1460 liquid_alternative_flowing = deSerializeString(is);
1461 liquid_alternative_source = deSerializeString(is);
1462 liquid_viscosity = readU8(is);
1463 liquid_renewable = readU8(is);
1464 light_source = readU8(is);
1465 damage_per_second = readU32(is);
1466 node_box.deSerialize(is);
1467 selection_box.deSerialize(is);
1468 legacy_facedir_simple = readU8(is);
1469 legacy_wallmounted = readU8(is);
1470 deSerializeSimpleSoundSpec(sound_footstep, is);
1471 deSerializeSimpleSoundSpec(sound_dig, is);
1472 deSerializeSimpleSoundSpec(sound_dug, is);
1473 rightclickable = readU8(is);
1474 drowning = readU8(is);
1475 leveled = readU8(is);
1476 liquid_range = readU8(is);
1478 throw SerializationError("unsupported ContentFeatures version");
1483 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1485 return m_node_registration_complete;
1489 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1491 m_node_registration_complete = completed;
1495 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1498 if (m_node_registration_complete)
1499 nr->nodeResolveInternal();
1501 m_pending_resolve_callbacks.push_back(nr);
1505 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1507 size_t len = m_pending_resolve_callbacks.size();
1508 for (size_t i = 0; i != len; i++) {
1509 if (nr != m_pending_resolve_callbacks[i])
1513 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1514 m_pending_resolve_callbacks.resize(len);
1522 void CNodeDefManager::runNodeResolveCallbacks()
1524 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1525 NodeResolver *nr = m_pending_resolve_callbacks[i];
1526 nr->nodeResolveInternal();
1529 m_pending_resolve_callbacks.clear();
1533 void CNodeDefManager::resetNodeResolveState()
1535 m_node_registration_complete = false;
1536 m_pending_resolve_callbacks.clear();
1539 void CNodeDefManager::mapNodeboxConnections()
1541 for (u32 i = 0; i < m_content_features.size(); i++) {
1542 ContentFeatures *f = &m_content_features[i];
1543 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1545 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1546 it != f->connects_to.end(); ++it) {
1547 getIds(*it, f->connects_to_ids);
1552 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1554 const ContentFeatures &f1 = get(from);
1556 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1559 // lookup target in connected set
1560 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1563 const ContentFeatures &f2 = get(to);
1565 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1566 // ignores actually looking if back connection exists
1567 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1569 // does to node declare usable faces?
1570 if (f2.connect_sides > 0) {
1571 if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) {
1572 static const u8 rot[33 * 4] = {
1573 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1574 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back
1575 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right
1576 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1577 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front
1578 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,
1580 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1581 32, 16, 8, 4 // 32 - left
1583 return (f2.connect_sides & rot[(connect_face * 4) + to.param2]);
1585 return (f2.connect_sides & connect_face);
1587 // the target is just a regular node, so connect no matter back connection
1595 NodeResolver::NodeResolver()
1598 m_nodenames_idx = 0;
1599 m_nnlistsizes_idx = 0;
1600 m_resolve_done = false;
1602 m_nodenames.reserve(16);
1603 m_nnlistsizes.reserve(4);
1607 NodeResolver::~NodeResolver()
1609 if (!m_resolve_done && m_ndef)
1610 m_ndef->cancelNodeResolveCallback(this);
1614 void NodeResolver::nodeResolveInternal()
1616 m_nodenames_idx = 0;
1617 m_nnlistsizes_idx = 0;
1620 m_resolve_done = true;
1622 m_nodenames.clear();
1623 m_nnlistsizes.clear();
1627 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1628 const std::string &node_alt, content_t c_fallback)
1630 if (m_nodenames_idx == m_nodenames.size()) {
1631 *result_out = c_fallback;
1632 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1637 std::string name = m_nodenames[m_nodenames_idx++];
1639 bool success = m_ndef->getId(name, c);
1640 if (!success && node_alt != "") {
1642 success = m_ndef->getId(name, c);
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 std::set<content_t> cids;
1688 std::set<content_t>::iterator it;
1689 m_ndef->getIds(name, cids);
1690 for (it = cids.begin(); it != cids.end(); ++it)
1691 result_out->push_back(*it);