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(TileDef *tiles, int length)
474 // alpha == 0 means that the node is using texture alpha
475 if (alpha == 0 || alpha == 255)
478 for (int i = 0; i < length; i++) {
479 if (tiles[i].name == "")
482 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
483 tiles[i].name = s.str();
487 void ContentFeatures::deSerialize(std::istream &is)
490 int version = readU8(is);
492 deSerializeOld(is, version);
494 } else if (version > 10) {
495 throw SerializationError("unsupported ContentFeatures version");
499 name = deSerializeString(is);
501 u32 groups_size = readU16(is);
502 for (u32 i = 0; i < groups_size; i++) {
503 std::string name = deSerializeString(is);
504 int value = readS16(is);
505 groups[name] = value;
507 param_type = (enum ContentParamType) readU8(is);
508 param_type_2 = (enum ContentParamType2) readU8(is);
511 drawtype = (enum NodeDrawType) readU8(is);
512 mesh = deSerializeString(is);
513 visual_scale = readF1000(is);
515 throw SerializationError("unsupported tile count");
516 for (u32 i = 0; i < 6; i++)
517 tiledef[i].deSerialize(is, version, drawtype);
519 for (u32 i = 0; i < 6; i++)
520 tiledef_overlay[i].deSerialize(is, version, drawtype);
521 if (readU8(is) != CF_SPECIAL_COUNT)
522 throw SerializationError("unsupported CF_SPECIAL_COUNT");
523 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
524 tiledef_special[i].deSerialize(is, version, drawtype);
526 color.setRed(readU8(is));
527 color.setGreen(readU8(is));
528 color.setBlue(readU8(is));
529 palette_name = deSerializeString(is);
531 connect_sides = readU8(is);
532 u16 connects_to_size = readU16(is);
533 connects_to_ids.clear();
534 for (u16 i = 0; i < connects_to_size; i++)
535 connects_to_ids.insert(readU16(is));
536 post_effect_color.setAlpha(readU8(is));
537 post_effect_color.setRed(readU8(is));
538 post_effect_color.setGreen(readU8(is));
539 post_effect_color.setBlue(readU8(is));
540 leveled = readU8(is);
543 light_propagates = readU8(is);
544 sunlight_propagates = readU8(is);
545 light_source = readU8(is);
546 light_source = MYMIN(light_source, LIGHT_MAX);
549 is_ground_content = readU8(is);
552 walkable = readU8(is);
553 pointable = readU8(is);
554 diggable = readU8(is);
555 climbable = readU8(is);
556 buildable_to = readU8(is);
557 rightclickable = readU8(is);
558 damage_per_second = readU32(is);
561 liquid_type = (enum LiquidType) readU8(is);
562 liquid_alternative_flowing = deSerializeString(is);
563 liquid_alternative_source = deSerializeString(is);
564 liquid_viscosity = readU8(is);
565 liquid_renewable = readU8(is);
566 liquid_range = readU8(is);
567 drowning = readU8(is);
568 floodable = readU8(is);
571 node_box.deSerialize(is);
572 selection_box.deSerialize(is);
573 collision_box.deSerialize(is);
576 deSerializeSimpleSoundSpec(sound_footstep, is);
577 deSerializeSimpleSoundSpec(sound_dig, is);
578 deSerializeSimpleSoundSpec(sound_dug, is);
580 // read legacy properties
581 legacy_facedir_simple = readU8(is);
582 legacy_wallmounted = readU8(is);
586 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
587 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
588 bool backface_culling, u8 material_type)
590 tile->shader_id = shader_id;
591 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
592 tile->material_type = material_type;
594 // Normal texture and shader flags texture
595 if (use_normal_texture) {
596 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
598 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
601 tile->material_flags = 0;
602 if (backface_culling)
603 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
604 if (tiledef->animation.type != TAT_NONE)
605 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
606 if (tiledef->tileable_horizontal)
607 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
608 if (tiledef->tileable_vertical)
609 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
612 tile->has_color = tiledef->has_color;
613 if (tiledef->has_color)
614 tile->color = tiledef->color;
618 // Animation parameters
620 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
622 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
623 &frame_count, &frame_length_ms, NULL);
624 tile->animation_frame_count = frame_count;
625 tile->animation_frame_length_ms = frame_length_ms;
628 if (frame_count == 1) {
629 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
631 std::ostringstream os(std::ios::binary);
632 tile->frames.resize(frame_count);
634 for (int i = 0; i < frame_count; i++) {
640 tiledef->animation.getTextureModifer(os,
641 tile->texture->getOriginalSize(), i);
643 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
644 if (tile->normal_texture)
645 frame.normal_texture = tsrc->getNormalTexture(os.str());
646 frame.flags_texture = tile->flags_texture;
647 tile->frames[i] = frame;
654 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
655 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
657 // minimap pixel color - the average color of a texture
658 if (tsettings.enable_minimap && tiledef[0].name != "")
659 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
661 // Figure out the actual tiles to use
663 for (u32 j = 0; j < 6; j++) {
664 tdef[j] = tiledef[j];
665 if (tdef[j].name == "")
666 tdef[j].name = "unknown_node.png";
668 // also the overlay tiles
669 TileDef tdef_overlay[6];
670 for (u32 j = 0; j < 6; j++)
671 tdef_overlay[j] = tiledef_overlay[j];
672 // also the special tiles
673 TileDef tdef_spec[6];
674 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
675 tdef_spec[j] = tiledef_special[j];
677 bool is_liquid = false;
679 u8 material_type = (alpha == 255) ?
680 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
691 assert(liquid_type == LIQUID_SOURCE);
692 if (tsettings.opaque_water)
697 case NDT_FLOWINGLIQUID:
698 assert(liquid_type == LIQUID_FLOWING);
700 if (tsettings.opaque_water)
706 visual_solidness = 1;
708 case NDT_GLASSLIKE_FRAMED:
710 visual_solidness = 1;
712 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
714 visual_solidness = 1;
715 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
719 visual_solidness = 1;
721 case NDT_ALLFACES_OPTIONAL:
722 if (tsettings.leaves_style == LEAVES_FANCY) {
723 drawtype = NDT_ALLFACES;
725 visual_solidness = 1;
726 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
727 for (u32 j = 0; j < 6; j++) {
728 if (tdef_spec[j].name != "")
729 tdef[j].name = tdef_spec[j].name;
731 drawtype = NDT_GLASSLIKE;
733 visual_solidness = 1;
735 drawtype = NDT_NORMAL;
737 for (u32 i = 0; i < 6; i++)
738 tdef[i].name += std::string("^[noalpha");
741 material_type = TILE_MATERIAL_WAVING_LEAVES;
746 material_type = TILE_MATERIAL_WAVING_PLANTS;
755 material_type = TILE_MATERIAL_WAVING_PLANTS;
756 else if (waving == 2)
757 material_type = TILE_MATERIAL_WAVING_LEAVES;
768 // Vertex alpha is no longer supported, correct if necessary.
769 correctAlpha(tdef, 6);
770 correctAlpha(tdef_overlay, 6);
771 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
772 material_type = (alpha == 255) ?
773 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
777 for (u16 j = 0; j < 6; j++) {
778 tile_shader[j] = shdsrc->getShader("nodes_shader",
779 material_type, drawtype);
782 // Tiles (fill in f->tiles[])
783 for (u16 j = 0; j < 6; j++) {
784 fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader[j],
785 tsettings.use_normal_texture,
786 tdef[j].backface_culling, material_type);
787 if (tdef_overlay[j].name != "")
788 fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j],
789 tile_shader[j], tsettings.use_normal_texture,
790 tdef[j].backface_culling, material_type);
793 // Special tiles (fill in f->special_tiles[])
794 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
795 fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j],
796 tile_shader[j], tsettings.use_normal_texture,
797 tdef_spec[j].backface_culling, material_type);
800 if (param_type_2 == CPT2_COLOR ||
801 param_type_2 == CPT2_COLORED_FACEDIR ||
802 param_type_2 == CPT2_COLORED_WALLMOUNTED)
803 palette = tsrc->getPalette(palette_name);
805 if ((drawtype == NDT_MESH) && (mesh != "")) {
807 // Read the mesh and apply scale
808 mesh_ptr[0] = client->getMesh(mesh);
810 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
811 scaleMesh(mesh_ptr[0], scale);
812 recalculateBoundingBox(mesh_ptr[0]);
813 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
815 } else if ((drawtype == NDT_NODEBOX) &&
816 ((node_box.type == NODEBOX_REGULAR) ||
817 (node_box.type == NODEBOX_FIXED)) &&
818 (!node_box.fixed.empty())) {
819 //Convert regular nodebox nodes to meshnodes
820 //Change the drawtype and apply scale
822 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
823 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
824 scaleMesh(mesh_ptr[0], scale);
825 recalculateBoundingBox(mesh_ptr[0]);
826 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
829 //Cache 6dfacedir and wallmounted rotated clones of meshes
830 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
831 (param_type_2 == CPT2_FACEDIR
832 || param_type_2 == CPT2_COLORED_FACEDIR)) {
833 for (u16 j = 1; j < 24; j++) {
834 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
835 rotateMeshBy6dFacedir(mesh_ptr[j], j);
836 recalculateBoundingBox(mesh_ptr[j]);
837 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
839 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
840 && (param_type_2 == CPT2_WALLMOUNTED ||
841 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
842 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
843 for (u16 j = 1; j < 6; j++) {
844 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
845 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
846 recalculateBoundingBox(mesh_ptr[j]);
847 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
849 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
850 recalculateBoundingBox(mesh_ptr[0]);
851 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
860 class CNodeDefManager: public IWritableNodeDefManager {
863 virtual ~CNodeDefManager();
865 virtual IWritableNodeDefManager *clone();
866 inline virtual const ContentFeatures& get(content_t c) const;
867 inline virtual const ContentFeatures& get(const MapNode &n) const;
868 virtual bool getId(const std::string &name, content_t &result) const;
869 virtual content_t getId(const std::string &name) const;
870 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
871 virtual const ContentFeatures& get(const std::string &name) const;
872 content_t allocateId();
873 virtual content_t set(const std::string &name, const ContentFeatures &def);
874 virtual content_t allocateDummy(const std::string &name);
875 virtual void removeNode(const std::string &name);
876 virtual void updateAliases(IItemDefManager *idef);
877 virtual void applyTextureOverrides(const std::string &override_filepath);
878 virtual void updateTextures(IGameDef *gamedef,
879 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
880 void *progress_cbk_args);
881 void serialize(std::ostream &os, u16 protocol_version) const;
882 void deSerialize(std::istream &is);
884 inline virtual bool getNodeRegistrationStatus() const;
885 inline virtual void setNodeRegistrationStatus(bool completed);
887 virtual void pendNodeResolve(NodeResolver *nr);
888 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
889 virtual void runNodeResolveCallbacks();
890 virtual void resetNodeResolveState();
891 virtual void mapNodeboxConnections();
892 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
893 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
895 return m_selection_box_int_union;
899 void addNameIdMapping(content_t i, std::string name);
901 * Recalculates m_selection_box_int_union based on
902 * m_selection_box_union.
904 void fixSelectionBoxIntUnion();
906 // Features indexed by id
907 std::vector<ContentFeatures> m_content_features;
909 // A mapping for fast converting back and forth between names and ids
910 NameIdMapping m_name_id_mapping;
912 // Like m_name_id_mapping, but only from names to ids, and includes
913 // item aliases too. Updated by updateAliases()
914 // Note: Not serialized.
916 UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
918 // A mapping from groups to a list of content_ts (and their levels)
919 // that belong to it. Necessary for a direct lookup in getIds().
920 // Note: Not serialized.
921 UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
923 // Next possibly free id
926 // NodeResolvers to callback once node registration has ended
927 std::vector<NodeResolver *> m_pending_resolve_callbacks;
929 // True when all nodes have been registered
930 bool m_node_registration_complete;
932 //! The union of all nodes' selection boxes.
933 aabb3f m_selection_box_union;
935 * The smallest box in node coordinates that
936 * contains all nodes' selection boxes.
938 core::aabbox3d<s16> m_selection_box_int_union;
942 CNodeDefManager::CNodeDefManager()
948 CNodeDefManager::~CNodeDefManager()
951 for (u32 i = 0; i < m_content_features.size(); i++) {
952 ContentFeatures *f = &m_content_features[i];
953 for (u32 j = 0; j < 24; j++) {
955 f->mesh_ptr[j]->drop();
962 void CNodeDefManager::clear()
964 m_content_features.clear();
965 m_name_id_mapping.clear();
966 m_name_id_mapping_with_aliases.clear();
967 m_group_to_items.clear();
969 m_selection_box_union.reset(0,0,0);
970 m_selection_box_int_union.reset(0,0,0);
972 resetNodeResolveState();
974 u32 initial_length = 0;
975 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
976 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
977 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
978 m_content_features.resize(initial_length);
980 // Set CONTENT_UNKNOWN
984 // Insert directly into containers
985 content_t c = CONTENT_UNKNOWN;
986 m_content_features[c] = f;
987 addNameIdMapping(c, f.name);
994 f.drawtype = NDT_AIRLIKE;
995 f.param_type = CPT_LIGHT;
996 f.light_propagates = true;
997 f.sunlight_propagates = true;
1001 f.buildable_to = true;
1003 f.is_ground_content = true;
1004 // Insert directly into containers
1005 content_t c = CONTENT_AIR;
1006 m_content_features[c] = f;
1007 addNameIdMapping(c, f.name);
1010 // Set CONTENT_IGNORE
1014 f.drawtype = NDT_AIRLIKE;
1015 f.param_type = CPT_NONE;
1016 f.light_propagates = false;
1017 f.sunlight_propagates = false;
1019 f.pointable = false;
1021 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1022 f.is_ground_content = true;
1023 // Insert directly into containers
1024 content_t c = CONTENT_IGNORE;
1025 m_content_features[c] = f;
1026 addNameIdMapping(c, f.name);
1031 IWritableNodeDefManager *CNodeDefManager::clone()
1033 CNodeDefManager *mgr = new CNodeDefManager();
1039 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1041 return c < m_content_features.size()
1042 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1046 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1048 return get(n.getContent());
1052 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1054 UNORDERED_MAP<std::string, content_t>::const_iterator
1055 i = m_name_id_mapping_with_aliases.find(name);
1056 if(i == m_name_id_mapping_with_aliases.end())
1063 content_t CNodeDefManager::getId(const std::string &name) const
1065 content_t id = CONTENT_IGNORE;
1071 bool CNodeDefManager::getIds(const std::string &name,
1072 std::set<content_t> &result) const
1074 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1075 if (name.substr(0,6) != "group:") {
1076 content_t id = CONTENT_IGNORE;
1077 bool exists = getId(name, id);
1082 std::string group = name.substr(6);
1084 UNORDERED_MAP<std::string, GroupItems>::const_iterator
1085 i = m_group_to_items.find(group);
1086 if (i == m_group_to_items.end())
1089 const GroupItems &items = i->second;
1090 for (GroupItems::const_iterator j = items.begin();
1091 j != items.end(); ++j) {
1092 if ((*j).second != 0)
1093 result.insert((*j).first);
1095 //printf("getIds: %dus\n", t.stop());
1100 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1102 content_t id = CONTENT_UNKNOWN;
1108 // returns CONTENT_IGNORE if no free ID found
1109 content_t CNodeDefManager::allocateId()
1111 for (content_t id = m_next_id;
1112 id >= m_next_id; // overflow?
1114 while (id >= m_content_features.size()) {
1115 m_content_features.push_back(ContentFeatures());
1117 const ContentFeatures &f = m_content_features[id];
1123 // If we arrive here, an overflow occurred in id.
1124 // That means no ID was found
1125 return CONTENT_IGNORE;
1130 * Returns the smallest box that contains all boxes
1131 * in the vector. Box_union is expanded.
1132 * @param[in] boxes the vector containing the boxes
1133 * @param[in, out] box_union the union of the arguments
1135 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1137 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1138 it != boxes.end(); ++it) {
1139 box_union->addInternalBox(*it);
1145 * Returns a box that contains the nodebox in every case.
1146 * The argument node_union is expanded.
1147 * @param[in] nodebox the nodebox to be measured
1148 * @param[in] features used to decide whether the nodebox
1150 * @param[in, out] box_union the union of the arguments
1152 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1155 switch(nodebox.type) {
1157 case NODEBOX_LEVELED: {
1159 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1160 boxVectorUnion(nodebox.fixed, &half_processed);
1161 // Set leveled boxes to maximal
1162 if (nodebox.type == NODEBOX_LEVELED) {
1163 half_processed.MaxEdge.Y = +BS / 2;
1165 if (features.param_type_2 == CPT2_FACEDIR ||
1166 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1167 // Get maximal coordinate
1169 fabsf(half_processed.MinEdge.X),
1170 fabsf(half_processed.MinEdge.Y),
1171 fabsf(half_processed.MinEdge.Z),
1172 fabsf(half_processed.MaxEdge.X),
1173 fabsf(half_processed.MaxEdge.Y),
1174 fabsf(half_processed.MaxEdge.Z) };
1176 for (int i = 0; i < 6; i++) {
1177 if (max < coords[i]) {
1181 // Add the union of all possible rotated boxes
1182 box_union->addInternalPoint(-max, -max, -max);
1183 box_union->addInternalPoint(+max, +max, +max);
1185 box_union->addInternalBox(half_processed);
1189 case NODEBOX_WALLMOUNTED: {
1191 box_union->addInternalBox(nodebox.wall_top);
1192 box_union->addInternalBox(nodebox.wall_bottom);
1193 // Find maximal coordinate in the X-Z plane
1195 fabsf(nodebox.wall_side.MinEdge.X),
1196 fabsf(nodebox.wall_side.MinEdge.Z),
1197 fabsf(nodebox.wall_side.MaxEdge.X),
1198 fabsf(nodebox.wall_side.MaxEdge.Z) };
1200 for (int i = 0; i < 4; i++) {
1201 if (max < coords[i]) {
1205 // Add the union of all possible rotated boxes
1206 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1207 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1210 case NODEBOX_CONNECTED: {
1211 // Add all possible connected boxes
1212 boxVectorUnion(nodebox.fixed, box_union);
1213 boxVectorUnion(nodebox.connect_top, box_union);
1214 boxVectorUnion(nodebox.connect_bottom, box_union);
1215 boxVectorUnion(nodebox.connect_front, box_union);
1216 boxVectorUnion(nodebox.connect_left, box_union);
1217 boxVectorUnion(nodebox.connect_back, box_union);
1218 boxVectorUnion(nodebox.connect_right, box_union);
1223 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1224 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1230 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1232 m_selection_box_int_union.MinEdge.X = floorf(
1233 m_selection_box_union.MinEdge.X / BS + 0.5f);
1234 m_selection_box_int_union.MinEdge.Y = floorf(
1235 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1236 m_selection_box_int_union.MinEdge.Z = floorf(
1237 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1238 m_selection_box_int_union.MaxEdge.X = ceilf(
1239 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1240 m_selection_box_int_union.MaxEdge.Y = ceilf(
1241 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1242 m_selection_box_int_union.MaxEdge.Z = ceilf(
1243 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1247 // IWritableNodeDefManager
1248 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1252 assert(name == def.name);
1254 // Don't allow redefining ignore (but allow air and unknown)
1255 if (name == "ignore") {
1256 warningstream << "NodeDefManager: Ignoring "
1257 "CONTENT_IGNORE redefinition"<<std::endl;
1258 return CONTENT_IGNORE;
1261 content_t id = CONTENT_IGNORE;
1262 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1265 if (id == CONTENT_IGNORE) {
1266 warningstream << "NodeDefManager: Absolute "
1267 "limit reached" << std::endl;
1268 return CONTENT_IGNORE;
1270 assert(id != CONTENT_IGNORE);
1271 addNameIdMapping(id, name);
1273 m_content_features[id] = def;
1274 verbosestream << "NodeDefManager: registering content id \"" << id
1275 << "\": name=\"" << def.name << "\""<<std::endl;
1277 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1278 fixSelectionBoxIntUnion();
1279 // Add this content to the list of all groups it belongs to
1280 // FIXME: This should remove a node from groups it no longer
1281 // belongs to when a node is re-registered
1282 for (ItemGroupList::const_iterator i = def.groups.begin();
1283 i != def.groups.end(); ++i) {
1284 std::string group_name = i->first;
1286 UNORDERED_MAP<std::string, GroupItems>::iterator
1287 j = m_group_to_items.find(group_name);
1288 if (j == m_group_to_items.end()) {
1289 m_group_to_items[group_name].push_back(
1290 std::make_pair(id, i->second));
1292 GroupItems &items = j->second;
1293 items.push_back(std::make_pair(id, i->second));
1300 content_t CNodeDefManager::allocateDummy(const std::string &name)
1302 assert(name != ""); // Pre-condition
1305 return set(name, f);
1309 void CNodeDefManager::removeNode(const std::string &name)
1314 // Erase name from name ID mapping
1315 content_t id = CONTENT_IGNORE;
1316 if (m_name_id_mapping.getId(name, id)) {
1317 m_name_id_mapping.eraseName(name);
1318 m_name_id_mapping_with_aliases.erase(name);
1321 // Erase node content from all groups it belongs to
1322 for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1323 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1324 GroupItems &items = iter_groups->second;
1325 for (GroupItems::iterator iter_groupitems = items.begin();
1326 iter_groupitems != items.end();) {
1327 if (iter_groupitems->first == id)
1328 items.erase(iter_groupitems++);
1333 // Check if group is empty
1334 if (items.size() == 0)
1335 m_group_to_items.erase(iter_groups++);
1342 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1344 std::set<std::string> all;
1346 m_name_id_mapping_with_aliases.clear();
1347 for (std::set<std::string>::const_iterator
1348 i = all.begin(); i != all.end(); ++i) {
1349 const std::string &name = *i;
1350 const std::string &convert_to = idef->getAlias(name);
1352 if (m_name_id_mapping.getId(convert_to, id)) {
1353 m_name_id_mapping_with_aliases.insert(
1354 std::make_pair(name, id));
1359 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1361 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1362 "overrides to textures from " << override_filepath << std::endl;
1364 std::ifstream infile(override_filepath.c_str());
1367 while (std::getline(infile, line)) {
1369 if (trim(line) == "")
1371 std::vector<std::string> splitted = str_split(line, ' ');
1372 if (splitted.size() != 3) {
1373 errorstream << override_filepath
1374 << ":" << line_c << " Could not apply texture override \""
1375 << line << "\": Syntax error" << std::endl;
1380 if (!getId(splitted[0], id))
1381 continue; // Ignore unknown node
1383 ContentFeatures &nodedef = m_content_features[id];
1385 if (splitted[1] == "top")
1386 nodedef.tiledef[0].name = splitted[2];
1387 else if (splitted[1] == "bottom")
1388 nodedef.tiledef[1].name = splitted[2];
1389 else if (splitted[1] == "right")
1390 nodedef.tiledef[2].name = splitted[2];
1391 else if (splitted[1] == "left")
1392 nodedef.tiledef[3].name = splitted[2];
1393 else if (splitted[1] == "back")
1394 nodedef.tiledef[4].name = splitted[2];
1395 else if (splitted[1] == "front")
1396 nodedef.tiledef[5].name = splitted[2];
1397 else if (splitted[1] == "all" || splitted[1] == "*")
1398 for (int i = 0; i < 6; i++)
1399 nodedef.tiledef[i].name = splitted[2];
1400 else if (splitted[1] == "sides")
1401 for (int i = 2; i < 6; i++)
1402 nodedef.tiledef[i].name = splitted[2];
1404 errorstream << override_filepath
1405 << ":" << line_c << " Could not apply texture override \""
1406 << line << "\": Unknown node side \""
1407 << splitted[1] << "\"" << std::endl;
1413 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1414 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1415 void *progress_callback_args)
1418 infostream << "CNodeDefManager::updateTextures(): Updating "
1419 "textures in node definitions" << std::endl;
1421 Client *client = (Client *)gamedef;
1422 ITextureSource *tsrc = client->tsrc();
1423 IShaderSource *shdsrc = client->getShaderSource();
1424 scene::ISceneManager* smgr = client->getSceneManager();
1425 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1426 TextureSettings tsettings;
1427 tsettings.readSettings();
1429 u32 size = m_content_features.size();
1431 for (u32 i = 0; i < size; i++) {
1432 ContentFeatures *f = &(m_content_features[i]);
1433 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1434 progress_callback(progress_callback_args, i, size);
1439 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1441 writeU8(os, 1); // version
1443 std::ostringstream os2(std::ios::binary);
1444 for (u32 i = 0; i < m_content_features.size(); i++) {
1445 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1446 || i == CONTENT_UNKNOWN)
1448 const ContentFeatures *f = &m_content_features[i];
1452 // Wrap it in a string to allow different lengths without
1453 // strict version incompatibilities
1454 std::ostringstream wrapper_os(std::ios::binary);
1455 f->serialize(wrapper_os, protocol_version);
1456 os2<<serializeString(wrapper_os.str());
1458 // must not overflow
1459 u16 next = count + 1;
1460 FATAL_ERROR_IF(next < count, "Overflow");
1463 writeU16(os, count);
1464 os << serializeLongString(os2.str());
1468 void CNodeDefManager::deSerialize(std::istream &is)
1471 int version = readU8(is);
1473 throw SerializationError("unsupported NodeDefinitionManager version");
1474 u16 count = readU16(is);
1475 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1477 for (u16 n = 0; n < count; n++) {
1478 u16 i = readU16(is2);
1480 // Read it from the string wrapper
1481 std::string wrapper = deSerializeString(is2);
1482 std::istringstream wrapper_is(wrapper, std::ios::binary);
1483 f.deSerialize(wrapper_is);
1485 // Check error conditions
1486 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1487 warningstream << "NodeDefManager::deSerialize(): "
1488 "not changing builtin node " << i << std::endl;
1492 warningstream << "NodeDefManager::deSerialize(): "
1493 "received empty name" << std::endl;
1499 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1500 warningstream << "NodeDefManager::deSerialize(): "
1501 "already defined with different ID: " << f.name << std::endl;
1505 // All is ok, add node definition with the requested ID
1506 if (i >= m_content_features.size())
1507 m_content_features.resize((u32)(i) + 1);
1508 m_content_features[i] = f;
1509 addNameIdMapping(i, f.name);
1510 verbosestream << "deserialized " << f.name << std::endl;
1512 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1513 fixSelectionBoxIntUnion();
1518 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1520 m_name_id_mapping.set(i, name);
1521 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1525 IWritableNodeDefManager *createNodeDefManager()
1527 return new CNodeDefManager();
1531 //// Serialization of old ContentFeatures formats
1532 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1534 u8 compatible_param_type_2 = param_type_2;
1535 if ((protocol_version < 28)
1536 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1537 compatible_param_type_2 = CPT2_NONE;
1538 else if (protocol_version < 30) {
1539 if (compatible_param_type_2 == CPT2_COLOR)
1540 compatible_param_type_2 = CPT2_NONE;
1541 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1542 compatible_param_type_2 = CPT2_FACEDIR;
1543 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1544 compatible_param_type_2 = CPT2_WALLMOUNTED;
1547 float compatible_visual_scale = visual_scale;
1548 if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1549 compatible_visual_scale = sqrt(visual_scale);
1551 TileDef compatible_tiles[6];
1552 for (u8 i = 0; i < 6; i++) {
1553 compatible_tiles[i] = tiledef[i];
1554 if (tiledef_overlay[i].name != "") {
1555 std::stringstream s;
1556 s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name
1558 compatible_tiles[i].name = s.str();
1563 if (protocol_version < 31) {
1564 writeU8(os, protocol_version < 27 ? 7 : 8);
1566 os << serializeString(name);
1567 writeU16(os, groups.size());
1568 for (ItemGroupList::const_iterator i = groups.begin();
1569 i != groups.end(); ++i) {
1570 os << serializeString(i->first);
1571 writeS16(os, i->second);
1573 writeU8(os, drawtype);
1574 writeF1000(os, compatible_visual_scale);
1576 for (u32 i = 0; i < 6; i++)
1577 compatible_tiles[i].serialize(os, protocol_version);
1578 writeU8(os, CF_SPECIAL_COUNT);
1579 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1580 tiledef_special[i].serialize(os, protocol_version);
1582 writeU8(os, post_effect_color.getAlpha());
1583 writeU8(os, post_effect_color.getRed());
1584 writeU8(os, post_effect_color.getGreen());
1585 writeU8(os, post_effect_color.getBlue());
1586 writeU8(os, param_type);
1587 writeU8(os, compatible_param_type_2);
1588 writeU8(os, is_ground_content);
1589 writeU8(os, light_propagates);
1590 writeU8(os, sunlight_propagates);
1591 writeU8(os, walkable);
1592 writeU8(os, pointable);
1593 writeU8(os, diggable);
1594 writeU8(os, climbable);
1595 writeU8(os, buildable_to);
1596 os << serializeString(""); // legacy: used to be metadata_name
1597 writeU8(os, liquid_type);
1598 os << serializeString(liquid_alternative_flowing);
1599 os << serializeString(liquid_alternative_source);
1600 writeU8(os, liquid_viscosity);
1601 writeU8(os, liquid_renewable);
1602 writeU8(os, light_source);
1603 writeU32(os, damage_per_second);
1604 node_box.serialize(os, protocol_version);
1605 selection_box.serialize(os, protocol_version);
1606 writeU8(os, legacy_facedir_simple);
1607 writeU8(os, legacy_wallmounted);
1608 serializeSimpleSoundSpec(sound_footstep, os);
1609 serializeSimpleSoundSpec(sound_dig, os);
1610 serializeSimpleSoundSpec(sound_dug, os);
1611 writeU8(os, rightclickable);
1612 writeU8(os, drowning);
1613 writeU8(os, leveled);
1614 writeU8(os, liquid_range);
1615 writeU8(os, waving);
1616 os << serializeString(mesh);
1617 collision_box.serialize(os, protocol_version);
1618 writeU8(os, floodable);
1619 writeU16(os, connects_to_ids.size());
1620 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1621 i != connects_to_ids.end(); ++i)
1623 writeU8(os, connect_sides);
1625 throw SerializationError("ContentFeatures::serialize(): "
1626 "Unsupported version requested");
1630 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1632 if (version == 5) // In PROTOCOL_VERSION 13
1634 name = deSerializeString(is);
1636 u32 groups_size = readU16(is);
1637 for(u32 i=0; i<groups_size; i++){
1638 std::string name = deSerializeString(is);
1639 int value = readS16(is);
1640 groups[name] = value;
1642 drawtype = (enum NodeDrawType)readU8(is);
1644 visual_scale = readF1000(is);
1645 if (readU8(is) != 6)
1646 throw SerializationError("unsupported tile count");
1647 for (u32 i = 0; i < 6; i++)
1648 tiledef[i].deSerialize(is, version, drawtype);
1649 if (readU8(is) != CF_SPECIAL_COUNT)
1650 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1651 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1652 tiledef_special[i].deSerialize(is, version, drawtype);
1654 post_effect_color.setAlpha(readU8(is));
1655 post_effect_color.setRed(readU8(is));
1656 post_effect_color.setGreen(readU8(is));
1657 post_effect_color.setBlue(readU8(is));
1658 param_type = (enum ContentParamType)readU8(is);
1659 param_type_2 = (enum ContentParamType2)readU8(is);
1660 is_ground_content = readU8(is);
1661 light_propagates = readU8(is);
1662 sunlight_propagates = readU8(is);
1663 walkable = readU8(is);
1664 pointable = readU8(is);
1665 diggable = readU8(is);
1666 climbable = readU8(is);
1667 buildable_to = readU8(is);
1668 deSerializeString(is); // legacy: used to be metadata_name
1669 liquid_type = (enum LiquidType)readU8(is);
1670 liquid_alternative_flowing = deSerializeString(is);
1671 liquid_alternative_source = deSerializeString(is);
1672 liquid_viscosity = readU8(is);
1673 light_source = readU8(is);
1674 light_source = MYMIN(light_source, LIGHT_MAX);
1675 damage_per_second = readU32(is);
1676 node_box.deSerialize(is);
1677 selection_box.deSerialize(is);
1678 legacy_facedir_simple = readU8(is);
1679 legacy_wallmounted = readU8(is);
1680 deSerializeSimpleSoundSpec(sound_footstep, is);
1681 deSerializeSimpleSoundSpec(sound_dig, is);
1682 deSerializeSimpleSoundSpec(sound_dug, is);
1683 } else if (version == 6) {
1684 name = deSerializeString(is);
1686 u32 groups_size = readU16(is);
1687 for (u32 i = 0; i < groups_size; i++) {
1688 std::string name = deSerializeString(is);
1689 int value = readS16(is);
1690 groups[name] = value;
1692 drawtype = (enum NodeDrawType)readU8(is);
1693 visual_scale = readF1000(is);
1694 if (readU8(is) != 6)
1695 throw SerializationError("unsupported tile count");
1696 for (u32 i = 0; i < 6; i++)
1697 tiledef[i].deSerialize(is, version, drawtype);
1698 // CF_SPECIAL_COUNT in version 6 = 2
1699 if (readU8(is) != 2)
1700 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1701 for (u32 i = 0; i < 2; i++)
1702 tiledef_special[i].deSerialize(is, version, drawtype);
1704 post_effect_color.setAlpha(readU8(is));
1705 post_effect_color.setRed(readU8(is));
1706 post_effect_color.setGreen(readU8(is));
1707 post_effect_color.setBlue(readU8(is));
1708 param_type = (enum ContentParamType)readU8(is);
1709 param_type_2 = (enum ContentParamType2)readU8(is);
1710 is_ground_content = readU8(is);
1711 light_propagates = readU8(is);
1712 sunlight_propagates = readU8(is);
1713 walkable = readU8(is);
1714 pointable = readU8(is);
1715 diggable = readU8(is);
1716 climbable = readU8(is);
1717 buildable_to = readU8(is);
1718 deSerializeString(is); // legacy: used to be metadata_name
1719 liquid_type = (enum LiquidType)readU8(is);
1720 liquid_alternative_flowing = deSerializeString(is);
1721 liquid_alternative_source = deSerializeString(is);
1722 liquid_viscosity = readU8(is);
1723 liquid_renewable = readU8(is);
1724 light_source = readU8(is);
1725 damage_per_second = readU32(is);
1726 node_box.deSerialize(is);
1727 selection_box.deSerialize(is);
1728 legacy_facedir_simple = readU8(is);
1729 legacy_wallmounted = readU8(is);
1730 deSerializeSimpleSoundSpec(sound_footstep, is);
1731 deSerializeSimpleSoundSpec(sound_dig, is);
1732 deSerializeSimpleSoundSpec(sound_dug, is);
1733 rightclickable = readU8(is);
1734 drowning = readU8(is);
1735 leveled = readU8(is);
1736 liquid_range = readU8(is);
1737 } else if (version == 7 || version == 8){
1738 name = deSerializeString(is);
1740 u32 groups_size = readU16(is);
1741 for (u32 i = 0; i < groups_size; i++) {
1742 std::string name = deSerializeString(is);
1743 int value = readS16(is);
1744 groups[name] = value;
1746 drawtype = (enum NodeDrawType) readU8(is);
1748 visual_scale = readF1000(is);
1749 if (readU8(is) != 6)
1750 throw SerializationError("unsupported tile count");
1751 for (u32 i = 0; i < 6; i++)
1752 tiledef[i].deSerialize(is, version, drawtype);
1753 if (readU8(is) != CF_SPECIAL_COUNT)
1754 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1755 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1756 tiledef_special[i].deSerialize(is, version, drawtype);
1758 post_effect_color.setAlpha(readU8(is));
1759 post_effect_color.setRed(readU8(is));
1760 post_effect_color.setGreen(readU8(is));
1761 post_effect_color.setBlue(readU8(is));
1762 param_type = (enum ContentParamType) readU8(is);
1763 param_type_2 = (enum ContentParamType2) readU8(is);
1764 is_ground_content = readU8(is);
1765 light_propagates = readU8(is);
1766 sunlight_propagates = readU8(is);
1767 walkable = readU8(is);
1768 pointable = readU8(is);
1769 diggable = readU8(is);
1770 climbable = readU8(is);
1771 buildable_to = readU8(is);
1772 deSerializeString(is); // legacy: used to be metadata_name
1773 liquid_type = (enum LiquidType) readU8(is);
1774 liquid_alternative_flowing = deSerializeString(is);
1775 liquid_alternative_source = deSerializeString(is);
1776 liquid_viscosity = readU8(is);
1777 liquid_renewable = readU8(is);
1778 light_source = readU8(is);
1779 light_source = MYMIN(light_source, LIGHT_MAX);
1780 damage_per_second = readU32(is);
1781 node_box.deSerialize(is);
1782 selection_box.deSerialize(is);
1783 legacy_facedir_simple = readU8(is);
1784 legacy_wallmounted = readU8(is);
1785 deSerializeSimpleSoundSpec(sound_footstep, is);
1786 deSerializeSimpleSoundSpec(sound_dig, is);
1787 deSerializeSimpleSoundSpec(sound_dug, is);
1788 rightclickable = readU8(is);
1789 drowning = readU8(is);
1790 leveled = readU8(is);
1791 liquid_range = readU8(is);
1792 waving = readU8(is);
1794 mesh = deSerializeString(is);
1795 collision_box.deSerialize(is);
1796 floodable = readU8(is);
1797 u16 connects_to_size = readU16(is);
1798 connects_to_ids.clear();
1799 for (u16 i = 0; i < connects_to_size; i++)
1800 connects_to_ids.insert(readU16(is));
1801 connect_sides = readU8(is);
1802 } catch (SerializationError &e) {};
1804 throw SerializationError("unsupported ContentFeatures version");
1809 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1811 return m_node_registration_complete;
1815 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1817 m_node_registration_complete = completed;
1821 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1824 if (m_node_registration_complete)
1825 nr->nodeResolveInternal();
1827 m_pending_resolve_callbacks.push_back(nr);
1831 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1833 size_t len = m_pending_resolve_callbacks.size();
1834 for (size_t i = 0; i != len; i++) {
1835 if (nr != m_pending_resolve_callbacks[i])
1839 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1840 m_pending_resolve_callbacks.resize(len);
1848 void CNodeDefManager::runNodeResolveCallbacks()
1850 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1851 NodeResolver *nr = m_pending_resolve_callbacks[i];
1852 nr->nodeResolveInternal();
1855 m_pending_resolve_callbacks.clear();
1859 void CNodeDefManager::resetNodeResolveState()
1861 m_node_registration_complete = false;
1862 m_pending_resolve_callbacks.clear();
1865 void CNodeDefManager::mapNodeboxConnections()
1867 for (u32 i = 0; i < m_content_features.size(); i++) {
1868 ContentFeatures *f = &m_content_features[i];
1869 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1871 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1872 it != f->connects_to.end(); ++it) {
1873 getIds(*it, f->connects_to_ids);
1878 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1880 const ContentFeatures &f1 = get(from);
1882 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1885 // lookup target in connected set
1886 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1889 const ContentFeatures &f2 = get(to);
1891 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1892 // ignores actually looking if back connection exists
1893 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1895 // does to node declare usable faces?
1896 if (f2.connect_sides > 0) {
1897 if ((f2.param_type_2 == CPT2_FACEDIR ||
1898 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1899 && (connect_face >= 4)) {
1900 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1901 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1903 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1905 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1906 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1910 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1912 return (f2.connect_sides
1913 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1915 return (f2.connect_sides & connect_face);
1917 // the target is just a regular node, so connect no matter back connection
1925 NodeResolver::NodeResolver()
1928 m_nodenames_idx = 0;
1929 m_nnlistsizes_idx = 0;
1930 m_resolve_done = false;
1932 m_nodenames.reserve(16);
1933 m_nnlistsizes.reserve(4);
1937 NodeResolver::~NodeResolver()
1939 if (!m_resolve_done && m_ndef)
1940 m_ndef->cancelNodeResolveCallback(this);
1944 void NodeResolver::nodeResolveInternal()
1946 m_nodenames_idx = 0;
1947 m_nnlistsizes_idx = 0;
1950 m_resolve_done = true;
1952 m_nodenames.clear();
1953 m_nnlistsizes.clear();
1957 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1958 const std::string &node_alt, content_t c_fallback)
1960 if (m_nodenames_idx == m_nodenames.size()) {
1961 *result_out = c_fallback;
1962 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1967 std::string name = m_nodenames[m_nodenames_idx++];
1969 bool success = m_ndef->getId(name, c);
1970 if (!success && node_alt != "") {
1972 success = m_ndef->getId(name, c);
1976 errorstream << "NodeResolver: failed to resolve node name '" << name
1977 << "'." << std::endl;
1986 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1987 bool all_required, content_t c_fallback)
1989 bool success = true;
1991 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1992 errorstream << "NodeResolver: no more node lists" << std::endl;
1996 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1999 if (m_nodenames_idx == m_nodenames.size()) {
2000 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2005 std::string &name = m_nodenames[m_nodenames_idx++];
2007 if (name.substr(0,6) != "group:") {
2008 if (m_ndef->getId(name, c)) {
2009 result_out->push_back(c);
2010 } else if (all_required) {
2011 errorstream << "NodeResolver: failed to resolve node name '"
2012 << name << "'." << std::endl;
2013 result_out->push_back(c_fallback);
2017 std::set<content_t> cids;
2018 std::set<content_t>::iterator it;
2019 m_ndef->getIds(name, cids);
2020 for (it = cids.begin(); it != cids.end(); ++it)
2021 result_out->push_back(*it);