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 < 30) {
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 writeU8(os, CF_SPECIAL_COUNT);
408 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
409 tiledef_special[i].serialize(os, protocol_version);
412 writeU8(os, color.getRed());
413 writeU8(os, color.getGreen());
414 writeU8(os, color.getBlue());
415 os << serializeString(palette_name);
417 writeU8(os, connect_sides);
418 writeU16(os, connects_to_ids.size());
419 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
420 i != connects_to_ids.end(); ++i)
422 writeU8(os, post_effect_color.getAlpha());
423 writeU8(os, post_effect_color.getRed());
424 writeU8(os, post_effect_color.getGreen());
425 writeU8(os, post_effect_color.getBlue());
426 writeU8(os, leveled);
429 writeU8(os, light_propagates);
430 writeU8(os, sunlight_propagates);
431 writeU8(os, light_source);
434 writeU8(os, is_ground_content);
437 writeU8(os, walkable);
438 writeU8(os, pointable);
439 writeU8(os, diggable);
440 writeU8(os, climbable);
441 writeU8(os, buildable_to);
442 writeU8(os, rightclickable);
443 writeU32(os, damage_per_second);
446 writeU8(os, liquid_type);
447 os << serializeString(liquid_alternative_flowing);
448 os << serializeString(liquid_alternative_source);
449 writeU8(os, liquid_viscosity);
450 writeU8(os, liquid_renewable);
451 writeU8(os, liquid_range);
452 writeU8(os, drowning);
453 writeU8(os, floodable);
456 node_box.serialize(os, protocol_version);
457 selection_box.serialize(os, protocol_version);
458 collision_box.serialize(os, protocol_version);
461 serializeSimpleSoundSpec(sound_footstep, os);
462 serializeSimpleSoundSpec(sound_dig, os);
463 serializeSimpleSoundSpec(sound_dug, os);
466 writeU8(os, legacy_facedir_simple);
467 writeU8(os, legacy_wallmounted);
470 void ContentFeatures::correctAlpha()
472 if (alpha == 0 || alpha == 255)
475 for (u32 i = 0; i < 6; i++) {
477 s << tiledef[i].name << "^[noalpha^[opacity:" << ((int)alpha);
478 tiledef[i].name = s.str();
481 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
483 s << tiledef_special[i].name << "^[noalpha^[opacity:" << ((int)alpha);
484 tiledef_special[i].name = s.str();
488 void ContentFeatures::deSerialize(std::istream &is)
491 int version = readU8(is);
493 deSerializeOld(is, version);
495 } else if (version > 9) {
496 throw SerializationError("unsupported ContentFeatures version");
500 name = deSerializeString(is);
502 u32 groups_size = readU16(is);
503 for (u32 i = 0; i < groups_size; i++) {
504 std::string name = deSerializeString(is);
505 int value = readS16(is);
506 groups[name] = value;
508 param_type = (enum ContentParamType) readU8(is);
509 param_type_2 = (enum ContentParamType2) readU8(is);
512 drawtype = (enum NodeDrawType) readU8(is);
513 mesh = deSerializeString(is);
514 visual_scale = readF1000(is);
516 throw SerializationError("unsupported tile count");
517 for (u32 i = 0; i < 6; i++)
518 tiledef[i].deSerialize(is, version, drawtype);
519 if (readU8(is) != CF_SPECIAL_COUNT)
520 throw SerializationError("unsupported CF_SPECIAL_COUNT");
521 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
522 tiledef_special[i].deSerialize(is, version, drawtype);
524 color.setRed(readU8(is));
525 color.setGreen(readU8(is));
526 color.setBlue(readU8(is));
527 palette_name = deSerializeString(is);
529 connect_sides = readU8(is);
530 u16 connects_to_size = readU16(is);
531 connects_to_ids.clear();
532 for (u16 i = 0; i < connects_to_size; i++)
533 connects_to_ids.insert(readU16(is));
534 post_effect_color.setAlpha(readU8(is));
535 post_effect_color.setRed(readU8(is));
536 post_effect_color.setGreen(readU8(is));
537 post_effect_color.setBlue(readU8(is));
538 leveled = readU8(is);
541 light_propagates = readU8(is);
542 sunlight_propagates = readU8(is);
543 light_source = readU8(is);
544 light_source = MYMIN(light_source, LIGHT_MAX);
547 is_ground_content = readU8(is);
550 walkable = readU8(is);
551 pointable = readU8(is);
552 diggable = readU8(is);
553 climbable = readU8(is);
554 buildable_to = readU8(is);
555 rightclickable = readU8(is);
556 damage_per_second = readU32(is);
559 liquid_type = (enum LiquidType) readU8(is);
560 liquid_alternative_flowing = deSerializeString(is);
561 liquid_alternative_source = deSerializeString(is);
562 liquid_viscosity = readU8(is);
563 liquid_renewable = readU8(is);
564 liquid_range = readU8(is);
565 drowning = readU8(is);
566 floodable = readU8(is);
569 node_box.deSerialize(is);
570 selection_box.deSerialize(is);
571 collision_box.deSerialize(is);
574 deSerializeSimpleSoundSpec(sound_footstep, is);
575 deSerializeSimpleSoundSpec(sound_dig, is);
576 deSerializeSimpleSoundSpec(sound_dug, is);
578 // read legacy properties
579 legacy_facedir_simple = readU8(is);
580 legacy_wallmounted = readU8(is);
584 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
585 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
586 bool backface_culling, u8 material_type)
588 tile->shader_id = shader_id;
589 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
590 tile->material_type = material_type;
592 // Normal texture and shader flags texture
593 if (use_normal_texture) {
594 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
596 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
599 tile->material_flags = 0;
600 if (backface_culling)
601 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
602 if (tiledef->animation.type != TAT_NONE)
603 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
604 if (tiledef->tileable_horizontal)
605 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
606 if (tiledef->tileable_vertical)
607 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
610 tile->has_color = tiledef->has_color;
611 if (tiledef->has_color)
612 tile->color = tiledef->color;
616 // Animation parameters
618 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
620 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
621 &frame_count, &frame_length_ms, NULL);
622 tile->animation_frame_count = frame_count;
623 tile->animation_frame_length_ms = frame_length_ms;
626 if (frame_count == 1) {
627 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
629 std::ostringstream os(std::ios::binary);
630 tile->frames.resize(frame_count);
632 for (int i = 0; i < frame_count; i++) {
638 tiledef->animation.getTextureModifer(os,
639 tile->texture->getOriginalSize(), i);
641 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
642 if (tile->normal_texture)
643 frame.normal_texture = tsrc->getNormalTexture(os.str());
644 frame.flags_texture = tile->flags_texture;
645 tile->frames[i] = frame;
652 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
653 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
655 // minimap pixel color - the average color of a texture
656 if (tsettings.enable_minimap && tiledef[0].name != "")
657 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
659 // Figure out the actual tiles to use
661 for (u32 j = 0; j < 6; j++) {
662 tdef[j] = tiledef[j];
663 if (tdef[j].name == "")
664 tdef[j].name = "unknown_node.png";
667 bool is_liquid = false;
668 bool is_water_surface = false;
670 u8 material_type = (alpha == 255) ?
671 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
682 assert(liquid_type == LIQUID_SOURCE);
683 if (tsettings.opaque_water)
688 case NDT_FLOWINGLIQUID:
689 assert(liquid_type == LIQUID_FLOWING);
691 if (tsettings.opaque_water)
697 visual_solidness = 1;
699 case NDT_GLASSLIKE_FRAMED:
701 visual_solidness = 1;
703 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
705 visual_solidness = 1;
706 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
710 visual_solidness = 1;
712 case NDT_ALLFACES_OPTIONAL:
713 if (tsettings.leaves_style == LEAVES_FANCY) {
714 drawtype = NDT_ALLFACES;
716 visual_solidness = 1;
717 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
718 for (u32 j = 0; j < 6; j++) {
719 if (tiledef_special[j].name != "")
720 tdef[j].name = tiledef_special[j].name;
722 drawtype = NDT_GLASSLIKE;
724 visual_solidness = 1;
726 drawtype = NDT_NORMAL;
728 for (u32 i = 0; i < 6; i++)
729 tdef[i].name += std::string("^[noalpha");
732 material_type = TILE_MATERIAL_WAVING_LEAVES;
737 material_type = TILE_MATERIAL_WAVING_PLANTS;
755 material_type = (alpha == 255) ?
756 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
757 if (name == "default:water_source")
758 is_water_surface = true;
761 // Vertex alpha is no longer supported, correct if necessary.
765 for (u16 j = 0; j < 6; j++) {
766 tile_shader[j] = shdsrc->getShader("nodes_shader",
767 material_type, drawtype);
770 if (is_water_surface) {
771 tile_shader[0] = shdsrc->getShader("water_surface_shader",
772 material_type, drawtype);
775 // Tiles (fill in f->tiles[])
776 for (u16 j = 0; j < 6; j++) {
777 fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j],
778 tsettings.use_normal_texture,
779 tiledef[j].backface_culling, material_type);
782 // Special tiles (fill in f->special_tiles[])
783 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
784 fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j],
785 tile_shader[j], tsettings.use_normal_texture,
786 tiledef_special[j].backface_culling, material_type);
789 if (param_type_2 == CPT2_COLOR ||
790 param_type_2 == CPT2_COLORED_FACEDIR ||
791 param_type_2 == CPT2_COLORED_WALLMOUNTED)
792 palette = tsrc->getPalette(palette_name);
794 if ((drawtype == NDT_MESH) && (mesh != "")) {
796 // Read the mesh and apply scale
797 mesh_ptr[0] = client->getMesh(mesh);
799 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
800 scaleMesh(mesh_ptr[0], scale);
801 recalculateBoundingBox(mesh_ptr[0]);
802 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
804 } else if ((drawtype == NDT_NODEBOX) &&
805 ((node_box.type == NODEBOX_REGULAR) ||
806 (node_box.type == NODEBOX_FIXED)) &&
807 (!node_box.fixed.empty())) {
808 //Convert regular nodebox nodes to meshnodes
809 //Change the drawtype and apply scale
811 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
812 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
813 scaleMesh(mesh_ptr[0], scale);
814 recalculateBoundingBox(mesh_ptr[0]);
815 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
818 //Cache 6dfacedir and wallmounted rotated clones of meshes
819 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
820 (param_type_2 == CPT2_FACEDIR
821 || param_type_2 == CPT2_COLORED_FACEDIR)) {
822 for (u16 j = 1; j < 24; j++) {
823 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
824 rotateMeshBy6dFacedir(mesh_ptr[j], j);
825 recalculateBoundingBox(mesh_ptr[j]);
826 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
828 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
829 && (param_type_2 == CPT2_WALLMOUNTED ||
830 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
831 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
832 for (u16 j = 1; j < 6; j++) {
833 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
834 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
835 recalculateBoundingBox(mesh_ptr[j]);
836 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
838 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
839 recalculateBoundingBox(mesh_ptr[0]);
840 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
849 class CNodeDefManager: public IWritableNodeDefManager {
852 virtual ~CNodeDefManager();
854 virtual IWritableNodeDefManager *clone();
855 inline virtual const ContentFeatures& get(content_t c) const;
856 inline virtual const ContentFeatures& get(const MapNode &n) const;
857 virtual bool getId(const std::string &name, content_t &result) const;
858 virtual content_t getId(const std::string &name) const;
859 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
860 virtual const ContentFeatures& get(const std::string &name) const;
861 content_t allocateId();
862 virtual content_t set(const std::string &name, const ContentFeatures &def);
863 virtual content_t allocateDummy(const std::string &name);
864 virtual void removeNode(const std::string &name);
865 virtual void updateAliases(IItemDefManager *idef);
866 virtual void applyTextureOverrides(const std::string &override_filepath);
867 virtual void updateTextures(IGameDef *gamedef,
868 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
869 void *progress_cbk_args);
870 void serialize(std::ostream &os, u16 protocol_version) const;
871 void deSerialize(std::istream &is);
873 inline virtual bool getNodeRegistrationStatus() const;
874 inline virtual void setNodeRegistrationStatus(bool completed);
876 virtual void pendNodeResolve(NodeResolver *nr);
877 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
878 virtual void runNodeResolveCallbacks();
879 virtual void resetNodeResolveState();
880 virtual void mapNodeboxConnections();
881 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
882 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
884 return m_selection_box_int_union;
888 void addNameIdMapping(content_t i, std::string name);
890 * Recalculates m_selection_box_int_union based on
891 * m_selection_box_union.
893 void fixSelectionBoxIntUnion();
895 // Features indexed by id
896 std::vector<ContentFeatures> m_content_features;
898 // A mapping for fast converting back and forth between names and ids
899 NameIdMapping m_name_id_mapping;
901 // Like m_name_id_mapping, but only from names to ids, and includes
902 // item aliases too. Updated by updateAliases()
903 // Note: Not serialized.
905 UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
907 // A mapping from groups to a list of content_ts (and their levels)
908 // that belong to it. Necessary for a direct lookup in getIds().
909 // Note: Not serialized.
910 UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
912 // Next possibly free id
915 // NodeResolvers to callback once node registration has ended
916 std::vector<NodeResolver *> m_pending_resolve_callbacks;
918 // True when all nodes have been registered
919 bool m_node_registration_complete;
921 //! The union of all nodes' selection boxes.
922 aabb3f m_selection_box_union;
924 * The smallest box in node coordinates that
925 * contains all nodes' selection boxes.
927 core::aabbox3d<s16> m_selection_box_int_union;
931 CNodeDefManager::CNodeDefManager()
937 CNodeDefManager::~CNodeDefManager()
940 for (u32 i = 0; i < m_content_features.size(); i++) {
941 ContentFeatures *f = &m_content_features[i];
942 for (u32 j = 0; j < 24; j++) {
944 f->mesh_ptr[j]->drop();
951 void CNodeDefManager::clear()
953 m_content_features.clear();
954 m_name_id_mapping.clear();
955 m_name_id_mapping_with_aliases.clear();
956 m_group_to_items.clear();
958 m_selection_box_union.reset(0,0,0);
959 m_selection_box_int_union.reset(0,0,0);
961 resetNodeResolveState();
963 u32 initial_length = 0;
964 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
965 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
966 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
967 m_content_features.resize(initial_length);
969 // Set CONTENT_UNKNOWN
973 // Insert directly into containers
974 content_t c = CONTENT_UNKNOWN;
975 m_content_features[c] = f;
976 addNameIdMapping(c, f.name);
983 f.drawtype = NDT_AIRLIKE;
984 f.param_type = CPT_LIGHT;
985 f.light_propagates = true;
986 f.sunlight_propagates = true;
990 f.buildable_to = true;
992 f.is_ground_content = true;
993 // Insert directly into containers
994 content_t c = CONTENT_AIR;
995 m_content_features[c] = f;
996 addNameIdMapping(c, f.name);
999 // Set CONTENT_IGNORE
1003 f.drawtype = NDT_AIRLIKE;
1004 f.param_type = CPT_NONE;
1005 f.light_propagates = false;
1006 f.sunlight_propagates = false;
1008 f.pointable = false;
1010 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1011 f.is_ground_content = true;
1012 // Insert directly into containers
1013 content_t c = CONTENT_IGNORE;
1014 m_content_features[c] = f;
1015 addNameIdMapping(c, f.name);
1020 IWritableNodeDefManager *CNodeDefManager::clone()
1022 CNodeDefManager *mgr = new CNodeDefManager();
1028 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1030 return c < m_content_features.size()
1031 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1035 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1037 return get(n.getContent());
1041 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1043 UNORDERED_MAP<std::string, content_t>::const_iterator
1044 i = m_name_id_mapping_with_aliases.find(name);
1045 if(i == m_name_id_mapping_with_aliases.end())
1052 content_t CNodeDefManager::getId(const std::string &name) const
1054 content_t id = CONTENT_IGNORE;
1060 bool CNodeDefManager::getIds(const std::string &name,
1061 std::set<content_t> &result) const
1063 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1064 if (name.substr(0,6) != "group:") {
1065 content_t id = CONTENT_IGNORE;
1066 bool exists = getId(name, id);
1071 std::string group = name.substr(6);
1073 UNORDERED_MAP<std::string, GroupItems>::const_iterator
1074 i = m_group_to_items.find(group);
1075 if (i == m_group_to_items.end())
1078 const GroupItems &items = i->second;
1079 for (GroupItems::const_iterator j = items.begin();
1080 j != items.end(); ++j) {
1081 if ((*j).second != 0)
1082 result.insert((*j).first);
1084 //printf("getIds: %dus\n", t.stop());
1089 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1091 content_t id = CONTENT_UNKNOWN;
1097 // returns CONTENT_IGNORE if no free ID found
1098 content_t CNodeDefManager::allocateId()
1100 for (content_t id = m_next_id;
1101 id >= m_next_id; // overflow?
1103 while (id >= m_content_features.size()) {
1104 m_content_features.push_back(ContentFeatures());
1106 const ContentFeatures &f = m_content_features[id];
1112 // If we arrive here, an overflow occurred in id.
1113 // That means no ID was found
1114 return CONTENT_IGNORE;
1119 * Returns the smallest box that contains all boxes
1120 * in the vector. Box_union is expanded.
1121 * @param[in] boxes the vector containing the boxes
1122 * @param[in, out] box_union the union of the arguments
1124 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1126 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1127 it != boxes.end(); ++it) {
1128 box_union->addInternalBox(*it);
1134 * Returns a box that contains the nodebox in every case.
1135 * The argument node_union is expanded.
1136 * @param[in] nodebox the nodebox to be measured
1137 * @param[in] features used to decide whether the nodebox
1139 * @param[in, out] box_union the union of the arguments
1141 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1144 switch(nodebox.type) {
1146 case NODEBOX_LEVELED: {
1148 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1149 boxVectorUnion(nodebox.fixed, &half_processed);
1150 // Set leveled boxes to maximal
1151 if (nodebox.type == NODEBOX_LEVELED) {
1152 half_processed.MaxEdge.Y = +BS / 2;
1154 if (features.param_type_2 == CPT2_FACEDIR ||
1155 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1156 // Get maximal coordinate
1158 fabsf(half_processed.MinEdge.X),
1159 fabsf(half_processed.MinEdge.Y),
1160 fabsf(half_processed.MinEdge.Z),
1161 fabsf(half_processed.MaxEdge.X),
1162 fabsf(half_processed.MaxEdge.Y),
1163 fabsf(half_processed.MaxEdge.Z) };
1165 for (int i = 0; i < 6; i++) {
1166 if (max < coords[i]) {
1170 // Add the union of all possible rotated boxes
1171 box_union->addInternalPoint(-max, -max, -max);
1172 box_union->addInternalPoint(+max, +max, +max);
1174 box_union->addInternalBox(half_processed);
1178 case NODEBOX_WALLMOUNTED: {
1180 box_union->addInternalBox(nodebox.wall_top);
1181 box_union->addInternalBox(nodebox.wall_bottom);
1182 // Find maximal coordinate in the X-Z plane
1184 fabsf(nodebox.wall_side.MinEdge.X),
1185 fabsf(nodebox.wall_side.MinEdge.Z),
1186 fabsf(nodebox.wall_side.MaxEdge.X),
1187 fabsf(nodebox.wall_side.MaxEdge.Z) };
1189 for (int i = 0; i < 4; i++) {
1190 if (max < coords[i]) {
1194 // Add the union of all possible rotated boxes
1195 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1196 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1199 case NODEBOX_CONNECTED: {
1200 // Add all possible connected boxes
1201 boxVectorUnion(nodebox.fixed, box_union);
1202 boxVectorUnion(nodebox.connect_top, box_union);
1203 boxVectorUnion(nodebox.connect_bottom, box_union);
1204 boxVectorUnion(nodebox.connect_front, box_union);
1205 boxVectorUnion(nodebox.connect_left, box_union);
1206 boxVectorUnion(nodebox.connect_back, box_union);
1207 boxVectorUnion(nodebox.connect_right, box_union);
1212 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1213 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1219 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1221 m_selection_box_int_union.MinEdge.X = floorf(
1222 m_selection_box_union.MinEdge.X / BS + 0.5f);
1223 m_selection_box_int_union.MinEdge.Y = floorf(
1224 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1225 m_selection_box_int_union.MinEdge.Z = floorf(
1226 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1227 m_selection_box_int_union.MaxEdge.X = ceilf(
1228 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1229 m_selection_box_int_union.MaxEdge.Y = ceilf(
1230 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1231 m_selection_box_int_union.MaxEdge.Z = ceilf(
1232 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1236 // IWritableNodeDefManager
1237 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1241 assert(name == def.name);
1243 // Don't allow redefining ignore (but allow air and unknown)
1244 if (name == "ignore") {
1245 warningstream << "NodeDefManager: Ignoring "
1246 "CONTENT_IGNORE redefinition"<<std::endl;
1247 return CONTENT_IGNORE;
1250 content_t id = CONTENT_IGNORE;
1251 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1254 if (id == CONTENT_IGNORE) {
1255 warningstream << "NodeDefManager: Absolute "
1256 "limit reached" << std::endl;
1257 return CONTENT_IGNORE;
1259 assert(id != CONTENT_IGNORE);
1260 addNameIdMapping(id, name);
1262 m_content_features[id] = def;
1263 verbosestream << "NodeDefManager: registering content id \"" << id
1264 << "\": name=\"" << def.name << "\""<<std::endl;
1266 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1267 fixSelectionBoxIntUnion();
1268 // Add this content to the list of all groups it belongs to
1269 // FIXME: This should remove a node from groups it no longer
1270 // belongs to when a node is re-registered
1271 for (ItemGroupList::const_iterator i = def.groups.begin();
1272 i != def.groups.end(); ++i) {
1273 std::string group_name = i->first;
1275 UNORDERED_MAP<std::string, GroupItems>::iterator
1276 j = m_group_to_items.find(group_name);
1277 if (j == m_group_to_items.end()) {
1278 m_group_to_items[group_name].push_back(
1279 std::make_pair(id, i->second));
1281 GroupItems &items = j->second;
1282 items.push_back(std::make_pair(id, i->second));
1289 content_t CNodeDefManager::allocateDummy(const std::string &name)
1291 assert(name != ""); // Pre-condition
1294 return set(name, f);
1298 void CNodeDefManager::removeNode(const std::string &name)
1303 // Erase name from name ID mapping
1304 content_t id = CONTENT_IGNORE;
1305 if (m_name_id_mapping.getId(name, id)) {
1306 m_name_id_mapping.eraseName(name);
1307 m_name_id_mapping_with_aliases.erase(name);
1310 // Erase node content from all groups it belongs to
1311 for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1312 m_group_to_items.begin();
1313 iter_groups != m_group_to_items.end();) {
1314 GroupItems &items = iter_groups->second;
1315 for (GroupItems::iterator iter_groupitems = items.begin();
1316 iter_groupitems != items.end();) {
1317 if (iter_groupitems->first == id)
1318 items.erase(iter_groupitems++);
1323 // Check if group is empty
1324 if (items.size() == 0)
1325 m_group_to_items.erase(iter_groups++);
1332 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1334 std::set<std::string> all;
1336 m_name_id_mapping_with_aliases.clear();
1337 for (std::set<std::string>::const_iterator
1338 i = all.begin(); i != all.end(); ++i) {
1339 const std::string &name = *i;
1340 const std::string &convert_to = idef->getAlias(name);
1342 if (m_name_id_mapping.getId(convert_to, id)) {
1343 m_name_id_mapping_with_aliases.insert(
1344 std::make_pair(name, id));
1349 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1351 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1352 "overrides to textures from " << override_filepath << std::endl;
1354 std::ifstream infile(override_filepath.c_str());
1357 while (std::getline(infile, line)) {
1359 if (trim(line) == "")
1361 std::vector<std::string> splitted = str_split(line, ' ');
1362 if (splitted.size() != 3) {
1363 errorstream << override_filepath
1364 << ":" << line_c << " Could not apply texture override \""
1365 << line << "\": Syntax error" << std::endl;
1370 if (!getId(splitted[0], id))
1371 continue; // Ignore unknown node
1373 ContentFeatures &nodedef = m_content_features[id];
1375 if (splitted[1] == "top")
1376 nodedef.tiledef[0].name = splitted[2];
1377 else if (splitted[1] == "bottom")
1378 nodedef.tiledef[1].name = splitted[2];
1379 else if (splitted[1] == "right")
1380 nodedef.tiledef[2].name = splitted[2];
1381 else if (splitted[1] == "left")
1382 nodedef.tiledef[3].name = splitted[2];
1383 else if (splitted[1] == "back")
1384 nodedef.tiledef[4].name = splitted[2];
1385 else if (splitted[1] == "front")
1386 nodedef.tiledef[5].name = splitted[2];
1387 else if (splitted[1] == "all" || splitted[1] == "*")
1388 for (int i = 0; i < 6; i++)
1389 nodedef.tiledef[i].name = splitted[2];
1390 else if (splitted[1] == "sides")
1391 for (int i = 2; i < 6; i++)
1392 nodedef.tiledef[i].name = splitted[2];
1394 errorstream << override_filepath
1395 << ":" << line_c << " Could not apply texture override \""
1396 << line << "\": Unknown node side \""
1397 << splitted[1] << "\"" << std::endl;
1403 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1404 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1405 void *progress_callback_args)
1408 infostream << "CNodeDefManager::updateTextures(): Updating "
1409 "textures in node definitions" << std::endl;
1411 Client *client = (Client *)gamedef;
1412 ITextureSource *tsrc = client->tsrc();
1413 IShaderSource *shdsrc = client->getShaderSource();
1414 scene::ISceneManager* smgr = client->getSceneManager();
1415 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1416 TextureSettings tsettings;
1417 tsettings.readSettings();
1419 u32 size = m_content_features.size();
1421 for (u32 i = 0; i < size; i++) {
1422 ContentFeatures *f = &(m_content_features[i]);
1423 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1424 progress_callback(progress_callback_args, i, size);
1429 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1431 writeU8(os, 1); // version
1433 std::ostringstream os2(std::ios::binary);
1434 for (u32 i = 0; i < m_content_features.size(); i++) {
1435 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1436 || i == CONTENT_UNKNOWN)
1438 const ContentFeatures *f = &m_content_features[i];
1442 // Wrap it in a string to allow different lengths without
1443 // strict version incompatibilities
1444 std::ostringstream wrapper_os(std::ios::binary);
1445 f->serialize(wrapper_os, protocol_version);
1446 os2<<serializeString(wrapper_os.str());
1448 // must not overflow
1449 u16 next = count + 1;
1450 FATAL_ERROR_IF(next < count, "Overflow");
1453 writeU16(os, count);
1454 os << serializeLongString(os2.str());
1458 void CNodeDefManager::deSerialize(std::istream &is)
1461 int version = readU8(is);
1463 throw SerializationError("unsupported NodeDefinitionManager version");
1464 u16 count = readU16(is);
1465 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1467 for (u16 n = 0; n < count; n++) {
1468 u16 i = readU16(is2);
1470 // Read it from the string wrapper
1471 std::string wrapper = deSerializeString(is2);
1472 std::istringstream wrapper_is(wrapper, std::ios::binary);
1473 f.deSerialize(wrapper_is);
1475 // Check error conditions
1476 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1477 warningstream << "NodeDefManager::deSerialize(): "
1478 "not changing builtin node " << i << std::endl;
1482 warningstream << "NodeDefManager::deSerialize(): "
1483 "received empty name" << std::endl;
1489 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1490 warningstream << "NodeDefManager::deSerialize(): "
1491 "already defined with different ID: " << f.name << std::endl;
1495 // All is ok, add node definition with the requested ID
1496 if (i >= m_content_features.size())
1497 m_content_features.resize((u32)(i) + 1);
1498 m_content_features[i] = f;
1499 addNameIdMapping(i, f.name);
1500 verbosestream << "deserialized " << f.name << std::endl;
1502 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1503 fixSelectionBoxIntUnion();
1508 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1510 m_name_id_mapping.set(i, name);
1511 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1515 IWritableNodeDefManager *createNodeDefManager()
1517 return new CNodeDefManager();
1521 //// Serialization of old ContentFeatures formats
1522 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1524 u8 compatible_param_type_2 = param_type_2;
1525 if ((protocol_version < 28)
1526 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1527 compatible_param_type_2 = CPT2_NONE;
1528 else if (protocol_version < 30) {
1529 if (compatible_param_type_2 == CPT2_COLOR)
1530 compatible_param_type_2 = CPT2_NONE;
1531 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1532 compatible_param_type_2 = CPT2_FACEDIR;
1533 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1534 compatible_param_type_2 = CPT2_WALLMOUNTED;
1537 float compatible_visual_scale = visual_scale;
1538 if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1539 compatible_visual_scale = sqrt(visual_scale);
1542 if (protocol_version < 30) {
1543 writeU8(os, protocol_version < 27 ? 7 : 8);
1545 os << serializeString(name);
1546 writeU16(os, groups.size());
1547 for (ItemGroupList::const_iterator i = groups.begin();
1548 i != groups.end(); ++i) {
1549 os << serializeString(i->first);
1550 writeS16(os, i->second);
1552 writeU8(os, drawtype);
1553 writeF1000(os, compatible_visual_scale);
1555 for (u32 i = 0; i < 6; i++)
1556 tiledef[i].serialize(os, protocol_version);
1557 writeU8(os, CF_SPECIAL_COUNT);
1558 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1559 tiledef_special[i].serialize(os, protocol_version);
1561 writeU8(os, post_effect_color.getAlpha());
1562 writeU8(os, post_effect_color.getRed());
1563 writeU8(os, post_effect_color.getGreen());
1564 writeU8(os, post_effect_color.getBlue());
1565 writeU8(os, param_type);
1566 writeU8(os, compatible_param_type_2);
1567 writeU8(os, is_ground_content);
1568 writeU8(os, light_propagates);
1569 writeU8(os, sunlight_propagates);
1570 writeU8(os, walkable);
1571 writeU8(os, pointable);
1572 writeU8(os, diggable);
1573 writeU8(os, climbable);
1574 writeU8(os, buildable_to);
1575 os << serializeString(""); // legacy: used to be metadata_name
1576 writeU8(os, liquid_type);
1577 os << serializeString(liquid_alternative_flowing);
1578 os << serializeString(liquid_alternative_source);
1579 writeU8(os, liquid_viscosity);
1580 writeU8(os, liquid_renewable);
1581 writeU8(os, light_source);
1582 writeU32(os, damage_per_second);
1583 node_box.serialize(os, protocol_version);
1584 selection_box.serialize(os, protocol_version);
1585 writeU8(os, legacy_facedir_simple);
1586 writeU8(os, legacy_wallmounted);
1587 serializeSimpleSoundSpec(sound_footstep, os);
1588 serializeSimpleSoundSpec(sound_dig, os);
1589 serializeSimpleSoundSpec(sound_dug, os);
1590 writeU8(os, rightclickable);
1591 writeU8(os, drowning);
1592 writeU8(os, leveled);
1593 writeU8(os, liquid_range);
1594 writeU8(os, waving);
1595 os << serializeString(mesh);
1596 collision_box.serialize(os, protocol_version);
1597 writeU8(os, floodable);
1598 writeU16(os, connects_to_ids.size());
1599 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1600 i != connects_to_ids.end(); ++i)
1602 writeU8(os, connect_sides);
1604 throw SerializationError("ContentFeatures::serialize(): "
1605 "Unsupported version requested");
1609 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1611 if (version == 5) // In PROTOCOL_VERSION 13
1613 name = deSerializeString(is);
1615 u32 groups_size = readU16(is);
1616 for(u32 i=0; i<groups_size; i++){
1617 std::string name = deSerializeString(is);
1618 int value = readS16(is);
1619 groups[name] = value;
1621 drawtype = (enum NodeDrawType)readU8(is);
1623 visual_scale = readF1000(is);
1624 if (readU8(is) != 6)
1625 throw SerializationError("unsupported tile count");
1626 for (u32 i = 0; i < 6; i++)
1627 tiledef[i].deSerialize(is, version, drawtype);
1628 if (readU8(is) != CF_SPECIAL_COUNT)
1629 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1630 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1631 tiledef_special[i].deSerialize(is, version, drawtype);
1633 post_effect_color.setAlpha(readU8(is));
1634 post_effect_color.setRed(readU8(is));
1635 post_effect_color.setGreen(readU8(is));
1636 post_effect_color.setBlue(readU8(is));
1637 param_type = (enum ContentParamType)readU8(is);
1638 param_type_2 = (enum ContentParamType2)readU8(is);
1639 is_ground_content = readU8(is);
1640 light_propagates = readU8(is);
1641 sunlight_propagates = readU8(is);
1642 walkable = readU8(is);
1643 pointable = readU8(is);
1644 diggable = readU8(is);
1645 climbable = readU8(is);
1646 buildable_to = readU8(is);
1647 deSerializeString(is); // legacy: used to be metadata_name
1648 liquid_type = (enum LiquidType)readU8(is);
1649 liquid_alternative_flowing = deSerializeString(is);
1650 liquid_alternative_source = deSerializeString(is);
1651 liquid_viscosity = readU8(is);
1652 light_source = readU8(is);
1653 light_source = MYMIN(light_source, LIGHT_MAX);
1654 damage_per_second = readU32(is);
1655 node_box.deSerialize(is);
1656 selection_box.deSerialize(is);
1657 legacy_facedir_simple = readU8(is);
1658 legacy_wallmounted = readU8(is);
1659 deSerializeSimpleSoundSpec(sound_footstep, is);
1660 deSerializeSimpleSoundSpec(sound_dig, is);
1661 deSerializeSimpleSoundSpec(sound_dug, is);
1662 } else if (version == 6) {
1663 name = deSerializeString(is);
1665 u32 groups_size = readU16(is);
1666 for (u32 i = 0; i < groups_size; i++) {
1667 std::string name = deSerializeString(is);
1668 int value = readS16(is);
1669 groups[name] = value;
1671 drawtype = (enum NodeDrawType)readU8(is);
1672 visual_scale = readF1000(is);
1673 if (readU8(is) != 6)
1674 throw SerializationError("unsupported tile count");
1675 for (u32 i = 0; i < 6; i++)
1676 tiledef[i].deSerialize(is, version, drawtype);
1677 // CF_SPECIAL_COUNT in version 6 = 2
1678 if (readU8(is) != 2)
1679 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1680 for (u32 i = 0; i < 2; i++)
1681 tiledef_special[i].deSerialize(is, version, drawtype);
1683 post_effect_color.setAlpha(readU8(is));
1684 post_effect_color.setRed(readU8(is));
1685 post_effect_color.setGreen(readU8(is));
1686 post_effect_color.setBlue(readU8(is));
1687 param_type = (enum ContentParamType)readU8(is);
1688 param_type_2 = (enum ContentParamType2)readU8(is);
1689 is_ground_content = readU8(is);
1690 light_propagates = readU8(is);
1691 sunlight_propagates = readU8(is);
1692 walkable = readU8(is);
1693 pointable = readU8(is);
1694 diggable = readU8(is);
1695 climbable = readU8(is);
1696 buildable_to = readU8(is);
1697 deSerializeString(is); // legacy: used to be metadata_name
1698 liquid_type = (enum LiquidType)readU8(is);
1699 liquid_alternative_flowing = deSerializeString(is);
1700 liquid_alternative_source = deSerializeString(is);
1701 liquid_viscosity = readU8(is);
1702 liquid_renewable = readU8(is);
1703 light_source = readU8(is);
1704 damage_per_second = readU32(is);
1705 node_box.deSerialize(is);
1706 selection_box.deSerialize(is);
1707 legacy_facedir_simple = readU8(is);
1708 legacy_wallmounted = readU8(is);
1709 deSerializeSimpleSoundSpec(sound_footstep, is);
1710 deSerializeSimpleSoundSpec(sound_dig, is);
1711 deSerializeSimpleSoundSpec(sound_dug, is);
1712 rightclickable = readU8(is);
1713 drowning = readU8(is);
1714 leveled = readU8(is);
1715 liquid_range = readU8(is);
1716 } else if (version == 7 || version == 8){
1717 name = deSerializeString(is);
1719 u32 groups_size = readU16(is);
1720 for (u32 i = 0; i < groups_size; i++) {
1721 std::string name = deSerializeString(is);
1722 int value = readS16(is);
1723 groups[name] = value;
1725 drawtype = (enum NodeDrawType) readU8(is);
1727 visual_scale = readF1000(is);
1728 if (readU8(is) != 6)
1729 throw SerializationError("unsupported tile count");
1730 for (u32 i = 0; i < 6; i++)
1731 tiledef[i].deSerialize(is, version, drawtype);
1732 if (readU8(is) != CF_SPECIAL_COUNT)
1733 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1734 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1735 tiledef_special[i].deSerialize(is, version, drawtype);
1737 post_effect_color.setAlpha(readU8(is));
1738 post_effect_color.setRed(readU8(is));
1739 post_effect_color.setGreen(readU8(is));
1740 post_effect_color.setBlue(readU8(is));
1741 param_type = (enum ContentParamType) readU8(is);
1742 param_type_2 = (enum ContentParamType2) readU8(is);
1743 is_ground_content = readU8(is);
1744 light_propagates = readU8(is);
1745 sunlight_propagates = readU8(is);
1746 walkable = readU8(is);
1747 pointable = readU8(is);
1748 diggable = readU8(is);
1749 climbable = readU8(is);
1750 buildable_to = readU8(is);
1751 deSerializeString(is); // legacy: used to be metadata_name
1752 liquid_type = (enum LiquidType) readU8(is);
1753 liquid_alternative_flowing = deSerializeString(is);
1754 liquid_alternative_source = deSerializeString(is);
1755 liquid_viscosity = readU8(is);
1756 liquid_renewable = readU8(is);
1757 light_source = readU8(is);
1758 light_source = MYMIN(light_source, LIGHT_MAX);
1759 damage_per_second = readU32(is);
1760 node_box.deSerialize(is);
1761 selection_box.deSerialize(is);
1762 legacy_facedir_simple = readU8(is);
1763 legacy_wallmounted = readU8(is);
1764 deSerializeSimpleSoundSpec(sound_footstep, is);
1765 deSerializeSimpleSoundSpec(sound_dig, is);
1766 deSerializeSimpleSoundSpec(sound_dug, is);
1767 rightclickable = readU8(is);
1768 drowning = readU8(is);
1769 leveled = readU8(is);
1770 liquid_range = readU8(is);
1771 waving = readU8(is);
1773 mesh = deSerializeString(is);
1774 collision_box.deSerialize(is);
1775 floodable = readU8(is);
1776 u16 connects_to_size = readU16(is);
1777 connects_to_ids.clear();
1778 for (u16 i = 0; i < connects_to_size; i++)
1779 connects_to_ids.insert(readU16(is));
1780 connect_sides = readU8(is);
1781 } catch (SerializationError &e) {};
1783 throw SerializationError("unsupported ContentFeatures version");
1788 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1790 return m_node_registration_complete;
1794 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1796 m_node_registration_complete = completed;
1800 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1803 if (m_node_registration_complete)
1804 nr->nodeResolveInternal();
1806 m_pending_resolve_callbacks.push_back(nr);
1810 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1812 size_t len = m_pending_resolve_callbacks.size();
1813 for (size_t i = 0; i != len; i++) {
1814 if (nr != m_pending_resolve_callbacks[i])
1818 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1819 m_pending_resolve_callbacks.resize(len);
1827 void CNodeDefManager::runNodeResolveCallbacks()
1829 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1830 NodeResolver *nr = m_pending_resolve_callbacks[i];
1831 nr->nodeResolveInternal();
1834 m_pending_resolve_callbacks.clear();
1838 void CNodeDefManager::resetNodeResolveState()
1840 m_node_registration_complete = false;
1841 m_pending_resolve_callbacks.clear();
1844 void CNodeDefManager::mapNodeboxConnections()
1846 for (u32 i = 0; i < m_content_features.size(); i++) {
1847 ContentFeatures *f = &m_content_features[i];
1848 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1850 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1851 it != f->connects_to.end(); ++it) {
1852 getIds(*it, f->connects_to_ids);
1857 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1859 const ContentFeatures &f1 = get(from);
1861 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1864 // lookup target in connected set
1865 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1868 const ContentFeatures &f2 = get(to);
1870 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1871 // ignores actually looking if back connection exists
1872 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1874 // does to node declare usable faces?
1875 if (f2.connect_sides > 0) {
1876 if ((f2.param_type_2 == CPT2_FACEDIR ||
1877 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1878 && (connect_face >= 4)) {
1879 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1880 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1882 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1884 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1885 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1887 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1888 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1889 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1891 return (f2.connect_sides
1892 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1894 return (f2.connect_sides & connect_face);
1896 // the target is just a regular node, so connect no matter back connection
1904 NodeResolver::NodeResolver()
1907 m_nodenames_idx = 0;
1908 m_nnlistsizes_idx = 0;
1909 m_resolve_done = false;
1911 m_nodenames.reserve(16);
1912 m_nnlistsizes.reserve(4);
1916 NodeResolver::~NodeResolver()
1918 if (!m_resolve_done && m_ndef)
1919 m_ndef->cancelNodeResolveCallback(this);
1923 void NodeResolver::nodeResolveInternal()
1925 m_nodenames_idx = 0;
1926 m_nnlistsizes_idx = 0;
1929 m_resolve_done = true;
1931 m_nodenames.clear();
1932 m_nnlistsizes.clear();
1936 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1937 const std::string &node_alt, content_t c_fallback)
1939 if (m_nodenames_idx == m_nodenames.size()) {
1940 *result_out = c_fallback;
1941 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1946 std::string name = m_nodenames[m_nodenames_idx++];
1948 bool success = m_ndef->getId(name, c);
1949 if (!success && node_alt != "") {
1951 success = m_ndef->getId(name, c);
1955 errorstream << "NodeResolver: failed to resolve node name '" << name
1956 << "'." << std::endl;
1965 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1966 bool all_required, content_t c_fallback)
1968 bool success = true;
1970 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1971 errorstream << "NodeResolver: no more node lists" << std::endl;
1975 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1978 if (m_nodenames_idx == m_nodenames.size()) {
1979 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1984 std::string &name = m_nodenames[m_nodenames_idx++];
1986 if (name.substr(0,6) != "group:") {
1987 if (m_ndef->getId(name, c)) {
1988 result_out->push_back(c);
1989 } else if (all_required) {
1990 errorstream << "NodeResolver: failed to resolve node name '"
1991 << name << "'." << std::endl;
1992 result_out->push_back(c_fallback);
1996 std::set<content_t> cids;
1997 std::set<content_t>::iterator it;
1998 m_ndef->getIds(name, cids);
1999 for (it = cids.begin(); it != cids.end(); ++it)
2000 result_out->push_back(*it);