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"
27 #include <IMeshManipulator.h>
31 #include "nameidmapping.h"
32 #include "util/numeric.h"
33 #include "util/serialize.h"
34 #include "exceptions.h"
38 #include <fstream> // Used in applyTextureOverrides()
46 type = NODEBOX_REGULAR;
49 // default is sign/ladder-like
50 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
51 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
52 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
53 // no default for other parts
55 connect_bottom.clear();
56 connect_front.clear();
59 connect_right.clear();
62 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
66 if (protocol_version >= 27)
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 >= 30)
193 else if (protocol_version >= 29)
195 else if (protocol_version >= 26)
200 os << serializeString(name);
201 animation.serialize(os, protocol_version);
202 writeU8(os, backface_culling);
203 if (protocol_version >= 26) {
204 writeU8(os, tileable_horizontal);
205 writeU8(os, tileable_vertical);
207 if (protocol_version >= 30) {
208 writeU8(os, has_color);
210 writeU8(os, color.getRed());
211 writeU8(os, color.getGreen());
212 writeU8(os, color.getBlue());
217 void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
219 int version = readU8(is);
220 name = deSerializeString(is);
221 animation.deSerialize(is, version >= 3 ? 29 : 26);
223 backface_culling = readU8(is);
225 tileable_horizontal = readU8(is);
226 tileable_vertical = readU8(is);
229 has_color = readU8(is);
231 color.setRed(readU8(is));
232 color.setGreen(readU8(is));
233 color.setBlue(readU8(is));
237 if ((contenfeatures_version < 8) &&
238 ((drawtype == NDT_MESH) ||
239 (drawtype == NDT_FIRELIKE) ||
240 (drawtype == NDT_LIQUID) ||
241 (drawtype == NDT_PLANTLIKE)))
242 backface_culling = false;
247 SimpleSoundSpec serialization
250 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
253 os<<serializeString(ss.name);
254 writeF1000(os, ss.gain);
256 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
258 ss.name = deSerializeString(is);
259 ss.gain = readF1000(is);
262 void TextureSettings::readSettings()
264 connected_glass = g_settings->getBool("connected_glass");
265 opaque_water = g_settings->getBool("opaque_water");
266 bool enable_shaders = g_settings->getBool("enable_shaders");
267 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
268 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
269 bool smooth_lighting = g_settings->getBool("smooth_lighting");
270 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
271 enable_minimap = g_settings->getBool("enable_minimap");
272 std::string leaves_style_str = g_settings->get("leaves_style");
274 // Mesh cache is not supported in combination with smooth lighting
276 enable_mesh_cache = false;
278 use_normal_texture = enable_shaders &&
279 (enable_bumpmapping || enable_parallax_occlusion);
280 if (leaves_style_str == "fancy") {
281 leaves_style = LEAVES_FANCY;
282 } else if (leaves_style_str == "simple") {
283 leaves_style = LEAVES_SIMPLE;
285 leaves_style = LEAVES_OPAQUE;
293 ContentFeatures::ContentFeatures()
298 ContentFeatures::~ContentFeatures()
302 void ContentFeatures::reset()
309 visual_solidness = 0;
310 backface_culling = true;
313 has_on_construct = false;
314 has_on_destruct = false;
315 has_after_destruct = false;
319 NOTE: Most of this is always overridden by the default values given
324 // Unknown nodes can be dug
325 groups["dig_immediate"] = 2;
326 drawtype = NDT_NORMAL;
329 for(u32 i = 0; i < 24; i++)
331 minimap_color = video::SColor(0, 0, 0, 0);
334 for(u32 i = 0; i < 6; i++)
335 tiledef[i] = TileDef();
336 for(u16 j = 0; j < CF_SPECIAL_COUNT; j++)
337 tiledef_special[j] = TileDef();
339 post_effect_color = video::SColor(0, 0, 0, 0);
340 param_type = CPT_NONE;
341 param_type_2 = CPT2_NONE;
342 is_ground_content = false;
343 light_propagates = false;
344 sunlight_propagates = false;
349 buildable_to = false;
351 rightclickable = true;
353 liquid_type = LIQUID_NONE;
354 liquid_alternative_flowing = "";
355 liquid_alternative_source = "";
356 liquid_viscosity = 0;
357 liquid_renewable = true;
358 liquid_range = LIQUID_LEVEL_MAX+1;
361 damage_per_second = 0;
362 node_box = NodeBox();
363 selection_box = NodeBox();
364 collision_box = NodeBox();
366 legacy_facedir_simple = false;
367 legacy_wallmounted = false;
368 sound_footstep = SimpleSoundSpec();
369 sound_dig = SimpleSoundSpec("__group");
370 sound_dug = SimpleSoundSpec();
372 connects_to_ids.clear();
374 color = video::SColor(0xFFFFFFFF);
379 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
381 if (protocol_version < 31) {
382 serializeOld(os, protocol_version);
390 os << serializeString(name);
391 writeU16(os, groups.size());
392 for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end();
394 os << serializeString(i->first);
395 writeS16(os, i->second);
397 writeU8(os, param_type);
398 writeU8(os, param_type_2);
401 writeU8(os, drawtype);
402 os << serializeString(mesh);
403 writeF1000(os, visual_scale);
405 for (u32 i = 0; i < 6; i++)
406 tiledef[i].serialize(os, protocol_version);
407 for (u32 i = 0; i < 6; i++)
408 tiledef_overlay[i].serialize(os, protocol_version);
409 writeU8(os, CF_SPECIAL_COUNT);
410 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
411 tiledef_special[i].serialize(os, protocol_version);
414 writeU8(os, color.getRed());
415 writeU8(os, color.getGreen());
416 writeU8(os, color.getBlue());
417 os << serializeString(palette_name);
419 writeU8(os, connect_sides);
420 writeU16(os, connects_to_ids.size());
421 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
422 i != connects_to_ids.end(); ++i)
424 writeU8(os, post_effect_color.getAlpha());
425 writeU8(os, post_effect_color.getRed());
426 writeU8(os, post_effect_color.getGreen());
427 writeU8(os, post_effect_color.getBlue());
428 writeU8(os, leveled);
431 writeU8(os, light_propagates);
432 writeU8(os, sunlight_propagates);
433 writeU8(os, light_source);
436 writeU8(os, is_ground_content);
439 writeU8(os, walkable);
440 writeU8(os, pointable);
441 writeU8(os, diggable);
442 writeU8(os, climbable);
443 writeU8(os, buildable_to);
444 writeU8(os, rightclickable);
445 writeU32(os, damage_per_second);
448 writeU8(os, liquid_type);
449 os << serializeString(liquid_alternative_flowing);
450 os << serializeString(liquid_alternative_source);
451 writeU8(os, liquid_viscosity);
452 writeU8(os, liquid_renewable);
453 writeU8(os, liquid_range);
454 writeU8(os, drowning);
455 writeU8(os, floodable);
458 node_box.serialize(os, protocol_version);
459 selection_box.serialize(os, protocol_version);
460 collision_box.serialize(os, protocol_version);
463 serializeSimpleSoundSpec(sound_footstep, os);
464 serializeSimpleSoundSpec(sound_dig, os);
465 serializeSimpleSoundSpec(sound_dug, os);
468 writeU8(os, legacy_facedir_simple);
469 writeU8(os, legacy_wallmounted);
472 void ContentFeatures::correctAlpha()
474 if (alpha == 0 || alpha == 255)
477 for (u32 i = 0; i < 6; i++) {
479 s << tiledef[i].name << "^[noalpha^[opacity:" << ((int)alpha);
480 tiledef[i].name = s.str();
483 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
485 s << tiledef_special[i].name << "^[noalpha^[opacity:" << ((int)alpha);
486 tiledef_special[i].name = s.str();
490 void ContentFeatures::deSerialize(std::istream &is)
493 int version = readU8(is);
495 deSerializeOld(is, version);
497 } else if (version > 10) {
498 throw SerializationError("unsupported ContentFeatures version");
502 name = deSerializeString(is);
504 u32 groups_size = readU16(is);
505 for (u32 i = 0; i < groups_size; i++) {
506 std::string name = deSerializeString(is);
507 int value = readS16(is);
508 groups[name] = value;
510 param_type = (enum ContentParamType) readU8(is);
511 param_type_2 = (enum ContentParamType2) readU8(is);
514 drawtype = (enum NodeDrawType) readU8(is);
515 mesh = deSerializeString(is);
516 visual_scale = readF1000(is);
518 throw SerializationError("unsupported tile count");
519 for (u32 i = 0; i < 6; i++)
520 tiledef[i].deSerialize(is, version, drawtype);
522 for (u32 i = 0; i < 6; i++)
523 tiledef_overlay[i].deSerialize(is, version, drawtype);
524 if (readU8(is) != CF_SPECIAL_COUNT)
525 throw SerializationError("unsupported CF_SPECIAL_COUNT");
526 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
527 tiledef_special[i].deSerialize(is, version, drawtype);
529 color.setRed(readU8(is));
530 color.setGreen(readU8(is));
531 color.setBlue(readU8(is));
532 palette_name = deSerializeString(is);
534 connect_sides = readU8(is);
535 u16 connects_to_size = readU16(is);
536 connects_to_ids.clear();
537 for (u16 i = 0; i < connects_to_size; i++)
538 connects_to_ids.insert(readU16(is));
539 post_effect_color.setAlpha(readU8(is));
540 post_effect_color.setRed(readU8(is));
541 post_effect_color.setGreen(readU8(is));
542 post_effect_color.setBlue(readU8(is));
543 leveled = readU8(is);
546 light_propagates = readU8(is);
547 sunlight_propagates = readU8(is);
548 light_source = readU8(is);
549 light_source = MYMIN(light_source, LIGHT_MAX);
552 is_ground_content = readU8(is);
555 walkable = readU8(is);
556 pointable = readU8(is);
557 diggable = readU8(is);
558 climbable = readU8(is);
559 buildable_to = readU8(is);
560 rightclickable = readU8(is);
561 damage_per_second = readU32(is);
564 liquid_type = (enum LiquidType) readU8(is);
565 liquid_alternative_flowing = deSerializeString(is);
566 liquid_alternative_source = deSerializeString(is);
567 liquid_viscosity = readU8(is);
568 liquid_renewable = readU8(is);
569 liquid_range = readU8(is);
570 drowning = readU8(is);
571 floodable = readU8(is);
574 node_box.deSerialize(is);
575 selection_box.deSerialize(is);
576 collision_box.deSerialize(is);
579 deSerializeSimpleSoundSpec(sound_footstep, is);
580 deSerializeSimpleSoundSpec(sound_dig, is);
581 deSerializeSimpleSoundSpec(sound_dug, is);
583 // read legacy properties
584 legacy_facedir_simple = readU8(is);
585 legacy_wallmounted = readU8(is);
589 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
590 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
591 bool backface_culling, u8 material_type)
593 tile->shader_id = shader_id;
594 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
595 tile->material_type = material_type;
597 // Normal texture and shader flags texture
598 if (use_normal_texture) {
599 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
601 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
604 tile->material_flags = 0;
605 if (backface_culling)
606 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
607 if (tiledef->animation.type != TAT_NONE)
608 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
609 if (tiledef->tileable_horizontal)
610 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
611 if (tiledef->tileable_vertical)
612 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
615 tile->has_color = tiledef->has_color;
616 if (tiledef->has_color)
617 tile->color = tiledef->color;
621 // Animation parameters
623 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
625 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
626 &frame_count, &frame_length_ms, NULL);
627 tile->animation_frame_count = frame_count;
628 tile->animation_frame_length_ms = frame_length_ms;
631 if (frame_count == 1) {
632 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
634 std::ostringstream os(std::ios::binary);
635 tile->frames.resize(frame_count);
637 for (int i = 0; i < frame_count; i++) {
643 tiledef->animation.getTextureModifer(os,
644 tile->texture->getOriginalSize(), i);
646 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
647 if (tile->normal_texture)
648 frame.normal_texture = tsrc->getNormalTexture(os.str());
649 frame.flags_texture = tile->flags_texture;
650 tile->frames[i] = frame;
657 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
658 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
660 // minimap pixel color - the average color of a texture
661 if (tsettings.enable_minimap && tiledef[0].name != "")
662 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
664 // Figure out the actual tiles to use
666 for (u32 j = 0; j < 6; j++) {
667 tdef[j] = tiledef[j];
668 if (tdef[j].name == "")
669 tdef[j].name = "unknown_node.png";
672 bool is_liquid = false;
674 u8 material_type = (alpha == 255) ?
675 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
686 assert(liquid_type == LIQUID_SOURCE);
687 if (tsettings.opaque_water)
692 case NDT_FLOWINGLIQUID:
693 assert(liquid_type == LIQUID_FLOWING);
695 if (tsettings.opaque_water)
701 visual_solidness = 1;
703 case NDT_GLASSLIKE_FRAMED:
705 visual_solidness = 1;
707 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
709 visual_solidness = 1;
710 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
714 visual_solidness = 1;
716 case NDT_ALLFACES_OPTIONAL:
717 if (tsettings.leaves_style == LEAVES_FANCY) {
718 drawtype = NDT_ALLFACES;
720 visual_solidness = 1;
721 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
722 for (u32 j = 0; j < 6; j++) {
723 if (tiledef_special[j].name != "")
724 tdef[j].name = tiledef_special[j].name;
726 drawtype = NDT_GLASSLIKE;
728 visual_solidness = 1;
730 drawtype = NDT_NORMAL;
732 for (u32 i = 0; i < 6; i++)
733 tdef[i].name += std::string("^[noalpha");
736 material_type = TILE_MATERIAL_WAVING_LEAVES;
741 material_type = TILE_MATERIAL_WAVING_PLANTS;
750 material_type = TILE_MATERIAL_WAVING_PLANTS;
751 else if (waving == 2)
752 material_type = TILE_MATERIAL_WAVING_LEAVES;
763 material_type = (alpha == 255) ?
764 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
766 // Vertex alpha is no longer supported, correct if necessary.
770 for (u16 j = 0; j < 6; j++) {
771 tile_shader[j] = shdsrc->getShader("nodes_shader",
772 material_type, drawtype);
775 // Tiles (fill in f->tiles[])
776 for (u16 j = 0; j < 6; j++) {
777 fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader[j],
778 tsettings.use_normal_texture,
779 tiledef[j].backface_culling, material_type);
780 if (tiledef_overlay[j].name!="")
781 fillTileAttribs(tsrc, &tiles[j].layers[1], &tiledef_overlay[j],
782 tile_shader[j], tsettings.use_normal_texture,
783 tiledef[j].backface_culling, material_type);
786 // Special tiles (fill in f->special_tiles[])
787 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
788 fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tiledef_special[j],
789 tile_shader[j], tsettings.use_normal_texture,
790 tiledef_special[j].backface_culling, material_type);
793 if (param_type_2 == CPT2_COLOR ||
794 param_type_2 == CPT2_COLORED_FACEDIR ||
795 param_type_2 == CPT2_COLORED_WALLMOUNTED)
796 palette = tsrc->getPalette(palette_name);
798 if ((drawtype == NDT_MESH) && (mesh != "")) {
800 // Read the mesh and apply scale
801 mesh_ptr[0] = client->getMesh(mesh);
803 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
804 scaleMesh(mesh_ptr[0], scale);
805 recalculateBoundingBox(mesh_ptr[0]);
806 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
808 } else if ((drawtype == NDT_NODEBOX) &&
809 ((node_box.type == NODEBOX_REGULAR) ||
810 (node_box.type == NODEBOX_FIXED)) &&
811 (!node_box.fixed.empty())) {
812 //Convert regular nodebox nodes to meshnodes
813 //Change the drawtype and apply scale
815 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
816 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
817 scaleMesh(mesh_ptr[0], scale);
818 recalculateBoundingBox(mesh_ptr[0]);
819 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
822 //Cache 6dfacedir and wallmounted rotated clones of meshes
823 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
824 (param_type_2 == CPT2_FACEDIR
825 || param_type_2 == CPT2_COLORED_FACEDIR)) {
826 for (u16 j = 1; j < 24; j++) {
827 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
828 rotateMeshBy6dFacedir(mesh_ptr[j], j);
829 recalculateBoundingBox(mesh_ptr[j]);
830 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
832 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
833 && (param_type_2 == CPT2_WALLMOUNTED ||
834 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
835 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
836 for (u16 j = 1; j < 6; j++) {
837 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
838 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
839 recalculateBoundingBox(mesh_ptr[j]);
840 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
842 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
843 recalculateBoundingBox(mesh_ptr[0]);
844 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
853 class CNodeDefManager: public IWritableNodeDefManager {
856 virtual ~CNodeDefManager();
858 virtual IWritableNodeDefManager *clone();
859 inline virtual const ContentFeatures& get(content_t c) const;
860 inline virtual const ContentFeatures& get(const MapNode &n) const;
861 virtual bool getId(const std::string &name, content_t &result) const;
862 virtual content_t getId(const std::string &name) const;
863 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
864 virtual const ContentFeatures& get(const std::string &name) const;
865 content_t allocateId();
866 virtual content_t set(const std::string &name, const ContentFeatures &def);
867 virtual content_t allocateDummy(const std::string &name);
868 virtual void removeNode(const std::string &name);
869 virtual void updateAliases(IItemDefManager *idef);
870 virtual void applyTextureOverrides(const std::string &override_filepath);
871 virtual void updateTextures(IGameDef *gamedef,
872 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
873 void *progress_cbk_args);
874 void serialize(std::ostream &os, u16 protocol_version) const;
875 void deSerialize(std::istream &is);
877 inline virtual bool getNodeRegistrationStatus() const;
878 inline virtual void setNodeRegistrationStatus(bool completed);
880 virtual void pendNodeResolve(NodeResolver *nr);
881 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
882 virtual void runNodeResolveCallbacks();
883 virtual void resetNodeResolveState();
884 virtual void mapNodeboxConnections();
885 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
886 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
888 return m_selection_box_int_union;
892 void addNameIdMapping(content_t i, std::string name);
894 * Recalculates m_selection_box_int_union based on
895 * m_selection_box_union.
897 void fixSelectionBoxIntUnion();
899 // Features indexed by id
900 std::vector<ContentFeatures> m_content_features;
902 // A mapping for fast converting back and forth between names and ids
903 NameIdMapping m_name_id_mapping;
905 // Like m_name_id_mapping, but only from names to ids, and includes
906 // item aliases too. Updated by updateAliases()
907 // Note: Not serialized.
909 UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
911 // A mapping from groups to a list of content_ts (and their levels)
912 // that belong to it. Necessary for a direct lookup in getIds().
913 // Note: Not serialized.
914 UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
916 // Next possibly free id
919 // NodeResolvers to callback once node registration has ended
920 std::vector<NodeResolver *> m_pending_resolve_callbacks;
922 // True when all nodes have been registered
923 bool m_node_registration_complete;
925 //! The union of all nodes' selection boxes.
926 aabb3f m_selection_box_union;
928 * The smallest box in node coordinates that
929 * contains all nodes' selection boxes.
931 core::aabbox3d<s16> m_selection_box_int_union;
935 CNodeDefManager::CNodeDefManager()
941 CNodeDefManager::~CNodeDefManager()
944 for (u32 i = 0; i < m_content_features.size(); i++) {
945 ContentFeatures *f = &m_content_features[i];
946 for (u32 j = 0; j < 24; j++) {
948 f->mesh_ptr[j]->drop();
955 void CNodeDefManager::clear()
957 m_content_features.clear();
958 m_name_id_mapping.clear();
959 m_name_id_mapping_with_aliases.clear();
960 m_group_to_items.clear();
962 m_selection_box_union.reset(0,0,0);
963 m_selection_box_int_union.reset(0,0,0);
965 resetNodeResolveState();
967 u32 initial_length = 0;
968 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
969 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
970 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
971 m_content_features.resize(initial_length);
973 // Set CONTENT_UNKNOWN
977 // Insert directly into containers
978 content_t c = CONTENT_UNKNOWN;
979 m_content_features[c] = f;
980 addNameIdMapping(c, f.name);
987 f.drawtype = NDT_AIRLIKE;
988 f.param_type = CPT_LIGHT;
989 f.light_propagates = true;
990 f.sunlight_propagates = true;
994 f.buildable_to = true;
996 f.is_ground_content = true;
997 // Insert directly into containers
998 content_t c = CONTENT_AIR;
999 m_content_features[c] = f;
1000 addNameIdMapping(c, f.name);
1003 // Set CONTENT_IGNORE
1007 f.drawtype = NDT_AIRLIKE;
1008 f.param_type = CPT_NONE;
1009 f.light_propagates = false;
1010 f.sunlight_propagates = false;
1012 f.pointable = false;
1014 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1015 f.is_ground_content = true;
1016 // Insert directly into containers
1017 content_t c = CONTENT_IGNORE;
1018 m_content_features[c] = f;
1019 addNameIdMapping(c, f.name);
1024 IWritableNodeDefManager *CNodeDefManager::clone()
1026 CNodeDefManager *mgr = new CNodeDefManager();
1032 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1034 return c < m_content_features.size()
1035 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1039 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1041 return get(n.getContent());
1045 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1047 UNORDERED_MAP<std::string, content_t>::const_iterator
1048 i = m_name_id_mapping_with_aliases.find(name);
1049 if(i == m_name_id_mapping_with_aliases.end())
1056 content_t CNodeDefManager::getId(const std::string &name) const
1058 content_t id = CONTENT_IGNORE;
1064 bool CNodeDefManager::getIds(const std::string &name,
1065 std::set<content_t> &result) const
1067 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1068 if (name.substr(0,6) != "group:") {
1069 content_t id = CONTENT_IGNORE;
1070 bool exists = getId(name, id);
1075 std::string group = name.substr(6);
1077 UNORDERED_MAP<std::string, GroupItems>::const_iterator
1078 i = m_group_to_items.find(group);
1079 if (i == m_group_to_items.end())
1082 const GroupItems &items = i->second;
1083 for (GroupItems::const_iterator j = items.begin();
1084 j != items.end(); ++j) {
1085 if ((*j).second != 0)
1086 result.insert((*j).first);
1088 //printf("getIds: %dus\n", t.stop());
1093 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1095 content_t id = CONTENT_UNKNOWN;
1101 // returns CONTENT_IGNORE if no free ID found
1102 content_t CNodeDefManager::allocateId()
1104 for (content_t id = m_next_id;
1105 id >= m_next_id; // overflow?
1107 while (id >= m_content_features.size()) {
1108 m_content_features.push_back(ContentFeatures());
1110 const ContentFeatures &f = m_content_features[id];
1116 // If we arrive here, an overflow occurred in id.
1117 // That means no ID was found
1118 return CONTENT_IGNORE;
1123 * Returns the smallest box that contains all boxes
1124 * in the vector. Box_union is expanded.
1125 * @param[in] boxes the vector containing the boxes
1126 * @param[in, out] box_union the union of the arguments
1128 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1130 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1131 it != boxes.end(); ++it) {
1132 box_union->addInternalBox(*it);
1138 * Returns a box that contains the nodebox in every case.
1139 * The argument node_union is expanded.
1140 * @param[in] nodebox the nodebox to be measured
1141 * @param[in] features used to decide whether the nodebox
1143 * @param[in, out] box_union the union of the arguments
1145 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1148 switch(nodebox.type) {
1150 case NODEBOX_LEVELED: {
1152 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1153 boxVectorUnion(nodebox.fixed, &half_processed);
1154 // Set leveled boxes to maximal
1155 if (nodebox.type == NODEBOX_LEVELED) {
1156 half_processed.MaxEdge.Y = +BS / 2;
1158 if (features.param_type_2 == CPT2_FACEDIR ||
1159 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1160 // Get maximal coordinate
1162 fabsf(half_processed.MinEdge.X),
1163 fabsf(half_processed.MinEdge.Y),
1164 fabsf(half_processed.MinEdge.Z),
1165 fabsf(half_processed.MaxEdge.X),
1166 fabsf(half_processed.MaxEdge.Y),
1167 fabsf(half_processed.MaxEdge.Z) };
1169 for (int i = 0; i < 6; i++) {
1170 if (max < coords[i]) {
1174 // Add the union of all possible rotated boxes
1175 box_union->addInternalPoint(-max, -max, -max);
1176 box_union->addInternalPoint(+max, +max, +max);
1178 box_union->addInternalBox(half_processed);
1182 case NODEBOX_WALLMOUNTED: {
1184 box_union->addInternalBox(nodebox.wall_top);
1185 box_union->addInternalBox(nodebox.wall_bottom);
1186 // Find maximal coordinate in the X-Z plane
1188 fabsf(nodebox.wall_side.MinEdge.X),
1189 fabsf(nodebox.wall_side.MinEdge.Z),
1190 fabsf(nodebox.wall_side.MaxEdge.X),
1191 fabsf(nodebox.wall_side.MaxEdge.Z) };
1193 for (int i = 0; i < 4; i++) {
1194 if (max < coords[i]) {
1198 // Add the union of all possible rotated boxes
1199 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1200 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1203 case NODEBOX_CONNECTED: {
1204 // Add all possible connected boxes
1205 boxVectorUnion(nodebox.fixed, box_union);
1206 boxVectorUnion(nodebox.connect_top, box_union);
1207 boxVectorUnion(nodebox.connect_bottom, box_union);
1208 boxVectorUnion(nodebox.connect_front, box_union);
1209 boxVectorUnion(nodebox.connect_left, box_union);
1210 boxVectorUnion(nodebox.connect_back, box_union);
1211 boxVectorUnion(nodebox.connect_right, box_union);
1216 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1217 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1223 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1225 m_selection_box_int_union.MinEdge.X = floorf(
1226 m_selection_box_union.MinEdge.X / BS + 0.5f);
1227 m_selection_box_int_union.MinEdge.Y = floorf(
1228 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1229 m_selection_box_int_union.MinEdge.Z = floorf(
1230 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1231 m_selection_box_int_union.MaxEdge.X = ceilf(
1232 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1233 m_selection_box_int_union.MaxEdge.Y = ceilf(
1234 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1235 m_selection_box_int_union.MaxEdge.Z = ceilf(
1236 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1240 // IWritableNodeDefManager
1241 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1245 assert(name == def.name);
1247 // Don't allow redefining ignore (but allow air and unknown)
1248 if (name == "ignore") {
1249 warningstream << "NodeDefManager: Ignoring "
1250 "CONTENT_IGNORE redefinition"<<std::endl;
1251 return CONTENT_IGNORE;
1254 content_t id = CONTENT_IGNORE;
1255 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1258 if (id == CONTENT_IGNORE) {
1259 warningstream << "NodeDefManager: Absolute "
1260 "limit reached" << std::endl;
1261 return CONTENT_IGNORE;
1263 assert(id != CONTENT_IGNORE);
1264 addNameIdMapping(id, name);
1266 m_content_features[id] = def;
1267 verbosestream << "NodeDefManager: registering content id \"" << id
1268 << "\": name=\"" << def.name << "\""<<std::endl;
1270 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1271 fixSelectionBoxIntUnion();
1272 // Add this content to the list of all groups it belongs to
1273 // FIXME: This should remove a node from groups it no longer
1274 // belongs to when a node is re-registered
1275 for (ItemGroupList::const_iterator i = def.groups.begin();
1276 i != def.groups.end(); ++i) {
1277 std::string group_name = i->first;
1279 UNORDERED_MAP<std::string, GroupItems>::iterator
1280 j = m_group_to_items.find(group_name);
1281 if (j == m_group_to_items.end()) {
1282 m_group_to_items[group_name].push_back(
1283 std::make_pair(id, i->second));
1285 GroupItems &items = j->second;
1286 items.push_back(std::make_pair(id, i->second));
1293 content_t CNodeDefManager::allocateDummy(const std::string &name)
1295 assert(name != ""); // Pre-condition
1298 return set(name, f);
1302 void CNodeDefManager::removeNode(const std::string &name)
1307 // Erase name from name ID mapping
1308 content_t id = CONTENT_IGNORE;
1309 if (m_name_id_mapping.getId(name, id)) {
1310 m_name_id_mapping.eraseName(name);
1311 m_name_id_mapping_with_aliases.erase(name);
1314 // Erase node content from all groups it belongs to
1315 for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1316 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1317 GroupItems &items = iter_groups->second;
1318 for (GroupItems::iterator iter_groupitems = items.begin();
1319 iter_groupitems != items.end();) {
1320 if (iter_groupitems->first == id)
1321 items.erase(iter_groupitems++);
1326 // Check if group is empty
1327 if (items.size() == 0)
1328 m_group_to_items.erase(iter_groups++);
1335 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1337 std::set<std::string> all;
1339 m_name_id_mapping_with_aliases.clear();
1340 for (std::set<std::string>::const_iterator
1341 i = all.begin(); i != all.end(); ++i) {
1342 const std::string &name = *i;
1343 const std::string &convert_to = idef->getAlias(name);
1345 if (m_name_id_mapping.getId(convert_to, id)) {
1346 m_name_id_mapping_with_aliases.insert(
1347 std::make_pair(name, id));
1352 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1354 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1355 "overrides to textures from " << override_filepath << std::endl;
1357 std::ifstream infile(override_filepath.c_str());
1360 while (std::getline(infile, line)) {
1362 if (trim(line) == "")
1364 std::vector<std::string> splitted = str_split(line, ' ');
1365 if (splitted.size() != 3) {
1366 errorstream << override_filepath
1367 << ":" << line_c << " Could not apply texture override \""
1368 << line << "\": Syntax error" << std::endl;
1373 if (!getId(splitted[0], id))
1374 continue; // Ignore unknown node
1376 ContentFeatures &nodedef = m_content_features[id];
1378 if (splitted[1] == "top")
1379 nodedef.tiledef[0].name = splitted[2];
1380 else if (splitted[1] == "bottom")
1381 nodedef.tiledef[1].name = splitted[2];
1382 else if (splitted[1] == "right")
1383 nodedef.tiledef[2].name = splitted[2];
1384 else if (splitted[1] == "left")
1385 nodedef.tiledef[3].name = splitted[2];
1386 else if (splitted[1] == "back")
1387 nodedef.tiledef[4].name = splitted[2];
1388 else if (splitted[1] == "front")
1389 nodedef.tiledef[5].name = splitted[2];
1390 else if (splitted[1] == "all" || splitted[1] == "*")
1391 for (int i = 0; i < 6; i++)
1392 nodedef.tiledef[i].name = splitted[2];
1393 else if (splitted[1] == "sides")
1394 for (int i = 2; i < 6; i++)
1395 nodedef.tiledef[i].name = splitted[2];
1397 errorstream << override_filepath
1398 << ":" << line_c << " Could not apply texture override \""
1399 << line << "\": Unknown node side \""
1400 << splitted[1] << "\"" << std::endl;
1406 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1407 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1408 void *progress_callback_args)
1411 infostream << "CNodeDefManager::updateTextures(): Updating "
1412 "textures in node definitions" << std::endl;
1414 Client *client = (Client *)gamedef;
1415 ITextureSource *tsrc = client->tsrc();
1416 IShaderSource *shdsrc = client->getShaderSource();
1417 scene::ISceneManager* smgr = client->getSceneManager();
1418 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1419 TextureSettings tsettings;
1420 tsettings.readSettings();
1422 u32 size = m_content_features.size();
1424 for (u32 i = 0; i < size; i++) {
1425 ContentFeatures *f = &(m_content_features[i]);
1426 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1427 progress_callback(progress_callback_args, i, size);
1432 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1434 writeU8(os, 1); // version
1436 std::ostringstream os2(std::ios::binary);
1437 for (u32 i = 0; i < m_content_features.size(); i++) {
1438 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1439 || i == CONTENT_UNKNOWN)
1441 const ContentFeatures *f = &m_content_features[i];
1445 // Wrap it in a string to allow different lengths without
1446 // strict version incompatibilities
1447 std::ostringstream wrapper_os(std::ios::binary);
1448 f->serialize(wrapper_os, protocol_version);
1449 os2<<serializeString(wrapper_os.str());
1451 // must not overflow
1452 u16 next = count + 1;
1453 FATAL_ERROR_IF(next < count, "Overflow");
1456 writeU16(os, count);
1457 os << serializeLongString(os2.str());
1461 void CNodeDefManager::deSerialize(std::istream &is)
1464 int version = readU8(is);
1466 throw SerializationError("unsupported NodeDefinitionManager version");
1467 u16 count = readU16(is);
1468 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1470 for (u16 n = 0; n < count; n++) {
1471 u16 i = readU16(is2);
1473 // Read it from the string wrapper
1474 std::string wrapper = deSerializeString(is2);
1475 std::istringstream wrapper_is(wrapper, std::ios::binary);
1476 f.deSerialize(wrapper_is);
1478 // Check error conditions
1479 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1480 warningstream << "NodeDefManager::deSerialize(): "
1481 "not changing builtin node " << i << std::endl;
1485 warningstream << "NodeDefManager::deSerialize(): "
1486 "received empty name" << std::endl;
1492 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1493 warningstream << "NodeDefManager::deSerialize(): "
1494 "already defined with different ID: " << f.name << std::endl;
1498 // All is ok, add node definition with the requested ID
1499 if (i >= m_content_features.size())
1500 m_content_features.resize((u32)(i) + 1);
1501 m_content_features[i] = f;
1502 addNameIdMapping(i, f.name);
1503 verbosestream << "deserialized " << f.name << std::endl;
1505 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1506 fixSelectionBoxIntUnion();
1511 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1513 m_name_id_mapping.set(i, name);
1514 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1518 IWritableNodeDefManager *createNodeDefManager()
1520 return new CNodeDefManager();
1524 //// Serialization of old ContentFeatures formats
1525 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1527 u8 compatible_param_type_2 = param_type_2;
1528 if ((protocol_version < 28)
1529 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1530 compatible_param_type_2 = CPT2_NONE;
1531 else if (protocol_version < 30) {
1532 if (compatible_param_type_2 == CPT2_COLOR)
1533 compatible_param_type_2 = CPT2_NONE;
1534 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1535 compatible_param_type_2 = CPT2_FACEDIR;
1536 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1537 compatible_param_type_2 = CPT2_WALLMOUNTED;
1540 float compatible_visual_scale = visual_scale;
1541 if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1542 compatible_visual_scale = sqrt(visual_scale);
1544 TileDef compatible_tiles[6];
1545 for (u8 i = 0; i < 6; i++) {
1546 compatible_tiles[i] = tiledef[i];
1547 if (tiledef_overlay[i].name != "") {
1548 std::stringstream s;
1549 s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name
1551 compatible_tiles[i].name = s.str();
1556 if (protocol_version < 31) {
1557 writeU8(os, protocol_version < 27 ? 7 : 8);
1559 os << serializeString(name);
1560 writeU16(os, groups.size());
1561 for (ItemGroupList::const_iterator i = groups.begin();
1562 i != groups.end(); ++i) {
1563 os << serializeString(i->first);
1564 writeS16(os, i->second);
1566 writeU8(os, drawtype);
1567 writeF1000(os, compatible_visual_scale);
1569 for (u32 i = 0; i < 6; i++)
1570 compatible_tiles[i].serialize(os, protocol_version);
1571 writeU8(os, CF_SPECIAL_COUNT);
1572 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1573 tiledef_special[i].serialize(os, protocol_version);
1575 writeU8(os, post_effect_color.getAlpha());
1576 writeU8(os, post_effect_color.getRed());
1577 writeU8(os, post_effect_color.getGreen());
1578 writeU8(os, post_effect_color.getBlue());
1579 writeU8(os, param_type);
1580 writeU8(os, compatible_param_type_2);
1581 writeU8(os, is_ground_content);
1582 writeU8(os, light_propagates);
1583 writeU8(os, sunlight_propagates);
1584 writeU8(os, walkable);
1585 writeU8(os, pointable);
1586 writeU8(os, diggable);
1587 writeU8(os, climbable);
1588 writeU8(os, buildable_to);
1589 os << serializeString(""); // legacy: used to be metadata_name
1590 writeU8(os, liquid_type);
1591 os << serializeString(liquid_alternative_flowing);
1592 os << serializeString(liquid_alternative_source);
1593 writeU8(os, liquid_viscosity);
1594 writeU8(os, liquid_renewable);
1595 writeU8(os, light_source);
1596 writeU32(os, damage_per_second);
1597 node_box.serialize(os, protocol_version);
1598 selection_box.serialize(os, protocol_version);
1599 writeU8(os, legacy_facedir_simple);
1600 writeU8(os, legacy_wallmounted);
1601 serializeSimpleSoundSpec(sound_footstep, os);
1602 serializeSimpleSoundSpec(sound_dig, os);
1603 serializeSimpleSoundSpec(sound_dug, os);
1604 writeU8(os, rightclickable);
1605 writeU8(os, drowning);
1606 writeU8(os, leveled);
1607 writeU8(os, liquid_range);
1608 writeU8(os, waving);
1609 os << serializeString(mesh);
1610 collision_box.serialize(os, protocol_version);
1611 writeU8(os, floodable);
1612 writeU16(os, connects_to_ids.size());
1613 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1614 i != connects_to_ids.end(); ++i)
1616 writeU8(os, connect_sides);
1618 throw SerializationError("ContentFeatures::serialize(): "
1619 "Unsupported version requested");
1623 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1625 if (version == 5) // In PROTOCOL_VERSION 13
1627 name = deSerializeString(is);
1629 u32 groups_size = readU16(is);
1630 for(u32 i=0; i<groups_size; i++){
1631 std::string name = deSerializeString(is);
1632 int value = readS16(is);
1633 groups[name] = value;
1635 drawtype = (enum NodeDrawType)readU8(is);
1637 visual_scale = readF1000(is);
1638 if (readU8(is) != 6)
1639 throw SerializationError("unsupported tile count");
1640 for (u32 i = 0; i < 6; i++)
1641 tiledef[i].deSerialize(is, version, drawtype);
1642 if (readU8(is) != CF_SPECIAL_COUNT)
1643 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1644 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1645 tiledef_special[i].deSerialize(is, version, drawtype);
1647 post_effect_color.setAlpha(readU8(is));
1648 post_effect_color.setRed(readU8(is));
1649 post_effect_color.setGreen(readU8(is));
1650 post_effect_color.setBlue(readU8(is));
1651 param_type = (enum ContentParamType)readU8(is);
1652 param_type_2 = (enum ContentParamType2)readU8(is);
1653 is_ground_content = readU8(is);
1654 light_propagates = readU8(is);
1655 sunlight_propagates = readU8(is);
1656 walkable = readU8(is);
1657 pointable = readU8(is);
1658 diggable = readU8(is);
1659 climbable = readU8(is);
1660 buildable_to = readU8(is);
1661 deSerializeString(is); // legacy: used to be metadata_name
1662 liquid_type = (enum LiquidType)readU8(is);
1663 liquid_alternative_flowing = deSerializeString(is);
1664 liquid_alternative_source = deSerializeString(is);
1665 liquid_viscosity = readU8(is);
1666 light_source = readU8(is);
1667 light_source = MYMIN(light_source, LIGHT_MAX);
1668 damage_per_second = readU32(is);
1669 node_box.deSerialize(is);
1670 selection_box.deSerialize(is);
1671 legacy_facedir_simple = readU8(is);
1672 legacy_wallmounted = readU8(is);
1673 deSerializeSimpleSoundSpec(sound_footstep, is);
1674 deSerializeSimpleSoundSpec(sound_dig, is);
1675 deSerializeSimpleSoundSpec(sound_dug, is);
1676 } else if (version == 6) {
1677 name = deSerializeString(is);
1679 u32 groups_size = readU16(is);
1680 for (u32 i = 0; i < groups_size; i++) {
1681 std::string name = deSerializeString(is);
1682 int value = readS16(is);
1683 groups[name] = value;
1685 drawtype = (enum NodeDrawType)readU8(is);
1686 visual_scale = readF1000(is);
1687 if (readU8(is) != 6)
1688 throw SerializationError("unsupported tile count");
1689 for (u32 i = 0; i < 6; i++)
1690 tiledef[i].deSerialize(is, version, drawtype);
1691 // CF_SPECIAL_COUNT in version 6 = 2
1692 if (readU8(is) != 2)
1693 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1694 for (u32 i = 0; i < 2; i++)
1695 tiledef_special[i].deSerialize(is, version, drawtype);
1697 post_effect_color.setAlpha(readU8(is));
1698 post_effect_color.setRed(readU8(is));
1699 post_effect_color.setGreen(readU8(is));
1700 post_effect_color.setBlue(readU8(is));
1701 param_type = (enum ContentParamType)readU8(is);
1702 param_type_2 = (enum ContentParamType2)readU8(is);
1703 is_ground_content = readU8(is);
1704 light_propagates = readU8(is);
1705 sunlight_propagates = readU8(is);
1706 walkable = readU8(is);
1707 pointable = readU8(is);
1708 diggable = readU8(is);
1709 climbable = readU8(is);
1710 buildable_to = readU8(is);
1711 deSerializeString(is); // legacy: used to be metadata_name
1712 liquid_type = (enum LiquidType)readU8(is);
1713 liquid_alternative_flowing = deSerializeString(is);
1714 liquid_alternative_source = deSerializeString(is);
1715 liquid_viscosity = readU8(is);
1716 liquid_renewable = readU8(is);
1717 light_source = readU8(is);
1718 damage_per_second = readU32(is);
1719 node_box.deSerialize(is);
1720 selection_box.deSerialize(is);
1721 legacy_facedir_simple = readU8(is);
1722 legacy_wallmounted = readU8(is);
1723 deSerializeSimpleSoundSpec(sound_footstep, is);
1724 deSerializeSimpleSoundSpec(sound_dig, is);
1725 deSerializeSimpleSoundSpec(sound_dug, is);
1726 rightclickable = readU8(is);
1727 drowning = readU8(is);
1728 leveled = readU8(is);
1729 liquid_range = readU8(is);
1730 } else if (version == 7 || version == 8){
1731 name = deSerializeString(is);
1733 u32 groups_size = readU16(is);
1734 for (u32 i = 0; i < groups_size; i++) {
1735 std::string name = deSerializeString(is);
1736 int value = readS16(is);
1737 groups[name] = value;
1739 drawtype = (enum NodeDrawType) readU8(is);
1741 visual_scale = readF1000(is);
1742 if (readU8(is) != 6)
1743 throw SerializationError("unsupported tile count");
1744 for (u32 i = 0; i < 6; i++)
1745 tiledef[i].deSerialize(is, version, drawtype);
1746 if (readU8(is) != CF_SPECIAL_COUNT)
1747 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1748 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1749 tiledef_special[i].deSerialize(is, version, drawtype);
1751 post_effect_color.setAlpha(readU8(is));
1752 post_effect_color.setRed(readU8(is));
1753 post_effect_color.setGreen(readU8(is));
1754 post_effect_color.setBlue(readU8(is));
1755 param_type = (enum ContentParamType) readU8(is);
1756 param_type_2 = (enum ContentParamType2) readU8(is);
1757 is_ground_content = readU8(is);
1758 light_propagates = readU8(is);
1759 sunlight_propagates = readU8(is);
1760 walkable = readU8(is);
1761 pointable = readU8(is);
1762 diggable = readU8(is);
1763 climbable = readU8(is);
1764 buildable_to = readU8(is);
1765 deSerializeString(is); // legacy: used to be metadata_name
1766 liquid_type = (enum LiquidType) readU8(is);
1767 liquid_alternative_flowing = deSerializeString(is);
1768 liquid_alternative_source = deSerializeString(is);
1769 liquid_viscosity = readU8(is);
1770 liquid_renewable = readU8(is);
1771 light_source = readU8(is);
1772 light_source = MYMIN(light_source, LIGHT_MAX);
1773 damage_per_second = readU32(is);
1774 node_box.deSerialize(is);
1775 selection_box.deSerialize(is);
1776 legacy_facedir_simple = readU8(is);
1777 legacy_wallmounted = readU8(is);
1778 deSerializeSimpleSoundSpec(sound_footstep, is);
1779 deSerializeSimpleSoundSpec(sound_dig, is);
1780 deSerializeSimpleSoundSpec(sound_dug, is);
1781 rightclickable = readU8(is);
1782 drowning = readU8(is);
1783 leveled = readU8(is);
1784 liquid_range = readU8(is);
1785 waving = readU8(is);
1787 mesh = deSerializeString(is);
1788 collision_box.deSerialize(is);
1789 floodable = readU8(is);
1790 u16 connects_to_size = readU16(is);
1791 connects_to_ids.clear();
1792 for (u16 i = 0; i < connects_to_size; i++)
1793 connects_to_ids.insert(readU16(is));
1794 connect_sides = readU8(is);
1795 } catch (SerializationError &e) {};
1797 throw SerializationError("unsupported ContentFeatures version");
1802 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1804 return m_node_registration_complete;
1808 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1810 m_node_registration_complete = completed;
1814 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1817 if (m_node_registration_complete)
1818 nr->nodeResolveInternal();
1820 m_pending_resolve_callbacks.push_back(nr);
1824 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1826 size_t len = m_pending_resolve_callbacks.size();
1827 for (size_t i = 0; i != len; i++) {
1828 if (nr != m_pending_resolve_callbacks[i])
1832 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1833 m_pending_resolve_callbacks.resize(len);
1841 void CNodeDefManager::runNodeResolveCallbacks()
1843 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1844 NodeResolver *nr = m_pending_resolve_callbacks[i];
1845 nr->nodeResolveInternal();
1848 m_pending_resolve_callbacks.clear();
1852 void CNodeDefManager::resetNodeResolveState()
1854 m_node_registration_complete = false;
1855 m_pending_resolve_callbacks.clear();
1858 void CNodeDefManager::mapNodeboxConnections()
1860 for (u32 i = 0; i < m_content_features.size(); i++) {
1861 ContentFeatures *f = &m_content_features[i];
1862 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1864 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1865 it != f->connects_to.end(); ++it) {
1866 getIds(*it, f->connects_to_ids);
1871 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1873 const ContentFeatures &f1 = get(from);
1875 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1878 // lookup target in connected set
1879 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1882 const ContentFeatures &f2 = get(to);
1884 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1885 // ignores actually looking if back connection exists
1886 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1888 // does to node declare usable faces?
1889 if (f2.connect_sides > 0) {
1890 if ((f2.param_type_2 == CPT2_FACEDIR ||
1891 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1892 && (connect_face >= 4)) {
1893 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1894 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1896 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1898 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1899 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1901 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1902 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1903 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1905 return (f2.connect_sides
1906 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1908 return (f2.connect_sides & connect_face);
1910 // the target is just a regular node, so connect no matter back connection
1918 NodeResolver::NodeResolver()
1921 m_nodenames_idx = 0;
1922 m_nnlistsizes_idx = 0;
1923 m_resolve_done = false;
1925 m_nodenames.reserve(16);
1926 m_nnlistsizes.reserve(4);
1930 NodeResolver::~NodeResolver()
1932 if (!m_resolve_done && m_ndef)
1933 m_ndef->cancelNodeResolveCallback(this);
1937 void NodeResolver::nodeResolveInternal()
1939 m_nodenames_idx = 0;
1940 m_nnlistsizes_idx = 0;
1943 m_resolve_done = true;
1945 m_nodenames.clear();
1946 m_nnlistsizes.clear();
1950 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1951 const std::string &node_alt, content_t c_fallback)
1953 if (m_nodenames_idx == m_nodenames.size()) {
1954 *result_out = c_fallback;
1955 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1960 std::string name = m_nodenames[m_nodenames_idx++];
1962 bool success = m_ndef->getId(name, c);
1963 if (!success && node_alt != "") {
1965 success = m_ndef->getId(name, c);
1969 errorstream << "NodeResolver: failed to resolve node name '" << name
1970 << "'." << std::endl;
1979 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1980 bool all_required, content_t c_fallback)
1982 bool success = true;
1984 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1985 errorstream << "NodeResolver: no more node lists" << std::endl;
1989 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1992 if (m_nodenames_idx == m_nodenames.size()) {
1993 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1998 std::string &name = m_nodenames[m_nodenames_idx++];
2000 if (name.substr(0,6) != "group:") {
2001 if (m_ndef->getId(name, c)) {
2002 result_out->push_back(c);
2003 } else if (all_required) {
2004 errorstream << "NodeResolver: failed to resolve node name '"
2005 << name << "'." << std::endl;
2006 result_out->push_back(c_fallback);
2010 std::set<content_t> cids;
2011 std::set<content_t>::iterator it;
2012 m_ndef->getIds(name, cids);
2013 for (it = cids.begin(); it != cids.end(); ++it)
2014 result_out->push_back(*it);