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 void setNodeRegistrationStatus(bool completed);
886 virtual void pendNodeResolve(NodeResolver *nr);
887 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
888 virtual void runNodeResolveCallbacks();
889 virtual void resetNodeResolveState();
890 virtual void mapNodeboxConnections();
891 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
892 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
894 return m_selection_box_int_union;
898 void addNameIdMapping(content_t i, std::string name);
900 * Recalculates m_selection_box_int_union based on
901 * m_selection_box_union.
903 void fixSelectionBoxIntUnion();
905 // Features indexed by id
906 std::vector<ContentFeatures> m_content_features;
908 // A mapping for fast converting back and forth between names and ids
909 NameIdMapping m_name_id_mapping;
911 // Like m_name_id_mapping, but only from names to ids, and includes
912 // item aliases too. Updated by updateAliases()
913 // Note: Not serialized.
915 UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
917 // A mapping from groups to a list of content_ts (and their levels)
918 // that belong to it. Necessary for a direct lookup in getIds().
919 // Note: Not serialized.
920 UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
922 // Next possibly free id
925 // NodeResolvers to callback once node registration has ended
926 std::vector<NodeResolver *> m_pending_resolve_callbacks;
928 // True when all nodes have been registered
929 bool m_node_registration_complete;
931 //! The union of all nodes' selection boxes.
932 aabb3f m_selection_box_union;
934 * The smallest box in node coordinates that
935 * contains all nodes' selection boxes.
937 core::aabbox3d<s16> m_selection_box_int_union;
941 CNodeDefManager::CNodeDefManager()
947 CNodeDefManager::~CNodeDefManager()
950 for (u32 i = 0; i < m_content_features.size(); i++) {
951 ContentFeatures *f = &m_content_features[i];
952 for (u32 j = 0; j < 24; j++) {
954 f->mesh_ptr[j]->drop();
961 void CNodeDefManager::clear()
963 m_content_features.clear();
964 m_name_id_mapping.clear();
965 m_name_id_mapping_with_aliases.clear();
966 m_group_to_items.clear();
968 m_selection_box_union.reset(0,0,0);
969 m_selection_box_int_union.reset(0,0,0);
971 resetNodeResolveState();
973 u32 initial_length = 0;
974 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
975 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
976 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
977 m_content_features.resize(initial_length);
979 // Set CONTENT_UNKNOWN
983 // Insert directly into containers
984 content_t c = CONTENT_UNKNOWN;
985 m_content_features[c] = f;
986 addNameIdMapping(c, f.name);
993 f.drawtype = NDT_AIRLIKE;
994 f.param_type = CPT_LIGHT;
995 f.light_propagates = true;
996 f.sunlight_propagates = true;
1000 f.buildable_to = true;
1002 f.is_ground_content = true;
1003 // Insert directly into containers
1004 content_t c = CONTENT_AIR;
1005 m_content_features[c] = f;
1006 addNameIdMapping(c, f.name);
1009 // Set CONTENT_IGNORE
1013 f.drawtype = NDT_AIRLIKE;
1014 f.param_type = CPT_NONE;
1015 f.light_propagates = false;
1016 f.sunlight_propagates = false;
1018 f.pointable = false;
1020 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1021 f.is_ground_content = true;
1022 // Insert directly into containers
1023 content_t c = CONTENT_IGNORE;
1024 m_content_features[c] = f;
1025 addNameIdMapping(c, f.name);
1030 IWritableNodeDefManager *CNodeDefManager::clone()
1032 CNodeDefManager *mgr = new CNodeDefManager();
1038 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1040 return c < m_content_features.size()
1041 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1045 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1047 return get(n.getContent());
1051 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1053 UNORDERED_MAP<std::string, content_t>::const_iterator
1054 i = m_name_id_mapping_with_aliases.find(name);
1055 if(i == m_name_id_mapping_with_aliases.end())
1062 content_t CNodeDefManager::getId(const std::string &name) const
1064 content_t id = CONTENT_IGNORE;
1070 bool CNodeDefManager::getIds(const std::string &name,
1071 std::set<content_t> &result) const
1073 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1074 if (name.substr(0,6) != "group:") {
1075 content_t id = CONTENT_IGNORE;
1076 bool exists = getId(name, id);
1081 std::string group = name.substr(6);
1083 UNORDERED_MAP<std::string, GroupItems>::const_iterator
1084 i = m_group_to_items.find(group);
1085 if (i == m_group_to_items.end())
1088 const GroupItems &items = i->second;
1089 for (GroupItems::const_iterator j = items.begin();
1090 j != items.end(); ++j) {
1091 if ((*j).second != 0)
1092 result.insert((*j).first);
1094 //printf("getIds: %dus\n", t.stop());
1099 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1101 content_t id = CONTENT_UNKNOWN;
1107 // returns CONTENT_IGNORE if no free ID found
1108 content_t CNodeDefManager::allocateId()
1110 for (content_t id = m_next_id;
1111 id >= m_next_id; // overflow?
1113 while (id >= m_content_features.size()) {
1114 m_content_features.push_back(ContentFeatures());
1116 const ContentFeatures &f = m_content_features[id];
1122 // If we arrive here, an overflow occurred in id.
1123 // That means no ID was found
1124 return CONTENT_IGNORE;
1129 * Returns the smallest box that contains all boxes
1130 * in the vector. Box_union is expanded.
1131 * @param[in] boxes the vector containing the boxes
1132 * @param[in, out] box_union the union of the arguments
1134 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1136 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1137 it != boxes.end(); ++it) {
1138 box_union->addInternalBox(*it);
1144 * Returns a box that contains the nodebox in every case.
1145 * The argument node_union is expanded.
1146 * @param[in] nodebox the nodebox to be measured
1147 * @param[in] features used to decide whether the nodebox
1149 * @param[in, out] box_union the union of the arguments
1151 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1154 switch(nodebox.type) {
1156 case NODEBOX_LEVELED: {
1158 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1159 boxVectorUnion(nodebox.fixed, &half_processed);
1160 // Set leveled boxes to maximal
1161 if (nodebox.type == NODEBOX_LEVELED) {
1162 half_processed.MaxEdge.Y = +BS / 2;
1164 if (features.param_type_2 == CPT2_FACEDIR ||
1165 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1166 // Get maximal coordinate
1168 fabsf(half_processed.MinEdge.X),
1169 fabsf(half_processed.MinEdge.Y),
1170 fabsf(half_processed.MinEdge.Z),
1171 fabsf(half_processed.MaxEdge.X),
1172 fabsf(half_processed.MaxEdge.Y),
1173 fabsf(half_processed.MaxEdge.Z) };
1175 for (int i = 0; i < 6; i++) {
1176 if (max < coords[i]) {
1180 // Add the union of all possible rotated boxes
1181 box_union->addInternalPoint(-max, -max, -max);
1182 box_union->addInternalPoint(+max, +max, +max);
1184 box_union->addInternalBox(half_processed);
1188 case NODEBOX_WALLMOUNTED: {
1190 box_union->addInternalBox(nodebox.wall_top);
1191 box_union->addInternalBox(nodebox.wall_bottom);
1192 // Find maximal coordinate in the X-Z plane
1194 fabsf(nodebox.wall_side.MinEdge.X),
1195 fabsf(nodebox.wall_side.MinEdge.Z),
1196 fabsf(nodebox.wall_side.MaxEdge.X),
1197 fabsf(nodebox.wall_side.MaxEdge.Z) };
1199 for (int i = 0; i < 4; i++) {
1200 if (max < coords[i]) {
1204 // Add the union of all possible rotated boxes
1205 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1206 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1209 case NODEBOX_CONNECTED: {
1210 // Add all possible connected boxes
1211 boxVectorUnion(nodebox.fixed, box_union);
1212 boxVectorUnion(nodebox.connect_top, box_union);
1213 boxVectorUnion(nodebox.connect_bottom, box_union);
1214 boxVectorUnion(nodebox.connect_front, box_union);
1215 boxVectorUnion(nodebox.connect_left, box_union);
1216 boxVectorUnion(nodebox.connect_back, box_union);
1217 boxVectorUnion(nodebox.connect_right, box_union);
1222 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1223 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1229 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1231 m_selection_box_int_union.MinEdge.X = floorf(
1232 m_selection_box_union.MinEdge.X / BS + 0.5f);
1233 m_selection_box_int_union.MinEdge.Y = floorf(
1234 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1235 m_selection_box_int_union.MinEdge.Z = floorf(
1236 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1237 m_selection_box_int_union.MaxEdge.X = ceilf(
1238 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1239 m_selection_box_int_union.MaxEdge.Y = ceilf(
1240 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1241 m_selection_box_int_union.MaxEdge.Z = ceilf(
1242 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1246 // IWritableNodeDefManager
1247 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1251 assert(name == def.name);
1253 // Don't allow redefining ignore (but allow air and unknown)
1254 if (name == "ignore") {
1255 warningstream << "NodeDefManager: Ignoring "
1256 "CONTENT_IGNORE redefinition"<<std::endl;
1257 return CONTENT_IGNORE;
1260 content_t id = CONTENT_IGNORE;
1261 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1264 if (id == CONTENT_IGNORE) {
1265 warningstream << "NodeDefManager: Absolute "
1266 "limit reached" << std::endl;
1267 return CONTENT_IGNORE;
1269 assert(id != CONTENT_IGNORE);
1270 addNameIdMapping(id, name);
1272 m_content_features[id] = def;
1273 verbosestream << "NodeDefManager: registering content id \"" << id
1274 << "\": name=\"" << def.name << "\""<<std::endl;
1276 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1277 fixSelectionBoxIntUnion();
1278 // Add this content to the list of all groups it belongs to
1279 // FIXME: This should remove a node from groups it no longer
1280 // belongs to when a node is re-registered
1281 for (ItemGroupList::const_iterator i = def.groups.begin();
1282 i != def.groups.end(); ++i) {
1283 std::string group_name = i->first;
1285 UNORDERED_MAP<std::string, GroupItems>::iterator
1286 j = m_group_to_items.find(group_name);
1287 if (j == m_group_to_items.end()) {
1288 m_group_to_items[group_name].push_back(
1289 std::make_pair(id, i->second));
1291 GroupItems &items = j->second;
1292 items.push_back(std::make_pair(id, i->second));
1299 content_t CNodeDefManager::allocateDummy(const std::string &name)
1301 assert(name != ""); // Pre-condition
1304 return set(name, f);
1308 void CNodeDefManager::removeNode(const std::string &name)
1313 // Erase name from name ID mapping
1314 content_t id = CONTENT_IGNORE;
1315 if (m_name_id_mapping.getId(name, id)) {
1316 m_name_id_mapping.eraseName(name);
1317 m_name_id_mapping_with_aliases.erase(name);
1320 // Erase node content from all groups it belongs to
1321 for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1322 m_group_to_items.begin(); 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");
1807 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1809 m_node_registration_complete = completed;
1813 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1816 if (m_node_registration_complete)
1817 nr->nodeResolveInternal();
1819 m_pending_resolve_callbacks.push_back(nr);
1823 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1825 size_t len = m_pending_resolve_callbacks.size();
1826 for (size_t i = 0; i != len; i++) {
1827 if (nr != m_pending_resolve_callbacks[i])
1831 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1832 m_pending_resolve_callbacks.resize(len);
1840 void CNodeDefManager::runNodeResolveCallbacks()
1842 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1843 NodeResolver *nr = m_pending_resolve_callbacks[i];
1844 nr->nodeResolveInternal();
1847 m_pending_resolve_callbacks.clear();
1851 void CNodeDefManager::resetNodeResolveState()
1853 m_node_registration_complete = false;
1854 m_pending_resolve_callbacks.clear();
1857 void CNodeDefManager::mapNodeboxConnections()
1859 for (u32 i = 0; i < m_content_features.size(); i++) {
1860 ContentFeatures *f = &m_content_features[i];
1861 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1863 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1864 it != f->connects_to.end(); ++it) {
1865 getIds(*it, f->connects_to_ids);
1870 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1872 const ContentFeatures &f1 = get(from);
1874 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1877 // lookup target in connected set
1878 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1881 const ContentFeatures &f2 = get(to);
1883 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1884 // ignores actually looking if back connection exists
1885 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1887 // does to node declare usable faces?
1888 if (f2.connect_sides > 0) {
1889 if ((f2.param_type_2 == CPT2_FACEDIR ||
1890 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1891 && (connect_face >= 4)) {
1892 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1893 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1895 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1897 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1898 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1900 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1901 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1902 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1904 return (f2.connect_sides
1905 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1907 return (f2.connect_sides & connect_face);
1909 // the target is just a regular node, so connect no matter back connection
1917 NodeResolver::NodeResolver()
1920 m_nodenames_idx = 0;
1921 m_nnlistsizes_idx = 0;
1922 m_resolve_done = false;
1924 m_nodenames.reserve(16);
1925 m_nnlistsizes.reserve(4);
1929 NodeResolver::~NodeResolver()
1931 if (!m_resolve_done && m_ndef)
1932 m_ndef->cancelNodeResolveCallback(this);
1936 void NodeResolver::nodeResolveInternal()
1938 m_nodenames_idx = 0;
1939 m_nnlistsizes_idx = 0;
1942 m_resolve_done = true;
1944 m_nodenames.clear();
1945 m_nnlistsizes.clear();
1949 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1950 const std::string &node_alt, content_t c_fallback)
1952 if (m_nodenames_idx == m_nodenames.size()) {
1953 *result_out = c_fallback;
1954 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1959 std::string name = m_nodenames[m_nodenames_idx++];
1961 bool success = m_ndef->getId(name, c);
1962 if (!success && node_alt != "") {
1964 success = m_ndef->getId(name, c);
1968 errorstream << "NodeResolver: failed to resolve node name '" << name
1969 << "'." << std::endl;
1978 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1979 bool all_required, content_t c_fallback)
1981 bool success = true;
1983 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1984 errorstream << "NodeResolver: no more node lists" << std::endl;
1988 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1991 if (m_nodenames_idx == m_nodenames.size()) {
1992 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1997 std::string &name = m_nodenames[m_nodenames_idx++];
1999 if (name.substr(0,6) != "group:") {
2000 if (m_ndef->getId(name, c)) {
2001 result_out->push_back(c);
2002 } else if (all_required) {
2003 errorstream << "NodeResolver: failed to resolve node name '"
2004 << name << "'." << std::endl;
2005 result_out->push_back(c_fallback);
2009 std::set<content_t> cids;
2010 std::set<content_t>::iterator it;
2011 m_ndef->getIds(name, cids);
2012 for (it = cids.begin(); it != cids.end(); ++it)
2013 result_out->push_back(*it);