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 removeNode(const std::string &name);
782 virtual void updateAliases(IItemDefManager *idef);
783 virtual void applyTextureOverrides(const std::string &override_filepath);
784 virtual void updateTextures(IGameDef *gamedef,
785 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
786 void *progress_cbk_args);
787 void serialize(std::ostream &os, u16 protocol_version) const;
788 void deSerialize(std::istream &is);
790 inline virtual bool getNodeRegistrationStatus() const;
791 inline virtual void setNodeRegistrationStatus(bool completed);
793 virtual void pendNodeResolve(NodeResolver *nr);
794 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
795 virtual void runNodeResolveCallbacks();
796 virtual void resetNodeResolveState();
797 virtual void mapNodeboxConnections();
798 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
801 void addNameIdMapping(content_t i, std::string name);
803 // Features indexed by id
804 std::vector<ContentFeatures> m_content_features;
806 // A mapping for fast converting back and forth between names and ids
807 NameIdMapping m_name_id_mapping;
809 // Like m_name_id_mapping, but only from names to ids, and includes
810 // item aliases too. Updated by updateAliases()
811 // Note: Not serialized.
813 UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
815 // A mapping from groups to a list of content_ts (and their levels)
816 // that belong to it. Necessary for a direct lookup in getIds().
817 // Note: Not serialized.
818 UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
820 // Next possibly free id
823 // NodeResolvers to callback once node registration has ended
824 std::vector<NodeResolver *> m_pending_resolve_callbacks;
826 // True when all nodes have been registered
827 bool m_node_registration_complete;
831 CNodeDefManager::CNodeDefManager()
837 CNodeDefManager::~CNodeDefManager()
840 for (u32 i = 0; i < m_content_features.size(); i++) {
841 ContentFeatures *f = &m_content_features[i];
842 for (u32 j = 0; j < 24; j++) {
844 f->mesh_ptr[j]->drop();
851 void CNodeDefManager::clear()
853 m_content_features.clear();
854 m_name_id_mapping.clear();
855 m_name_id_mapping_with_aliases.clear();
856 m_group_to_items.clear();
859 resetNodeResolveState();
861 u32 initial_length = 0;
862 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
863 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
864 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
865 m_content_features.resize(initial_length);
867 // Set CONTENT_UNKNOWN
871 // Insert directly into containers
872 content_t c = CONTENT_UNKNOWN;
873 m_content_features[c] = f;
874 addNameIdMapping(c, f.name);
881 f.drawtype = NDT_AIRLIKE;
882 f.param_type = CPT_LIGHT;
883 f.light_propagates = true;
884 f.sunlight_propagates = true;
888 f.buildable_to = true;
890 f.is_ground_content = true;
891 // Insert directly into containers
892 content_t c = CONTENT_AIR;
893 m_content_features[c] = f;
894 addNameIdMapping(c, f.name);
897 // Set CONTENT_IGNORE
901 f.drawtype = NDT_AIRLIKE;
902 f.param_type = CPT_NONE;
903 f.light_propagates = false;
904 f.sunlight_propagates = false;
908 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
909 f.is_ground_content = true;
910 // Insert directly into containers
911 content_t c = CONTENT_IGNORE;
912 m_content_features[c] = f;
913 addNameIdMapping(c, f.name);
918 IWritableNodeDefManager *CNodeDefManager::clone()
920 CNodeDefManager *mgr = new CNodeDefManager();
926 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
928 return c < m_content_features.size()
929 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
933 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
935 return get(n.getContent());
939 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
941 UNORDERED_MAP<std::string, content_t>::const_iterator
942 i = m_name_id_mapping_with_aliases.find(name);
943 if(i == m_name_id_mapping_with_aliases.end())
950 content_t CNodeDefManager::getId(const std::string &name) const
952 content_t id = CONTENT_IGNORE;
958 bool CNodeDefManager::getIds(const std::string &name,
959 std::set<content_t> &result) const
961 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
962 if (name.substr(0,6) != "group:") {
963 content_t id = CONTENT_IGNORE;
964 bool exists = getId(name, id);
969 std::string group = name.substr(6);
971 UNORDERED_MAP<std::string, GroupItems>::const_iterator
972 i = m_group_to_items.find(group);
973 if (i == m_group_to_items.end())
976 const GroupItems &items = i->second;
977 for (GroupItems::const_iterator j = items.begin();
978 j != items.end(); ++j) {
979 if ((*j).second != 0)
980 result.insert((*j).first);
982 //printf("getIds: %dus\n", t.stop());
987 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
989 content_t id = CONTENT_UNKNOWN;
995 // returns CONTENT_IGNORE if no free ID found
996 content_t CNodeDefManager::allocateId()
998 for (content_t id = m_next_id;
999 id >= m_next_id; // overflow?
1001 while (id >= m_content_features.size()) {
1002 m_content_features.push_back(ContentFeatures());
1004 const ContentFeatures &f = m_content_features[id];
1010 // If we arrive here, an overflow occurred in id.
1011 // That means no ID was found
1012 return CONTENT_IGNORE;
1016 // IWritableNodeDefManager
1017 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1021 assert(name == def.name);
1023 // Don't allow redefining ignore (but allow air and unknown)
1024 if (name == "ignore") {
1025 warningstream << "NodeDefManager: Ignoring "
1026 "CONTENT_IGNORE redefinition"<<std::endl;
1027 return CONTENT_IGNORE;
1030 content_t id = CONTENT_IGNORE;
1031 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1034 if (id == CONTENT_IGNORE) {
1035 warningstream << "NodeDefManager: Absolute "
1036 "limit reached" << std::endl;
1037 return CONTENT_IGNORE;
1039 assert(id != CONTENT_IGNORE);
1040 addNameIdMapping(id, name);
1042 m_content_features[id] = def;
1043 verbosestream << "NodeDefManager: registering content id \"" << id
1044 << "\": name=\"" << def.name << "\""<<std::endl;
1046 // Add this content to the list of all groups it belongs to
1047 // FIXME: This should remove a node from groups it no longer
1048 // belongs to when a node is re-registered
1049 for (ItemGroupList::const_iterator i = def.groups.begin();
1050 i != def.groups.end(); ++i) {
1051 std::string group_name = i->first;
1053 UNORDERED_MAP<std::string, GroupItems>::iterator
1054 j = m_group_to_items.find(group_name);
1055 if (j == m_group_to_items.end()) {
1056 m_group_to_items[group_name].push_back(
1057 std::make_pair(id, i->second));
1059 GroupItems &items = j->second;
1060 items.push_back(std::make_pair(id, i->second));
1067 content_t CNodeDefManager::allocateDummy(const std::string &name)
1069 assert(name != ""); // Pre-condition
1072 return set(name, f);
1076 void CNodeDefManager::removeNode(const std::string &name)
1081 // Erase name from name ID mapping
1082 content_t id = CONTENT_IGNORE;
1083 if (m_name_id_mapping.getId(name, id)) {
1084 m_name_id_mapping.eraseName(name);
1085 m_name_id_mapping_with_aliases.erase(name);
1088 // Erase node content from all groups it belongs to
1089 for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1090 m_group_to_items.begin();
1091 iter_groups != m_group_to_items.end();) {
1092 GroupItems &items = iter_groups->second;
1093 for (GroupItems::iterator iter_groupitems = items.begin();
1094 iter_groupitems != items.end();) {
1095 if (iter_groupitems->first == id)
1096 items.erase(iter_groupitems++);
1101 // Check if group is empty
1102 if (items.size() == 0)
1103 m_group_to_items.erase(iter_groups++);
1110 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1112 std::set<std::string> all = idef->getAll();
1113 m_name_id_mapping_with_aliases.clear();
1114 for (std::set<std::string>::iterator
1115 i = all.begin(); i != all.end(); ++i) {
1116 std::string name = *i;
1117 std::string convert_to = idef->getAlias(name);
1119 if (m_name_id_mapping.getId(convert_to, id)) {
1120 m_name_id_mapping_with_aliases.insert(
1121 std::make_pair(name, id));
1126 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1128 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1129 "overrides to textures from " << override_filepath << std::endl;
1131 std::ifstream infile(override_filepath.c_str());
1134 while (std::getline(infile, line)) {
1136 if (trim(line) == "")
1138 std::vector<std::string> splitted = str_split(line, ' ');
1139 if (splitted.size() != 3) {
1140 errorstream << override_filepath
1141 << ":" << line_c << " Could not apply texture override \""
1142 << line << "\": Syntax error" << std::endl;
1147 if (!getId(splitted[0], id))
1148 continue; // Ignore unknown node
1150 ContentFeatures &nodedef = m_content_features[id];
1152 if (splitted[1] == "top")
1153 nodedef.tiledef[0].name = splitted[2];
1154 else if (splitted[1] == "bottom")
1155 nodedef.tiledef[1].name = splitted[2];
1156 else if (splitted[1] == "right")
1157 nodedef.tiledef[2].name = splitted[2];
1158 else if (splitted[1] == "left")
1159 nodedef.tiledef[3].name = splitted[2];
1160 else if (splitted[1] == "back")
1161 nodedef.tiledef[4].name = splitted[2];
1162 else if (splitted[1] == "front")
1163 nodedef.tiledef[5].name = splitted[2];
1164 else if (splitted[1] == "all" || splitted[1] == "*")
1165 for (int i = 0; i < 6; i++)
1166 nodedef.tiledef[i].name = splitted[2];
1167 else if (splitted[1] == "sides")
1168 for (int i = 2; i < 6; i++)
1169 nodedef.tiledef[i].name = splitted[2];
1171 errorstream << override_filepath
1172 << ":" << line_c << " Could not apply texture override \""
1173 << line << "\": Unknown node side \""
1174 << splitted[1] << "\"" << std::endl;
1180 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1181 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1182 void *progress_callback_args)
1185 infostream << "CNodeDefManager::updateTextures(): Updating "
1186 "textures in node definitions" << std::endl;
1187 ITextureSource *tsrc = gamedef->tsrc();
1188 IShaderSource *shdsrc = gamedef->getShaderSource();
1189 scene::ISceneManager* smgr = gamedef->getSceneManager();
1190 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1191 TextureSettings tsettings;
1192 tsettings.readSettings();
1194 u32 size = m_content_features.size();
1196 for (u32 i = 0; i < size; i++) {
1197 m_content_features[i].updateTextures(tsrc, shdsrc, smgr, meshmanip, gamedef, tsettings);
1198 progress_callback(progress_callback_args, i, size);
1203 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1205 writeU8(os, 1); // version
1207 std::ostringstream os2(std::ios::binary);
1208 for (u32 i = 0; i < m_content_features.size(); i++) {
1209 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1210 || i == CONTENT_UNKNOWN)
1212 const ContentFeatures *f = &m_content_features[i];
1216 // Wrap it in a string to allow different lengths without
1217 // strict version incompatibilities
1218 std::ostringstream wrapper_os(std::ios::binary);
1219 f->serialize(wrapper_os, protocol_version);
1220 os2<<serializeString(wrapper_os.str());
1222 // must not overflow
1223 u16 next = count + 1;
1224 FATAL_ERROR_IF(next < count, "Overflow");
1227 writeU16(os, count);
1228 os << serializeLongString(os2.str());
1232 void CNodeDefManager::deSerialize(std::istream &is)
1235 int version = readU8(is);
1237 throw SerializationError("unsupported NodeDefinitionManager version");
1238 u16 count = readU16(is);
1239 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1241 for (u16 n = 0; n < count; n++) {
1242 u16 i = readU16(is2);
1244 // Read it from the string wrapper
1245 std::string wrapper = deSerializeString(is2);
1246 std::istringstream wrapper_is(wrapper, std::ios::binary);
1247 f.deSerialize(wrapper_is);
1249 // Check error conditions
1250 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1251 warningstream << "NodeDefManager::deSerialize(): "
1252 "not changing builtin node " << i << std::endl;
1256 warningstream << "NodeDefManager::deSerialize(): "
1257 "received empty name" << std::endl;
1263 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1264 warningstream << "NodeDefManager::deSerialize(): "
1265 "already defined with different ID: " << f.name << std::endl;
1269 // All is ok, add node definition with the requested ID
1270 if (i >= m_content_features.size())
1271 m_content_features.resize((u32)(i) + 1);
1272 m_content_features[i] = f;
1273 addNameIdMapping(i, f.name);
1274 verbosestream << "deserialized " << f.name << std::endl;
1279 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1281 m_name_id_mapping.set(i, name);
1282 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1286 IWritableNodeDefManager *createNodeDefManager()
1288 return new CNodeDefManager();
1292 //// Serialization of old ContentFeatures formats
1293 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1295 if (protocol_version == 13)
1297 writeU8(os, 5); // version
1298 os<<serializeString(name);
1299 writeU16(os, groups.size());
1300 for (ItemGroupList::const_iterator
1301 i = groups.begin(); i != groups.end(); ++i) {
1302 os<<serializeString(i->first);
1303 writeS16(os, i->second);
1305 writeU8(os, drawtype);
1306 writeF1000(os, visual_scale);
1308 for (u32 i = 0; i < 6; i++)
1309 tiledef[i].serialize(os, protocol_version);
1310 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1312 for (u32 i = 0; i < 2; i++)
1313 tiledef_special[i].serialize(os, protocol_version);
1315 writeU8(os, post_effect_color.getAlpha());
1316 writeU8(os, post_effect_color.getRed());
1317 writeU8(os, post_effect_color.getGreen());
1318 writeU8(os, post_effect_color.getBlue());
1319 writeU8(os, param_type);
1320 writeU8(os, param_type_2);
1321 writeU8(os, is_ground_content);
1322 writeU8(os, light_propagates);
1323 writeU8(os, sunlight_propagates);
1324 writeU8(os, walkable);
1325 writeU8(os, pointable);
1326 writeU8(os, diggable);
1327 writeU8(os, climbable);
1328 writeU8(os, buildable_to);
1329 os<<serializeString(""); // legacy: used to be metadata_name
1330 writeU8(os, liquid_type);
1331 os<<serializeString(liquid_alternative_flowing);
1332 os<<serializeString(liquid_alternative_source);
1333 writeU8(os, liquid_viscosity);
1334 writeU8(os, light_source);
1335 writeU32(os, damage_per_second);
1336 node_box.serialize(os, protocol_version);
1337 selection_box.serialize(os, protocol_version);
1338 writeU8(os, legacy_facedir_simple);
1339 writeU8(os, legacy_wallmounted);
1340 serializeSimpleSoundSpec(sound_footstep, os);
1341 serializeSimpleSoundSpec(sound_dig, os);
1342 serializeSimpleSoundSpec(sound_dug, os);
1344 else if (protocol_version > 13 && protocol_version < 24) {
1345 writeU8(os, 6); // version
1346 os<<serializeString(name);
1347 writeU16(os, groups.size());
1348 for (ItemGroupList::const_iterator
1349 i = groups.begin(); i != groups.end(); ++i) {
1350 os<<serializeString(i->first);
1351 writeS16(os, i->second);
1353 writeU8(os, drawtype);
1354 writeF1000(os, visual_scale);
1356 for (u32 i = 0; i < 6; i++)
1357 tiledef[i].serialize(os, protocol_version);
1358 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1360 for (u32 i = 0; i < 2; i++)
1361 tiledef_special[i].serialize(os, protocol_version);
1363 writeU8(os, post_effect_color.getAlpha());
1364 writeU8(os, post_effect_color.getRed());
1365 writeU8(os, post_effect_color.getGreen());
1366 writeU8(os, post_effect_color.getBlue());
1367 writeU8(os, param_type);
1368 writeU8(os, param_type_2);
1369 writeU8(os, is_ground_content);
1370 writeU8(os, light_propagates);
1371 writeU8(os, sunlight_propagates);
1372 writeU8(os, walkable);
1373 writeU8(os, pointable);
1374 writeU8(os, diggable);
1375 writeU8(os, climbable);
1376 writeU8(os, buildable_to);
1377 os<<serializeString(""); // legacy: used to be metadata_name
1378 writeU8(os, liquid_type);
1379 os<<serializeString(liquid_alternative_flowing);
1380 os<<serializeString(liquid_alternative_source);
1381 writeU8(os, liquid_viscosity);
1382 writeU8(os, liquid_renewable);
1383 writeU8(os, light_source);
1384 writeU32(os, damage_per_second);
1385 node_box.serialize(os, protocol_version);
1386 selection_box.serialize(os, protocol_version);
1387 writeU8(os, legacy_facedir_simple);
1388 writeU8(os, legacy_wallmounted);
1389 serializeSimpleSoundSpec(sound_footstep, os);
1390 serializeSimpleSoundSpec(sound_dig, os);
1391 serializeSimpleSoundSpec(sound_dug, os);
1392 writeU8(os, rightclickable);
1393 writeU8(os, drowning);
1394 writeU8(os, leveled);
1395 writeU8(os, liquid_range);
1397 throw SerializationError("ContentFeatures::serialize(): "
1398 "Unsupported version requested");
1401 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1403 if (version == 5) // In PROTOCOL_VERSION 13
1405 name = deSerializeString(is);
1407 u32 groups_size = readU16(is);
1408 for(u32 i=0; i<groups_size; i++){
1409 std::string name = deSerializeString(is);
1410 int value = readS16(is);
1411 groups[name] = value;
1413 drawtype = (enum NodeDrawType)readU8(is);
1415 visual_scale = readF1000(is);
1416 if (readU8(is) != 6)
1417 throw SerializationError("unsupported tile count");
1418 for (u32 i = 0; i < 6; i++)
1419 tiledef[i].deSerialize(is, version, drawtype);
1420 if (readU8(is) != CF_SPECIAL_COUNT)
1421 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1422 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1423 tiledef_special[i].deSerialize(is, version, drawtype);
1425 post_effect_color.setAlpha(readU8(is));
1426 post_effect_color.setRed(readU8(is));
1427 post_effect_color.setGreen(readU8(is));
1428 post_effect_color.setBlue(readU8(is));
1429 param_type = (enum ContentParamType)readU8(is);
1430 param_type_2 = (enum ContentParamType2)readU8(is);
1431 is_ground_content = readU8(is);
1432 light_propagates = readU8(is);
1433 sunlight_propagates = readU8(is);
1434 walkable = readU8(is);
1435 pointable = readU8(is);
1436 diggable = readU8(is);
1437 climbable = readU8(is);
1438 buildable_to = readU8(is);
1439 deSerializeString(is); // legacy: used to be metadata_name
1440 liquid_type = (enum LiquidType)readU8(is);
1441 liquid_alternative_flowing = deSerializeString(is);
1442 liquid_alternative_source = deSerializeString(is);
1443 liquid_viscosity = readU8(is);
1444 light_source = readU8(is);
1445 damage_per_second = readU32(is);
1446 node_box.deSerialize(is);
1447 selection_box.deSerialize(is);
1448 legacy_facedir_simple = readU8(is);
1449 legacy_wallmounted = readU8(is);
1450 deSerializeSimpleSoundSpec(sound_footstep, is);
1451 deSerializeSimpleSoundSpec(sound_dig, is);
1452 deSerializeSimpleSoundSpec(sound_dug, is);
1453 } else if (version == 6) {
1454 name = deSerializeString(is);
1456 u32 groups_size = readU16(is);
1457 for (u32 i = 0; i < groups_size; i++) {
1458 std::string name = deSerializeString(is);
1459 int value = readS16(is);
1460 groups[name] = value;
1462 drawtype = (enum NodeDrawType)readU8(is);
1463 visual_scale = readF1000(is);
1464 if (readU8(is) != 6)
1465 throw SerializationError("unsupported tile count");
1466 for (u32 i = 0; i < 6; i++)
1467 tiledef[i].deSerialize(is, version, drawtype);
1468 // CF_SPECIAL_COUNT in version 6 = 2
1469 if (readU8(is) != 2)
1470 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1471 for (u32 i = 0; i < 2; i++)
1472 tiledef_special[i].deSerialize(is, version, drawtype);
1474 post_effect_color.setAlpha(readU8(is));
1475 post_effect_color.setRed(readU8(is));
1476 post_effect_color.setGreen(readU8(is));
1477 post_effect_color.setBlue(readU8(is));
1478 param_type = (enum ContentParamType)readU8(is);
1479 param_type_2 = (enum ContentParamType2)readU8(is);
1480 is_ground_content = readU8(is);
1481 light_propagates = readU8(is);
1482 sunlight_propagates = readU8(is);
1483 walkable = readU8(is);
1484 pointable = readU8(is);
1485 diggable = readU8(is);
1486 climbable = readU8(is);
1487 buildable_to = readU8(is);
1488 deSerializeString(is); // legacy: used to be metadata_name
1489 liquid_type = (enum LiquidType)readU8(is);
1490 liquid_alternative_flowing = deSerializeString(is);
1491 liquid_alternative_source = deSerializeString(is);
1492 liquid_viscosity = readU8(is);
1493 liquid_renewable = readU8(is);
1494 light_source = readU8(is);
1495 damage_per_second = readU32(is);
1496 node_box.deSerialize(is);
1497 selection_box.deSerialize(is);
1498 legacy_facedir_simple = readU8(is);
1499 legacy_wallmounted = readU8(is);
1500 deSerializeSimpleSoundSpec(sound_footstep, is);
1501 deSerializeSimpleSoundSpec(sound_dig, is);
1502 deSerializeSimpleSoundSpec(sound_dug, is);
1503 rightclickable = readU8(is);
1504 drowning = readU8(is);
1505 leveled = readU8(is);
1506 liquid_range = readU8(is);
1508 throw SerializationError("unsupported ContentFeatures version");
1513 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1515 return m_node_registration_complete;
1519 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1521 m_node_registration_complete = completed;
1525 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1528 if (m_node_registration_complete)
1529 nr->nodeResolveInternal();
1531 m_pending_resolve_callbacks.push_back(nr);
1535 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1537 size_t len = m_pending_resolve_callbacks.size();
1538 for (size_t i = 0; i != len; i++) {
1539 if (nr != m_pending_resolve_callbacks[i])
1543 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1544 m_pending_resolve_callbacks.resize(len);
1552 void CNodeDefManager::runNodeResolveCallbacks()
1554 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1555 NodeResolver *nr = m_pending_resolve_callbacks[i];
1556 nr->nodeResolveInternal();
1559 m_pending_resolve_callbacks.clear();
1563 void CNodeDefManager::resetNodeResolveState()
1565 m_node_registration_complete = false;
1566 m_pending_resolve_callbacks.clear();
1569 void CNodeDefManager::mapNodeboxConnections()
1571 for (u32 i = 0; i < m_content_features.size(); i++) {
1572 ContentFeatures *f = &m_content_features[i];
1573 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1575 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1576 it != f->connects_to.end(); ++it) {
1577 getIds(*it, f->connects_to_ids);
1582 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1584 const ContentFeatures &f1 = get(from);
1586 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1589 // lookup target in connected set
1590 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1593 const ContentFeatures &f2 = get(to);
1595 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1596 // ignores actually looking if back connection exists
1597 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1599 // does to node declare usable faces?
1600 if (f2.connect_sides > 0) {
1601 if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) {
1602 static const u8 rot[33 * 4] = {
1603 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1604 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back
1605 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right
1606 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1607 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front
1608 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1609 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1610 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1611 32, 16, 8, 4 // 32 - left
1613 return (f2.connect_sides & rot[(connect_face * 4) + to.param2]);
1615 return (f2.connect_sides & connect_face);
1617 // the target is just a regular node, so connect no matter back connection
1625 NodeResolver::NodeResolver()
1628 m_nodenames_idx = 0;
1629 m_nnlistsizes_idx = 0;
1630 m_resolve_done = false;
1632 m_nodenames.reserve(16);
1633 m_nnlistsizes.reserve(4);
1637 NodeResolver::~NodeResolver()
1639 if (!m_resolve_done && m_ndef)
1640 m_ndef->cancelNodeResolveCallback(this);
1644 void NodeResolver::nodeResolveInternal()
1646 m_nodenames_idx = 0;
1647 m_nnlistsizes_idx = 0;
1650 m_resolve_done = true;
1652 m_nodenames.clear();
1653 m_nnlistsizes.clear();
1657 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1658 const std::string &node_alt, content_t c_fallback)
1660 if (m_nodenames_idx == m_nodenames.size()) {
1661 *result_out = c_fallback;
1662 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1667 std::string name = m_nodenames[m_nodenames_idx++];
1669 bool success = m_ndef->getId(name, c);
1670 if (!success && node_alt != "") {
1672 success = m_ndef->getId(name, c);
1676 errorstream << "NodeResolver: failed to resolve node name '" << name
1677 << "'." << std::endl;
1686 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1687 bool all_required, content_t c_fallback)
1689 bool success = true;
1691 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1692 errorstream << "NodeResolver: no more node lists" << std::endl;
1696 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1699 if (m_nodenames_idx == m_nodenames.size()) {
1700 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1705 std::string &name = m_nodenames[m_nodenames_idx++];
1707 if (name.substr(0,6) != "group:") {
1708 if (m_ndef->getId(name, c)) {
1709 result_out->push_back(c);
1710 } else if (all_required) {
1711 errorstream << "NodeResolver: failed to resolve node name '"
1712 << name << "'." << std::endl;
1713 result_out->push_back(c_fallback);
1717 std::set<content_t> cids;
1718 std::set<content_t>::iterator it;
1719 m_ndef->getIds(name, cids);
1720 for (it = cids.begin(); it != cids.end(); ++it)
1721 result_out->push_back(*it);