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;
673 bool is_water_surface = false;
675 u8 material_type = (alpha == 255) ?
676 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
687 assert(liquid_type == LIQUID_SOURCE);
688 if (tsettings.opaque_water)
693 case NDT_FLOWINGLIQUID:
694 assert(liquid_type == LIQUID_FLOWING);
696 if (tsettings.opaque_water)
702 visual_solidness = 1;
704 case NDT_GLASSLIKE_FRAMED:
706 visual_solidness = 1;
708 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
710 visual_solidness = 1;
711 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
715 visual_solidness = 1;
717 case NDT_ALLFACES_OPTIONAL:
718 if (tsettings.leaves_style == LEAVES_FANCY) {
719 drawtype = NDT_ALLFACES;
721 visual_solidness = 1;
722 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
723 for (u32 j = 0; j < 6; j++) {
724 if (tiledef_special[j].name != "")
725 tdef[j].name = tiledef_special[j].name;
727 drawtype = NDT_GLASSLIKE;
729 visual_solidness = 1;
731 drawtype = NDT_NORMAL;
733 for (u32 i = 0; i < 6; i++)
734 tdef[i].name += std::string("^[noalpha");
737 material_type = TILE_MATERIAL_WAVING_LEAVES;
742 material_type = TILE_MATERIAL_WAVING_PLANTS;
760 material_type = (alpha == 255) ?
761 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
762 if (name == "default:water_source")
763 is_water_surface = true;
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 if (is_water_surface) {
776 tile_shader[0] = shdsrc->getShader("water_surface_shader",
777 material_type, drawtype);
780 // Tiles (fill in f->tiles[])
781 for (u16 j = 0; j < 6; j++) {
782 fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader[j],
783 tsettings.use_normal_texture,
784 tiledef[j].backface_culling, material_type);
785 if (tiledef_overlay[j].name!="")
786 fillTileAttribs(tsrc, &tiles[j].layers[1], &tiledef_overlay[j],
787 tile_shader[j], tsettings.use_normal_texture,
788 tiledef[j].backface_culling, material_type);
791 // Special tiles (fill in f->special_tiles[])
792 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
793 fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tiledef_special[j],
794 tile_shader[j], tsettings.use_normal_texture,
795 tiledef_special[j].backface_culling, material_type);
798 if (param_type_2 == CPT2_COLOR ||
799 param_type_2 == CPT2_COLORED_FACEDIR ||
800 param_type_2 == CPT2_COLORED_WALLMOUNTED)
801 palette = tsrc->getPalette(palette_name);
803 if ((drawtype == NDT_MESH) && (mesh != "")) {
805 // Read the mesh and apply scale
806 mesh_ptr[0] = client->getMesh(mesh);
808 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
809 scaleMesh(mesh_ptr[0], scale);
810 recalculateBoundingBox(mesh_ptr[0]);
811 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
813 } else if ((drawtype == NDT_NODEBOX) &&
814 ((node_box.type == NODEBOX_REGULAR) ||
815 (node_box.type == NODEBOX_FIXED)) &&
816 (!node_box.fixed.empty())) {
817 //Convert regular nodebox nodes to meshnodes
818 //Change the drawtype and apply scale
820 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
821 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
822 scaleMesh(mesh_ptr[0], scale);
823 recalculateBoundingBox(mesh_ptr[0]);
824 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
827 //Cache 6dfacedir and wallmounted rotated clones of meshes
828 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
829 (param_type_2 == CPT2_FACEDIR
830 || param_type_2 == CPT2_COLORED_FACEDIR)) {
831 for (u16 j = 1; j < 24; j++) {
832 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
833 rotateMeshBy6dFacedir(mesh_ptr[j], j);
834 recalculateBoundingBox(mesh_ptr[j]);
835 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
837 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
838 && (param_type_2 == CPT2_WALLMOUNTED ||
839 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
840 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
841 for (u16 j = 1; j < 6; j++) {
842 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
843 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
844 recalculateBoundingBox(mesh_ptr[j]);
845 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
847 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
848 recalculateBoundingBox(mesh_ptr[0]);
849 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
858 class CNodeDefManager: public IWritableNodeDefManager {
861 virtual ~CNodeDefManager();
863 virtual IWritableNodeDefManager *clone();
864 inline virtual const ContentFeatures& get(content_t c) const;
865 inline virtual const ContentFeatures& get(const MapNode &n) const;
866 virtual bool getId(const std::string &name, content_t &result) const;
867 virtual content_t getId(const std::string &name) const;
868 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
869 virtual const ContentFeatures& get(const std::string &name) const;
870 content_t allocateId();
871 virtual content_t set(const std::string &name, const ContentFeatures &def);
872 virtual content_t allocateDummy(const std::string &name);
873 virtual void removeNode(const std::string &name);
874 virtual void updateAliases(IItemDefManager *idef);
875 virtual void applyTextureOverrides(const std::string &override_filepath);
876 virtual void updateTextures(IGameDef *gamedef,
877 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
878 void *progress_cbk_args);
879 void serialize(std::ostream &os, u16 protocol_version) const;
880 void deSerialize(std::istream &is);
882 inline virtual bool getNodeRegistrationStatus() const;
883 inline virtual void setNodeRegistrationStatus(bool completed);
885 virtual void pendNodeResolve(NodeResolver *nr);
886 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
887 virtual void runNodeResolveCallbacks();
888 virtual void resetNodeResolveState();
889 virtual void mapNodeboxConnections();
890 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
891 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
893 return m_selection_box_int_union;
897 void addNameIdMapping(content_t i, std::string name);
899 * Recalculates m_selection_box_int_union based on
900 * m_selection_box_union.
902 void fixSelectionBoxIntUnion();
904 // Features indexed by id
905 std::vector<ContentFeatures> m_content_features;
907 // A mapping for fast converting back and forth between names and ids
908 NameIdMapping m_name_id_mapping;
910 // Like m_name_id_mapping, but only from names to ids, and includes
911 // item aliases too. Updated by updateAliases()
912 // Note: Not serialized.
914 UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
916 // A mapping from groups to a list of content_ts (and their levels)
917 // that belong to it. Necessary for a direct lookup in getIds().
918 // Note: Not serialized.
919 UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
921 // Next possibly free id
924 // NodeResolvers to callback once node registration has ended
925 std::vector<NodeResolver *> m_pending_resolve_callbacks;
927 // True when all nodes have been registered
928 bool m_node_registration_complete;
930 //! The union of all nodes' selection boxes.
931 aabb3f m_selection_box_union;
933 * The smallest box in node coordinates that
934 * contains all nodes' selection boxes.
936 core::aabbox3d<s16> m_selection_box_int_union;
940 CNodeDefManager::CNodeDefManager()
946 CNodeDefManager::~CNodeDefManager()
949 for (u32 i = 0; i < m_content_features.size(); i++) {
950 ContentFeatures *f = &m_content_features[i];
951 for (u32 j = 0; j < 24; j++) {
953 f->mesh_ptr[j]->drop();
960 void CNodeDefManager::clear()
962 m_content_features.clear();
963 m_name_id_mapping.clear();
964 m_name_id_mapping_with_aliases.clear();
965 m_group_to_items.clear();
967 m_selection_box_union.reset(0,0,0);
968 m_selection_box_int_union.reset(0,0,0);
970 resetNodeResolveState();
972 u32 initial_length = 0;
973 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
974 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
975 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
976 m_content_features.resize(initial_length);
978 // Set CONTENT_UNKNOWN
982 // Insert directly into containers
983 content_t c = CONTENT_UNKNOWN;
984 m_content_features[c] = f;
985 addNameIdMapping(c, f.name);
992 f.drawtype = NDT_AIRLIKE;
993 f.param_type = CPT_LIGHT;
994 f.light_propagates = true;
995 f.sunlight_propagates = true;
999 f.buildable_to = true;
1001 f.is_ground_content = true;
1002 // Insert directly into containers
1003 content_t c = CONTENT_AIR;
1004 m_content_features[c] = f;
1005 addNameIdMapping(c, f.name);
1008 // Set CONTENT_IGNORE
1012 f.drawtype = NDT_AIRLIKE;
1013 f.param_type = CPT_NONE;
1014 f.light_propagates = false;
1015 f.sunlight_propagates = false;
1017 f.pointable = false;
1019 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1020 f.is_ground_content = true;
1021 // Insert directly into containers
1022 content_t c = CONTENT_IGNORE;
1023 m_content_features[c] = f;
1024 addNameIdMapping(c, f.name);
1029 IWritableNodeDefManager *CNodeDefManager::clone()
1031 CNodeDefManager *mgr = new CNodeDefManager();
1037 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1039 return c < m_content_features.size()
1040 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1044 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1046 return get(n.getContent());
1050 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1052 UNORDERED_MAP<std::string, content_t>::const_iterator
1053 i = m_name_id_mapping_with_aliases.find(name);
1054 if(i == m_name_id_mapping_with_aliases.end())
1061 content_t CNodeDefManager::getId(const std::string &name) const
1063 content_t id = CONTENT_IGNORE;
1069 bool CNodeDefManager::getIds(const std::string &name,
1070 std::set<content_t> &result) const
1072 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1073 if (name.substr(0,6) != "group:") {
1074 content_t id = CONTENT_IGNORE;
1075 bool exists = getId(name, id);
1080 std::string group = name.substr(6);
1082 UNORDERED_MAP<std::string, GroupItems>::const_iterator
1083 i = m_group_to_items.find(group);
1084 if (i == m_group_to_items.end())
1087 const GroupItems &items = i->second;
1088 for (GroupItems::const_iterator j = items.begin();
1089 j != items.end(); ++j) {
1090 if ((*j).second != 0)
1091 result.insert((*j).first);
1093 //printf("getIds: %dus\n", t.stop());
1098 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1100 content_t id = CONTENT_UNKNOWN;
1106 // returns CONTENT_IGNORE if no free ID found
1107 content_t CNodeDefManager::allocateId()
1109 for (content_t id = m_next_id;
1110 id >= m_next_id; // overflow?
1112 while (id >= m_content_features.size()) {
1113 m_content_features.push_back(ContentFeatures());
1115 const ContentFeatures &f = m_content_features[id];
1121 // If we arrive here, an overflow occurred in id.
1122 // That means no ID was found
1123 return CONTENT_IGNORE;
1128 * Returns the smallest box that contains all boxes
1129 * in the vector. Box_union is expanded.
1130 * @param[in] boxes the vector containing the boxes
1131 * @param[in, out] box_union the union of the arguments
1133 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1135 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1136 it != boxes.end(); ++it) {
1137 box_union->addInternalBox(*it);
1143 * Returns a box that contains the nodebox in every case.
1144 * The argument node_union is expanded.
1145 * @param[in] nodebox the nodebox to be measured
1146 * @param[in] features used to decide whether the nodebox
1148 * @param[in, out] box_union the union of the arguments
1150 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1153 switch(nodebox.type) {
1155 case NODEBOX_LEVELED: {
1157 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1158 boxVectorUnion(nodebox.fixed, &half_processed);
1159 // Set leveled boxes to maximal
1160 if (nodebox.type == NODEBOX_LEVELED) {
1161 half_processed.MaxEdge.Y = +BS / 2;
1163 if (features.param_type_2 == CPT2_FACEDIR ||
1164 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1165 // Get maximal coordinate
1167 fabsf(half_processed.MinEdge.X),
1168 fabsf(half_processed.MinEdge.Y),
1169 fabsf(half_processed.MinEdge.Z),
1170 fabsf(half_processed.MaxEdge.X),
1171 fabsf(half_processed.MaxEdge.Y),
1172 fabsf(half_processed.MaxEdge.Z) };
1174 for (int i = 0; i < 6; i++) {
1175 if (max < coords[i]) {
1179 // Add the union of all possible rotated boxes
1180 box_union->addInternalPoint(-max, -max, -max);
1181 box_union->addInternalPoint(+max, +max, +max);
1183 box_union->addInternalBox(half_processed);
1187 case NODEBOX_WALLMOUNTED: {
1189 box_union->addInternalBox(nodebox.wall_top);
1190 box_union->addInternalBox(nodebox.wall_bottom);
1191 // Find maximal coordinate in the X-Z plane
1193 fabsf(nodebox.wall_side.MinEdge.X),
1194 fabsf(nodebox.wall_side.MinEdge.Z),
1195 fabsf(nodebox.wall_side.MaxEdge.X),
1196 fabsf(nodebox.wall_side.MaxEdge.Z) };
1198 for (int i = 0; i < 4; i++) {
1199 if (max < coords[i]) {
1203 // Add the union of all possible rotated boxes
1204 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1205 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1208 case NODEBOX_CONNECTED: {
1209 // Add all possible connected boxes
1210 boxVectorUnion(nodebox.fixed, box_union);
1211 boxVectorUnion(nodebox.connect_top, box_union);
1212 boxVectorUnion(nodebox.connect_bottom, box_union);
1213 boxVectorUnion(nodebox.connect_front, box_union);
1214 boxVectorUnion(nodebox.connect_left, box_union);
1215 boxVectorUnion(nodebox.connect_back, box_union);
1216 boxVectorUnion(nodebox.connect_right, box_union);
1221 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1222 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1228 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1230 m_selection_box_int_union.MinEdge.X = floorf(
1231 m_selection_box_union.MinEdge.X / BS + 0.5f);
1232 m_selection_box_int_union.MinEdge.Y = floorf(
1233 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1234 m_selection_box_int_union.MinEdge.Z = floorf(
1235 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1236 m_selection_box_int_union.MaxEdge.X = ceilf(
1237 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1238 m_selection_box_int_union.MaxEdge.Y = ceilf(
1239 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1240 m_selection_box_int_union.MaxEdge.Z = ceilf(
1241 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1245 // IWritableNodeDefManager
1246 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1250 assert(name == def.name);
1252 // Don't allow redefining ignore (but allow air and unknown)
1253 if (name == "ignore") {
1254 warningstream << "NodeDefManager: Ignoring "
1255 "CONTENT_IGNORE redefinition"<<std::endl;
1256 return CONTENT_IGNORE;
1259 content_t id = CONTENT_IGNORE;
1260 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1263 if (id == CONTENT_IGNORE) {
1264 warningstream << "NodeDefManager: Absolute "
1265 "limit reached" << std::endl;
1266 return CONTENT_IGNORE;
1268 assert(id != CONTENT_IGNORE);
1269 addNameIdMapping(id, name);
1271 m_content_features[id] = def;
1272 verbosestream << "NodeDefManager: registering content id \"" << id
1273 << "\": name=\"" << def.name << "\""<<std::endl;
1275 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1276 fixSelectionBoxIntUnion();
1277 // Add this content to the list of all groups it belongs to
1278 // FIXME: This should remove a node from groups it no longer
1279 // belongs to when a node is re-registered
1280 for (ItemGroupList::const_iterator i = def.groups.begin();
1281 i != def.groups.end(); ++i) {
1282 std::string group_name = i->first;
1284 UNORDERED_MAP<std::string, GroupItems>::iterator
1285 j = m_group_to_items.find(group_name);
1286 if (j == m_group_to_items.end()) {
1287 m_group_to_items[group_name].push_back(
1288 std::make_pair(id, i->second));
1290 GroupItems &items = j->second;
1291 items.push_back(std::make_pair(id, i->second));
1298 content_t CNodeDefManager::allocateDummy(const std::string &name)
1300 assert(name != ""); // Pre-condition
1303 return set(name, f);
1307 void CNodeDefManager::removeNode(const std::string &name)
1312 // Erase name from name ID mapping
1313 content_t id = CONTENT_IGNORE;
1314 if (m_name_id_mapping.getId(name, id)) {
1315 m_name_id_mapping.eraseName(name);
1316 m_name_id_mapping_with_aliases.erase(name);
1319 // Erase node content from all groups it belongs to
1320 for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1321 m_group_to_items.begin();
1322 iter_groups != m_group_to_items.end();) {
1323 GroupItems &items = iter_groups->second;
1324 for (GroupItems::iterator iter_groupitems = items.begin();
1325 iter_groupitems != items.end();) {
1326 if (iter_groupitems->first == id)
1327 items.erase(iter_groupitems++);
1332 // Check if group is empty
1333 if (items.size() == 0)
1334 m_group_to_items.erase(iter_groups++);
1341 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1343 std::set<std::string> all;
1345 m_name_id_mapping_with_aliases.clear();
1346 for (std::set<std::string>::const_iterator
1347 i = all.begin(); i != all.end(); ++i) {
1348 const std::string &name = *i;
1349 const std::string &convert_to = idef->getAlias(name);
1351 if (m_name_id_mapping.getId(convert_to, id)) {
1352 m_name_id_mapping_with_aliases.insert(
1353 std::make_pair(name, id));
1358 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1360 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1361 "overrides to textures from " << override_filepath << std::endl;
1363 std::ifstream infile(override_filepath.c_str());
1366 while (std::getline(infile, line)) {
1368 if (trim(line) == "")
1370 std::vector<std::string> splitted = str_split(line, ' ');
1371 if (splitted.size() != 3) {
1372 errorstream << override_filepath
1373 << ":" << line_c << " Could not apply texture override \""
1374 << line << "\": Syntax error" << std::endl;
1379 if (!getId(splitted[0], id))
1380 continue; // Ignore unknown node
1382 ContentFeatures &nodedef = m_content_features[id];
1384 if (splitted[1] == "top")
1385 nodedef.tiledef[0].name = splitted[2];
1386 else if (splitted[1] == "bottom")
1387 nodedef.tiledef[1].name = splitted[2];
1388 else if (splitted[1] == "right")
1389 nodedef.tiledef[2].name = splitted[2];
1390 else if (splitted[1] == "left")
1391 nodedef.tiledef[3].name = splitted[2];
1392 else if (splitted[1] == "back")
1393 nodedef.tiledef[4].name = splitted[2];
1394 else if (splitted[1] == "front")
1395 nodedef.tiledef[5].name = splitted[2];
1396 else if (splitted[1] == "all" || splitted[1] == "*")
1397 for (int i = 0; i < 6; i++)
1398 nodedef.tiledef[i].name = splitted[2];
1399 else if (splitted[1] == "sides")
1400 for (int i = 2; i < 6; i++)
1401 nodedef.tiledef[i].name = splitted[2];
1403 errorstream << override_filepath
1404 << ":" << line_c << " Could not apply texture override \""
1405 << line << "\": Unknown node side \""
1406 << splitted[1] << "\"" << std::endl;
1412 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1413 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1414 void *progress_callback_args)
1417 infostream << "CNodeDefManager::updateTextures(): Updating "
1418 "textures in node definitions" << std::endl;
1420 Client *client = (Client *)gamedef;
1421 ITextureSource *tsrc = client->tsrc();
1422 IShaderSource *shdsrc = client->getShaderSource();
1423 scene::ISceneManager* smgr = client->getSceneManager();
1424 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1425 TextureSettings tsettings;
1426 tsettings.readSettings();
1428 u32 size = m_content_features.size();
1430 for (u32 i = 0; i < size; i++) {
1431 ContentFeatures *f = &(m_content_features[i]);
1432 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1433 progress_callback(progress_callback_args, i, size);
1438 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1440 writeU8(os, 1); // version
1442 std::ostringstream os2(std::ios::binary);
1443 for (u32 i = 0; i < m_content_features.size(); i++) {
1444 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1445 || i == CONTENT_UNKNOWN)
1447 const ContentFeatures *f = &m_content_features[i];
1451 // Wrap it in a string to allow different lengths without
1452 // strict version incompatibilities
1453 std::ostringstream wrapper_os(std::ios::binary);
1454 f->serialize(wrapper_os, protocol_version);
1455 os2<<serializeString(wrapper_os.str());
1457 // must not overflow
1458 u16 next = count + 1;
1459 FATAL_ERROR_IF(next < count, "Overflow");
1462 writeU16(os, count);
1463 os << serializeLongString(os2.str());
1467 void CNodeDefManager::deSerialize(std::istream &is)
1470 int version = readU8(is);
1472 throw SerializationError("unsupported NodeDefinitionManager version");
1473 u16 count = readU16(is);
1474 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1476 for (u16 n = 0; n < count; n++) {
1477 u16 i = readU16(is2);
1479 // Read it from the string wrapper
1480 std::string wrapper = deSerializeString(is2);
1481 std::istringstream wrapper_is(wrapper, std::ios::binary);
1482 f.deSerialize(wrapper_is);
1484 // Check error conditions
1485 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1486 warningstream << "NodeDefManager::deSerialize(): "
1487 "not changing builtin node " << i << std::endl;
1491 warningstream << "NodeDefManager::deSerialize(): "
1492 "received empty name" << std::endl;
1498 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1499 warningstream << "NodeDefManager::deSerialize(): "
1500 "already defined with different ID: " << f.name << std::endl;
1504 // All is ok, add node definition with the requested ID
1505 if (i >= m_content_features.size())
1506 m_content_features.resize((u32)(i) + 1);
1507 m_content_features[i] = f;
1508 addNameIdMapping(i, f.name);
1509 verbosestream << "deserialized " << f.name << std::endl;
1511 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1512 fixSelectionBoxIntUnion();
1517 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1519 m_name_id_mapping.set(i, name);
1520 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1524 IWritableNodeDefManager *createNodeDefManager()
1526 return new CNodeDefManager();
1530 //// Serialization of old ContentFeatures formats
1531 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1533 u8 compatible_param_type_2 = param_type_2;
1534 if ((protocol_version < 28)
1535 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1536 compatible_param_type_2 = CPT2_NONE;
1537 else if (protocol_version < 30) {
1538 if (compatible_param_type_2 == CPT2_COLOR)
1539 compatible_param_type_2 = CPT2_NONE;
1540 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1541 compatible_param_type_2 = CPT2_FACEDIR;
1542 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1543 compatible_param_type_2 = CPT2_WALLMOUNTED;
1546 float compatible_visual_scale = visual_scale;
1547 if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1548 compatible_visual_scale = sqrt(visual_scale);
1550 TileDef compatible_tiles[6];
1551 for (u8 i = 0; i < 6; i++) {
1552 compatible_tiles[i] = tiledef[i];
1553 if (tiledef_overlay[i].name != "") {
1554 std::stringstream s;
1555 s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name
1557 compatible_tiles[i].name = s.str();
1562 if (protocol_version < 31) {
1563 writeU8(os, protocol_version < 27 ? 7 : 8);
1565 os << serializeString(name);
1566 writeU16(os, groups.size());
1567 for (ItemGroupList::const_iterator i = groups.begin();
1568 i != groups.end(); ++i) {
1569 os << serializeString(i->first);
1570 writeS16(os, i->second);
1572 writeU8(os, drawtype);
1573 writeF1000(os, compatible_visual_scale);
1575 for (u32 i = 0; i < 6; i++)
1576 compatible_tiles[i].serialize(os, protocol_version);
1577 writeU8(os, CF_SPECIAL_COUNT);
1578 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1579 tiledef_special[i].serialize(os, protocol_version);
1581 writeU8(os, post_effect_color.getAlpha());
1582 writeU8(os, post_effect_color.getRed());
1583 writeU8(os, post_effect_color.getGreen());
1584 writeU8(os, post_effect_color.getBlue());
1585 writeU8(os, param_type);
1586 writeU8(os, compatible_param_type_2);
1587 writeU8(os, is_ground_content);
1588 writeU8(os, light_propagates);
1589 writeU8(os, sunlight_propagates);
1590 writeU8(os, walkable);
1591 writeU8(os, pointable);
1592 writeU8(os, diggable);
1593 writeU8(os, climbable);
1594 writeU8(os, buildable_to);
1595 os << serializeString(""); // legacy: used to be metadata_name
1596 writeU8(os, liquid_type);
1597 os << serializeString(liquid_alternative_flowing);
1598 os << serializeString(liquid_alternative_source);
1599 writeU8(os, liquid_viscosity);
1600 writeU8(os, liquid_renewable);
1601 writeU8(os, light_source);
1602 writeU32(os, damage_per_second);
1603 node_box.serialize(os, protocol_version);
1604 selection_box.serialize(os, protocol_version);
1605 writeU8(os, legacy_facedir_simple);
1606 writeU8(os, legacy_wallmounted);
1607 serializeSimpleSoundSpec(sound_footstep, os);
1608 serializeSimpleSoundSpec(sound_dig, os);
1609 serializeSimpleSoundSpec(sound_dug, os);
1610 writeU8(os, rightclickable);
1611 writeU8(os, drowning);
1612 writeU8(os, leveled);
1613 writeU8(os, liquid_range);
1614 writeU8(os, waving);
1615 os << serializeString(mesh);
1616 collision_box.serialize(os, protocol_version);
1617 writeU8(os, floodable);
1618 writeU16(os, connects_to_ids.size());
1619 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1620 i != connects_to_ids.end(); ++i)
1622 writeU8(os, connect_sides);
1624 throw SerializationError("ContentFeatures::serialize(): "
1625 "Unsupported version requested");
1629 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1631 if (version == 5) // In PROTOCOL_VERSION 13
1633 name = deSerializeString(is);
1635 u32 groups_size = readU16(is);
1636 for(u32 i=0; i<groups_size; i++){
1637 std::string name = deSerializeString(is);
1638 int value = readS16(is);
1639 groups[name] = value;
1641 drawtype = (enum NodeDrawType)readU8(is);
1643 visual_scale = readF1000(is);
1644 if (readU8(is) != 6)
1645 throw SerializationError("unsupported tile count");
1646 for (u32 i = 0; i < 6; i++)
1647 tiledef[i].deSerialize(is, version, drawtype);
1648 if (readU8(is) != CF_SPECIAL_COUNT)
1649 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1650 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1651 tiledef_special[i].deSerialize(is, version, drawtype);
1653 post_effect_color.setAlpha(readU8(is));
1654 post_effect_color.setRed(readU8(is));
1655 post_effect_color.setGreen(readU8(is));
1656 post_effect_color.setBlue(readU8(is));
1657 param_type = (enum ContentParamType)readU8(is);
1658 param_type_2 = (enum ContentParamType2)readU8(is);
1659 is_ground_content = readU8(is);
1660 light_propagates = readU8(is);
1661 sunlight_propagates = readU8(is);
1662 walkable = readU8(is);
1663 pointable = readU8(is);
1664 diggable = readU8(is);
1665 climbable = readU8(is);
1666 buildable_to = readU8(is);
1667 deSerializeString(is); // legacy: used to be metadata_name
1668 liquid_type = (enum LiquidType)readU8(is);
1669 liquid_alternative_flowing = deSerializeString(is);
1670 liquid_alternative_source = deSerializeString(is);
1671 liquid_viscosity = readU8(is);
1672 light_source = readU8(is);
1673 light_source = MYMIN(light_source, LIGHT_MAX);
1674 damage_per_second = readU32(is);
1675 node_box.deSerialize(is);
1676 selection_box.deSerialize(is);
1677 legacy_facedir_simple = readU8(is);
1678 legacy_wallmounted = readU8(is);
1679 deSerializeSimpleSoundSpec(sound_footstep, is);
1680 deSerializeSimpleSoundSpec(sound_dig, is);
1681 deSerializeSimpleSoundSpec(sound_dug, is);
1682 } else if (version == 6) {
1683 name = deSerializeString(is);
1685 u32 groups_size = readU16(is);
1686 for (u32 i = 0; i < groups_size; i++) {
1687 std::string name = deSerializeString(is);
1688 int value = readS16(is);
1689 groups[name] = value;
1691 drawtype = (enum NodeDrawType)readU8(is);
1692 visual_scale = readF1000(is);
1693 if (readU8(is) != 6)
1694 throw SerializationError("unsupported tile count");
1695 for (u32 i = 0; i < 6; i++)
1696 tiledef[i].deSerialize(is, version, drawtype);
1697 // CF_SPECIAL_COUNT in version 6 = 2
1698 if (readU8(is) != 2)
1699 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1700 for (u32 i = 0; i < 2; i++)
1701 tiledef_special[i].deSerialize(is, version, drawtype);
1703 post_effect_color.setAlpha(readU8(is));
1704 post_effect_color.setRed(readU8(is));
1705 post_effect_color.setGreen(readU8(is));
1706 post_effect_color.setBlue(readU8(is));
1707 param_type = (enum ContentParamType)readU8(is);
1708 param_type_2 = (enum ContentParamType2)readU8(is);
1709 is_ground_content = readU8(is);
1710 light_propagates = readU8(is);
1711 sunlight_propagates = readU8(is);
1712 walkable = readU8(is);
1713 pointable = readU8(is);
1714 diggable = readU8(is);
1715 climbable = readU8(is);
1716 buildable_to = readU8(is);
1717 deSerializeString(is); // legacy: used to be metadata_name
1718 liquid_type = (enum LiquidType)readU8(is);
1719 liquid_alternative_flowing = deSerializeString(is);
1720 liquid_alternative_source = deSerializeString(is);
1721 liquid_viscosity = readU8(is);
1722 liquid_renewable = readU8(is);
1723 light_source = readU8(is);
1724 damage_per_second = readU32(is);
1725 node_box.deSerialize(is);
1726 selection_box.deSerialize(is);
1727 legacy_facedir_simple = readU8(is);
1728 legacy_wallmounted = readU8(is);
1729 deSerializeSimpleSoundSpec(sound_footstep, is);
1730 deSerializeSimpleSoundSpec(sound_dig, is);
1731 deSerializeSimpleSoundSpec(sound_dug, is);
1732 rightclickable = readU8(is);
1733 drowning = readU8(is);
1734 leveled = readU8(is);
1735 liquid_range = readU8(is);
1736 } else if (version == 7 || version == 8){
1737 name = deSerializeString(is);
1739 u32 groups_size = readU16(is);
1740 for (u32 i = 0; i < groups_size; i++) {
1741 std::string name = deSerializeString(is);
1742 int value = readS16(is);
1743 groups[name] = value;
1745 drawtype = (enum NodeDrawType) readU8(is);
1747 visual_scale = readF1000(is);
1748 if (readU8(is) != 6)
1749 throw SerializationError("unsupported tile count");
1750 for (u32 i = 0; i < 6; i++)
1751 tiledef[i].deSerialize(is, version, drawtype);
1752 if (readU8(is) != CF_SPECIAL_COUNT)
1753 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1754 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1755 tiledef_special[i].deSerialize(is, version, drawtype);
1757 post_effect_color.setAlpha(readU8(is));
1758 post_effect_color.setRed(readU8(is));
1759 post_effect_color.setGreen(readU8(is));
1760 post_effect_color.setBlue(readU8(is));
1761 param_type = (enum ContentParamType) readU8(is);
1762 param_type_2 = (enum ContentParamType2) readU8(is);
1763 is_ground_content = readU8(is);
1764 light_propagates = readU8(is);
1765 sunlight_propagates = readU8(is);
1766 walkable = readU8(is);
1767 pointable = readU8(is);
1768 diggable = readU8(is);
1769 climbable = readU8(is);
1770 buildable_to = readU8(is);
1771 deSerializeString(is); // legacy: used to be metadata_name
1772 liquid_type = (enum LiquidType) readU8(is);
1773 liquid_alternative_flowing = deSerializeString(is);
1774 liquid_alternative_source = deSerializeString(is);
1775 liquid_viscosity = readU8(is);
1776 liquid_renewable = readU8(is);
1777 light_source = readU8(is);
1778 light_source = MYMIN(light_source, LIGHT_MAX);
1779 damage_per_second = readU32(is);
1780 node_box.deSerialize(is);
1781 selection_box.deSerialize(is);
1782 legacy_facedir_simple = readU8(is);
1783 legacy_wallmounted = readU8(is);
1784 deSerializeSimpleSoundSpec(sound_footstep, is);
1785 deSerializeSimpleSoundSpec(sound_dig, is);
1786 deSerializeSimpleSoundSpec(sound_dug, is);
1787 rightclickable = readU8(is);
1788 drowning = readU8(is);
1789 leveled = readU8(is);
1790 liquid_range = readU8(is);
1791 waving = readU8(is);
1793 mesh = deSerializeString(is);
1794 collision_box.deSerialize(is);
1795 floodable = readU8(is);
1796 u16 connects_to_size = readU16(is);
1797 connects_to_ids.clear();
1798 for (u16 i = 0; i < connects_to_size; i++)
1799 connects_to_ids.insert(readU16(is));
1800 connect_sides = readU8(is);
1801 } catch (SerializationError &e) {};
1803 throw SerializationError("unsupported ContentFeatures version");
1808 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1810 return m_node_registration_complete;
1814 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1816 m_node_registration_complete = completed;
1820 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1823 if (m_node_registration_complete)
1824 nr->nodeResolveInternal();
1826 m_pending_resolve_callbacks.push_back(nr);
1830 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1832 size_t len = m_pending_resolve_callbacks.size();
1833 for (size_t i = 0; i != len; i++) {
1834 if (nr != m_pending_resolve_callbacks[i])
1838 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1839 m_pending_resolve_callbacks.resize(len);
1847 void CNodeDefManager::runNodeResolveCallbacks()
1849 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1850 NodeResolver *nr = m_pending_resolve_callbacks[i];
1851 nr->nodeResolveInternal();
1854 m_pending_resolve_callbacks.clear();
1858 void CNodeDefManager::resetNodeResolveState()
1860 m_node_registration_complete = false;
1861 m_pending_resolve_callbacks.clear();
1864 void CNodeDefManager::mapNodeboxConnections()
1866 for (u32 i = 0; i < m_content_features.size(); i++) {
1867 ContentFeatures *f = &m_content_features[i];
1868 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1870 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1871 it != f->connects_to.end(); ++it) {
1872 getIds(*it, f->connects_to_ids);
1877 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1879 const ContentFeatures &f1 = get(from);
1881 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1884 // lookup target in connected set
1885 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1888 const ContentFeatures &f2 = get(to);
1890 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1891 // ignores actually looking if back connection exists
1892 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1894 // does to node declare usable faces?
1895 if (f2.connect_sides > 0) {
1896 if ((f2.param_type_2 == CPT2_FACEDIR ||
1897 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1898 && (connect_face >= 4)) {
1899 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1900 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1902 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1904 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1905 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1907 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1908 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1909 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1911 return (f2.connect_sides
1912 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1914 return (f2.connect_sides & connect_face);
1916 // the target is just a regular node, so connect no matter back connection
1924 NodeResolver::NodeResolver()
1927 m_nodenames_idx = 0;
1928 m_nnlistsizes_idx = 0;
1929 m_resolve_done = false;
1931 m_nodenames.reserve(16);
1932 m_nnlistsizes.reserve(4);
1936 NodeResolver::~NodeResolver()
1938 if (!m_resolve_done && m_ndef)
1939 m_ndef->cancelNodeResolveCallback(this);
1943 void NodeResolver::nodeResolveInternal()
1945 m_nodenames_idx = 0;
1946 m_nnlistsizes_idx = 0;
1949 m_resolve_done = true;
1951 m_nodenames.clear();
1952 m_nnlistsizes.clear();
1956 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1957 const std::string &node_alt, content_t c_fallback)
1959 if (m_nodenames_idx == m_nodenames.size()) {
1960 *result_out = c_fallback;
1961 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1966 std::string name = m_nodenames[m_nodenames_idx++];
1968 bool success = m_ndef->getId(name, c);
1969 if (!success && node_alt != "") {
1971 success = m_ndef->getId(name, c);
1975 errorstream << "NodeResolver: failed to resolve node name '" << name
1976 << "'." << std::endl;
1985 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1986 bool all_required, content_t c_fallback)
1988 bool success = true;
1990 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1991 errorstream << "NodeResolver: no more node lists" << std::endl;
1995 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1998 if (m_nodenames_idx == m_nodenames.size()) {
1999 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2004 std::string &name = m_nodenames[m_nodenames_idx++];
2006 if (name.substr(0,6) != "group:") {
2007 if (m_ndef->getId(name, c)) {
2008 result_out->push_back(c);
2009 } else if (all_required) {
2010 errorstream << "NodeResolver: failed to resolve node name '"
2011 << name << "'." << std::endl;
2012 result_out->push_back(c_fallback);
2016 std::set<content_t> cids;
2017 std::set<content_t>::iterator it;
2018 m_ndef->getIds(name, cids);
2019 for (it = cids.begin(); it != cids.end(); ++it)
2020 result_out->push_back(*it);