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 >= 29)
193 else if (protocol_version >= 26)
195 else if (protocol_version >= 17)
199 os<<serializeString(name);
200 animation.serialize(os, protocol_version);
201 if (protocol_version >= 17)
202 writeU8(os, backface_culling);
203 if (protocol_version >= 26) {
204 writeU8(os, tileable_horizontal);
205 writeU8(os, tileable_vertical);
209 void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
211 int version = readU8(is);
212 name = deSerializeString(is);
213 animation.deSerialize(is, version >= 3 ? 29 : 26);
215 backface_culling = readU8(is);
217 tileable_horizontal = readU8(is);
218 tileable_vertical = readU8(is);
221 if ((contenfeatures_version < 8) &&
222 ((drawtype == NDT_MESH) ||
223 (drawtype == NDT_FIRELIKE) ||
224 (drawtype == NDT_LIQUID) ||
225 (drawtype == NDT_PLANTLIKE)))
226 backface_culling = false;
231 SimpleSoundSpec serialization
234 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
237 os<<serializeString(ss.name);
238 writeF1000(os, ss.gain);
240 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
242 ss.name = deSerializeString(is);
243 ss.gain = readF1000(is);
246 void TextureSettings::readSettings()
248 connected_glass = g_settings->getBool("connected_glass");
249 opaque_water = g_settings->getBool("opaque_water");
250 bool enable_shaders = g_settings->getBool("enable_shaders");
251 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
252 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
253 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
254 enable_minimap = g_settings->getBool("enable_minimap");
255 std::string leaves_style_str = g_settings->get("leaves_style");
257 use_normal_texture = enable_shaders &&
258 (enable_bumpmapping || enable_parallax_occlusion);
259 if (leaves_style_str == "fancy") {
260 leaves_style = LEAVES_FANCY;
261 } else if (leaves_style_str == "simple") {
262 leaves_style = LEAVES_SIMPLE;
264 leaves_style = LEAVES_OPAQUE;
272 ContentFeatures::ContentFeatures()
277 ContentFeatures::~ContentFeatures()
281 void ContentFeatures::reset()
288 visual_solidness = 0;
289 backface_culling = true;
292 has_on_construct = false;
293 has_on_destruct = false;
294 has_after_destruct = false;
298 NOTE: Most of this is always overridden by the default values given
303 // Unknown nodes can be dug
304 groups["dig_immediate"] = 2;
305 drawtype = NDT_NORMAL;
308 for(u32 i = 0; i < 24; i++)
310 minimap_color = video::SColor(0, 0, 0, 0);
313 for(u32 i = 0; i < 6; i++)
314 tiledef[i] = TileDef();
315 for(u16 j = 0; j < CF_SPECIAL_COUNT; j++)
316 tiledef_special[j] = TileDef();
318 post_effect_color = video::SColor(0, 0, 0, 0);
319 param_type = CPT_NONE;
320 param_type_2 = CPT2_NONE;
321 is_ground_content = false;
322 light_propagates = false;
323 sunlight_propagates = false;
328 buildable_to = false;
330 rightclickable = true;
332 liquid_type = LIQUID_NONE;
333 liquid_alternative_flowing = "";
334 liquid_alternative_source = "";
335 liquid_viscosity = 0;
336 liquid_renewable = true;
337 liquid_range = LIQUID_LEVEL_MAX+1;
340 damage_per_second = 0;
341 node_box = NodeBox();
342 selection_box = NodeBox();
343 collision_box = NodeBox();
345 legacy_facedir_simple = false;
346 legacy_wallmounted = false;
347 sound_footstep = SimpleSoundSpec();
348 sound_dig = SimpleSoundSpec("__group");
349 sound_dug = SimpleSoundSpec();
351 connects_to_ids.clear();
355 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
357 if(protocol_version < 24){
358 serializeOld(os, protocol_version);
362 writeU8(os, protocol_version < 27 ? 7 : 8);
364 os<<serializeString(name);
365 writeU16(os, groups.size());
366 for(ItemGroupList::const_iterator
367 i = groups.begin(); i != groups.end(); ++i){
368 os<<serializeString(i->first);
369 writeS16(os, i->second);
371 writeU8(os, drawtype);
372 writeF1000(os, visual_scale);
374 for(u32 i = 0; i < 6; i++)
375 tiledef[i].serialize(os, protocol_version);
376 writeU8(os, CF_SPECIAL_COUNT);
377 for(u32 i = 0; i < CF_SPECIAL_COUNT; i++){
378 tiledef_special[i].serialize(os, protocol_version);
381 writeU8(os, post_effect_color.getAlpha());
382 writeU8(os, post_effect_color.getRed());
383 writeU8(os, post_effect_color.getGreen());
384 writeU8(os, post_effect_color.getBlue());
385 writeU8(os, param_type);
386 if ((protocol_version < 28) && (param_type_2 == CPT2_MESHOPTIONS))
387 writeU8(os, CPT2_NONE);
389 writeU8(os, param_type_2);
390 writeU8(os, is_ground_content);
391 writeU8(os, light_propagates);
392 writeU8(os, sunlight_propagates);
393 writeU8(os, walkable);
394 writeU8(os, pointable);
395 writeU8(os, diggable);
396 writeU8(os, climbable);
397 writeU8(os, buildable_to);
398 os<<serializeString(""); // legacy: used to be metadata_name
399 writeU8(os, liquid_type);
400 os<<serializeString(liquid_alternative_flowing);
401 os<<serializeString(liquid_alternative_source);
402 writeU8(os, liquid_viscosity);
403 writeU8(os, liquid_renewable);
404 writeU8(os, light_source);
405 writeU32(os, damage_per_second);
406 node_box.serialize(os, protocol_version);
407 selection_box.serialize(os, protocol_version);
408 writeU8(os, legacy_facedir_simple);
409 writeU8(os, legacy_wallmounted);
410 serializeSimpleSoundSpec(sound_footstep, os);
411 serializeSimpleSoundSpec(sound_dig, os);
412 serializeSimpleSoundSpec(sound_dug, os);
413 writeU8(os, rightclickable);
414 writeU8(os, drowning);
415 writeU8(os, leveled);
416 writeU8(os, liquid_range);
418 // Stuff below should be moved to correct place in a version that otherwise changes
419 // the protocol version
420 os<<serializeString(mesh);
421 collision_box.serialize(os, protocol_version);
422 writeU8(os, floodable);
423 writeU16(os, connects_to_ids.size());
424 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
425 i != connects_to_ids.end(); ++i)
427 writeU8(os, connect_sides);
430 void ContentFeatures::deSerialize(std::istream &is)
432 int version = readU8(is);
434 deSerializeOld(is, version);
436 } else if (version > 8) {
437 throw SerializationError("unsupported ContentFeatures version");
440 name = deSerializeString(is);
442 u32 groups_size = readU16(is);
443 for(u32 i = 0; i < groups_size; i++){
444 std::string name = deSerializeString(is);
445 int value = readS16(is);
446 groups[name] = value;
448 drawtype = (enum NodeDrawType)readU8(is);
450 visual_scale = readF1000(is);
452 throw SerializationError("unsupported tile count");
453 for(u32 i = 0; i < 6; i++)
454 tiledef[i].deSerialize(is, version, drawtype);
455 if(readU8(is) != CF_SPECIAL_COUNT)
456 throw SerializationError("unsupported CF_SPECIAL_COUNT");
457 for(u32 i = 0; i < CF_SPECIAL_COUNT; i++)
458 tiledef_special[i].deSerialize(is, version, drawtype);
460 post_effect_color.setAlpha(readU8(is));
461 post_effect_color.setRed(readU8(is));
462 post_effect_color.setGreen(readU8(is));
463 post_effect_color.setBlue(readU8(is));
464 param_type = (enum ContentParamType)readU8(is);
465 param_type_2 = (enum ContentParamType2)readU8(is);
466 is_ground_content = readU8(is);
467 light_propagates = readU8(is);
468 sunlight_propagates = readU8(is);
469 walkable = readU8(is);
470 pointable = readU8(is);
471 diggable = readU8(is);
472 climbable = readU8(is);
473 buildable_to = readU8(is);
474 deSerializeString(is); // legacy: used to be metadata_name
475 liquid_type = (enum LiquidType)readU8(is);
476 liquid_alternative_flowing = deSerializeString(is);
477 liquid_alternative_source = deSerializeString(is);
478 liquid_viscosity = readU8(is);
479 liquid_renewable = readU8(is);
480 light_source = readU8(is);
481 light_source = MYMIN(light_source, LIGHT_MAX);
482 damage_per_second = readU32(is);
483 node_box.deSerialize(is);
484 selection_box.deSerialize(is);
485 legacy_facedir_simple = readU8(is);
486 legacy_wallmounted = readU8(is);
487 deSerializeSimpleSoundSpec(sound_footstep, is);
488 deSerializeSimpleSoundSpec(sound_dig, is);
489 deSerializeSimpleSoundSpec(sound_dug, is);
490 rightclickable = readU8(is);
491 drowning = readU8(is);
492 leveled = readU8(is);
493 liquid_range = readU8(is);
495 // If you add anything here, insert it primarily inside the try-catch
496 // block to not need to increase the version.
498 // Stuff below should be moved to correct place in a version that
499 // otherwise changes the protocol version
500 mesh = deSerializeString(is);
501 collision_box.deSerialize(is);
502 floodable = readU8(is);
503 u16 connects_to_size = readU16(is);
504 connects_to_ids.clear();
505 for (u16 i = 0; i < connects_to_size; i++)
506 connects_to_ids.insert(readU16(is));
507 connect_sides = readU8(is);
508 }catch(SerializationError &e) {};
512 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
513 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
514 bool backface_culling, u8 alpha, u8 material_type)
516 tile->shader_id = shader_id;
517 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
519 tile->material_type = material_type;
521 // Normal texture and shader flags texture
522 if (use_normal_texture) {
523 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
525 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
528 tile->material_flags = 0;
529 if (backface_culling)
530 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
531 if (tiledef->animation.type == TAT_VERTICAL_FRAMES)
532 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
533 if (tiledef->tileable_horizontal)
534 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
535 if (tiledef->tileable_vertical)
536 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
538 // Animation parameters
540 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
542 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
543 &frame_count, &frame_length_ms);
544 tile->animation_frame_count = frame_count;
545 tile->animation_frame_length_ms = frame_length_ms;
548 if (frame_count == 1) {
549 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
551 std::ostringstream os(std::ios::binary);
552 tile->frames.resize(frame_count);
554 for (int i = 0; i < frame_count; i++) {
560 tiledef->animation.getTextureModifer(os,
561 tile->texture->getOriginalSize(), i);
563 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
564 if (tile->normal_texture)
565 frame.normal_texture = tsrc->getNormalTexture(os.str());
566 frame.flags_texture = tile->flags_texture;
567 tile->frames[i] = frame;
574 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
575 scene::ISceneManager *smgr, scene::IMeshManipulator *meshmanip,
576 IGameDef *gamedef, const TextureSettings &tsettings)
578 // minimap pixel color - the average color of a texture
579 if (tsettings.enable_minimap && tiledef[0].name != "")
580 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
582 // Figure out the actual tiles to use
584 for (u32 j = 0; j < 6; j++) {
585 tdef[j] = tiledef[j];
586 if (tdef[j].name == "")
587 tdef[j].name = "unknown_node.png";
590 bool is_liquid = false;
591 bool is_water_surface = false;
593 u8 material_type = (alpha == 255) ?
594 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
605 assert(liquid_type == LIQUID_SOURCE);
606 if (tsettings.opaque_water)
611 case NDT_FLOWINGLIQUID:
612 assert(liquid_type == LIQUID_FLOWING);
614 if (tsettings.opaque_water)
620 visual_solidness = 1;
622 case NDT_GLASSLIKE_FRAMED:
624 visual_solidness = 1;
626 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
628 visual_solidness = 1;
629 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
633 visual_solidness = 1;
635 case NDT_ALLFACES_OPTIONAL:
636 if (tsettings.leaves_style == LEAVES_FANCY) {
637 drawtype = NDT_ALLFACES;
639 visual_solidness = 1;
640 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
641 for (u32 j = 0; j < 6; j++) {
642 if (tiledef_special[j].name != "")
643 tdef[j].name = tiledef_special[j].name;
645 drawtype = NDT_GLASSLIKE;
647 visual_solidness = 1;
649 drawtype = NDT_NORMAL;
651 for (u32 i = 0; i < 6; i++)
652 tdef[i].name += std::string("^[noalpha");
655 material_type = TILE_MATERIAL_WAVING_LEAVES;
660 material_type = TILE_MATERIAL_WAVING_PLANTS;
678 material_type = (alpha == 255) ?
679 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
680 if (name == "default:water_source")
681 is_water_surface = true;
685 for (u16 j = 0; j < 6; j++) {
686 tile_shader[j] = shdsrc->getShader("nodes_shader",
687 material_type, drawtype);
690 if (is_water_surface) {
691 tile_shader[0] = shdsrc->getShader("water_surface_shader",
692 material_type, drawtype);
695 // Tiles (fill in f->tiles[])
696 for (u16 j = 0; j < 6; j++) {
697 fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j],
698 tsettings.use_normal_texture,
699 tiledef[j].backface_culling, alpha, material_type);
702 // Special tiles (fill in f->special_tiles[])
703 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
704 fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j],
705 tile_shader[j], tsettings.use_normal_texture,
706 tiledef_special[j].backface_culling, alpha, material_type);
709 if ((drawtype == NDT_MESH) && (mesh != "")) {
711 // Read the mesh and apply scale
712 mesh_ptr[0] = gamedef->getMesh(mesh);
714 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
715 scaleMesh(mesh_ptr[0], scale);
716 recalculateBoundingBox(mesh_ptr[0]);
717 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
719 } else if ((drawtype == NDT_NODEBOX) &&
720 ((node_box.type == NODEBOX_REGULAR) ||
721 (node_box.type == NODEBOX_FIXED)) &&
722 (!node_box.fixed.empty())) {
723 //Convert regular nodebox nodes to meshnodes
724 //Change the drawtype and apply scale
726 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
727 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
728 scaleMesh(mesh_ptr[0], scale);
729 recalculateBoundingBox(mesh_ptr[0]);
730 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
733 //Cache 6dfacedir and wallmounted rotated clones of meshes
734 if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_FACEDIR)) {
735 for (u16 j = 1; j < 24; j++) {
736 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
737 rotateMeshBy6dFacedir(mesh_ptr[j], j);
738 recalculateBoundingBox(mesh_ptr[j]);
739 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
741 } else if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_WALLMOUNTED)) {
742 static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
743 for (u16 j = 1; j < 6; j++) {
744 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
745 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
746 recalculateBoundingBox(mesh_ptr[j]);
747 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
749 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
750 recalculateBoundingBox(mesh_ptr[0]);
751 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
760 class CNodeDefManager: public IWritableNodeDefManager {
763 virtual ~CNodeDefManager();
765 virtual IWritableNodeDefManager *clone();
766 inline virtual const ContentFeatures& get(content_t c) const;
767 inline virtual const ContentFeatures& get(const MapNode &n) const;
768 virtual bool getId(const std::string &name, content_t &result) const;
769 virtual content_t getId(const std::string &name) const;
770 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
771 virtual const ContentFeatures& get(const std::string &name) const;
772 content_t allocateId();
773 virtual content_t set(const std::string &name, const ContentFeatures &def);
774 virtual content_t allocateDummy(const std::string &name);
775 virtual void removeNode(const std::string &name);
776 virtual void updateAliases(IItemDefManager *idef);
777 virtual void applyTextureOverrides(const std::string &override_filepath);
778 virtual void updateTextures(IGameDef *gamedef,
779 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
780 void *progress_cbk_args);
781 void serialize(std::ostream &os, u16 protocol_version) const;
782 void deSerialize(std::istream &is);
784 inline virtual bool getNodeRegistrationStatus() const;
785 inline virtual void setNodeRegistrationStatus(bool completed);
787 virtual void pendNodeResolve(NodeResolver *nr);
788 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
789 virtual void runNodeResolveCallbacks();
790 virtual void resetNodeResolveState();
791 virtual void mapNodeboxConnections();
792 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
795 void addNameIdMapping(content_t i, std::string name);
797 // Features indexed by id
798 std::vector<ContentFeatures> m_content_features;
800 // A mapping for fast converting back and forth between names and ids
801 NameIdMapping m_name_id_mapping;
803 // Like m_name_id_mapping, but only from names to ids, and includes
804 // item aliases too. Updated by updateAliases()
805 // Note: Not serialized.
807 UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
809 // A mapping from groups to a list of content_ts (and their levels)
810 // that belong to it. Necessary for a direct lookup in getIds().
811 // Note: Not serialized.
812 UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
814 // Next possibly free id
817 // NodeResolvers to callback once node registration has ended
818 std::vector<NodeResolver *> m_pending_resolve_callbacks;
820 // True when all nodes have been registered
821 bool m_node_registration_complete;
825 CNodeDefManager::CNodeDefManager()
831 CNodeDefManager::~CNodeDefManager()
834 for (u32 i = 0; i < m_content_features.size(); i++) {
835 ContentFeatures *f = &m_content_features[i];
836 for (u32 j = 0; j < 24; j++) {
838 f->mesh_ptr[j]->drop();
845 void CNodeDefManager::clear()
847 m_content_features.clear();
848 m_name_id_mapping.clear();
849 m_name_id_mapping_with_aliases.clear();
850 m_group_to_items.clear();
853 resetNodeResolveState();
855 u32 initial_length = 0;
856 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
857 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
858 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
859 m_content_features.resize(initial_length);
861 // Set CONTENT_UNKNOWN
865 // Insert directly into containers
866 content_t c = CONTENT_UNKNOWN;
867 m_content_features[c] = f;
868 addNameIdMapping(c, f.name);
875 f.drawtype = NDT_AIRLIKE;
876 f.param_type = CPT_LIGHT;
877 f.light_propagates = true;
878 f.sunlight_propagates = true;
882 f.buildable_to = true;
884 f.is_ground_content = true;
885 // Insert directly into containers
886 content_t c = CONTENT_AIR;
887 m_content_features[c] = f;
888 addNameIdMapping(c, f.name);
891 // Set CONTENT_IGNORE
895 f.drawtype = NDT_AIRLIKE;
896 f.param_type = CPT_NONE;
897 f.light_propagates = false;
898 f.sunlight_propagates = false;
902 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
903 f.is_ground_content = true;
904 // Insert directly into containers
905 content_t c = CONTENT_IGNORE;
906 m_content_features[c] = f;
907 addNameIdMapping(c, f.name);
912 IWritableNodeDefManager *CNodeDefManager::clone()
914 CNodeDefManager *mgr = new CNodeDefManager();
920 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
922 return c < m_content_features.size()
923 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
927 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
929 return get(n.getContent());
933 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
935 UNORDERED_MAP<std::string, content_t>::const_iterator
936 i = m_name_id_mapping_with_aliases.find(name);
937 if(i == m_name_id_mapping_with_aliases.end())
944 content_t CNodeDefManager::getId(const std::string &name) const
946 content_t id = CONTENT_IGNORE;
952 bool CNodeDefManager::getIds(const std::string &name,
953 std::set<content_t> &result) const
955 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
956 if (name.substr(0,6) != "group:") {
957 content_t id = CONTENT_IGNORE;
958 bool exists = getId(name, id);
963 std::string group = name.substr(6);
965 UNORDERED_MAP<std::string, GroupItems>::const_iterator
966 i = m_group_to_items.find(group);
967 if (i == m_group_to_items.end())
970 const GroupItems &items = i->second;
971 for (GroupItems::const_iterator j = items.begin();
972 j != items.end(); ++j) {
973 if ((*j).second != 0)
974 result.insert((*j).first);
976 //printf("getIds: %dus\n", t.stop());
981 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
983 content_t id = CONTENT_UNKNOWN;
989 // returns CONTENT_IGNORE if no free ID found
990 content_t CNodeDefManager::allocateId()
992 for (content_t id = m_next_id;
993 id >= m_next_id; // overflow?
995 while (id >= m_content_features.size()) {
996 m_content_features.push_back(ContentFeatures());
998 const ContentFeatures &f = m_content_features[id];
1004 // If we arrive here, an overflow occurred in id.
1005 // That means no ID was found
1006 return CONTENT_IGNORE;
1010 // IWritableNodeDefManager
1011 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1015 assert(name == def.name);
1017 // Don't allow redefining ignore (but allow air and unknown)
1018 if (name == "ignore") {
1019 warningstream << "NodeDefManager: Ignoring "
1020 "CONTENT_IGNORE redefinition"<<std::endl;
1021 return CONTENT_IGNORE;
1024 content_t id = CONTENT_IGNORE;
1025 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1028 if (id == CONTENT_IGNORE) {
1029 warningstream << "NodeDefManager: Absolute "
1030 "limit reached" << std::endl;
1031 return CONTENT_IGNORE;
1033 assert(id != CONTENT_IGNORE);
1034 addNameIdMapping(id, name);
1036 m_content_features[id] = def;
1037 verbosestream << "NodeDefManager: registering content id \"" << id
1038 << "\": name=\"" << def.name << "\""<<std::endl;
1040 // Add this content to the list of all groups it belongs to
1041 // FIXME: This should remove a node from groups it no longer
1042 // belongs to when a node is re-registered
1043 for (ItemGroupList::const_iterator i = def.groups.begin();
1044 i != def.groups.end(); ++i) {
1045 std::string group_name = i->first;
1047 UNORDERED_MAP<std::string, GroupItems>::iterator
1048 j = m_group_to_items.find(group_name);
1049 if (j == m_group_to_items.end()) {
1050 m_group_to_items[group_name].push_back(
1051 std::make_pair(id, i->second));
1053 GroupItems &items = j->second;
1054 items.push_back(std::make_pair(id, i->second));
1061 content_t CNodeDefManager::allocateDummy(const std::string &name)
1063 assert(name != ""); // Pre-condition
1066 return set(name, f);
1070 void CNodeDefManager::removeNode(const std::string &name)
1075 // Erase name from name ID mapping
1076 content_t id = CONTENT_IGNORE;
1077 if (m_name_id_mapping.getId(name, id)) {
1078 m_name_id_mapping.eraseName(name);
1079 m_name_id_mapping_with_aliases.erase(name);
1082 // Erase node content from all groups it belongs to
1083 for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1084 m_group_to_items.begin();
1085 iter_groups != m_group_to_items.end();) {
1086 GroupItems &items = iter_groups->second;
1087 for (GroupItems::iterator iter_groupitems = items.begin();
1088 iter_groupitems != items.end();) {
1089 if (iter_groupitems->first == id)
1090 items.erase(iter_groupitems++);
1095 // Check if group is empty
1096 if (items.size() == 0)
1097 m_group_to_items.erase(iter_groups++);
1104 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1106 std::set<std::string> all = idef->getAll();
1107 m_name_id_mapping_with_aliases.clear();
1108 for (std::set<std::string>::iterator
1109 i = all.begin(); i != all.end(); ++i) {
1110 std::string name = *i;
1111 std::string convert_to = idef->getAlias(name);
1113 if (m_name_id_mapping.getId(convert_to, id)) {
1114 m_name_id_mapping_with_aliases.insert(
1115 std::make_pair(name, id));
1120 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1122 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1123 "overrides to textures from " << override_filepath << std::endl;
1125 std::ifstream infile(override_filepath.c_str());
1128 while (std::getline(infile, line)) {
1130 if (trim(line) == "")
1132 std::vector<std::string> splitted = str_split(line, ' ');
1133 if (splitted.size() != 3) {
1134 errorstream << override_filepath
1135 << ":" << line_c << " Could not apply texture override \""
1136 << line << "\": Syntax error" << std::endl;
1141 if (!getId(splitted[0], id))
1142 continue; // Ignore unknown node
1144 ContentFeatures &nodedef = m_content_features[id];
1146 if (splitted[1] == "top")
1147 nodedef.tiledef[0].name = splitted[2];
1148 else if (splitted[1] == "bottom")
1149 nodedef.tiledef[1].name = splitted[2];
1150 else if (splitted[1] == "right")
1151 nodedef.tiledef[2].name = splitted[2];
1152 else if (splitted[1] == "left")
1153 nodedef.tiledef[3].name = splitted[2];
1154 else if (splitted[1] == "back")
1155 nodedef.tiledef[4].name = splitted[2];
1156 else if (splitted[1] == "front")
1157 nodedef.tiledef[5].name = splitted[2];
1158 else if (splitted[1] == "all" || splitted[1] == "*")
1159 for (int i = 0; i < 6; i++)
1160 nodedef.tiledef[i].name = splitted[2];
1161 else if (splitted[1] == "sides")
1162 for (int i = 2; i < 6; i++)
1163 nodedef.tiledef[i].name = splitted[2];
1165 errorstream << override_filepath
1166 << ":" << line_c << " Could not apply texture override \""
1167 << line << "\": Unknown node side \""
1168 << splitted[1] << "\"" << std::endl;
1174 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1175 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1176 void *progress_callback_args)
1179 infostream << "CNodeDefManager::updateTextures(): Updating "
1180 "textures in node definitions" << std::endl;
1181 ITextureSource *tsrc = gamedef->tsrc();
1182 IShaderSource *shdsrc = gamedef->getShaderSource();
1183 scene::ISceneManager* smgr = gamedef->getSceneManager();
1184 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1185 TextureSettings tsettings;
1186 tsettings.readSettings();
1188 u32 size = m_content_features.size();
1190 for (u32 i = 0; i < size; i++) {
1191 m_content_features[i].updateTextures(tsrc, shdsrc, smgr, meshmanip, gamedef, tsettings);
1192 progress_callback(progress_callback_args, i, size);
1197 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1199 writeU8(os, 1); // version
1201 std::ostringstream os2(std::ios::binary);
1202 for (u32 i = 0; i < m_content_features.size(); i++) {
1203 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1204 || i == CONTENT_UNKNOWN)
1206 const ContentFeatures *f = &m_content_features[i];
1210 // Wrap it in a string to allow different lengths without
1211 // strict version incompatibilities
1212 std::ostringstream wrapper_os(std::ios::binary);
1213 f->serialize(wrapper_os, protocol_version);
1214 os2<<serializeString(wrapper_os.str());
1216 // must not overflow
1217 u16 next = count + 1;
1218 FATAL_ERROR_IF(next < count, "Overflow");
1221 writeU16(os, count);
1222 os << serializeLongString(os2.str());
1226 void CNodeDefManager::deSerialize(std::istream &is)
1229 int version = readU8(is);
1231 throw SerializationError("unsupported NodeDefinitionManager version");
1232 u16 count = readU16(is);
1233 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1235 for (u16 n = 0; n < count; n++) {
1236 u16 i = readU16(is2);
1238 // Read it from the string wrapper
1239 std::string wrapper = deSerializeString(is2);
1240 std::istringstream wrapper_is(wrapper, std::ios::binary);
1241 f.deSerialize(wrapper_is);
1243 // Check error conditions
1244 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1245 warningstream << "NodeDefManager::deSerialize(): "
1246 "not changing builtin node " << i << std::endl;
1250 warningstream << "NodeDefManager::deSerialize(): "
1251 "received empty name" << std::endl;
1257 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1258 warningstream << "NodeDefManager::deSerialize(): "
1259 "already defined with different ID: " << f.name << std::endl;
1263 // All is ok, add node definition with the requested ID
1264 if (i >= m_content_features.size())
1265 m_content_features.resize((u32)(i) + 1);
1266 m_content_features[i] = f;
1267 addNameIdMapping(i, f.name);
1268 verbosestream << "deserialized " << f.name << std::endl;
1273 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1275 m_name_id_mapping.set(i, name);
1276 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1280 IWritableNodeDefManager *createNodeDefManager()
1282 return new CNodeDefManager();
1286 //// Serialization of old ContentFeatures formats
1287 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1289 if (protocol_version == 13)
1291 writeU8(os, 5); // version
1292 os<<serializeString(name);
1293 writeU16(os, groups.size());
1294 for (ItemGroupList::const_iterator
1295 i = groups.begin(); i != groups.end(); ++i) {
1296 os<<serializeString(i->first);
1297 writeS16(os, i->second);
1299 writeU8(os, drawtype);
1300 writeF1000(os, visual_scale);
1302 for (u32 i = 0; i < 6; i++)
1303 tiledef[i].serialize(os, protocol_version);
1304 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1306 for (u32 i = 0; i < 2; i++)
1307 tiledef_special[i].serialize(os, protocol_version);
1309 writeU8(os, post_effect_color.getAlpha());
1310 writeU8(os, post_effect_color.getRed());
1311 writeU8(os, post_effect_color.getGreen());
1312 writeU8(os, post_effect_color.getBlue());
1313 writeU8(os, param_type);
1314 writeU8(os, param_type_2);
1315 writeU8(os, is_ground_content);
1316 writeU8(os, light_propagates);
1317 writeU8(os, sunlight_propagates);
1318 writeU8(os, walkable);
1319 writeU8(os, pointable);
1320 writeU8(os, diggable);
1321 writeU8(os, climbable);
1322 writeU8(os, buildable_to);
1323 os<<serializeString(""); // legacy: used to be metadata_name
1324 writeU8(os, liquid_type);
1325 os<<serializeString(liquid_alternative_flowing);
1326 os<<serializeString(liquid_alternative_source);
1327 writeU8(os, liquid_viscosity);
1328 writeU8(os, light_source);
1329 writeU32(os, damage_per_second);
1330 node_box.serialize(os, protocol_version);
1331 selection_box.serialize(os, protocol_version);
1332 writeU8(os, legacy_facedir_simple);
1333 writeU8(os, legacy_wallmounted);
1334 serializeSimpleSoundSpec(sound_footstep, os);
1335 serializeSimpleSoundSpec(sound_dig, os);
1336 serializeSimpleSoundSpec(sound_dug, os);
1338 else if (protocol_version > 13 && protocol_version < 24) {
1339 writeU8(os, 6); // version
1340 os<<serializeString(name);
1341 writeU16(os, groups.size());
1342 for (ItemGroupList::const_iterator
1343 i = groups.begin(); i != groups.end(); ++i) {
1344 os<<serializeString(i->first);
1345 writeS16(os, i->second);
1347 writeU8(os, drawtype);
1348 writeF1000(os, visual_scale);
1350 for (u32 i = 0; i < 6; i++)
1351 tiledef[i].serialize(os, protocol_version);
1352 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1354 for (u32 i = 0; i < 2; i++)
1355 tiledef_special[i].serialize(os, protocol_version);
1357 writeU8(os, post_effect_color.getAlpha());
1358 writeU8(os, post_effect_color.getRed());
1359 writeU8(os, post_effect_color.getGreen());
1360 writeU8(os, post_effect_color.getBlue());
1361 writeU8(os, param_type);
1362 writeU8(os, param_type_2);
1363 writeU8(os, is_ground_content);
1364 writeU8(os, light_propagates);
1365 writeU8(os, sunlight_propagates);
1366 writeU8(os, walkable);
1367 writeU8(os, pointable);
1368 writeU8(os, diggable);
1369 writeU8(os, climbable);
1370 writeU8(os, buildable_to);
1371 os<<serializeString(""); // legacy: used to be metadata_name
1372 writeU8(os, liquid_type);
1373 os<<serializeString(liquid_alternative_flowing);
1374 os<<serializeString(liquid_alternative_source);
1375 writeU8(os, liquid_viscosity);
1376 writeU8(os, liquid_renewable);
1377 writeU8(os, light_source);
1378 writeU32(os, damage_per_second);
1379 node_box.serialize(os, protocol_version);
1380 selection_box.serialize(os, protocol_version);
1381 writeU8(os, legacy_facedir_simple);
1382 writeU8(os, legacy_wallmounted);
1383 serializeSimpleSoundSpec(sound_footstep, os);
1384 serializeSimpleSoundSpec(sound_dig, os);
1385 serializeSimpleSoundSpec(sound_dug, os);
1386 writeU8(os, rightclickable);
1387 writeU8(os, drowning);
1388 writeU8(os, leveled);
1389 writeU8(os, liquid_range);
1391 throw SerializationError("ContentFeatures::serialize(): "
1392 "Unsupported version requested");
1395 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1397 if (version == 5) // In PROTOCOL_VERSION 13
1399 name = deSerializeString(is);
1401 u32 groups_size = readU16(is);
1402 for(u32 i=0; i<groups_size; i++){
1403 std::string name = deSerializeString(is);
1404 int value = readS16(is);
1405 groups[name] = value;
1407 drawtype = (enum NodeDrawType)readU8(is);
1409 visual_scale = readF1000(is);
1410 if (readU8(is) != 6)
1411 throw SerializationError("unsupported tile count");
1412 for (u32 i = 0; i < 6; i++)
1413 tiledef[i].deSerialize(is, version, drawtype);
1414 if (readU8(is) != CF_SPECIAL_COUNT)
1415 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1416 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1417 tiledef_special[i].deSerialize(is, version, drawtype);
1419 post_effect_color.setAlpha(readU8(is));
1420 post_effect_color.setRed(readU8(is));
1421 post_effect_color.setGreen(readU8(is));
1422 post_effect_color.setBlue(readU8(is));
1423 param_type = (enum ContentParamType)readU8(is);
1424 param_type_2 = (enum ContentParamType2)readU8(is);
1425 is_ground_content = readU8(is);
1426 light_propagates = readU8(is);
1427 sunlight_propagates = readU8(is);
1428 walkable = readU8(is);
1429 pointable = readU8(is);
1430 diggable = readU8(is);
1431 climbable = readU8(is);
1432 buildable_to = readU8(is);
1433 deSerializeString(is); // legacy: used to be metadata_name
1434 liquid_type = (enum LiquidType)readU8(is);
1435 liquid_alternative_flowing = deSerializeString(is);
1436 liquid_alternative_source = deSerializeString(is);
1437 liquid_viscosity = readU8(is);
1438 light_source = readU8(is);
1439 light_source = MYMIN(light_source, LIGHT_MAX);
1440 damage_per_second = readU32(is);
1441 node_box.deSerialize(is);
1442 selection_box.deSerialize(is);
1443 legacy_facedir_simple = readU8(is);
1444 legacy_wallmounted = readU8(is);
1445 deSerializeSimpleSoundSpec(sound_footstep, is);
1446 deSerializeSimpleSoundSpec(sound_dig, is);
1447 deSerializeSimpleSoundSpec(sound_dug, is);
1448 } else if (version == 6) {
1449 name = deSerializeString(is);
1451 u32 groups_size = readU16(is);
1452 for (u32 i = 0; i < groups_size; i++) {
1453 std::string name = deSerializeString(is);
1454 int value = readS16(is);
1455 groups[name] = value;
1457 drawtype = (enum NodeDrawType)readU8(is);
1458 visual_scale = readF1000(is);
1459 if (readU8(is) != 6)
1460 throw SerializationError("unsupported tile count");
1461 for (u32 i = 0; i < 6; i++)
1462 tiledef[i].deSerialize(is, version, drawtype);
1463 // CF_SPECIAL_COUNT in version 6 = 2
1464 if (readU8(is) != 2)
1465 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1466 for (u32 i = 0; i < 2; i++)
1467 tiledef_special[i].deSerialize(is, version, drawtype);
1469 post_effect_color.setAlpha(readU8(is));
1470 post_effect_color.setRed(readU8(is));
1471 post_effect_color.setGreen(readU8(is));
1472 post_effect_color.setBlue(readU8(is));
1473 param_type = (enum ContentParamType)readU8(is);
1474 param_type_2 = (enum ContentParamType2)readU8(is);
1475 is_ground_content = readU8(is);
1476 light_propagates = readU8(is);
1477 sunlight_propagates = readU8(is);
1478 walkable = readU8(is);
1479 pointable = readU8(is);
1480 diggable = readU8(is);
1481 climbable = readU8(is);
1482 buildable_to = readU8(is);
1483 deSerializeString(is); // legacy: used to be metadata_name
1484 liquid_type = (enum LiquidType)readU8(is);
1485 liquid_alternative_flowing = deSerializeString(is);
1486 liquid_alternative_source = deSerializeString(is);
1487 liquid_viscosity = readU8(is);
1488 liquid_renewable = readU8(is);
1489 light_source = readU8(is);
1490 damage_per_second = readU32(is);
1491 node_box.deSerialize(is);
1492 selection_box.deSerialize(is);
1493 legacy_facedir_simple = readU8(is);
1494 legacy_wallmounted = readU8(is);
1495 deSerializeSimpleSoundSpec(sound_footstep, is);
1496 deSerializeSimpleSoundSpec(sound_dig, is);
1497 deSerializeSimpleSoundSpec(sound_dug, is);
1498 rightclickable = readU8(is);
1499 drowning = readU8(is);
1500 leveled = readU8(is);
1501 liquid_range = readU8(is);
1503 throw SerializationError("unsupported ContentFeatures version");
1508 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1510 return m_node_registration_complete;
1514 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1516 m_node_registration_complete = completed;
1520 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1523 if (m_node_registration_complete)
1524 nr->nodeResolveInternal();
1526 m_pending_resolve_callbacks.push_back(nr);
1530 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1532 size_t len = m_pending_resolve_callbacks.size();
1533 for (size_t i = 0; i != len; i++) {
1534 if (nr != m_pending_resolve_callbacks[i])
1538 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1539 m_pending_resolve_callbacks.resize(len);
1547 void CNodeDefManager::runNodeResolveCallbacks()
1549 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1550 NodeResolver *nr = m_pending_resolve_callbacks[i];
1551 nr->nodeResolveInternal();
1554 m_pending_resolve_callbacks.clear();
1558 void CNodeDefManager::resetNodeResolveState()
1560 m_node_registration_complete = false;
1561 m_pending_resolve_callbacks.clear();
1564 void CNodeDefManager::mapNodeboxConnections()
1566 for (u32 i = 0; i < m_content_features.size(); i++) {
1567 ContentFeatures *f = &m_content_features[i];
1568 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1570 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1571 it != f->connects_to.end(); ++it) {
1572 getIds(*it, f->connects_to_ids);
1577 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1579 const ContentFeatures &f1 = get(from);
1581 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1584 // lookup target in connected set
1585 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1588 const ContentFeatures &f2 = get(to);
1590 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1591 // ignores actually looking if back connection exists
1592 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1594 // does to node declare usable faces?
1595 if (f2.connect_sides > 0) {
1596 if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) {
1597 static const u8 rot[33 * 4] = {
1598 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1599 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back
1600 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right
1601 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1602 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front
1603 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1604 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1605 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1606 32, 16, 8, 4 // 32 - left
1608 return (f2.connect_sides & rot[(connect_face * 4) + to.param2]);
1610 return (f2.connect_sides & connect_face);
1612 // the target is just a regular node, so connect no matter back connection
1620 NodeResolver::NodeResolver()
1623 m_nodenames_idx = 0;
1624 m_nnlistsizes_idx = 0;
1625 m_resolve_done = false;
1627 m_nodenames.reserve(16);
1628 m_nnlistsizes.reserve(4);
1632 NodeResolver::~NodeResolver()
1634 if (!m_resolve_done && m_ndef)
1635 m_ndef->cancelNodeResolveCallback(this);
1639 void NodeResolver::nodeResolveInternal()
1641 m_nodenames_idx = 0;
1642 m_nnlistsizes_idx = 0;
1645 m_resolve_done = true;
1647 m_nodenames.clear();
1648 m_nnlistsizes.clear();
1652 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1653 const std::string &node_alt, content_t c_fallback)
1655 if (m_nodenames_idx == m_nodenames.size()) {
1656 *result_out = c_fallback;
1657 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1662 std::string name = m_nodenames[m_nodenames_idx++];
1664 bool success = m_ndef->getId(name, c);
1665 if (!success && node_alt != "") {
1667 success = m_ndef->getId(name, c);
1671 errorstream << "NodeResolver: failed to resolve node name '" << name
1672 << "'." << std::endl;
1681 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1682 bool all_required, content_t c_fallback)
1684 bool success = true;
1686 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1687 errorstream << "NodeResolver: no more node lists" << std::endl;
1691 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1694 if (m_nodenames_idx == m_nodenames.size()) {
1695 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1700 std::string &name = m_nodenames[m_nodenames_idx++];
1702 if (name.substr(0,6) != "group:") {
1703 if (m_ndef->getId(name, c)) {
1704 result_out->push_back(c);
1705 } else if (all_required) {
1706 errorstream << "NodeResolver: failed to resolve node name '"
1707 << name << "'." << std::endl;
1708 result_out->push_back(c_fallback);
1712 std::set<content_t> cids;
1713 std::set<content_t>::iterator it;
1714 m_ndef->getIds(name, cids);
1715 for (it = cids.begin(); it != cids.end(); ++it)
1716 result_out->push_back(*it);