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_NONE)
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);
793 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
795 return m_selection_box_int_union;
799 void addNameIdMapping(content_t i, std::string name);
801 * Recalculates m_selection_box_int_union based on
802 * m_selection_box_union.
804 void fixSelectionBoxIntUnion();
806 // Features indexed by id
807 std::vector<ContentFeatures> m_content_features;
809 // A mapping for fast converting back and forth between names and ids
810 NameIdMapping m_name_id_mapping;
812 // Like m_name_id_mapping, but only from names to ids, and includes
813 // item aliases too. Updated by updateAliases()
814 // Note: Not serialized.
816 UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
818 // A mapping from groups to a list of content_ts (and their levels)
819 // that belong to it. Necessary for a direct lookup in getIds().
820 // Note: Not serialized.
821 UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
823 // Next possibly free id
826 // NodeResolvers to callback once node registration has ended
827 std::vector<NodeResolver *> m_pending_resolve_callbacks;
829 // True when all nodes have been registered
830 bool m_node_registration_complete;
832 //! The union of all nodes' selection boxes.
833 aabb3f m_selection_box_union;
835 * The smallest box in node coordinates that
836 * contains all nodes' selection boxes.
838 core::aabbox3d<s16> m_selection_box_int_union;
842 CNodeDefManager::CNodeDefManager()
848 CNodeDefManager::~CNodeDefManager()
851 for (u32 i = 0; i < m_content_features.size(); i++) {
852 ContentFeatures *f = &m_content_features[i];
853 for (u32 j = 0; j < 24; j++) {
855 f->mesh_ptr[j]->drop();
862 void CNodeDefManager::clear()
864 m_content_features.clear();
865 m_name_id_mapping.clear();
866 m_name_id_mapping_with_aliases.clear();
867 m_group_to_items.clear();
869 m_selection_box_union.reset(0,0,0);
870 m_selection_box_int_union.reset(0,0,0);
872 resetNodeResolveState();
874 u32 initial_length = 0;
875 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
876 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
877 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
878 m_content_features.resize(initial_length);
880 // Set CONTENT_UNKNOWN
884 // Insert directly into containers
885 content_t c = CONTENT_UNKNOWN;
886 m_content_features[c] = f;
887 addNameIdMapping(c, f.name);
894 f.drawtype = NDT_AIRLIKE;
895 f.param_type = CPT_LIGHT;
896 f.light_propagates = true;
897 f.sunlight_propagates = true;
901 f.buildable_to = true;
903 f.is_ground_content = true;
904 // Insert directly into containers
905 content_t c = CONTENT_AIR;
906 m_content_features[c] = f;
907 addNameIdMapping(c, f.name);
910 // Set CONTENT_IGNORE
914 f.drawtype = NDT_AIRLIKE;
915 f.param_type = CPT_NONE;
916 f.light_propagates = false;
917 f.sunlight_propagates = false;
921 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
922 f.is_ground_content = true;
923 // Insert directly into containers
924 content_t c = CONTENT_IGNORE;
925 m_content_features[c] = f;
926 addNameIdMapping(c, f.name);
931 IWritableNodeDefManager *CNodeDefManager::clone()
933 CNodeDefManager *mgr = new CNodeDefManager();
939 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
941 return c < m_content_features.size()
942 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
946 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
948 return get(n.getContent());
952 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
954 UNORDERED_MAP<std::string, content_t>::const_iterator
955 i = m_name_id_mapping_with_aliases.find(name);
956 if(i == m_name_id_mapping_with_aliases.end())
963 content_t CNodeDefManager::getId(const std::string &name) const
965 content_t id = CONTENT_IGNORE;
971 bool CNodeDefManager::getIds(const std::string &name,
972 std::set<content_t> &result) const
974 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
975 if (name.substr(0,6) != "group:") {
976 content_t id = CONTENT_IGNORE;
977 bool exists = getId(name, id);
982 std::string group = name.substr(6);
984 UNORDERED_MAP<std::string, GroupItems>::const_iterator
985 i = m_group_to_items.find(group);
986 if (i == m_group_to_items.end())
989 const GroupItems &items = i->second;
990 for (GroupItems::const_iterator j = items.begin();
991 j != items.end(); ++j) {
992 if ((*j).second != 0)
993 result.insert((*j).first);
995 //printf("getIds: %dus\n", t.stop());
1000 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1002 content_t id = CONTENT_UNKNOWN;
1008 // returns CONTENT_IGNORE if no free ID found
1009 content_t CNodeDefManager::allocateId()
1011 for (content_t id = m_next_id;
1012 id >= m_next_id; // overflow?
1014 while (id >= m_content_features.size()) {
1015 m_content_features.push_back(ContentFeatures());
1017 const ContentFeatures &f = m_content_features[id];
1023 // If we arrive here, an overflow occurred in id.
1024 // That means no ID was found
1025 return CONTENT_IGNORE;
1030 * Returns the smallest box that contains all boxes
1031 * in the vector. Box_union is expanded.
1032 * @param[in] boxes the vector containing the boxes
1033 * @param[in, out] box_union the union of the arguments
1035 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1037 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1038 it != boxes.end(); ++it) {
1039 box_union->addInternalBox(*it);
1045 * Returns a box that contains the nodebox in every case.
1046 * The argument node_union is expanded.
1047 * @param[in] nodebox the nodebox to be measured
1048 * @param[in] features used to decide whether the nodebox
1050 * @param[in, out] box_union the union of the arguments
1052 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1055 switch(nodebox.type) {
1057 case NODEBOX_LEVELED: {
1059 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1060 boxVectorUnion(nodebox.fixed, &half_processed);
1061 // Set leveled boxes to maximal
1062 if (nodebox.type == NODEBOX_LEVELED) {
1063 half_processed.MaxEdge.Y = +BS / 2;
1065 if (features.param_type_2 == CPT2_FACEDIR) {
1066 // Get maximal coordinate
1068 fabsf(half_processed.MinEdge.X),
1069 fabsf(half_processed.MinEdge.Y),
1070 fabsf(half_processed.MinEdge.Z),
1071 fabsf(half_processed.MaxEdge.X),
1072 fabsf(half_processed.MaxEdge.Y),
1073 fabsf(half_processed.MaxEdge.Z) };
1075 for (int i = 0; i < 6; i++) {
1076 if (max < coords[i]) {
1080 // Add the union of all possible rotated boxes
1081 box_union->addInternalPoint(-max, -max, -max);
1082 box_union->addInternalPoint(+max, +max, +max);
1084 box_union->addInternalBox(half_processed);
1088 case NODEBOX_WALLMOUNTED: {
1090 box_union->addInternalBox(nodebox.wall_top);
1091 box_union->addInternalBox(nodebox.wall_bottom);
1092 // Find maximal coordinate in the X-Z plane
1094 fabsf(nodebox.wall_side.MinEdge.X),
1095 fabsf(nodebox.wall_side.MinEdge.Z),
1096 fabsf(nodebox.wall_side.MaxEdge.X),
1097 fabsf(nodebox.wall_side.MaxEdge.Z) };
1099 for (int i = 0; i < 4; i++) {
1100 if (max < coords[i]) {
1104 // Add the union of all possible rotated boxes
1105 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1106 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1109 case NODEBOX_CONNECTED: {
1110 // Add all possible connected boxes
1111 boxVectorUnion(nodebox.fixed, box_union);
1112 boxVectorUnion(nodebox.connect_top, box_union);
1113 boxVectorUnion(nodebox.connect_bottom, box_union);
1114 boxVectorUnion(nodebox.connect_front, box_union);
1115 boxVectorUnion(nodebox.connect_left, box_union);
1116 boxVectorUnion(nodebox.connect_back, box_union);
1117 boxVectorUnion(nodebox.connect_right, box_union);
1122 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1123 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1129 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1131 m_selection_box_int_union.MinEdge.X = floorf(
1132 m_selection_box_union.MinEdge.X / BS + 0.5f);
1133 m_selection_box_int_union.MinEdge.Y = floorf(
1134 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1135 m_selection_box_int_union.MinEdge.Z = floorf(
1136 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1137 m_selection_box_int_union.MaxEdge.X = ceilf(
1138 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1139 m_selection_box_int_union.MaxEdge.Y = ceilf(
1140 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1141 m_selection_box_int_union.MaxEdge.Z = ceilf(
1142 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1146 // IWritableNodeDefManager
1147 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1151 assert(name == def.name);
1153 // Don't allow redefining ignore (but allow air and unknown)
1154 if (name == "ignore") {
1155 warningstream << "NodeDefManager: Ignoring "
1156 "CONTENT_IGNORE redefinition"<<std::endl;
1157 return CONTENT_IGNORE;
1160 content_t id = CONTENT_IGNORE;
1161 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1164 if (id == CONTENT_IGNORE) {
1165 warningstream << "NodeDefManager: Absolute "
1166 "limit reached" << std::endl;
1167 return CONTENT_IGNORE;
1169 assert(id != CONTENT_IGNORE);
1170 addNameIdMapping(id, name);
1172 m_content_features[id] = def;
1173 verbosestream << "NodeDefManager: registering content id \"" << id
1174 << "\": name=\"" << def.name << "\""<<std::endl;
1176 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1177 fixSelectionBoxIntUnion();
1178 // Add this content to the list of all groups it belongs to
1179 // FIXME: This should remove a node from groups it no longer
1180 // belongs to when a node is re-registered
1181 for (ItemGroupList::const_iterator i = def.groups.begin();
1182 i != def.groups.end(); ++i) {
1183 std::string group_name = i->first;
1185 UNORDERED_MAP<std::string, GroupItems>::iterator
1186 j = m_group_to_items.find(group_name);
1187 if (j == m_group_to_items.end()) {
1188 m_group_to_items[group_name].push_back(
1189 std::make_pair(id, i->second));
1191 GroupItems &items = j->second;
1192 items.push_back(std::make_pair(id, i->second));
1199 content_t CNodeDefManager::allocateDummy(const std::string &name)
1201 assert(name != ""); // Pre-condition
1204 return set(name, f);
1208 void CNodeDefManager::removeNode(const std::string &name)
1213 // Erase name from name ID mapping
1214 content_t id = CONTENT_IGNORE;
1215 if (m_name_id_mapping.getId(name, id)) {
1216 m_name_id_mapping.eraseName(name);
1217 m_name_id_mapping_with_aliases.erase(name);
1220 // Erase node content from all groups it belongs to
1221 for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1222 m_group_to_items.begin();
1223 iter_groups != m_group_to_items.end();) {
1224 GroupItems &items = iter_groups->second;
1225 for (GroupItems::iterator iter_groupitems = items.begin();
1226 iter_groupitems != items.end();) {
1227 if (iter_groupitems->first == id)
1228 items.erase(iter_groupitems++);
1233 // Check if group is empty
1234 if (items.size() == 0)
1235 m_group_to_items.erase(iter_groups++);
1242 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1244 std::set<std::string> all = idef->getAll();
1245 m_name_id_mapping_with_aliases.clear();
1246 for (std::set<std::string>::iterator
1247 i = all.begin(); i != all.end(); ++i) {
1248 std::string name = *i;
1249 std::string convert_to = idef->getAlias(name);
1251 if (m_name_id_mapping.getId(convert_to, id)) {
1252 m_name_id_mapping_with_aliases.insert(
1253 std::make_pair(name, id));
1258 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1260 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1261 "overrides to textures from " << override_filepath << std::endl;
1263 std::ifstream infile(override_filepath.c_str());
1266 while (std::getline(infile, line)) {
1268 if (trim(line) == "")
1270 std::vector<std::string> splitted = str_split(line, ' ');
1271 if (splitted.size() != 3) {
1272 errorstream << override_filepath
1273 << ":" << line_c << " Could not apply texture override \""
1274 << line << "\": Syntax error" << std::endl;
1279 if (!getId(splitted[0], id))
1280 continue; // Ignore unknown node
1282 ContentFeatures &nodedef = m_content_features[id];
1284 if (splitted[1] == "top")
1285 nodedef.tiledef[0].name = splitted[2];
1286 else if (splitted[1] == "bottom")
1287 nodedef.tiledef[1].name = splitted[2];
1288 else if (splitted[1] == "right")
1289 nodedef.tiledef[2].name = splitted[2];
1290 else if (splitted[1] == "left")
1291 nodedef.tiledef[3].name = splitted[2];
1292 else if (splitted[1] == "back")
1293 nodedef.tiledef[4].name = splitted[2];
1294 else if (splitted[1] == "front")
1295 nodedef.tiledef[5].name = splitted[2];
1296 else if (splitted[1] == "all" || splitted[1] == "*")
1297 for (int i = 0; i < 6; i++)
1298 nodedef.tiledef[i].name = splitted[2];
1299 else if (splitted[1] == "sides")
1300 for (int i = 2; i < 6; i++)
1301 nodedef.tiledef[i].name = splitted[2];
1303 errorstream << override_filepath
1304 << ":" << line_c << " Could not apply texture override \""
1305 << line << "\": Unknown node side \""
1306 << splitted[1] << "\"" << std::endl;
1312 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1313 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1314 void *progress_callback_args)
1317 infostream << "CNodeDefManager::updateTextures(): Updating "
1318 "textures in node definitions" << std::endl;
1319 ITextureSource *tsrc = gamedef->tsrc();
1320 IShaderSource *shdsrc = gamedef->getShaderSource();
1321 scene::ISceneManager* smgr = gamedef->getSceneManager();
1322 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1323 TextureSettings tsettings;
1324 tsettings.readSettings();
1326 u32 size = m_content_features.size();
1328 for (u32 i = 0; i < size; i++) {
1329 m_content_features[i].updateTextures(tsrc, shdsrc, smgr, meshmanip, gamedef, tsettings);
1330 progress_callback(progress_callback_args, i, size);
1335 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1337 writeU8(os, 1); // version
1339 std::ostringstream os2(std::ios::binary);
1340 for (u32 i = 0; i < m_content_features.size(); i++) {
1341 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1342 || i == CONTENT_UNKNOWN)
1344 const ContentFeatures *f = &m_content_features[i];
1348 // Wrap it in a string to allow different lengths without
1349 // strict version incompatibilities
1350 std::ostringstream wrapper_os(std::ios::binary);
1351 f->serialize(wrapper_os, protocol_version);
1352 os2<<serializeString(wrapper_os.str());
1354 // must not overflow
1355 u16 next = count + 1;
1356 FATAL_ERROR_IF(next < count, "Overflow");
1359 writeU16(os, count);
1360 os << serializeLongString(os2.str());
1364 void CNodeDefManager::deSerialize(std::istream &is)
1367 int version = readU8(is);
1369 throw SerializationError("unsupported NodeDefinitionManager version");
1370 u16 count = readU16(is);
1371 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1373 for (u16 n = 0; n < count; n++) {
1374 u16 i = readU16(is2);
1376 // Read it from the string wrapper
1377 std::string wrapper = deSerializeString(is2);
1378 std::istringstream wrapper_is(wrapper, std::ios::binary);
1379 f.deSerialize(wrapper_is);
1381 // Check error conditions
1382 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1383 warningstream << "NodeDefManager::deSerialize(): "
1384 "not changing builtin node " << i << std::endl;
1388 warningstream << "NodeDefManager::deSerialize(): "
1389 "received empty name" << std::endl;
1395 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1396 warningstream << "NodeDefManager::deSerialize(): "
1397 "already defined with different ID: " << f.name << std::endl;
1401 // All is ok, add node definition with the requested ID
1402 if (i >= m_content_features.size())
1403 m_content_features.resize((u32)(i) + 1);
1404 m_content_features[i] = f;
1405 addNameIdMapping(i, f.name);
1406 verbosestream << "deserialized " << f.name << std::endl;
1408 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1409 fixSelectionBoxIntUnion();
1414 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1416 m_name_id_mapping.set(i, name);
1417 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1421 IWritableNodeDefManager *createNodeDefManager()
1423 return new CNodeDefManager();
1427 //// Serialization of old ContentFeatures formats
1428 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1430 if (protocol_version == 13)
1432 writeU8(os, 5); // version
1433 os<<serializeString(name);
1434 writeU16(os, groups.size());
1435 for (ItemGroupList::const_iterator
1436 i = groups.begin(); i != groups.end(); ++i) {
1437 os<<serializeString(i->first);
1438 writeS16(os, i->second);
1440 writeU8(os, drawtype);
1441 writeF1000(os, visual_scale);
1443 for (u32 i = 0; i < 6; i++)
1444 tiledef[i].serialize(os, protocol_version);
1445 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1447 for (u32 i = 0; i < 2; i++)
1448 tiledef_special[i].serialize(os, protocol_version);
1450 writeU8(os, post_effect_color.getAlpha());
1451 writeU8(os, post_effect_color.getRed());
1452 writeU8(os, post_effect_color.getGreen());
1453 writeU8(os, post_effect_color.getBlue());
1454 writeU8(os, param_type);
1455 writeU8(os, param_type_2);
1456 writeU8(os, is_ground_content);
1457 writeU8(os, light_propagates);
1458 writeU8(os, sunlight_propagates);
1459 writeU8(os, walkable);
1460 writeU8(os, pointable);
1461 writeU8(os, diggable);
1462 writeU8(os, climbable);
1463 writeU8(os, buildable_to);
1464 os<<serializeString(""); // legacy: used to be metadata_name
1465 writeU8(os, liquid_type);
1466 os<<serializeString(liquid_alternative_flowing);
1467 os<<serializeString(liquid_alternative_source);
1468 writeU8(os, liquid_viscosity);
1469 writeU8(os, light_source);
1470 writeU32(os, damage_per_second);
1471 node_box.serialize(os, protocol_version);
1472 selection_box.serialize(os, protocol_version);
1473 writeU8(os, legacy_facedir_simple);
1474 writeU8(os, legacy_wallmounted);
1475 serializeSimpleSoundSpec(sound_footstep, os);
1476 serializeSimpleSoundSpec(sound_dig, os);
1477 serializeSimpleSoundSpec(sound_dug, os);
1479 else if (protocol_version > 13 && protocol_version < 24) {
1480 writeU8(os, 6); // version
1481 os<<serializeString(name);
1482 writeU16(os, groups.size());
1483 for (ItemGroupList::const_iterator
1484 i = groups.begin(); i != groups.end(); ++i) {
1485 os<<serializeString(i->first);
1486 writeS16(os, i->second);
1488 writeU8(os, drawtype);
1489 writeF1000(os, visual_scale);
1491 for (u32 i = 0; i < 6; i++)
1492 tiledef[i].serialize(os, protocol_version);
1493 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1495 for (u32 i = 0; i < 2; i++)
1496 tiledef_special[i].serialize(os, protocol_version);
1498 writeU8(os, post_effect_color.getAlpha());
1499 writeU8(os, post_effect_color.getRed());
1500 writeU8(os, post_effect_color.getGreen());
1501 writeU8(os, post_effect_color.getBlue());
1502 writeU8(os, param_type);
1503 writeU8(os, param_type_2);
1504 writeU8(os, is_ground_content);
1505 writeU8(os, light_propagates);
1506 writeU8(os, sunlight_propagates);
1507 writeU8(os, walkable);
1508 writeU8(os, pointable);
1509 writeU8(os, diggable);
1510 writeU8(os, climbable);
1511 writeU8(os, buildable_to);
1512 os<<serializeString(""); // legacy: used to be metadata_name
1513 writeU8(os, liquid_type);
1514 os<<serializeString(liquid_alternative_flowing);
1515 os<<serializeString(liquid_alternative_source);
1516 writeU8(os, liquid_viscosity);
1517 writeU8(os, liquid_renewable);
1518 writeU8(os, light_source);
1519 writeU32(os, damage_per_second);
1520 node_box.serialize(os, protocol_version);
1521 selection_box.serialize(os, protocol_version);
1522 writeU8(os, legacy_facedir_simple);
1523 writeU8(os, legacy_wallmounted);
1524 serializeSimpleSoundSpec(sound_footstep, os);
1525 serializeSimpleSoundSpec(sound_dig, os);
1526 serializeSimpleSoundSpec(sound_dug, os);
1527 writeU8(os, rightclickable);
1528 writeU8(os, drowning);
1529 writeU8(os, leveled);
1530 writeU8(os, liquid_range);
1532 throw SerializationError("ContentFeatures::serialize(): "
1533 "Unsupported version requested");
1536 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1538 if (version == 5) // In PROTOCOL_VERSION 13
1540 name = deSerializeString(is);
1542 u32 groups_size = readU16(is);
1543 for(u32 i=0; i<groups_size; i++){
1544 std::string name = deSerializeString(is);
1545 int value = readS16(is);
1546 groups[name] = value;
1548 drawtype = (enum NodeDrawType)readU8(is);
1550 visual_scale = readF1000(is);
1551 if (readU8(is) != 6)
1552 throw SerializationError("unsupported tile count");
1553 for (u32 i = 0; i < 6; i++)
1554 tiledef[i].deSerialize(is, version, drawtype);
1555 if (readU8(is) != CF_SPECIAL_COUNT)
1556 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1557 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1558 tiledef_special[i].deSerialize(is, version, drawtype);
1560 post_effect_color.setAlpha(readU8(is));
1561 post_effect_color.setRed(readU8(is));
1562 post_effect_color.setGreen(readU8(is));
1563 post_effect_color.setBlue(readU8(is));
1564 param_type = (enum ContentParamType)readU8(is);
1565 param_type_2 = (enum ContentParamType2)readU8(is);
1566 is_ground_content = readU8(is);
1567 light_propagates = readU8(is);
1568 sunlight_propagates = readU8(is);
1569 walkable = readU8(is);
1570 pointable = readU8(is);
1571 diggable = readU8(is);
1572 climbable = readU8(is);
1573 buildable_to = readU8(is);
1574 deSerializeString(is); // legacy: used to be metadata_name
1575 liquid_type = (enum LiquidType)readU8(is);
1576 liquid_alternative_flowing = deSerializeString(is);
1577 liquid_alternative_source = deSerializeString(is);
1578 liquid_viscosity = readU8(is);
1579 light_source = readU8(is);
1580 light_source = MYMIN(light_source, LIGHT_MAX);
1581 damage_per_second = readU32(is);
1582 node_box.deSerialize(is);
1583 selection_box.deSerialize(is);
1584 legacy_facedir_simple = readU8(is);
1585 legacy_wallmounted = readU8(is);
1586 deSerializeSimpleSoundSpec(sound_footstep, is);
1587 deSerializeSimpleSoundSpec(sound_dig, is);
1588 deSerializeSimpleSoundSpec(sound_dug, is);
1589 } else if (version == 6) {
1590 name = deSerializeString(is);
1592 u32 groups_size = readU16(is);
1593 for (u32 i = 0; i < groups_size; i++) {
1594 std::string name = deSerializeString(is);
1595 int value = readS16(is);
1596 groups[name] = value;
1598 drawtype = (enum NodeDrawType)readU8(is);
1599 visual_scale = readF1000(is);
1600 if (readU8(is) != 6)
1601 throw SerializationError("unsupported tile count");
1602 for (u32 i = 0; i < 6; i++)
1603 tiledef[i].deSerialize(is, version, drawtype);
1604 // CF_SPECIAL_COUNT in version 6 = 2
1605 if (readU8(is) != 2)
1606 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1607 for (u32 i = 0; i < 2; i++)
1608 tiledef_special[i].deSerialize(is, version, drawtype);
1610 post_effect_color.setAlpha(readU8(is));
1611 post_effect_color.setRed(readU8(is));
1612 post_effect_color.setGreen(readU8(is));
1613 post_effect_color.setBlue(readU8(is));
1614 param_type = (enum ContentParamType)readU8(is);
1615 param_type_2 = (enum ContentParamType2)readU8(is);
1616 is_ground_content = readU8(is);
1617 light_propagates = readU8(is);
1618 sunlight_propagates = readU8(is);
1619 walkable = readU8(is);
1620 pointable = readU8(is);
1621 diggable = readU8(is);
1622 climbable = readU8(is);
1623 buildable_to = readU8(is);
1624 deSerializeString(is); // legacy: used to be metadata_name
1625 liquid_type = (enum LiquidType)readU8(is);
1626 liquid_alternative_flowing = deSerializeString(is);
1627 liquid_alternative_source = deSerializeString(is);
1628 liquid_viscosity = readU8(is);
1629 liquid_renewable = readU8(is);
1630 light_source = readU8(is);
1631 damage_per_second = readU32(is);
1632 node_box.deSerialize(is);
1633 selection_box.deSerialize(is);
1634 legacy_facedir_simple = readU8(is);
1635 legacy_wallmounted = readU8(is);
1636 deSerializeSimpleSoundSpec(sound_footstep, is);
1637 deSerializeSimpleSoundSpec(sound_dig, is);
1638 deSerializeSimpleSoundSpec(sound_dug, is);
1639 rightclickable = readU8(is);
1640 drowning = readU8(is);
1641 leveled = readU8(is);
1642 liquid_range = readU8(is);
1644 throw SerializationError("unsupported ContentFeatures version");
1649 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1651 return m_node_registration_complete;
1655 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1657 m_node_registration_complete = completed;
1661 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1664 if (m_node_registration_complete)
1665 nr->nodeResolveInternal();
1667 m_pending_resolve_callbacks.push_back(nr);
1671 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1673 size_t len = m_pending_resolve_callbacks.size();
1674 for (size_t i = 0; i != len; i++) {
1675 if (nr != m_pending_resolve_callbacks[i])
1679 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1680 m_pending_resolve_callbacks.resize(len);
1688 void CNodeDefManager::runNodeResolveCallbacks()
1690 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1691 NodeResolver *nr = m_pending_resolve_callbacks[i];
1692 nr->nodeResolveInternal();
1695 m_pending_resolve_callbacks.clear();
1699 void CNodeDefManager::resetNodeResolveState()
1701 m_node_registration_complete = false;
1702 m_pending_resolve_callbacks.clear();
1705 void CNodeDefManager::mapNodeboxConnections()
1707 for (u32 i = 0; i < m_content_features.size(); i++) {
1708 ContentFeatures *f = &m_content_features[i];
1709 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1711 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1712 it != f->connects_to.end(); ++it) {
1713 getIds(*it, f->connects_to_ids);
1718 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1720 const ContentFeatures &f1 = get(from);
1722 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1725 // lookup target in connected set
1726 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1729 const ContentFeatures &f2 = get(to);
1731 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1732 // ignores actually looking if back connection exists
1733 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1735 // does to node declare usable faces?
1736 if (f2.connect_sides > 0) {
1737 if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) {
1738 static const u8 rot[33 * 4] = {
1739 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1740 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back
1741 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right
1742 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1743 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front
1744 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1745 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1746 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1747 32, 16, 8, 4 // 32 - left
1749 return (f2.connect_sides & rot[(connect_face * 4) + to.param2]);
1751 return (f2.connect_sides & connect_face);
1753 // the target is just a regular node, so connect no matter back connection
1761 NodeResolver::NodeResolver()
1764 m_nodenames_idx = 0;
1765 m_nnlistsizes_idx = 0;
1766 m_resolve_done = false;
1768 m_nodenames.reserve(16);
1769 m_nnlistsizes.reserve(4);
1773 NodeResolver::~NodeResolver()
1775 if (!m_resolve_done && m_ndef)
1776 m_ndef->cancelNodeResolveCallback(this);
1780 void NodeResolver::nodeResolveInternal()
1782 m_nodenames_idx = 0;
1783 m_nnlistsizes_idx = 0;
1786 m_resolve_done = true;
1788 m_nodenames.clear();
1789 m_nnlistsizes.clear();
1793 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1794 const std::string &node_alt, content_t c_fallback)
1796 if (m_nodenames_idx == m_nodenames.size()) {
1797 *result_out = c_fallback;
1798 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1803 std::string name = m_nodenames[m_nodenames_idx++];
1805 bool success = m_ndef->getId(name, c);
1806 if (!success && node_alt != "") {
1808 success = m_ndef->getId(name, c);
1812 errorstream << "NodeResolver: failed to resolve node name '" << name
1813 << "'." << std::endl;
1822 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1823 bool all_required, content_t c_fallback)
1825 bool success = true;
1827 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1828 errorstream << "NodeResolver: no more node lists" << std::endl;
1832 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1835 if (m_nodenames_idx == m_nodenames.size()) {
1836 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1841 std::string &name = m_nodenames[m_nodenames_idx++];
1843 if (name.substr(0,6) != "group:") {
1844 if (m_ndef->getId(name, c)) {
1845 result_out->push_back(c);
1846 } else if (all_required) {
1847 errorstream << "NodeResolver: failed to resolve node name '"
1848 << name << "'." << std::endl;
1849 result_out->push_back(c_fallback);
1853 std::set<content_t> cids;
1854 std::set<content_t>::iterator it;
1855 m_ndef->getIds(name, cids);
1856 for (it = cids.begin(); it != cids.end(); ++it)
1857 result_out->push_back(*it);