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 = idef->getAll();
1336 m_name_id_mapping_with_aliases.clear();
1337 for (std::set<std::string>::iterator
1338 i = all.begin(); i != all.end(); ++i) {
1339 std::string name = *i;
1340 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 std::vector<video::SColor> *CNodeDefManager::getPalette(
1404 const ContentFeatures &f, const IGameDef *gamedef)
1407 // This works because colors always use the most significant bits
1408 // of param2. If you add a new colored type which uses param2
1409 // in a more advanced way, you should change this code, too.
1410 u32 palette_pixels = 0;
1411 switch (f.param_type_2) {
1413 palette_pixels = 256;
1415 case CPT2_COLORED_FACEDIR:
1418 case CPT2_COLORED_WALLMOUNTED:
1419 palette_pixels = 32;
1424 // This many param2 values will have the same color
1425 u32 step = 256 / palette_pixels;
1426 const std::string &name = f.palette_name;
1429 Client *client = (Client *) gamedef;
1430 ITextureSource *tsrc = client->tsrc();
1432 UNORDERED_MAP<std::string, std::vector<video::SColor> >::iterator it =
1433 m_palettes.find(name);
1434 if (it == m_palettes.end()) {
1436 if (!tsrc->isKnownSourceImage(name)) {
1437 warningstream << "CNodeDefManager::getPalette(): palette \"" << name
1438 << "\" could not be loaded." << std::endl;
1441 video::IImage *img = tsrc->generateImage(name);
1442 std::vector<video::SColor> new_palette;
1443 u32 w = img->getDimension().Width;
1444 u32 h = img->getDimension().Height;
1445 // Real area of the image
1447 if (area != palette_pixels)
1448 warningstream << "CNodeDefManager::getPalette(): the "
1449 << "specified palette image \"" << name << "\" does not "
1450 << "contain exactly " << palette_pixels
1451 << " pixels." << std::endl;
1452 if (area > palette_pixels)
1453 area = palette_pixels;
1454 // For each pixel in the image
1455 for (u32 i = 0; i < area; i++) {
1456 video::SColor c = img->getPixel(i % w, i / w);
1457 // Fill in palette with 'step' colors
1458 for (u32 j = 0; j < step; j++)
1459 new_palette.push_back(c);
1462 // Fill in remaining elements
1463 while (new_palette.size() < 256)
1464 new_palette.push_back(video::SColor(0xFFFFFFFF));
1465 m_palettes[name] = new_palette;
1466 it = m_palettes.find(name);
1468 if (it != m_palettes.end())
1469 return &((*it).second);
1475 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1476 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1477 void *progress_callback_args)
1480 infostream << "CNodeDefManager::updateTextures(): Updating "
1481 "textures in node definitions" << std::endl;
1483 Client *client = (Client *)gamedef;
1484 ITextureSource *tsrc = client->tsrc();
1485 IShaderSource *shdsrc = client->getShaderSource();
1486 scene::ISceneManager* smgr = client->getSceneManager();
1487 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1488 TextureSettings tsettings;
1489 tsettings.readSettings();
1492 u32 size = m_content_features.size();
1494 for (u32 i = 0; i < size; i++) {
1495 ContentFeatures *f = &(m_content_features[i]);
1496 f->palette = getPalette(*f, gamedef);
1497 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1498 progress_callback(progress_callback_args, i, size);
1503 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1505 writeU8(os, 1); // version
1507 std::ostringstream os2(std::ios::binary);
1508 for (u32 i = 0; i < m_content_features.size(); i++) {
1509 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1510 || i == CONTENT_UNKNOWN)
1512 const ContentFeatures *f = &m_content_features[i];
1516 // Wrap it in a string to allow different lengths without
1517 // strict version incompatibilities
1518 std::ostringstream wrapper_os(std::ios::binary);
1519 f->serialize(wrapper_os, protocol_version);
1520 os2<<serializeString(wrapper_os.str());
1522 // must not overflow
1523 u16 next = count + 1;
1524 FATAL_ERROR_IF(next < count, "Overflow");
1527 writeU16(os, count);
1528 os << serializeLongString(os2.str());
1532 void CNodeDefManager::deSerialize(std::istream &is)
1535 int version = readU8(is);
1537 throw SerializationError("unsupported NodeDefinitionManager version");
1538 u16 count = readU16(is);
1539 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1541 for (u16 n = 0; n < count; n++) {
1542 u16 i = readU16(is2);
1544 // Read it from the string wrapper
1545 std::string wrapper = deSerializeString(is2);
1546 std::istringstream wrapper_is(wrapper, std::ios::binary);
1547 f.deSerialize(wrapper_is);
1549 // Check error conditions
1550 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1551 warningstream << "NodeDefManager::deSerialize(): "
1552 "not changing builtin node " << i << std::endl;
1556 warningstream << "NodeDefManager::deSerialize(): "
1557 "received empty name" << std::endl;
1563 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1564 warningstream << "NodeDefManager::deSerialize(): "
1565 "already defined with different ID: " << f.name << std::endl;
1569 // All is ok, add node definition with the requested ID
1570 if (i >= m_content_features.size())
1571 m_content_features.resize((u32)(i) + 1);
1572 m_content_features[i] = f;
1573 addNameIdMapping(i, f.name);
1574 verbosestream << "deserialized " << f.name << std::endl;
1576 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1577 fixSelectionBoxIntUnion();
1582 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1584 m_name_id_mapping.set(i, name);
1585 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1589 IWritableNodeDefManager *createNodeDefManager()
1591 return new CNodeDefManager();
1595 //// Serialization of old ContentFeatures formats
1596 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1598 u8 compatible_param_type_2 = param_type_2;
1599 if ((protocol_version < 28)
1600 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1601 compatible_param_type_2 = CPT2_NONE;
1602 else if (protocol_version < 30) {
1603 if (compatible_param_type_2 == CPT2_COLOR)
1604 compatible_param_type_2 = CPT2_NONE;
1605 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1606 compatible_param_type_2 = CPT2_FACEDIR;
1607 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1608 compatible_param_type_2 = CPT2_WALLMOUNTED;
1611 float compatible_visual_scale = visual_scale;
1612 if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1613 compatible_visual_scale = sqrt(visual_scale);
1616 if (protocol_version < 30) {
1617 writeU8(os, protocol_version < 27 ? 7 : 8);
1619 os << serializeString(name);
1620 writeU16(os, groups.size());
1621 for (ItemGroupList::const_iterator i = groups.begin();
1622 i != groups.end(); ++i) {
1623 os << serializeString(i->first);
1624 writeS16(os, i->second);
1626 writeU8(os, drawtype);
1627 writeF1000(os, compatible_visual_scale);
1629 for (u32 i = 0; i < 6; i++)
1630 tiledef[i].serialize(os, protocol_version);
1631 writeU8(os, CF_SPECIAL_COUNT);
1632 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1633 tiledef_special[i].serialize(os, protocol_version);
1635 writeU8(os, post_effect_color.getAlpha());
1636 writeU8(os, post_effect_color.getRed());
1637 writeU8(os, post_effect_color.getGreen());
1638 writeU8(os, post_effect_color.getBlue());
1639 writeU8(os, param_type);
1640 writeU8(os, compatible_param_type_2);
1641 writeU8(os, is_ground_content);
1642 writeU8(os, light_propagates);
1643 writeU8(os, sunlight_propagates);
1644 writeU8(os, walkable);
1645 writeU8(os, pointable);
1646 writeU8(os, diggable);
1647 writeU8(os, climbable);
1648 writeU8(os, buildable_to);
1649 os << serializeString(""); // legacy: used to be metadata_name
1650 writeU8(os, liquid_type);
1651 os << serializeString(liquid_alternative_flowing);
1652 os << serializeString(liquid_alternative_source);
1653 writeU8(os, liquid_viscosity);
1654 writeU8(os, liquid_renewable);
1655 writeU8(os, light_source);
1656 writeU32(os, damage_per_second);
1657 node_box.serialize(os, protocol_version);
1658 selection_box.serialize(os, protocol_version);
1659 writeU8(os, legacy_facedir_simple);
1660 writeU8(os, legacy_wallmounted);
1661 serializeSimpleSoundSpec(sound_footstep, os);
1662 serializeSimpleSoundSpec(sound_dig, os);
1663 serializeSimpleSoundSpec(sound_dug, os);
1664 writeU8(os, rightclickable);
1665 writeU8(os, drowning);
1666 writeU8(os, leveled);
1667 writeU8(os, liquid_range);
1668 writeU8(os, waving);
1669 os << serializeString(mesh);
1670 collision_box.serialize(os, protocol_version);
1671 writeU8(os, floodable);
1672 writeU16(os, connects_to_ids.size());
1673 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1674 i != connects_to_ids.end(); ++i)
1676 writeU8(os, connect_sides);
1678 throw SerializationError("ContentFeatures::serialize(): "
1679 "Unsupported version requested");
1683 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1685 if (version == 5) // In PROTOCOL_VERSION 13
1687 name = deSerializeString(is);
1689 u32 groups_size = readU16(is);
1690 for(u32 i=0; i<groups_size; i++){
1691 std::string name = deSerializeString(is);
1692 int value = readS16(is);
1693 groups[name] = value;
1695 drawtype = (enum NodeDrawType)readU8(is);
1697 visual_scale = readF1000(is);
1698 if (readU8(is) != 6)
1699 throw SerializationError("unsupported tile count");
1700 for (u32 i = 0; i < 6; i++)
1701 tiledef[i].deSerialize(is, version, drawtype);
1702 if (readU8(is) != CF_SPECIAL_COUNT)
1703 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1704 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1705 tiledef_special[i].deSerialize(is, version, drawtype);
1707 post_effect_color.setAlpha(readU8(is));
1708 post_effect_color.setRed(readU8(is));
1709 post_effect_color.setGreen(readU8(is));
1710 post_effect_color.setBlue(readU8(is));
1711 param_type = (enum ContentParamType)readU8(is);
1712 param_type_2 = (enum ContentParamType2)readU8(is);
1713 is_ground_content = readU8(is);
1714 light_propagates = readU8(is);
1715 sunlight_propagates = readU8(is);
1716 walkable = readU8(is);
1717 pointable = readU8(is);
1718 diggable = readU8(is);
1719 climbable = readU8(is);
1720 buildable_to = readU8(is);
1721 deSerializeString(is); // legacy: used to be metadata_name
1722 liquid_type = (enum LiquidType)readU8(is);
1723 liquid_alternative_flowing = deSerializeString(is);
1724 liquid_alternative_source = deSerializeString(is);
1725 liquid_viscosity = readU8(is);
1726 light_source = readU8(is);
1727 light_source = MYMIN(light_source, LIGHT_MAX);
1728 damage_per_second = readU32(is);
1729 node_box.deSerialize(is);
1730 selection_box.deSerialize(is);
1731 legacy_facedir_simple = readU8(is);
1732 legacy_wallmounted = readU8(is);
1733 deSerializeSimpleSoundSpec(sound_footstep, is);
1734 deSerializeSimpleSoundSpec(sound_dig, is);
1735 deSerializeSimpleSoundSpec(sound_dug, is);
1736 } else if (version == 6) {
1737 name = deSerializeString(is);
1739 u32 groups_size = readU16(is);
1740 for (u32 i = 0; i < groups_size; i++) {
1741 std::string name = deSerializeString(is);
1742 int value = readS16(is);
1743 groups[name] = value;
1745 drawtype = (enum NodeDrawType)readU8(is);
1746 visual_scale = readF1000(is);
1747 if (readU8(is) != 6)
1748 throw SerializationError("unsupported tile count");
1749 for (u32 i = 0; i < 6; i++)
1750 tiledef[i].deSerialize(is, version, drawtype);
1751 // CF_SPECIAL_COUNT in version 6 = 2
1752 if (readU8(is) != 2)
1753 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1754 for (u32 i = 0; i < 2; i++)
1755 tiledef_special[i].deSerialize(is, version, drawtype);
1757 post_effect_color.setAlpha(readU8(is));
1758 post_effect_color.setRed(readU8(is));
1759 post_effect_color.setGreen(readU8(is));
1760 post_effect_color.setBlue(readU8(is));
1761 param_type = (enum ContentParamType)readU8(is);
1762 param_type_2 = (enum ContentParamType2)readU8(is);
1763 is_ground_content = readU8(is);
1764 light_propagates = readU8(is);
1765 sunlight_propagates = readU8(is);
1766 walkable = readU8(is);
1767 pointable = readU8(is);
1768 diggable = readU8(is);
1769 climbable = readU8(is);
1770 buildable_to = readU8(is);
1771 deSerializeString(is); // legacy: used to be metadata_name
1772 liquid_type = (enum LiquidType)readU8(is);
1773 liquid_alternative_flowing = deSerializeString(is);
1774 liquid_alternative_source = deSerializeString(is);
1775 liquid_viscosity = readU8(is);
1776 liquid_renewable = readU8(is);
1777 light_source = readU8(is);
1778 damage_per_second = readU32(is);
1779 node_box.deSerialize(is);
1780 selection_box.deSerialize(is);
1781 legacy_facedir_simple = readU8(is);
1782 legacy_wallmounted = readU8(is);
1783 deSerializeSimpleSoundSpec(sound_footstep, is);
1784 deSerializeSimpleSoundSpec(sound_dig, is);
1785 deSerializeSimpleSoundSpec(sound_dug, is);
1786 rightclickable = readU8(is);
1787 drowning = readU8(is);
1788 leveled = readU8(is);
1789 liquid_range = readU8(is);
1790 } else if (version == 7 || version == 8){
1791 name = deSerializeString(is);
1793 u32 groups_size = readU16(is);
1794 for (u32 i = 0; i < groups_size; i++) {
1795 std::string name = deSerializeString(is);
1796 int value = readS16(is);
1797 groups[name] = value;
1799 drawtype = (enum NodeDrawType) readU8(is);
1801 visual_scale = readF1000(is);
1802 if (readU8(is) != 6)
1803 throw SerializationError("unsupported tile count");
1804 for (u32 i = 0; i < 6; i++)
1805 tiledef[i].deSerialize(is, version, drawtype);
1806 if (readU8(is) != CF_SPECIAL_COUNT)
1807 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1808 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1809 tiledef_special[i].deSerialize(is, version, drawtype);
1811 post_effect_color.setAlpha(readU8(is));
1812 post_effect_color.setRed(readU8(is));
1813 post_effect_color.setGreen(readU8(is));
1814 post_effect_color.setBlue(readU8(is));
1815 param_type = (enum ContentParamType) readU8(is);
1816 param_type_2 = (enum ContentParamType2) readU8(is);
1817 is_ground_content = readU8(is);
1818 light_propagates = readU8(is);
1819 sunlight_propagates = readU8(is);
1820 walkable = readU8(is);
1821 pointable = readU8(is);
1822 diggable = readU8(is);
1823 climbable = readU8(is);
1824 buildable_to = readU8(is);
1825 deSerializeString(is); // legacy: used to be metadata_name
1826 liquid_type = (enum LiquidType) readU8(is);
1827 liquid_alternative_flowing = deSerializeString(is);
1828 liquid_alternative_source = deSerializeString(is);
1829 liquid_viscosity = readU8(is);
1830 liquid_renewable = readU8(is);
1831 light_source = readU8(is);
1832 light_source = MYMIN(light_source, LIGHT_MAX);
1833 damage_per_second = readU32(is);
1834 node_box.deSerialize(is);
1835 selection_box.deSerialize(is);
1836 legacy_facedir_simple = readU8(is);
1837 legacy_wallmounted = readU8(is);
1838 deSerializeSimpleSoundSpec(sound_footstep, is);
1839 deSerializeSimpleSoundSpec(sound_dig, is);
1840 deSerializeSimpleSoundSpec(sound_dug, is);
1841 rightclickable = readU8(is);
1842 drowning = readU8(is);
1843 leveled = readU8(is);
1844 liquid_range = readU8(is);
1845 waving = readU8(is);
1847 mesh = deSerializeString(is);
1848 collision_box.deSerialize(is);
1849 floodable = readU8(is);
1850 u16 connects_to_size = readU16(is);
1851 connects_to_ids.clear();
1852 for (u16 i = 0; i < connects_to_size; i++)
1853 connects_to_ids.insert(readU16(is));
1854 connect_sides = readU8(is);
1855 } catch (SerializationError &e) {};
1857 throw SerializationError("unsupported ContentFeatures version");
1862 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1864 return m_node_registration_complete;
1868 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1870 m_node_registration_complete = completed;
1874 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1877 if (m_node_registration_complete)
1878 nr->nodeResolveInternal();
1880 m_pending_resolve_callbacks.push_back(nr);
1884 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1886 size_t len = m_pending_resolve_callbacks.size();
1887 for (size_t i = 0; i != len; i++) {
1888 if (nr != m_pending_resolve_callbacks[i])
1892 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1893 m_pending_resolve_callbacks.resize(len);
1901 void CNodeDefManager::runNodeResolveCallbacks()
1903 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1904 NodeResolver *nr = m_pending_resolve_callbacks[i];
1905 nr->nodeResolveInternal();
1908 m_pending_resolve_callbacks.clear();
1912 void CNodeDefManager::resetNodeResolveState()
1914 m_node_registration_complete = false;
1915 m_pending_resolve_callbacks.clear();
1918 void CNodeDefManager::mapNodeboxConnections()
1920 for (u32 i = 0; i < m_content_features.size(); i++) {
1921 ContentFeatures *f = &m_content_features[i];
1922 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1924 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1925 it != f->connects_to.end(); ++it) {
1926 getIds(*it, f->connects_to_ids);
1931 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1933 const ContentFeatures &f1 = get(from);
1935 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1938 // lookup target in connected set
1939 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1942 const ContentFeatures &f2 = get(to);
1944 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1945 // ignores actually looking if back connection exists
1946 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1948 // does to node declare usable faces?
1949 if (f2.connect_sides > 0) {
1950 if ((f2.param_type_2 == CPT2_FACEDIR ||
1951 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1952 && (connect_face >= 4)) {
1953 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1954 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1956 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1958 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1959 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1961 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 32, 16, 8, 4 // 32 - left
1965 return (f2.connect_sides
1966 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1968 return (f2.connect_sides & connect_face);
1970 // the target is just a regular node, so connect no matter back connection
1978 NodeResolver::NodeResolver()
1981 m_nodenames_idx = 0;
1982 m_nnlistsizes_idx = 0;
1983 m_resolve_done = false;
1985 m_nodenames.reserve(16);
1986 m_nnlistsizes.reserve(4);
1990 NodeResolver::~NodeResolver()
1992 if (!m_resolve_done && m_ndef)
1993 m_ndef->cancelNodeResolveCallback(this);
1997 void NodeResolver::nodeResolveInternal()
1999 m_nodenames_idx = 0;
2000 m_nnlistsizes_idx = 0;
2003 m_resolve_done = true;
2005 m_nodenames.clear();
2006 m_nnlistsizes.clear();
2010 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
2011 const std::string &node_alt, content_t c_fallback)
2013 if (m_nodenames_idx == m_nodenames.size()) {
2014 *result_out = c_fallback;
2015 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2020 std::string name = m_nodenames[m_nodenames_idx++];
2022 bool success = m_ndef->getId(name, c);
2023 if (!success && node_alt != "") {
2025 success = m_ndef->getId(name, c);
2029 errorstream << "NodeResolver: failed to resolve node name '" << name
2030 << "'." << std::endl;
2039 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
2040 bool all_required, content_t c_fallback)
2042 bool success = true;
2044 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
2045 errorstream << "NodeResolver: no more node lists" << std::endl;
2049 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
2052 if (m_nodenames_idx == m_nodenames.size()) {
2053 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2058 std::string &name = m_nodenames[m_nodenames_idx++];
2060 if (name.substr(0,6) != "group:") {
2061 if (m_ndef->getId(name, c)) {
2062 result_out->push_back(c);
2063 } else if (all_required) {
2064 errorstream << "NodeResolver: failed to resolve node name '"
2065 << name << "'." << std::endl;
2066 result_out->push_back(c_fallback);
2070 std::set<content_t> cids;
2071 std::set<content_t>::iterator it;
2072 m_ndef->getIds(name, cids);
2073 for (it = cids.begin(); it != cids.end(); ++it)
2074 result_out->push_back(*it);