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 ((drawtype == NDT_MESH) && (mesh != "")) {
791 // Read the mesh and apply scale
792 mesh_ptr[0] = client->getMesh(mesh);
794 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
795 scaleMesh(mesh_ptr[0], scale);
796 recalculateBoundingBox(mesh_ptr[0]);
797 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
799 } else if ((drawtype == NDT_NODEBOX) &&
800 ((node_box.type == NODEBOX_REGULAR) ||
801 (node_box.type == NODEBOX_FIXED)) &&
802 (!node_box.fixed.empty())) {
803 //Convert regular nodebox nodes to meshnodes
804 //Change the drawtype and apply scale
806 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
807 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
808 scaleMesh(mesh_ptr[0], scale);
809 recalculateBoundingBox(mesh_ptr[0]);
810 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
813 //Cache 6dfacedir and wallmounted rotated clones of meshes
814 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
815 (param_type_2 == CPT2_FACEDIR
816 || param_type_2 == CPT2_COLORED_FACEDIR)) {
817 for (u16 j = 1; j < 24; j++) {
818 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
819 rotateMeshBy6dFacedir(mesh_ptr[j], j);
820 recalculateBoundingBox(mesh_ptr[j]);
821 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
823 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
824 && (param_type_2 == CPT2_WALLMOUNTED ||
825 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
826 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
827 for (u16 j = 1; j < 6; j++) {
828 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
829 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
830 recalculateBoundingBox(mesh_ptr[j]);
831 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
833 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
834 recalculateBoundingBox(mesh_ptr[0]);
835 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
844 class CNodeDefManager: public IWritableNodeDefManager {
847 virtual ~CNodeDefManager();
849 virtual IWritableNodeDefManager *clone();
850 inline virtual const ContentFeatures& get(content_t c) const;
851 inline virtual const ContentFeatures& get(const MapNode &n) const;
852 virtual bool getId(const std::string &name, content_t &result) const;
853 virtual content_t getId(const std::string &name) const;
854 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
855 virtual const ContentFeatures& get(const std::string &name) const;
856 content_t allocateId();
857 virtual content_t set(const std::string &name, const ContentFeatures &def);
858 virtual content_t allocateDummy(const std::string &name);
859 virtual void removeNode(const std::string &name);
860 virtual void updateAliases(IItemDefManager *idef);
861 virtual void applyTextureOverrides(const std::string &override_filepath);
862 //! Returns a palette or NULL if not found. Only on client.
863 std::vector<video::SColor> *getPalette(const ContentFeatures &f,
864 const IGameDef *gamedef);
865 virtual void updateTextures(IGameDef *gamedef,
866 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
867 void *progress_cbk_args);
868 void serialize(std::ostream &os, u16 protocol_version) const;
869 void deSerialize(std::istream &is);
871 inline virtual bool getNodeRegistrationStatus() const;
872 inline virtual void setNodeRegistrationStatus(bool completed);
874 virtual void pendNodeResolve(NodeResolver *nr);
875 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
876 virtual void runNodeResolveCallbacks();
877 virtual void resetNodeResolveState();
878 virtual void mapNodeboxConnections();
879 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
880 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
882 return m_selection_box_int_union;
886 void addNameIdMapping(content_t i, std::string name);
888 * Recalculates m_selection_box_int_union based on
889 * m_selection_box_union.
891 void fixSelectionBoxIntUnion();
893 // Features indexed by id
894 std::vector<ContentFeatures> m_content_features;
896 // A mapping for fast converting back and forth between names and ids
897 NameIdMapping m_name_id_mapping;
899 // Like m_name_id_mapping, but only from names to ids, and includes
900 // item aliases too. Updated by updateAliases()
901 // Note: Not serialized.
903 UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
905 // A mapping from groups to a list of content_ts (and their levels)
906 // that belong to it. Necessary for a direct lookup in getIds().
907 // Note: Not serialized.
908 UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
910 // Next possibly free id
913 // Maps image file names to loaded palettes.
914 UNORDERED_MAP<std::string, std::vector<video::SColor> > m_palettes;
916 // NodeResolvers to callback once node registration has ended
917 std::vector<NodeResolver *> m_pending_resolve_callbacks;
919 // True when all nodes have been registered
920 bool m_node_registration_complete;
922 //! The union of all nodes' selection boxes.
923 aabb3f m_selection_box_union;
925 * The smallest box in node coordinates that
926 * contains all nodes' selection boxes.
928 core::aabbox3d<s16> m_selection_box_int_union;
932 CNodeDefManager::CNodeDefManager()
938 CNodeDefManager::~CNodeDefManager()
941 for (u32 i = 0; i < m_content_features.size(); i++) {
942 ContentFeatures *f = &m_content_features[i];
943 for (u32 j = 0; j < 24; j++) {
945 f->mesh_ptr[j]->drop();
952 void CNodeDefManager::clear()
954 m_content_features.clear();
955 m_name_id_mapping.clear();
956 m_name_id_mapping_with_aliases.clear();
957 m_group_to_items.clear();
959 m_selection_box_union.reset(0,0,0);
960 m_selection_box_int_union.reset(0,0,0);
962 resetNodeResolveState();
964 u32 initial_length = 0;
965 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
966 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
967 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
968 m_content_features.resize(initial_length);
970 // Set CONTENT_UNKNOWN
974 // Insert directly into containers
975 content_t c = CONTENT_UNKNOWN;
976 m_content_features[c] = f;
977 addNameIdMapping(c, f.name);
984 f.drawtype = NDT_AIRLIKE;
985 f.param_type = CPT_LIGHT;
986 f.light_propagates = true;
987 f.sunlight_propagates = true;
991 f.buildable_to = true;
993 f.is_ground_content = true;
994 // Insert directly into containers
995 content_t c = CONTENT_AIR;
996 m_content_features[c] = f;
997 addNameIdMapping(c, f.name);
1000 // Set CONTENT_IGNORE
1004 f.drawtype = NDT_AIRLIKE;
1005 f.param_type = CPT_NONE;
1006 f.light_propagates = false;
1007 f.sunlight_propagates = false;
1009 f.pointable = false;
1011 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1012 f.is_ground_content = true;
1013 // Insert directly into containers
1014 content_t c = CONTENT_IGNORE;
1015 m_content_features[c] = f;
1016 addNameIdMapping(c, f.name);
1021 IWritableNodeDefManager *CNodeDefManager::clone()
1023 CNodeDefManager *mgr = new CNodeDefManager();
1029 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1031 return c < m_content_features.size()
1032 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1036 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1038 return get(n.getContent());
1042 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1044 UNORDERED_MAP<std::string, content_t>::const_iterator
1045 i = m_name_id_mapping_with_aliases.find(name);
1046 if(i == m_name_id_mapping_with_aliases.end())
1053 content_t CNodeDefManager::getId(const std::string &name) const
1055 content_t id = CONTENT_IGNORE;
1061 bool CNodeDefManager::getIds(const std::string &name,
1062 std::set<content_t> &result) const
1064 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1065 if (name.substr(0,6) != "group:") {
1066 content_t id = CONTENT_IGNORE;
1067 bool exists = getId(name, id);
1072 std::string group = name.substr(6);
1074 UNORDERED_MAP<std::string, GroupItems>::const_iterator
1075 i = m_group_to_items.find(group);
1076 if (i == m_group_to_items.end())
1079 const GroupItems &items = i->second;
1080 for (GroupItems::const_iterator j = items.begin();
1081 j != items.end(); ++j) {
1082 if ((*j).second != 0)
1083 result.insert((*j).first);
1085 //printf("getIds: %dus\n", t.stop());
1090 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1092 content_t id = CONTENT_UNKNOWN;
1098 // returns CONTENT_IGNORE if no free ID found
1099 content_t CNodeDefManager::allocateId()
1101 for (content_t id = m_next_id;
1102 id >= m_next_id; // overflow?
1104 while (id >= m_content_features.size()) {
1105 m_content_features.push_back(ContentFeatures());
1107 const ContentFeatures &f = m_content_features[id];
1113 // If we arrive here, an overflow occurred in id.
1114 // That means no ID was found
1115 return CONTENT_IGNORE;
1120 * Returns the smallest box that contains all boxes
1121 * in the vector. Box_union is expanded.
1122 * @param[in] boxes the vector containing the boxes
1123 * @param[in, out] box_union the union of the arguments
1125 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1127 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1128 it != boxes.end(); ++it) {
1129 box_union->addInternalBox(*it);
1135 * Returns a box that contains the nodebox in every case.
1136 * The argument node_union is expanded.
1137 * @param[in] nodebox the nodebox to be measured
1138 * @param[in] features used to decide whether the nodebox
1140 * @param[in, out] box_union the union of the arguments
1142 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1145 switch(nodebox.type) {
1147 case NODEBOX_LEVELED: {
1149 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1150 boxVectorUnion(nodebox.fixed, &half_processed);
1151 // Set leveled boxes to maximal
1152 if (nodebox.type == NODEBOX_LEVELED) {
1153 half_processed.MaxEdge.Y = +BS / 2;
1155 if (features.param_type_2 == CPT2_FACEDIR ||
1156 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1157 // Get maximal coordinate
1159 fabsf(half_processed.MinEdge.X),
1160 fabsf(half_processed.MinEdge.Y),
1161 fabsf(half_processed.MinEdge.Z),
1162 fabsf(half_processed.MaxEdge.X),
1163 fabsf(half_processed.MaxEdge.Y),
1164 fabsf(half_processed.MaxEdge.Z) };
1166 for (int i = 0; i < 6; i++) {
1167 if (max < coords[i]) {
1171 // Add the union of all possible rotated boxes
1172 box_union->addInternalPoint(-max, -max, -max);
1173 box_union->addInternalPoint(+max, +max, +max);
1175 box_union->addInternalBox(half_processed);
1179 case NODEBOX_WALLMOUNTED: {
1181 box_union->addInternalBox(nodebox.wall_top);
1182 box_union->addInternalBox(nodebox.wall_bottom);
1183 // Find maximal coordinate in the X-Z plane
1185 fabsf(nodebox.wall_side.MinEdge.X),
1186 fabsf(nodebox.wall_side.MinEdge.Z),
1187 fabsf(nodebox.wall_side.MaxEdge.X),
1188 fabsf(nodebox.wall_side.MaxEdge.Z) };
1190 for (int i = 0; i < 4; i++) {
1191 if (max < coords[i]) {
1195 // Add the union of all possible rotated boxes
1196 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1197 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1200 case NODEBOX_CONNECTED: {
1201 // Add all possible connected boxes
1202 boxVectorUnion(nodebox.fixed, box_union);
1203 boxVectorUnion(nodebox.connect_top, box_union);
1204 boxVectorUnion(nodebox.connect_bottom, box_union);
1205 boxVectorUnion(nodebox.connect_front, box_union);
1206 boxVectorUnion(nodebox.connect_left, box_union);
1207 boxVectorUnion(nodebox.connect_back, box_union);
1208 boxVectorUnion(nodebox.connect_right, box_union);
1213 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1214 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1220 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1222 m_selection_box_int_union.MinEdge.X = floorf(
1223 m_selection_box_union.MinEdge.X / BS + 0.5f);
1224 m_selection_box_int_union.MinEdge.Y = floorf(
1225 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1226 m_selection_box_int_union.MinEdge.Z = floorf(
1227 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1228 m_selection_box_int_union.MaxEdge.X = ceilf(
1229 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1230 m_selection_box_int_union.MaxEdge.Y = ceilf(
1231 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1232 m_selection_box_int_union.MaxEdge.Z = ceilf(
1233 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1237 // IWritableNodeDefManager
1238 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1242 assert(name == def.name);
1244 // Don't allow redefining ignore (but allow air and unknown)
1245 if (name == "ignore") {
1246 warningstream << "NodeDefManager: Ignoring "
1247 "CONTENT_IGNORE redefinition"<<std::endl;
1248 return CONTENT_IGNORE;
1251 content_t id = CONTENT_IGNORE;
1252 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1255 if (id == CONTENT_IGNORE) {
1256 warningstream << "NodeDefManager: Absolute "
1257 "limit reached" << std::endl;
1258 return CONTENT_IGNORE;
1260 assert(id != CONTENT_IGNORE);
1261 addNameIdMapping(id, name);
1263 m_content_features[id] = def;
1264 verbosestream << "NodeDefManager: registering content id \"" << id
1265 << "\": name=\"" << def.name << "\""<<std::endl;
1267 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1268 fixSelectionBoxIntUnion();
1269 // Add this content to the list of all groups it belongs to
1270 // FIXME: This should remove a node from groups it no longer
1271 // belongs to when a node is re-registered
1272 for (ItemGroupList::const_iterator i = def.groups.begin();
1273 i != def.groups.end(); ++i) {
1274 std::string group_name = i->first;
1276 UNORDERED_MAP<std::string, GroupItems>::iterator
1277 j = m_group_to_items.find(group_name);
1278 if (j == m_group_to_items.end()) {
1279 m_group_to_items[group_name].push_back(
1280 std::make_pair(id, i->second));
1282 GroupItems &items = j->second;
1283 items.push_back(std::make_pair(id, i->second));
1290 content_t CNodeDefManager::allocateDummy(const std::string &name)
1292 assert(name != ""); // Pre-condition
1295 return set(name, f);
1299 void CNodeDefManager::removeNode(const std::string &name)
1304 // Erase name from name ID mapping
1305 content_t id = CONTENT_IGNORE;
1306 if (m_name_id_mapping.getId(name, id)) {
1307 m_name_id_mapping.eraseName(name);
1308 m_name_id_mapping_with_aliases.erase(name);
1311 // Erase node content from all groups it belongs to
1312 for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1313 m_group_to_items.begin();
1314 iter_groups != m_group_to_items.end();) {
1315 GroupItems &items = iter_groups->second;
1316 for (GroupItems::iterator iter_groupitems = items.begin();
1317 iter_groupitems != items.end();) {
1318 if (iter_groupitems->first == id)
1319 items.erase(iter_groupitems++);
1324 // Check if group is empty
1325 if (items.size() == 0)
1326 m_group_to_items.erase(iter_groups++);
1333 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1335 std::set<std::string> all;
1337 m_name_id_mapping_with_aliases.clear();
1338 for (std::set<std::string>::const_iterator
1339 i = all.begin(); i != all.end(); ++i) {
1340 const std::string &name = *i;
1341 const std::string &convert_to = idef->getAlias(name);
1343 if (m_name_id_mapping.getId(convert_to, id)) {
1344 m_name_id_mapping_with_aliases.insert(
1345 std::make_pair(name, id));
1350 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1352 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1353 "overrides to textures from " << override_filepath << std::endl;
1355 std::ifstream infile(override_filepath.c_str());
1358 while (std::getline(infile, line)) {
1360 if (trim(line) == "")
1362 std::vector<std::string> splitted = str_split(line, ' ');
1363 if (splitted.size() != 3) {
1364 errorstream << override_filepath
1365 << ":" << line_c << " Could not apply texture override \""
1366 << line << "\": Syntax error" << std::endl;
1371 if (!getId(splitted[0], id))
1372 continue; // Ignore unknown node
1374 ContentFeatures &nodedef = m_content_features[id];
1376 if (splitted[1] == "top")
1377 nodedef.tiledef[0].name = splitted[2];
1378 else if (splitted[1] == "bottom")
1379 nodedef.tiledef[1].name = splitted[2];
1380 else if (splitted[1] == "right")
1381 nodedef.tiledef[2].name = splitted[2];
1382 else if (splitted[1] == "left")
1383 nodedef.tiledef[3].name = splitted[2];
1384 else if (splitted[1] == "back")
1385 nodedef.tiledef[4].name = splitted[2];
1386 else if (splitted[1] == "front")
1387 nodedef.tiledef[5].name = splitted[2];
1388 else if (splitted[1] == "all" || splitted[1] == "*")
1389 for (int i = 0; i < 6; i++)
1390 nodedef.tiledef[i].name = splitted[2];
1391 else if (splitted[1] == "sides")
1392 for (int i = 2; i < 6; i++)
1393 nodedef.tiledef[i].name = splitted[2];
1395 errorstream << override_filepath
1396 << ":" << line_c << " Could not apply texture override \""
1397 << line << "\": Unknown node side \""
1398 << splitted[1] << "\"" << std::endl;
1404 std::vector<video::SColor> *CNodeDefManager::getPalette(
1405 const ContentFeatures &f, const IGameDef *gamedef)
1408 // This works because colors always use the most significant bits
1409 // of param2. If you add a new colored type which uses param2
1410 // in a more advanced way, you should change this code, too.
1411 u32 palette_pixels = 0;
1412 switch (f.param_type_2) {
1414 palette_pixels = 256;
1416 case CPT2_COLORED_FACEDIR:
1419 case CPT2_COLORED_WALLMOUNTED:
1420 palette_pixels = 32;
1425 // This many param2 values will have the same color
1426 u32 step = 256 / palette_pixels;
1427 const std::string &name = f.palette_name;
1430 Client *client = (Client *) gamedef;
1431 ITextureSource *tsrc = client->tsrc();
1433 UNORDERED_MAP<std::string, std::vector<video::SColor> >::iterator it =
1434 m_palettes.find(name);
1435 if (it == m_palettes.end()) {
1437 if (!tsrc->isKnownSourceImage(name)) {
1438 warningstream << "CNodeDefManager::getPalette(): palette \"" << name
1439 << "\" could not be loaded." << std::endl;
1442 video::IImage *img = tsrc->generateImage(name);
1443 std::vector<video::SColor> new_palette;
1444 u32 w = img->getDimension().Width;
1445 u32 h = img->getDimension().Height;
1446 // Real area of the image
1448 if (area != palette_pixels)
1449 warningstream << "CNodeDefManager::getPalette(): the "
1450 << "specified palette image \"" << name << "\" does not "
1451 << "contain exactly " << palette_pixels
1452 << " pixels." << std::endl;
1453 if (area > palette_pixels)
1454 area = palette_pixels;
1455 // For each pixel in the image
1456 for (u32 i = 0; i < area; i++) {
1457 video::SColor c = img->getPixel(i % w, i / w);
1458 // Fill in palette with 'step' colors
1459 for (u32 j = 0; j < step; j++)
1460 new_palette.push_back(c);
1463 // Fill in remaining elements
1464 while (new_palette.size() < 256)
1465 new_palette.push_back(video::SColor(0xFFFFFFFF));
1466 m_palettes[name] = new_palette;
1467 it = m_palettes.find(name);
1469 if (it != m_palettes.end())
1470 return &((*it).second);
1476 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1477 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1478 void *progress_callback_args)
1481 infostream << "CNodeDefManager::updateTextures(): Updating "
1482 "textures in node definitions" << std::endl;
1484 Client *client = (Client *)gamedef;
1485 ITextureSource *tsrc = client->tsrc();
1486 IShaderSource *shdsrc = client->getShaderSource();
1487 scene::ISceneManager* smgr = client->getSceneManager();
1488 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1489 TextureSettings tsettings;
1490 tsettings.readSettings();
1493 u32 size = m_content_features.size();
1495 for (u32 i = 0; i < size; i++) {
1496 ContentFeatures *f = &(m_content_features[i]);
1497 f->palette = getPalette(*f, gamedef);
1498 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1499 progress_callback(progress_callback_args, i, size);
1504 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1506 writeU8(os, 1); // version
1508 std::ostringstream os2(std::ios::binary);
1509 for (u32 i = 0; i < m_content_features.size(); i++) {
1510 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1511 || i == CONTENT_UNKNOWN)
1513 const ContentFeatures *f = &m_content_features[i];
1517 // Wrap it in a string to allow different lengths without
1518 // strict version incompatibilities
1519 std::ostringstream wrapper_os(std::ios::binary);
1520 f->serialize(wrapper_os, protocol_version);
1521 os2<<serializeString(wrapper_os.str());
1523 // must not overflow
1524 u16 next = count + 1;
1525 FATAL_ERROR_IF(next < count, "Overflow");
1528 writeU16(os, count);
1529 os << serializeLongString(os2.str());
1533 void CNodeDefManager::deSerialize(std::istream &is)
1536 int version = readU8(is);
1538 throw SerializationError("unsupported NodeDefinitionManager version");
1539 u16 count = readU16(is);
1540 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1542 for (u16 n = 0; n < count; n++) {
1543 u16 i = readU16(is2);
1545 // Read it from the string wrapper
1546 std::string wrapper = deSerializeString(is2);
1547 std::istringstream wrapper_is(wrapper, std::ios::binary);
1548 f.deSerialize(wrapper_is);
1550 // Check error conditions
1551 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1552 warningstream << "NodeDefManager::deSerialize(): "
1553 "not changing builtin node " << i << std::endl;
1557 warningstream << "NodeDefManager::deSerialize(): "
1558 "received empty name" << std::endl;
1564 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1565 warningstream << "NodeDefManager::deSerialize(): "
1566 "already defined with different ID: " << f.name << std::endl;
1570 // All is ok, add node definition with the requested ID
1571 if (i >= m_content_features.size())
1572 m_content_features.resize((u32)(i) + 1);
1573 m_content_features[i] = f;
1574 addNameIdMapping(i, f.name);
1575 verbosestream << "deserialized " << f.name << std::endl;
1577 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1578 fixSelectionBoxIntUnion();
1583 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1585 m_name_id_mapping.set(i, name);
1586 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1590 IWritableNodeDefManager *createNodeDefManager()
1592 return new CNodeDefManager();
1596 //// Serialization of old ContentFeatures formats
1597 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1599 u8 compatible_param_type_2 = param_type_2;
1600 if ((protocol_version < 28)
1601 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1602 compatible_param_type_2 = CPT2_NONE;
1603 else if (protocol_version < 30) {
1604 if (compatible_param_type_2 == CPT2_COLOR)
1605 compatible_param_type_2 = CPT2_NONE;
1606 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1607 compatible_param_type_2 = CPT2_FACEDIR;
1608 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1609 compatible_param_type_2 = CPT2_WALLMOUNTED;
1612 float compatible_visual_scale = visual_scale;
1613 if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1614 compatible_visual_scale = sqrt(visual_scale);
1617 if (protocol_version < 30) {
1618 writeU8(os, protocol_version < 27 ? 7 : 8);
1620 os << serializeString(name);
1621 writeU16(os, groups.size());
1622 for (ItemGroupList::const_iterator i = groups.begin();
1623 i != groups.end(); ++i) {
1624 os << serializeString(i->first);
1625 writeS16(os, i->second);
1627 writeU8(os, drawtype);
1628 writeF1000(os, compatible_visual_scale);
1630 for (u32 i = 0; i < 6; i++)
1631 tiledef[i].serialize(os, protocol_version);
1632 writeU8(os, CF_SPECIAL_COUNT);
1633 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1634 tiledef_special[i].serialize(os, protocol_version);
1636 writeU8(os, post_effect_color.getAlpha());
1637 writeU8(os, post_effect_color.getRed());
1638 writeU8(os, post_effect_color.getGreen());
1639 writeU8(os, post_effect_color.getBlue());
1640 writeU8(os, param_type);
1641 writeU8(os, compatible_param_type_2);
1642 writeU8(os, is_ground_content);
1643 writeU8(os, light_propagates);
1644 writeU8(os, sunlight_propagates);
1645 writeU8(os, walkable);
1646 writeU8(os, pointable);
1647 writeU8(os, diggable);
1648 writeU8(os, climbable);
1649 writeU8(os, buildable_to);
1650 os << serializeString(""); // legacy: used to be metadata_name
1651 writeU8(os, liquid_type);
1652 os << serializeString(liquid_alternative_flowing);
1653 os << serializeString(liquid_alternative_source);
1654 writeU8(os, liquid_viscosity);
1655 writeU8(os, liquid_renewable);
1656 writeU8(os, light_source);
1657 writeU32(os, damage_per_second);
1658 node_box.serialize(os, protocol_version);
1659 selection_box.serialize(os, protocol_version);
1660 writeU8(os, legacy_facedir_simple);
1661 writeU8(os, legacy_wallmounted);
1662 serializeSimpleSoundSpec(sound_footstep, os);
1663 serializeSimpleSoundSpec(sound_dig, os);
1664 serializeSimpleSoundSpec(sound_dug, os);
1665 writeU8(os, rightclickable);
1666 writeU8(os, drowning);
1667 writeU8(os, leveled);
1668 writeU8(os, liquid_range);
1669 writeU8(os, waving);
1670 os << serializeString(mesh);
1671 collision_box.serialize(os, protocol_version);
1672 writeU8(os, floodable);
1673 writeU16(os, connects_to_ids.size());
1674 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1675 i != connects_to_ids.end(); ++i)
1677 writeU8(os, connect_sides);
1679 throw SerializationError("ContentFeatures::serialize(): "
1680 "Unsupported version requested");
1684 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1686 if (version == 5) // In PROTOCOL_VERSION 13
1688 name = deSerializeString(is);
1690 u32 groups_size = readU16(is);
1691 for(u32 i=0; i<groups_size; i++){
1692 std::string name = deSerializeString(is);
1693 int value = readS16(is);
1694 groups[name] = value;
1696 drawtype = (enum NodeDrawType)readU8(is);
1698 visual_scale = readF1000(is);
1699 if (readU8(is) != 6)
1700 throw SerializationError("unsupported tile count");
1701 for (u32 i = 0; i < 6; i++)
1702 tiledef[i].deSerialize(is, version, drawtype);
1703 if (readU8(is) != CF_SPECIAL_COUNT)
1704 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1705 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1706 tiledef_special[i].deSerialize(is, version, drawtype);
1708 post_effect_color.setAlpha(readU8(is));
1709 post_effect_color.setRed(readU8(is));
1710 post_effect_color.setGreen(readU8(is));
1711 post_effect_color.setBlue(readU8(is));
1712 param_type = (enum ContentParamType)readU8(is);
1713 param_type_2 = (enum ContentParamType2)readU8(is);
1714 is_ground_content = readU8(is);
1715 light_propagates = readU8(is);
1716 sunlight_propagates = readU8(is);
1717 walkable = readU8(is);
1718 pointable = readU8(is);
1719 diggable = readU8(is);
1720 climbable = readU8(is);
1721 buildable_to = readU8(is);
1722 deSerializeString(is); // legacy: used to be metadata_name
1723 liquid_type = (enum LiquidType)readU8(is);
1724 liquid_alternative_flowing = deSerializeString(is);
1725 liquid_alternative_source = deSerializeString(is);
1726 liquid_viscosity = readU8(is);
1727 light_source = readU8(is);
1728 light_source = MYMIN(light_source, LIGHT_MAX);
1729 damage_per_second = readU32(is);
1730 node_box.deSerialize(is);
1731 selection_box.deSerialize(is);
1732 legacy_facedir_simple = readU8(is);
1733 legacy_wallmounted = readU8(is);
1734 deSerializeSimpleSoundSpec(sound_footstep, is);
1735 deSerializeSimpleSoundSpec(sound_dig, is);
1736 deSerializeSimpleSoundSpec(sound_dug, is);
1737 } else if (version == 6) {
1738 name = deSerializeString(is);
1740 u32 groups_size = readU16(is);
1741 for (u32 i = 0; i < groups_size; i++) {
1742 std::string name = deSerializeString(is);
1743 int value = readS16(is);
1744 groups[name] = value;
1746 drawtype = (enum NodeDrawType)readU8(is);
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 // CF_SPECIAL_COUNT in version 6 = 2
1753 if (readU8(is) != 2)
1754 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1755 for (u32 i = 0; i < 2; i++)
1756 tiledef_special[i].deSerialize(is, version, drawtype);
1758 post_effect_color.setAlpha(readU8(is));
1759 post_effect_color.setRed(readU8(is));
1760 post_effect_color.setGreen(readU8(is));
1761 post_effect_color.setBlue(readU8(is));
1762 param_type = (enum ContentParamType)readU8(is);
1763 param_type_2 = (enum ContentParamType2)readU8(is);
1764 is_ground_content = readU8(is);
1765 light_propagates = readU8(is);
1766 sunlight_propagates = readU8(is);
1767 walkable = readU8(is);
1768 pointable = readU8(is);
1769 diggable = readU8(is);
1770 climbable = readU8(is);
1771 buildable_to = readU8(is);
1772 deSerializeString(is); // legacy: used to be metadata_name
1773 liquid_type = (enum LiquidType)readU8(is);
1774 liquid_alternative_flowing = deSerializeString(is);
1775 liquid_alternative_source = deSerializeString(is);
1776 liquid_viscosity = readU8(is);
1777 liquid_renewable = readU8(is);
1778 light_source = readU8(is);
1779 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 } else if (version == 7 || version == 8){
1792 name = deSerializeString(is);
1794 u32 groups_size = readU16(is);
1795 for (u32 i = 0; i < groups_size; i++) {
1796 std::string name = deSerializeString(is);
1797 int value = readS16(is);
1798 groups[name] = value;
1800 drawtype = (enum NodeDrawType) readU8(is);
1802 visual_scale = readF1000(is);
1803 if (readU8(is) != 6)
1804 throw SerializationError("unsupported tile count");
1805 for (u32 i = 0; i < 6; i++)
1806 tiledef[i].deSerialize(is, version, drawtype);
1807 if (readU8(is) != CF_SPECIAL_COUNT)
1808 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1809 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1810 tiledef_special[i].deSerialize(is, version, drawtype);
1812 post_effect_color.setAlpha(readU8(is));
1813 post_effect_color.setRed(readU8(is));
1814 post_effect_color.setGreen(readU8(is));
1815 post_effect_color.setBlue(readU8(is));
1816 param_type = (enum ContentParamType) readU8(is);
1817 param_type_2 = (enum ContentParamType2) readU8(is);
1818 is_ground_content = readU8(is);
1819 light_propagates = readU8(is);
1820 sunlight_propagates = readU8(is);
1821 walkable = readU8(is);
1822 pointable = readU8(is);
1823 diggable = readU8(is);
1824 climbable = readU8(is);
1825 buildable_to = readU8(is);
1826 deSerializeString(is); // legacy: used to be metadata_name
1827 liquid_type = (enum LiquidType) readU8(is);
1828 liquid_alternative_flowing = deSerializeString(is);
1829 liquid_alternative_source = deSerializeString(is);
1830 liquid_viscosity = readU8(is);
1831 liquid_renewable = readU8(is);
1832 light_source = readU8(is);
1833 light_source = MYMIN(light_source, LIGHT_MAX);
1834 damage_per_second = readU32(is);
1835 node_box.deSerialize(is);
1836 selection_box.deSerialize(is);
1837 legacy_facedir_simple = readU8(is);
1838 legacy_wallmounted = readU8(is);
1839 deSerializeSimpleSoundSpec(sound_footstep, is);
1840 deSerializeSimpleSoundSpec(sound_dig, is);
1841 deSerializeSimpleSoundSpec(sound_dug, is);
1842 rightclickable = readU8(is);
1843 drowning = readU8(is);
1844 leveled = readU8(is);
1845 liquid_range = readU8(is);
1846 waving = readU8(is);
1848 mesh = deSerializeString(is);
1849 collision_box.deSerialize(is);
1850 floodable = readU8(is);
1851 u16 connects_to_size = readU16(is);
1852 connects_to_ids.clear();
1853 for (u16 i = 0; i < connects_to_size; i++)
1854 connects_to_ids.insert(readU16(is));
1855 connect_sides = readU8(is);
1856 } catch (SerializationError &e) {};
1858 throw SerializationError("unsupported ContentFeatures version");
1863 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1865 return m_node_registration_complete;
1869 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1871 m_node_registration_complete = completed;
1875 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1878 if (m_node_registration_complete)
1879 nr->nodeResolveInternal();
1881 m_pending_resolve_callbacks.push_back(nr);
1885 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1887 size_t len = m_pending_resolve_callbacks.size();
1888 for (size_t i = 0; i != len; i++) {
1889 if (nr != m_pending_resolve_callbacks[i])
1893 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1894 m_pending_resolve_callbacks.resize(len);
1902 void CNodeDefManager::runNodeResolveCallbacks()
1904 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1905 NodeResolver *nr = m_pending_resolve_callbacks[i];
1906 nr->nodeResolveInternal();
1909 m_pending_resolve_callbacks.clear();
1913 void CNodeDefManager::resetNodeResolveState()
1915 m_node_registration_complete = false;
1916 m_pending_resolve_callbacks.clear();
1919 void CNodeDefManager::mapNodeboxConnections()
1921 for (u32 i = 0; i < m_content_features.size(); i++) {
1922 ContentFeatures *f = &m_content_features[i];
1923 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1925 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1926 it != f->connects_to.end(); ++it) {
1927 getIds(*it, f->connects_to_ids);
1932 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1934 const ContentFeatures &f1 = get(from);
1936 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1939 // lookup target in connected set
1940 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1943 const ContentFeatures &f2 = get(to);
1945 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1946 // ignores actually looking if back connection exists
1947 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1949 // does to node declare usable faces?
1950 if (f2.connect_sides > 0) {
1951 if ((f2.param_type_2 == CPT2_FACEDIR ||
1952 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1953 && (connect_face >= 4)) {
1954 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1955 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1957 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1959 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1960 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1962 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1963 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1964 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1966 return (f2.connect_sides
1967 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1969 return (f2.connect_sides & connect_face);
1971 // the target is just a regular node, so connect no matter back connection
1979 NodeResolver::NodeResolver()
1982 m_nodenames_idx = 0;
1983 m_nnlistsizes_idx = 0;
1984 m_resolve_done = false;
1986 m_nodenames.reserve(16);
1987 m_nnlistsizes.reserve(4);
1991 NodeResolver::~NodeResolver()
1993 if (!m_resolve_done && m_ndef)
1994 m_ndef->cancelNodeResolveCallback(this);
1998 void NodeResolver::nodeResolveInternal()
2000 m_nodenames_idx = 0;
2001 m_nnlistsizes_idx = 0;
2004 m_resolve_done = true;
2006 m_nodenames.clear();
2007 m_nnlistsizes.clear();
2011 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
2012 const std::string &node_alt, content_t c_fallback)
2014 if (m_nodenames_idx == m_nodenames.size()) {
2015 *result_out = c_fallback;
2016 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2021 std::string name = m_nodenames[m_nodenames_idx++];
2023 bool success = m_ndef->getId(name, c);
2024 if (!success && node_alt != "") {
2026 success = m_ndef->getId(name, c);
2030 errorstream << "NodeResolver: failed to resolve node name '" << name
2031 << "'." << std::endl;
2040 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
2041 bool all_required, content_t c_fallback)
2043 bool success = true;
2045 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
2046 errorstream << "NodeResolver: no more node lists" << std::endl;
2050 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
2053 if (m_nodenames_idx == m_nodenames.size()) {
2054 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2059 std::string &name = m_nodenames[m_nodenames_idx++];
2061 if (name.substr(0,6) != "group:") {
2062 if (m_ndef->getId(name, c)) {
2063 result_out->push_back(c);
2064 } else if (all_required) {
2065 errorstream << "NodeResolver: failed to resolve node name '"
2066 << name << "'." << std::endl;
2067 result_out->push_back(c_fallback);
2071 std::set<content_t> cids;
2072 std::set<content_t>::iterator it;
2073 m_ndef->getIds(name, cids);
2074 for (it = cids.begin(); it != cids.end(); ++it)
2075 result_out->push_back(*it);