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 std::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 std::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 std::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 std::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 std::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 (std::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 infostream << override_filepath
1149 << ":" << line_c << " Could not apply texture override \""
1150 << line << "\": Unknown node \""
1151 << splitted[0] << "\"" << std::endl;
1155 ContentFeatures &nodedef = m_content_features[id];
1157 if (splitted[1] == "top")
1158 nodedef.tiledef[0].name = splitted[2];
1159 else if (splitted[1] == "bottom")
1160 nodedef.tiledef[1].name = splitted[2];
1161 else if (splitted[1] == "right")
1162 nodedef.tiledef[2].name = splitted[2];
1163 else if (splitted[1] == "left")
1164 nodedef.tiledef[3].name = splitted[2];
1165 else if (splitted[1] == "back")
1166 nodedef.tiledef[4].name = splitted[2];
1167 else if (splitted[1] == "front")
1168 nodedef.tiledef[5].name = splitted[2];
1169 else if (splitted[1] == "all" || splitted[1] == "*")
1170 for (int i = 0; i < 6; i++)
1171 nodedef.tiledef[i].name = splitted[2];
1172 else if (splitted[1] == "sides")
1173 for (int i = 2; i < 6; i++)
1174 nodedef.tiledef[i].name = splitted[2];
1176 errorstream << override_filepath
1177 << ":" << line_c << " Could not apply texture override \""
1178 << line << "\": Unknown node side \""
1179 << splitted[1] << "\"" << std::endl;
1185 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1186 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1187 void *progress_callback_args)
1190 infostream << "CNodeDefManager::updateTextures(): Updating "
1191 "textures in node definitions" << std::endl;
1192 ITextureSource *tsrc = gamedef->tsrc();
1193 IShaderSource *shdsrc = gamedef->getShaderSource();
1194 scene::ISceneManager* smgr = gamedef->getSceneManager();
1195 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1196 TextureSettings tsettings;
1197 tsettings.readSettings();
1199 u32 size = m_content_features.size();
1201 for (u32 i = 0; i < size; i++) {
1202 m_content_features[i].updateTextures(tsrc, shdsrc, smgr, meshmanip, gamedef, tsettings);
1203 progress_callback(progress_callback_args, i, size);
1208 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1210 writeU8(os, 1); // version
1212 std::ostringstream os2(std::ios::binary);
1213 for (u32 i = 0; i < m_content_features.size(); i++) {
1214 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1215 || i == CONTENT_UNKNOWN)
1217 const ContentFeatures *f = &m_content_features[i];
1221 // Wrap it in a string to allow different lengths without
1222 // strict version incompatibilities
1223 std::ostringstream wrapper_os(std::ios::binary);
1224 f->serialize(wrapper_os, protocol_version);
1225 os2<<serializeString(wrapper_os.str());
1227 // must not overflow
1228 u16 next = count + 1;
1229 FATAL_ERROR_IF(next < count, "Overflow");
1232 writeU16(os, count);
1233 os << serializeLongString(os2.str());
1237 void CNodeDefManager::deSerialize(std::istream &is)
1240 int version = readU8(is);
1242 throw SerializationError("unsupported NodeDefinitionManager version");
1243 u16 count = readU16(is);
1244 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1246 for (u16 n = 0; n < count; n++) {
1247 u16 i = readU16(is2);
1249 // Read it from the string wrapper
1250 std::string wrapper = deSerializeString(is2);
1251 std::istringstream wrapper_is(wrapper, std::ios::binary);
1252 f.deSerialize(wrapper_is);
1254 // Check error conditions
1255 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1256 warningstream << "NodeDefManager::deSerialize(): "
1257 "not changing builtin node " << i << std::endl;
1261 warningstream << "NodeDefManager::deSerialize(): "
1262 "received empty name" << std::endl;
1268 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1269 warningstream << "NodeDefManager::deSerialize(): "
1270 "already defined with different ID: " << f.name << std::endl;
1274 // All is ok, add node definition with the requested ID
1275 if (i >= m_content_features.size())
1276 m_content_features.resize((u32)(i) + 1);
1277 m_content_features[i] = f;
1278 addNameIdMapping(i, f.name);
1279 verbosestream << "deserialized " << f.name << std::endl;
1284 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1286 m_name_id_mapping.set(i, name);
1287 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1291 IWritableNodeDefManager *createNodeDefManager()
1293 return new CNodeDefManager();
1297 //// Serialization of old ContentFeatures formats
1298 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1300 if (protocol_version == 13)
1302 writeU8(os, 5); // version
1303 os<<serializeString(name);
1304 writeU16(os, groups.size());
1305 for (ItemGroupList::const_iterator
1306 i = groups.begin(); i != groups.end(); ++i) {
1307 os<<serializeString(i->first);
1308 writeS16(os, i->second);
1310 writeU8(os, drawtype);
1311 writeF1000(os, visual_scale);
1313 for (u32 i = 0; i < 6; i++)
1314 tiledef[i].serialize(os, protocol_version);
1315 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1317 for (u32 i = 0; i < 2; i++)
1318 tiledef_special[i].serialize(os, protocol_version);
1320 writeU8(os, post_effect_color.getAlpha());
1321 writeU8(os, post_effect_color.getRed());
1322 writeU8(os, post_effect_color.getGreen());
1323 writeU8(os, post_effect_color.getBlue());
1324 writeU8(os, param_type);
1325 writeU8(os, param_type_2);
1326 writeU8(os, is_ground_content);
1327 writeU8(os, light_propagates);
1328 writeU8(os, sunlight_propagates);
1329 writeU8(os, walkable);
1330 writeU8(os, pointable);
1331 writeU8(os, diggable);
1332 writeU8(os, climbable);
1333 writeU8(os, buildable_to);
1334 os<<serializeString(""); // legacy: used to be metadata_name
1335 writeU8(os, liquid_type);
1336 os<<serializeString(liquid_alternative_flowing);
1337 os<<serializeString(liquid_alternative_source);
1338 writeU8(os, liquid_viscosity);
1339 writeU8(os, light_source);
1340 writeU32(os, damage_per_second);
1341 node_box.serialize(os, protocol_version);
1342 selection_box.serialize(os, protocol_version);
1343 writeU8(os, legacy_facedir_simple);
1344 writeU8(os, legacy_wallmounted);
1345 serializeSimpleSoundSpec(sound_footstep, os);
1346 serializeSimpleSoundSpec(sound_dig, os);
1347 serializeSimpleSoundSpec(sound_dug, os);
1349 else if (protocol_version > 13 && protocol_version < 24) {
1350 writeU8(os, 6); // version
1351 os<<serializeString(name);
1352 writeU16(os, groups.size());
1353 for (ItemGroupList::const_iterator
1354 i = groups.begin(); i != groups.end(); ++i) {
1355 os<<serializeString(i->first);
1356 writeS16(os, i->second);
1358 writeU8(os, drawtype);
1359 writeF1000(os, visual_scale);
1361 for (u32 i = 0; i < 6; i++)
1362 tiledef[i].serialize(os, protocol_version);
1363 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1365 for (u32 i = 0; i < 2; i++)
1366 tiledef_special[i].serialize(os, protocol_version);
1368 writeU8(os, post_effect_color.getAlpha());
1369 writeU8(os, post_effect_color.getRed());
1370 writeU8(os, post_effect_color.getGreen());
1371 writeU8(os, post_effect_color.getBlue());
1372 writeU8(os, param_type);
1373 writeU8(os, param_type_2);
1374 writeU8(os, is_ground_content);
1375 writeU8(os, light_propagates);
1376 writeU8(os, sunlight_propagates);
1377 writeU8(os, walkable);
1378 writeU8(os, pointable);
1379 writeU8(os, diggable);
1380 writeU8(os, climbable);
1381 writeU8(os, buildable_to);
1382 os<<serializeString(""); // legacy: used to be metadata_name
1383 writeU8(os, liquid_type);
1384 os<<serializeString(liquid_alternative_flowing);
1385 os<<serializeString(liquid_alternative_source);
1386 writeU8(os, liquid_viscosity);
1387 writeU8(os, liquid_renewable);
1388 writeU8(os, light_source);
1389 writeU32(os, damage_per_second);
1390 node_box.serialize(os, protocol_version);
1391 selection_box.serialize(os, protocol_version);
1392 writeU8(os, legacy_facedir_simple);
1393 writeU8(os, legacy_wallmounted);
1394 serializeSimpleSoundSpec(sound_footstep, os);
1395 serializeSimpleSoundSpec(sound_dig, os);
1396 serializeSimpleSoundSpec(sound_dug, os);
1397 writeU8(os, rightclickable);
1398 writeU8(os, drowning);
1399 writeU8(os, leveled);
1400 writeU8(os, liquid_range);
1402 throw SerializationError("ContentFeatures::serialize(): "
1403 "Unsupported version requested");
1406 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1408 if (version == 5) // In PROTOCOL_VERSION 13
1410 name = deSerializeString(is);
1412 u32 groups_size = readU16(is);
1413 for(u32 i=0; i<groups_size; i++){
1414 std::string name = deSerializeString(is);
1415 int value = readS16(is);
1416 groups[name] = value;
1418 drawtype = (enum NodeDrawType)readU8(is);
1420 visual_scale = readF1000(is);
1421 if (readU8(is) != 6)
1422 throw SerializationError("unsupported tile count");
1423 for (u32 i = 0; i < 6; i++)
1424 tiledef[i].deSerialize(is, version, drawtype);
1425 if (readU8(is) != CF_SPECIAL_COUNT)
1426 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1427 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1428 tiledef_special[i].deSerialize(is, version, drawtype);
1430 post_effect_color.setAlpha(readU8(is));
1431 post_effect_color.setRed(readU8(is));
1432 post_effect_color.setGreen(readU8(is));
1433 post_effect_color.setBlue(readU8(is));
1434 param_type = (enum ContentParamType)readU8(is);
1435 param_type_2 = (enum ContentParamType2)readU8(is);
1436 is_ground_content = readU8(is);
1437 light_propagates = readU8(is);
1438 sunlight_propagates = readU8(is);
1439 walkable = readU8(is);
1440 pointable = readU8(is);
1441 diggable = readU8(is);
1442 climbable = readU8(is);
1443 buildable_to = readU8(is);
1444 deSerializeString(is); // legacy: used to be metadata_name
1445 liquid_type = (enum LiquidType)readU8(is);
1446 liquid_alternative_flowing = deSerializeString(is);
1447 liquid_alternative_source = deSerializeString(is);
1448 liquid_viscosity = readU8(is);
1449 light_source = readU8(is);
1450 damage_per_second = readU32(is);
1451 node_box.deSerialize(is);
1452 selection_box.deSerialize(is);
1453 legacy_facedir_simple = readU8(is);
1454 legacy_wallmounted = readU8(is);
1455 deSerializeSimpleSoundSpec(sound_footstep, is);
1456 deSerializeSimpleSoundSpec(sound_dig, is);
1457 deSerializeSimpleSoundSpec(sound_dug, is);
1458 } else if (version == 6) {
1459 name = deSerializeString(is);
1461 u32 groups_size = readU16(is);
1462 for (u32 i = 0; i < groups_size; i++) {
1463 std::string name = deSerializeString(is);
1464 int value = readS16(is);
1465 groups[name] = value;
1467 drawtype = (enum NodeDrawType)readU8(is);
1468 visual_scale = readF1000(is);
1469 if (readU8(is) != 6)
1470 throw SerializationError("unsupported tile count");
1471 for (u32 i = 0; i < 6; i++)
1472 tiledef[i].deSerialize(is, version, drawtype);
1473 // CF_SPECIAL_COUNT in version 6 = 2
1474 if (readU8(is) != 2)
1475 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1476 for (u32 i = 0; i < 2; i++)
1477 tiledef_special[i].deSerialize(is, version, drawtype);
1479 post_effect_color.setAlpha(readU8(is));
1480 post_effect_color.setRed(readU8(is));
1481 post_effect_color.setGreen(readU8(is));
1482 post_effect_color.setBlue(readU8(is));
1483 param_type = (enum ContentParamType)readU8(is);
1484 param_type_2 = (enum ContentParamType2)readU8(is);
1485 is_ground_content = readU8(is);
1486 light_propagates = readU8(is);
1487 sunlight_propagates = readU8(is);
1488 walkable = readU8(is);
1489 pointable = readU8(is);
1490 diggable = readU8(is);
1491 climbable = readU8(is);
1492 buildable_to = readU8(is);
1493 deSerializeString(is); // legacy: used to be metadata_name
1494 liquid_type = (enum LiquidType)readU8(is);
1495 liquid_alternative_flowing = deSerializeString(is);
1496 liquid_alternative_source = deSerializeString(is);
1497 liquid_viscosity = readU8(is);
1498 liquid_renewable = readU8(is);
1499 light_source = readU8(is);
1500 damage_per_second = readU32(is);
1501 node_box.deSerialize(is);
1502 selection_box.deSerialize(is);
1503 legacy_facedir_simple = readU8(is);
1504 legacy_wallmounted = readU8(is);
1505 deSerializeSimpleSoundSpec(sound_footstep, is);
1506 deSerializeSimpleSoundSpec(sound_dig, is);
1507 deSerializeSimpleSoundSpec(sound_dug, is);
1508 rightclickable = readU8(is);
1509 drowning = readU8(is);
1510 leveled = readU8(is);
1511 liquid_range = readU8(is);
1513 throw SerializationError("unsupported ContentFeatures version");
1518 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1520 return m_node_registration_complete;
1524 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1526 m_node_registration_complete = completed;
1530 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1533 if (m_node_registration_complete)
1534 nr->nodeResolveInternal();
1536 m_pending_resolve_callbacks.push_back(nr);
1540 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1542 size_t len = m_pending_resolve_callbacks.size();
1543 for (size_t i = 0; i != len; i++) {
1544 if (nr != m_pending_resolve_callbacks[i])
1548 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1549 m_pending_resolve_callbacks.resize(len);
1557 void CNodeDefManager::runNodeResolveCallbacks()
1559 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1560 NodeResolver *nr = m_pending_resolve_callbacks[i];
1561 nr->nodeResolveInternal();
1564 m_pending_resolve_callbacks.clear();
1568 void CNodeDefManager::resetNodeResolveState()
1570 m_node_registration_complete = false;
1571 m_pending_resolve_callbacks.clear();
1574 void CNodeDefManager::mapNodeboxConnections()
1576 for (u32 i = 0; i < m_content_features.size(); i++) {
1577 ContentFeatures *f = &m_content_features[i];
1578 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1580 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1581 it != f->connects_to.end(); ++it) {
1582 getIds(*it, f->connects_to_ids);
1587 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1589 const ContentFeatures &f1 = get(from);
1591 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1594 // lookup target in connected set
1595 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1598 const ContentFeatures &f2 = get(to);
1600 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1601 // ignores actually looking if back connection exists
1602 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1604 // does to node declare usable faces?
1605 if (f2.connect_sides > 0) {
1606 if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) {
1607 static const u8 rot[33 * 4] = {
1608 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1609 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back
1610 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right
1611 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1612 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front
1613 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1614 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1615 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1616 32, 16, 8, 4 // 32 - left
1618 return (f2.connect_sides & rot[(connect_face * 4) + to.param2]);
1620 return (f2.connect_sides & connect_face);
1622 // the target is just a regular node, so connect no matter back connection
1630 NodeResolver::NodeResolver()
1633 m_nodenames_idx = 0;
1634 m_nnlistsizes_idx = 0;
1635 m_resolve_done = false;
1637 m_nodenames.reserve(16);
1638 m_nnlistsizes.reserve(4);
1642 NodeResolver::~NodeResolver()
1644 if (!m_resolve_done && m_ndef)
1645 m_ndef->cancelNodeResolveCallback(this);
1649 void NodeResolver::nodeResolveInternal()
1651 m_nodenames_idx = 0;
1652 m_nnlistsizes_idx = 0;
1655 m_resolve_done = true;
1657 m_nodenames.clear();
1658 m_nnlistsizes.clear();
1662 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1663 const std::string &node_alt, content_t c_fallback)
1665 if (m_nodenames_idx == m_nodenames.size()) {
1666 *result_out = c_fallback;
1667 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1672 std::string name = m_nodenames[m_nodenames_idx++];
1674 bool success = m_ndef->getId(name, c);
1675 if (!success && node_alt != "") {
1677 success = m_ndef->getId(name, c);
1681 errorstream << "NodeResolver: failed to resolve node name '" << name
1682 << "'." << std::endl;
1691 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1692 bool all_required, content_t c_fallback)
1694 bool success = true;
1696 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1697 errorstream << "NodeResolver: no more node lists" << std::endl;
1701 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1704 if (m_nodenames_idx == m_nodenames.size()) {
1705 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1710 std::string &name = m_nodenames[m_nodenames_idx++];
1712 if (name.substr(0,6) != "group:") {
1713 if (m_ndef->getId(name, c)) {
1714 result_out->push_back(c);
1715 } else if (all_required) {
1716 errorstream << "NodeResolver: failed to resolve node name '"
1717 << name << "'." << std::endl;
1718 result_out->push_back(c_fallback);
1722 std::set<content_t> cids;
1723 std::set<content_t>::iterator it;
1724 m_ndef->getIds(name, cids);
1725 for (it = cids.begin(); it != cids.end(); ++it)
1726 result_out->push_back(*it);