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
65 if (protocol_version >= 27)
67 else if (protocol_version >= 21)
75 writeU8(os, NODEBOX_FIXED);
79 writeU16(os, fixed.size());
80 for (std::vector<aabb3f>::const_iterator
82 i != fixed.end(); ++i)
84 writeV3F1000(os, i->MinEdge);
85 writeV3F1000(os, i->MaxEdge);
88 case NODEBOX_WALLMOUNTED:
91 writeV3F1000(os, wall_top.MinEdge);
92 writeV3F1000(os, wall_top.MaxEdge);
93 writeV3F1000(os, wall_bottom.MinEdge);
94 writeV3F1000(os, wall_bottom.MaxEdge);
95 writeV3F1000(os, wall_side.MinEdge);
96 writeV3F1000(os, wall_side.MaxEdge);
98 case NODEBOX_CONNECTED:
100 // send old clients nodes that can't be walked through
102 writeU8(os, NODEBOX_FIXED);
105 writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2));
106 writeV3F1000(os, v3f(BS/2, BS/2, BS/2));
110 #define WRITEBOX(box) do { \
111 writeU16(os, (box).size()); \
112 for (std::vector<aabb3f>::const_iterator \
114 i != (box).end(); ++i) { \
115 writeV3F1000(os, i->MinEdge); \
116 writeV3F1000(os, i->MaxEdge); \
120 WRITEBOX(connect_top);
121 WRITEBOX(connect_bottom);
122 WRITEBOX(connect_front);
123 WRITEBOX(connect_left);
124 WRITEBOX(connect_back);
125 WRITEBOX(connect_right);
134 void NodeBox::deSerialize(std::istream &is)
136 int version = readU8(is);
137 if (version < 1 || version > 3)
138 throw SerializationError("unsupported NodeBox version");
142 type = (enum NodeBoxType)readU8(is);
144 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
146 u16 fixed_count = readU16(is);
150 box.MinEdge = readV3F1000(is);
151 box.MaxEdge = readV3F1000(is);
152 fixed.push_back(box);
155 else if(type == NODEBOX_WALLMOUNTED)
157 wall_top.MinEdge = readV3F1000(is);
158 wall_top.MaxEdge = readV3F1000(is);
159 wall_bottom.MinEdge = readV3F1000(is);
160 wall_bottom.MaxEdge = readV3F1000(is);
161 wall_side.MinEdge = readV3F1000(is);
162 wall_side.MaxEdge = readV3F1000(is);
164 else if (type == NODEBOX_CONNECTED)
166 #define READBOXES(box) do { \
167 count = readU16(is); \
168 (box).reserve(count); \
170 v3f min = readV3F1000(is); \
171 v3f max = readV3F1000(is); \
172 (box).push_back(aabb3f(min, max)); }; } while (0)
177 READBOXES(connect_top);
178 READBOXES(connect_bottom);
179 READBOXES(connect_front);
180 READBOXES(connect_left);
181 READBOXES(connect_back);
182 READBOXES(connect_right);
190 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
192 if (protocol_version >= 30)
194 else if (protocol_version >= 29)
196 else if (protocol_version >= 26)
198 else if (protocol_version >= 17)
202 os<<serializeString(name);
203 animation.serialize(os, protocol_version);
204 if (protocol_version >= 17)
205 writeU8(os, backface_culling);
206 if (protocol_version >= 26) {
207 writeU8(os, tileable_horizontal);
208 writeU8(os, tileable_vertical);
210 if (protocol_version >= 30) {
211 writeU8(os, has_color);
213 writeU8(os, color.getRed());
214 writeU8(os, color.getGreen());
215 writeU8(os, color.getBlue());
220 void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
222 int version = readU8(is);
223 name = deSerializeString(is);
224 animation.deSerialize(is, version >= 3 ? 29 : 26);
226 backface_culling = readU8(is);
228 tileable_horizontal = readU8(is);
229 tileable_vertical = readU8(is);
232 has_color = readU8(is);
234 color.setRed(readU8(is));
235 color.setGreen(readU8(is));
236 color.setBlue(readU8(is));
240 if ((contenfeatures_version < 8) &&
241 ((drawtype == NDT_MESH) ||
242 (drawtype == NDT_FIRELIKE) ||
243 (drawtype == NDT_LIQUID) ||
244 (drawtype == NDT_PLANTLIKE)))
245 backface_culling = false;
250 SimpleSoundSpec serialization
253 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
256 os<<serializeString(ss.name);
257 writeF1000(os, ss.gain);
259 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
261 ss.name = deSerializeString(is);
262 ss.gain = readF1000(is);
265 void TextureSettings::readSettings()
267 connected_glass = g_settings->getBool("connected_glass");
268 opaque_water = g_settings->getBool("opaque_water");
269 bool enable_shaders = g_settings->getBool("enable_shaders");
270 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
271 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
272 bool smooth_lighting = g_settings->getBool("smooth_lighting");
273 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
274 enable_minimap = g_settings->getBool("enable_minimap");
275 std::string leaves_style_str = g_settings->get("leaves_style");
277 // Mesh cache is not supported in combination with smooth lighting
279 enable_mesh_cache = false;
281 use_normal_texture = enable_shaders &&
282 (enable_bumpmapping || enable_parallax_occlusion);
283 if (leaves_style_str == "fancy") {
284 leaves_style = LEAVES_FANCY;
285 } else if (leaves_style_str == "simple") {
286 leaves_style = LEAVES_SIMPLE;
288 leaves_style = LEAVES_OPAQUE;
296 ContentFeatures::ContentFeatures()
301 ContentFeatures::~ContentFeatures()
305 void ContentFeatures::reset()
312 visual_solidness = 0;
313 backface_culling = true;
316 has_on_construct = false;
317 has_on_destruct = false;
318 has_after_destruct = false;
322 NOTE: Most of this is always overridden by the default values given
327 // Unknown nodes can be dug
328 groups["dig_immediate"] = 2;
329 drawtype = NDT_NORMAL;
332 for(u32 i = 0; i < 24; i++)
334 minimap_color = video::SColor(0, 0, 0, 0);
337 for(u32 i = 0; i < 6; i++)
338 tiledef[i] = TileDef();
339 for(u16 j = 0; j < CF_SPECIAL_COUNT; j++)
340 tiledef_special[j] = TileDef();
342 post_effect_color = video::SColor(0, 0, 0, 0);
343 param_type = CPT_NONE;
344 param_type_2 = CPT2_NONE;
345 is_ground_content = false;
346 light_propagates = false;
347 sunlight_propagates = false;
352 buildable_to = false;
354 rightclickable = true;
356 liquid_type = LIQUID_NONE;
357 liquid_alternative_flowing = "";
358 liquid_alternative_source = "";
359 liquid_viscosity = 0;
360 liquid_renewable = true;
361 liquid_range = LIQUID_LEVEL_MAX+1;
364 damage_per_second = 0;
365 node_box = NodeBox();
366 selection_box = NodeBox();
367 collision_box = NodeBox();
369 legacy_facedir_simple = false;
370 legacy_wallmounted = false;
371 sound_footstep = SimpleSoundSpec();
372 sound_dig = SimpleSoundSpec("__group");
373 sound_dug = SimpleSoundSpec();
375 connects_to_ids.clear();
377 color = video::SColor(0xFFFFFFFF);
382 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
384 if (protocol_version < 30) {
385 serializeOld(os, protocol_version);
393 os << serializeString(name);
394 writeU16(os, groups.size());
395 for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end();
397 os << serializeString(i->first);
398 writeS16(os, i->second);
400 writeU8(os, param_type);
401 writeU8(os, param_type_2);
404 writeU8(os, drawtype);
405 os << serializeString(mesh);
406 writeF1000(os, visual_scale);
408 for (u32 i = 0; i < 6; i++)
409 tiledef[i].serialize(os, protocol_version);
410 writeU8(os, CF_SPECIAL_COUNT);
411 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
412 tiledef_special[i].serialize(os, protocol_version);
415 writeU8(os, color.getRed());
416 writeU8(os, color.getGreen());
417 writeU8(os, color.getBlue());
418 os << serializeString(palette_name);
420 writeU8(os, connect_sides);
421 writeU16(os, connects_to_ids.size());
422 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
423 i != connects_to_ids.end(); ++i)
425 writeU8(os, post_effect_color.getAlpha());
426 writeU8(os, post_effect_color.getRed());
427 writeU8(os, post_effect_color.getGreen());
428 writeU8(os, post_effect_color.getBlue());
429 writeU8(os, leveled);
432 writeU8(os, light_propagates);
433 writeU8(os, sunlight_propagates);
434 writeU8(os, light_source);
437 writeU8(os, is_ground_content);
440 writeU8(os, walkable);
441 writeU8(os, pointable);
442 writeU8(os, diggable);
443 writeU8(os, climbable);
444 writeU8(os, buildable_to);
445 writeU8(os, rightclickable);
446 writeU32(os, damage_per_second);
449 writeU8(os, liquid_type);
450 os << serializeString(liquid_alternative_flowing);
451 os << serializeString(liquid_alternative_source);
452 writeU8(os, liquid_viscosity);
453 writeU8(os, liquid_renewable);
454 writeU8(os, liquid_range);
455 writeU8(os, drowning);
456 writeU8(os, floodable);
459 node_box.serialize(os, protocol_version);
460 selection_box.serialize(os, protocol_version);
461 collision_box.serialize(os, protocol_version);
464 serializeSimpleSoundSpec(sound_footstep, os);
465 serializeSimpleSoundSpec(sound_dig, os);
466 serializeSimpleSoundSpec(sound_dug, os);
469 writeU8(os, legacy_facedir_simple);
470 writeU8(os, legacy_wallmounted);
473 void ContentFeatures::correctAlpha()
475 if (alpha == 0 || alpha == 255)
478 for (u32 i = 0; i < 6; i++) {
480 s << tiledef[i].name << "^[noalpha^[opacity:" << ((int)alpha);
481 tiledef[i].name = s.str();
484 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
486 s << tiledef_special[i].name << "^[noalpha^[opacity:" << ((int)alpha);
487 tiledef_special[i].name = s.str();
491 void ContentFeatures::deSerialize(std::istream &is)
494 int version = readU8(is);
496 deSerializeOld(is, version);
498 } else if (version > 9) {
499 throw SerializationError("unsupported ContentFeatures version");
503 name = deSerializeString(is);
505 u32 groups_size = readU16(is);
506 for (u32 i = 0; i < groups_size; i++) {
507 std::string name = deSerializeString(is);
508 int value = readS16(is);
509 groups[name] = value;
511 param_type = (enum ContentParamType) readU8(is);
512 param_type_2 = (enum ContentParamType2) readU8(is);
515 drawtype = (enum NodeDrawType) readU8(is);
516 mesh = deSerializeString(is);
517 visual_scale = readF1000(is);
519 throw SerializationError("unsupported tile count");
520 for (u32 i = 0; i < 6; i++)
521 tiledef[i].deSerialize(is, version, drawtype);
522 if (readU8(is) != CF_SPECIAL_COUNT)
523 throw SerializationError("unsupported CF_SPECIAL_COUNT");
524 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
525 tiledef_special[i].deSerialize(is, version, drawtype);
527 color.setRed(readU8(is));
528 color.setGreen(readU8(is));
529 color.setBlue(readU8(is));
530 palette_name = deSerializeString(is);
532 connect_sides = readU8(is);
533 u16 connects_to_size = readU16(is);
534 connects_to_ids.clear();
535 for (u16 i = 0; i < connects_to_size; i++)
536 connects_to_ids.insert(readU16(is));
537 post_effect_color.setAlpha(readU8(is));
538 post_effect_color.setRed(readU8(is));
539 post_effect_color.setGreen(readU8(is));
540 post_effect_color.setBlue(readU8(is));
541 leveled = readU8(is);
544 light_propagates = readU8(is);
545 sunlight_propagates = readU8(is);
546 light_source = readU8(is);
547 light_source = MYMIN(light_source, LIGHT_MAX);
550 is_ground_content = readU8(is);
553 walkable = readU8(is);
554 pointable = readU8(is);
555 diggable = readU8(is);
556 climbable = readU8(is);
557 buildable_to = readU8(is);
558 rightclickable = readU8(is);
559 damage_per_second = readU32(is);
562 liquid_type = (enum LiquidType) readU8(is);
563 liquid_alternative_flowing = deSerializeString(is);
564 liquid_alternative_source = deSerializeString(is);
565 liquid_viscosity = readU8(is);
566 liquid_renewable = readU8(is);
567 liquid_range = readU8(is);
568 drowning = readU8(is);
569 floodable = readU8(is);
572 node_box.deSerialize(is);
573 selection_box.deSerialize(is);
574 collision_box.deSerialize(is);
577 deSerializeSimpleSoundSpec(sound_footstep, is);
578 deSerializeSimpleSoundSpec(sound_dig, is);
579 deSerializeSimpleSoundSpec(sound_dug, is);
581 // read legacy properties
582 legacy_facedir_simple = readU8(is);
583 legacy_wallmounted = readU8(is);
587 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
588 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
589 bool backface_culling, u8 material_type)
591 tile->shader_id = shader_id;
592 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
593 tile->material_type = material_type;
595 // Normal texture and shader flags texture
596 if (use_normal_texture) {
597 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
599 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
602 tile->material_flags = 0;
603 if (backface_culling)
604 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
605 if (tiledef->animation.type != TAT_NONE)
606 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
607 if (tiledef->tileable_horizontal)
608 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
609 if (tiledef->tileable_vertical)
610 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
613 tile->has_color = tiledef->has_color;
614 if (tiledef->has_color)
615 tile->color = tiledef->color;
619 // Animation parameters
621 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
623 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
624 &frame_count, &frame_length_ms, NULL);
625 tile->animation_frame_count = frame_count;
626 tile->animation_frame_length_ms = frame_length_ms;
629 if (frame_count == 1) {
630 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
632 std::ostringstream os(std::ios::binary);
633 tile->frames.resize(frame_count);
635 for (int i = 0; i < frame_count; i++) {
641 tiledef->animation.getTextureModifer(os,
642 tile->texture->getOriginalSize(), i);
644 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
645 if (tile->normal_texture)
646 frame.normal_texture = tsrc->getNormalTexture(os.str());
647 frame.flags_texture = tile->flags_texture;
648 tile->frames[i] = frame;
655 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
656 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
658 // minimap pixel color - the average color of a texture
659 if (tsettings.enable_minimap && tiledef[0].name != "")
660 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
662 // Figure out the actual tiles to use
664 for (u32 j = 0; j < 6; j++) {
665 tdef[j] = tiledef[j];
666 if (tdef[j].name == "")
667 tdef[j].name = "unknown_node.png";
670 bool is_liquid = false;
671 bool is_water_surface = false;
673 u8 material_type = (alpha == 255) ?
674 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
685 assert(liquid_type == LIQUID_SOURCE);
686 if (tsettings.opaque_water)
691 case NDT_FLOWINGLIQUID:
692 assert(liquid_type == LIQUID_FLOWING);
694 if (tsettings.opaque_water)
700 visual_solidness = 1;
702 case NDT_GLASSLIKE_FRAMED:
704 visual_solidness = 1;
706 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
708 visual_solidness = 1;
709 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
713 visual_solidness = 1;
715 case NDT_ALLFACES_OPTIONAL:
716 if (tsettings.leaves_style == LEAVES_FANCY) {
717 drawtype = NDT_ALLFACES;
719 visual_solidness = 1;
720 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
721 for (u32 j = 0; j < 6; j++) {
722 if (tiledef_special[j].name != "")
723 tdef[j].name = tiledef_special[j].name;
725 drawtype = NDT_GLASSLIKE;
727 visual_solidness = 1;
729 drawtype = NDT_NORMAL;
731 for (u32 i = 0; i < 6; i++)
732 tdef[i].name += std::string("^[noalpha");
735 material_type = TILE_MATERIAL_WAVING_LEAVES;
740 material_type = TILE_MATERIAL_WAVING_PLANTS;
758 material_type = (alpha == 255) ?
759 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
760 if (name == "default:water_source")
761 is_water_surface = true;
764 // Vertex alpha is no longer supported, correct if necessary.
768 for (u16 j = 0; j < 6; j++) {
769 tile_shader[j] = shdsrc->getShader("nodes_shader",
770 material_type, drawtype);
773 if (is_water_surface) {
774 tile_shader[0] = shdsrc->getShader("water_surface_shader",
775 material_type, drawtype);
778 // Tiles (fill in f->tiles[])
779 for (u16 j = 0; j < 6; j++) {
780 fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j],
781 tsettings.use_normal_texture,
782 tiledef[j].backface_culling, material_type);
785 // Special tiles (fill in f->special_tiles[])
786 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
787 fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j],
788 tile_shader[j], tsettings.use_normal_texture,
789 tiledef_special[j].backface_culling, material_type);
792 if ((drawtype == NDT_MESH) && (mesh != "")) {
794 // Read the mesh and apply scale
795 mesh_ptr[0] = client->getMesh(mesh);
797 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
798 scaleMesh(mesh_ptr[0], scale);
799 recalculateBoundingBox(mesh_ptr[0]);
800 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
802 } else if ((drawtype == NDT_NODEBOX) &&
803 ((node_box.type == NODEBOX_REGULAR) ||
804 (node_box.type == NODEBOX_FIXED)) &&
805 (!node_box.fixed.empty())) {
806 //Convert regular nodebox nodes to meshnodes
807 //Change the drawtype and apply scale
809 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
810 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
811 scaleMesh(mesh_ptr[0], scale);
812 recalculateBoundingBox(mesh_ptr[0]);
813 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
816 //Cache 6dfacedir and wallmounted rotated clones of meshes
817 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
818 (param_type_2 == CPT2_FACEDIR
819 || param_type_2 == CPT2_COLORED_FACEDIR)) {
820 for (u16 j = 1; j < 24; j++) {
821 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
822 rotateMeshBy6dFacedir(mesh_ptr[j], j);
823 recalculateBoundingBox(mesh_ptr[j]);
824 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
826 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
827 && (param_type_2 == CPT2_WALLMOUNTED ||
828 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
829 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
830 for (u16 j = 1; j < 6; j++) {
831 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
832 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
833 recalculateBoundingBox(mesh_ptr[j]);
834 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
836 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
837 recalculateBoundingBox(mesh_ptr[0]);
838 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
847 class CNodeDefManager: public IWritableNodeDefManager {
850 virtual ~CNodeDefManager();
852 virtual IWritableNodeDefManager *clone();
853 inline virtual const ContentFeatures& get(content_t c) const;
854 inline virtual const ContentFeatures& get(const MapNode &n) const;
855 virtual bool getId(const std::string &name, content_t &result) const;
856 virtual content_t getId(const std::string &name) const;
857 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
858 virtual const ContentFeatures& get(const std::string &name) const;
859 content_t allocateId();
860 virtual content_t set(const std::string &name, const ContentFeatures &def);
861 virtual content_t allocateDummy(const std::string &name);
862 virtual void removeNode(const std::string &name);
863 virtual void updateAliases(IItemDefManager *idef);
864 virtual void applyTextureOverrides(const std::string &override_filepath);
865 //! Returns a palette or NULL if not found. Only on client.
866 std::vector<video::SColor> *getPalette(const ContentFeatures &f,
867 const IGameDef *gamedef);
868 virtual void updateTextures(IGameDef *gamedef,
869 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
870 void *progress_cbk_args);
871 void serialize(std::ostream &os, u16 protocol_version) const;
872 void deSerialize(std::istream &is);
874 inline virtual bool getNodeRegistrationStatus() const;
875 inline virtual void setNodeRegistrationStatus(bool completed);
877 virtual void pendNodeResolve(NodeResolver *nr);
878 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
879 virtual void runNodeResolveCallbacks();
880 virtual void resetNodeResolveState();
881 virtual void mapNodeboxConnections();
882 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
883 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
885 return m_selection_box_int_union;
889 void addNameIdMapping(content_t i, std::string name);
891 * Recalculates m_selection_box_int_union based on
892 * m_selection_box_union.
894 void fixSelectionBoxIntUnion();
896 // Features indexed by id
897 std::vector<ContentFeatures> m_content_features;
899 // A mapping for fast converting back and forth between names and ids
900 NameIdMapping m_name_id_mapping;
902 // Like m_name_id_mapping, but only from names to ids, and includes
903 // item aliases too. Updated by updateAliases()
904 // Note: Not serialized.
906 UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
908 // A mapping from groups to a list of content_ts (and their levels)
909 // that belong to it. Necessary for a direct lookup in getIds().
910 // Note: Not serialized.
911 UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
913 // Next possibly free id
916 // Maps image file names to loaded palettes.
917 UNORDERED_MAP<std::string, std::vector<video::SColor> > m_palettes;
919 // NodeResolvers to callback once node registration has ended
920 std::vector<NodeResolver *> m_pending_resolve_callbacks;
922 // True when all nodes have been registered
923 bool m_node_registration_complete;
925 //! The union of all nodes' selection boxes.
926 aabb3f m_selection_box_union;
928 * The smallest box in node coordinates that
929 * contains all nodes' selection boxes.
931 core::aabbox3d<s16> m_selection_box_int_union;
935 CNodeDefManager::CNodeDefManager()
941 CNodeDefManager::~CNodeDefManager()
944 for (u32 i = 0; i < m_content_features.size(); i++) {
945 ContentFeatures *f = &m_content_features[i];
946 for (u32 j = 0; j < 24; j++) {
948 f->mesh_ptr[j]->drop();
955 void CNodeDefManager::clear()
957 m_content_features.clear();
958 m_name_id_mapping.clear();
959 m_name_id_mapping_with_aliases.clear();
960 m_group_to_items.clear();
962 m_selection_box_union.reset(0,0,0);
963 m_selection_box_int_union.reset(0,0,0);
965 resetNodeResolveState();
967 u32 initial_length = 0;
968 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
969 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
970 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
971 m_content_features.resize(initial_length);
973 // Set CONTENT_UNKNOWN
977 // Insert directly into containers
978 content_t c = CONTENT_UNKNOWN;
979 m_content_features[c] = f;
980 addNameIdMapping(c, f.name);
987 f.drawtype = NDT_AIRLIKE;
988 f.param_type = CPT_LIGHT;
989 f.light_propagates = true;
990 f.sunlight_propagates = true;
994 f.buildable_to = true;
996 f.is_ground_content = true;
997 // Insert directly into containers
998 content_t c = CONTENT_AIR;
999 m_content_features[c] = f;
1000 addNameIdMapping(c, f.name);
1003 // Set CONTENT_IGNORE
1007 f.drawtype = NDT_AIRLIKE;
1008 f.param_type = CPT_NONE;
1009 f.light_propagates = false;
1010 f.sunlight_propagates = false;
1012 f.pointable = false;
1014 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1015 f.is_ground_content = true;
1016 // Insert directly into containers
1017 content_t c = CONTENT_IGNORE;
1018 m_content_features[c] = f;
1019 addNameIdMapping(c, f.name);
1024 IWritableNodeDefManager *CNodeDefManager::clone()
1026 CNodeDefManager *mgr = new CNodeDefManager();
1032 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1034 return c < m_content_features.size()
1035 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1039 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1041 return get(n.getContent());
1045 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1047 UNORDERED_MAP<std::string, content_t>::const_iterator
1048 i = m_name_id_mapping_with_aliases.find(name);
1049 if(i == m_name_id_mapping_with_aliases.end())
1056 content_t CNodeDefManager::getId(const std::string &name) const
1058 content_t id = CONTENT_IGNORE;
1064 bool CNodeDefManager::getIds(const std::string &name,
1065 std::set<content_t> &result) const
1067 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1068 if (name.substr(0,6) != "group:") {
1069 content_t id = CONTENT_IGNORE;
1070 bool exists = getId(name, id);
1075 std::string group = name.substr(6);
1077 UNORDERED_MAP<std::string, GroupItems>::const_iterator
1078 i = m_group_to_items.find(group);
1079 if (i == m_group_to_items.end())
1082 const GroupItems &items = i->second;
1083 for (GroupItems::const_iterator j = items.begin();
1084 j != items.end(); ++j) {
1085 if ((*j).second != 0)
1086 result.insert((*j).first);
1088 //printf("getIds: %dus\n", t.stop());
1093 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1095 content_t id = CONTENT_UNKNOWN;
1101 // returns CONTENT_IGNORE if no free ID found
1102 content_t CNodeDefManager::allocateId()
1104 for (content_t id = m_next_id;
1105 id >= m_next_id; // overflow?
1107 while (id >= m_content_features.size()) {
1108 m_content_features.push_back(ContentFeatures());
1110 const ContentFeatures &f = m_content_features[id];
1116 // If we arrive here, an overflow occurred in id.
1117 // That means no ID was found
1118 return CONTENT_IGNORE;
1123 * Returns the smallest box that contains all boxes
1124 * in the vector. Box_union is expanded.
1125 * @param[in] boxes the vector containing the boxes
1126 * @param[in, out] box_union the union of the arguments
1128 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1130 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1131 it != boxes.end(); ++it) {
1132 box_union->addInternalBox(*it);
1138 * Returns a box that contains the nodebox in every case.
1139 * The argument node_union is expanded.
1140 * @param[in] nodebox the nodebox to be measured
1141 * @param[in] features used to decide whether the nodebox
1143 * @param[in, out] box_union the union of the arguments
1145 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1148 switch(nodebox.type) {
1150 case NODEBOX_LEVELED: {
1152 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1153 boxVectorUnion(nodebox.fixed, &half_processed);
1154 // Set leveled boxes to maximal
1155 if (nodebox.type == NODEBOX_LEVELED) {
1156 half_processed.MaxEdge.Y = +BS / 2;
1158 if (features.param_type_2 == CPT2_FACEDIR ||
1159 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1160 // Get maximal coordinate
1162 fabsf(half_processed.MinEdge.X),
1163 fabsf(half_processed.MinEdge.Y),
1164 fabsf(half_processed.MinEdge.Z),
1165 fabsf(half_processed.MaxEdge.X),
1166 fabsf(half_processed.MaxEdge.Y),
1167 fabsf(half_processed.MaxEdge.Z) };
1169 for (int i = 0; i < 6; i++) {
1170 if (max < coords[i]) {
1174 // Add the union of all possible rotated boxes
1175 box_union->addInternalPoint(-max, -max, -max);
1176 box_union->addInternalPoint(+max, +max, +max);
1178 box_union->addInternalBox(half_processed);
1182 case NODEBOX_WALLMOUNTED: {
1184 box_union->addInternalBox(nodebox.wall_top);
1185 box_union->addInternalBox(nodebox.wall_bottom);
1186 // Find maximal coordinate in the X-Z plane
1188 fabsf(nodebox.wall_side.MinEdge.X),
1189 fabsf(nodebox.wall_side.MinEdge.Z),
1190 fabsf(nodebox.wall_side.MaxEdge.X),
1191 fabsf(nodebox.wall_side.MaxEdge.Z) };
1193 for (int i = 0; i < 4; i++) {
1194 if (max < coords[i]) {
1198 // Add the union of all possible rotated boxes
1199 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1200 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1203 case NODEBOX_CONNECTED: {
1204 // Add all possible connected boxes
1205 boxVectorUnion(nodebox.fixed, box_union);
1206 boxVectorUnion(nodebox.connect_top, box_union);
1207 boxVectorUnion(nodebox.connect_bottom, box_union);
1208 boxVectorUnion(nodebox.connect_front, box_union);
1209 boxVectorUnion(nodebox.connect_left, box_union);
1210 boxVectorUnion(nodebox.connect_back, box_union);
1211 boxVectorUnion(nodebox.connect_right, box_union);
1216 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1217 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1223 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1225 m_selection_box_int_union.MinEdge.X = floorf(
1226 m_selection_box_union.MinEdge.X / BS + 0.5f);
1227 m_selection_box_int_union.MinEdge.Y = floorf(
1228 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1229 m_selection_box_int_union.MinEdge.Z = floorf(
1230 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1231 m_selection_box_int_union.MaxEdge.X = ceilf(
1232 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1233 m_selection_box_int_union.MaxEdge.Y = ceilf(
1234 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1235 m_selection_box_int_union.MaxEdge.Z = ceilf(
1236 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1240 // IWritableNodeDefManager
1241 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1245 assert(name == def.name);
1247 // Don't allow redefining ignore (but allow air and unknown)
1248 if (name == "ignore") {
1249 warningstream << "NodeDefManager: Ignoring "
1250 "CONTENT_IGNORE redefinition"<<std::endl;
1251 return CONTENT_IGNORE;
1254 content_t id = CONTENT_IGNORE;
1255 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1258 if (id == CONTENT_IGNORE) {
1259 warningstream << "NodeDefManager: Absolute "
1260 "limit reached" << std::endl;
1261 return CONTENT_IGNORE;
1263 assert(id != CONTENT_IGNORE);
1264 addNameIdMapping(id, name);
1266 m_content_features[id] = def;
1267 verbosestream << "NodeDefManager: registering content id \"" << id
1268 << "\": name=\"" << def.name << "\""<<std::endl;
1270 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1271 fixSelectionBoxIntUnion();
1272 // Add this content to the list of all groups it belongs to
1273 // FIXME: This should remove a node from groups it no longer
1274 // belongs to when a node is re-registered
1275 for (ItemGroupList::const_iterator i = def.groups.begin();
1276 i != def.groups.end(); ++i) {
1277 std::string group_name = i->first;
1279 UNORDERED_MAP<std::string, GroupItems>::iterator
1280 j = m_group_to_items.find(group_name);
1281 if (j == m_group_to_items.end()) {
1282 m_group_to_items[group_name].push_back(
1283 std::make_pair(id, i->second));
1285 GroupItems &items = j->second;
1286 items.push_back(std::make_pair(id, i->second));
1293 content_t CNodeDefManager::allocateDummy(const std::string &name)
1295 assert(name != ""); // Pre-condition
1298 return set(name, f);
1302 void CNodeDefManager::removeNode(const std::string &name)
1307 // Erase name from name ID mapping
1308 content_t id = CONTENT_IGNORE;
1309 if (m_name_id_mapping.getId(name, id)) {
1310 m_name_id_mapping.eraseName(name);
1311 m_name_id_mapping_with_aliases.erase(name);
1314 // Erase node content from all groups it belongs to
1315 for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1316 m_group_to_items.begin();
1317 iter_groups != m_group_to_items.end();) {
1318 GroupItems &items = iter_groups->second;
1319 for (GroupItems::iterator iter_groupitems = items.begin();
1320 iter_groupitems != items.end();) {
1321 if (iter_groupitems->first == id)
1322 items.erase(iter_groupitems++);
1327 // Check if group is empty
1328 if (items.size() == 0)
1329 m_group_to_items.erase(iter_groups++);
1336 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1338 std::set<std::string> all = idef->getAll();
1339 m_name_id_mapping_with_aliases.clear();
1340 for (std::set<std::string>::iterator
1341 i = all.begin(); i != all.end(); ++i) {
1342 std::string name = *i;
1343 std::string convert_to = idef->getAlias(name);
1345 if (m_name_id_mapping.getId(convert_to, id)) {
1346 m_name_id_mapping_with_aliases.insert(
1347 std::make_pair(name, id));
1352 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1354 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1355 "overrides to textures from " << override_filepath << std::endl;
1357 std::ifstream infile(override_filepath.c_str());
1360 while (std::getline(infile, line)) {
1362 if (trim(line) == "")
1364 std::vector<std::string> splitted = str_split(line, ' ');
1365 if (splitted.size() != 3) {
1366 errorstream << override_filepath
1367 << ":" << line_c << " Could not apply texture override \""
1368 << line << "\": Syntax error" << std::endl;
1373 if (!getId(splitted[0], id))
1374 continue; // Ignore unknown node
1376 ContentFeatures &nodedef = m_content_features[id];
1378 if (splitted[1] == "top")
1379 nodedef.tiledef[0].name = splitted[2];
1380 else if (splitted[1] == "bottom")
1381 nodedef.tiledef[1].name = splitted[2];
1382 else if (splitted[1] == "right")
1383 nodedef.tiledef[2].name = splitted[2];
1384 else if (splitted[1] == "left")
1385 nodedef.tiledef[3].name = splitted[2];
1386 else if (splitted[1] == "back")
1387 nodedef.tiledef[4].name = splitted[2];
1388 else if (splitted[1] == "front")
1389 nodedef.tiledef[5].name = splitted[2];
1390 else if (splitted[1] == "all" || splitted[1] == "*")
1391 for (int i = 0; i < 6; i++)
1392 nodedef.tiledef[i].name = splitted[2];
1393 else if (splitted[1] == "sides")
1394 for (int i = 2; i < 6; i++)
1395 nodedef.tiledef[i].name = splitted[2];
1397 errorstream << override_filepath
1398 << ":" << line_c << " Could not apply texture override \""
1399 << line << "\": Unknown node side \""
1400 << splitted[1] << "\"" << std::endl;
1406 std::vector<video::SColor> *CNodeDefManager::getPalette(
1407 const ContentFeatures &f, const IGameDef *gamedef)
1410 // This works because colors always use the most significant bits
1411 // of param2. If you add a new colored type which uses param2
1412 // in a more advanced way, you should change this code, too.
1413 u32 palette_pixels = 0;
1414 switch (f.param_type_2) {
1416 palette_pixels = 256;
1418 case CPT2_COLORED_FACEDIR:
1421 case CPT2_COLORED_WALLMOUNTED:
1422 palette_pixels = 32;
1427 // This many param2 values will have the same color
1428 u32 step = 256 / palette_pixels;
1429 const std::string &name = f.palette_name;
1432 Client *client = (Client *) gamedef;
1433 ITextureSource *tsrc = client->tsrc();
1435 UNORDERED_MAP<std::string, std::vector<video::SColor> >::iterator it =
1436 m_palettes.find(name);
1437 if (it == m_palettes.end()) {
1439 if (!tsrc->isKnownSourceImage(name)) {
1440 warningstream << "CNodeDefManager::getPalette(): palette \"" << name
1441 << "\" could not be loaded." << std::endl;
1444 video::IImage *img = tsrc->generateImage(name);
1445 std::vector<video::SColor> new_palette;
1446 u32 w = img->getDimension().Width;
1447 u32 h = img->getDimension().Height;
1448 // Real area of the image
1450 if (area != palette_pixels)
1451 warningstream << "CNodeDefManager::getPalette(): the "
1452 << "specified palette image \"" << name << "\" does not "
1453 << "contain exactly " << palette_pixels
1454 << " pixels." << std::endl;
1455 if (area > palette_pixels)
1456 area = palette_pixels;
1457 // For each pixel in the image
1458 for (u32 i = 0; i < area; i++) {
1459 video::SColor c = img->getPixel(i % w, i / w);
1460 // Fill in palette with 'step' colors
1461 for (u32 j = 0; j < step; j++)
1462 new_palette.push_back(c);
1465 // Fill in remaining elements
1466 while (new_palette.size() < 256)
1467 new_palette.push_back(video::SColor(0xFFFFFFFF));
1468 m_palettes[name] = new_palette;
1469 it = m_palettes.find(name);
1471 if (it != m_palettes.end())
1472 return &((*it).second);
1478 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1479 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1480 void *progress_callback_args)
1483 infostream << "CNodeDefManager::updateTextures(): Updating "
1484 "textures in node definitions" << std::endl;
1486 Client *client = (Client *)gamedef;
1487 ITextureSource *tsrc = client->tsrc();
1488 IShaderSource *shdsrc = client->getShaderSource();
1489 scene::ISceneManager* smgr = client->getSceneManager();
1490 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1491 TextureSettings tsettings;
1492 tsettings.readSettings();
1495 u32 size = m_content_features.size();
1497 for (u32 i = 0; i < size; i++) {
1498 ContentFeatures *f = &(m_content_features[i]);
1499 f->palette = getPalette(*f, gamedef);
1500 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1501 progress_callback(progress_callback_args, i, size);
1506 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1508 writeU8(os, 1); // version
1510 std::ostringstream os2(std::ios::binary);
1511 for (u32 i = 0; i < m_content_features.size(); i++) {
1512 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1513 || i == CONTENT_UNKNOWN)
1515 const ContentFeatures *f = &m_content_features[i];
1519 // Wrap it in a string to allow different lengths without
1520 // strict version incompatibilities
1521 std::ostringstream wrapper_os(std::ios::binary);
1522 f->serialize(wrapper_os, protocol_version);
1523 os2<<serializeString(wrapper_os.str());
1525 // must not overflow
1526 u16 next = count + 1;
1527 FATAL_ERROR_IF(next < count, "Overflow");
1530 writeU16(os, count);
1531 os << serializeLongString(os2.str());
1535 void CNodeDefManager::deSerialize(std::istream &is)
1538 int version = readU8(is);
1540 throw SerializationError("unsupported NodeDefinitionManager version");
1541 u16 count = readU16(is);
1542 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1544 for (u16 n = 0; n < count; n++) {
1545 u16 i = readU16(is2);
1547 // Read it from the string wrapper
1548 std::string wrapper = deSerializeString(is2);
1549 std::istringstream wrapper_is(wrapper, std::ios::binary);
1550 f.deSerialize(wrapper_is);
1552 // Check error conditions
1553 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1554 warningstream << "NodeDefManager::deSerialize(): "
1555 "not changing builtin node " << i << std::endl;
1559 warningstream << "NodeDefManager::deSerialize(): "
1560 "received empty name" << std::endl;
1566 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1567 warningstream << "NodeDefManager::deSerialize(): "
1568 "already defined with different ID: " << f.name << std::endl;
1572 // All is ok, add node definition with the requested ID
1573 if (i >= m_content_features.size())
1574 m_content_features.resize((u32)(i) + 1);
1575 m_content_features[i] = f;
1576 addNameIdMapping(i, f.name);
1577 verbosestream << "deserialized " << f.name << std::endl;
1579 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1580 fixSelectionBoxIntUnion();
1585 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1587 m_name_id_mapping.set(i, name);
1588 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1592 IWritableNodeDefManager *createNodeDefManager()
1594 return new CNodeDefManager();
1598 //// Serialization of old ContentFeatures formats
1599 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1601 u8 compatible_param_type_2 = param_type_2;
1602 if ((protocol_version < 28)
1603 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1604 compatible_param_type_2 = CPT2_NONE;
1605 else if (protocol_version < 30) {
1606 if (compatible_param_type_2 == CPT2_COLOR)
1607 compatible_param_type_2 = CPT2_NONE;
1608 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1609 compatible_param_type_2 = CPT2_FACEDIR;
1610 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1611 compatible_param_type_2 = CPT2_WALLMOUNTED;
1614 float compatible_visual_scale = visual_scale;
1615 if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1616 compatible_visual_scale = sqrt(visual_scale);
1618 if (protocol_version == 13)
1620 writeU8(os, 5); // version
1621 os<<serializeString(name);
1622 writeU16(os, groups.size());
1623 for (ItemGroupList::const_iterator
1624 i = groups.begin(); i != groups.end(); ++i) {
1625 os<<serializeString(i->first);
1626 writeS16(os, i->second);
1628 writeU8(os, drawtype);
1629 writeF1000(os, compatible_visual_scale);
1631 for (u32 i = 0; i < 6; i++)
1632 tiledef[i].serialize(os, protocol_version);
1633 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1635 for (u32 i = 0; i < 2; i++)
1636 tiledef_special[i].serialize(os, protocol_version);
1638 writeU8(os, post_effect_color.getAlpha());
1639 writeU8(os, post_effect_color.getRed());
1640 writeU8(os, post_effect_color.getGreen());
1641 writeU8(os, post_effect_color.getBlue());
1642 writeU8(os, param_type);
1643 writeU8(os, compatible_param_type_2);
1644 writeU8(os, is_ground_content);
1645 writeU8(os, light_propagates);
1646 writeU8(os, sunlight_propagates);
1647 writeU8(os, walkable);
1648 writeU8(os, pointable);
1649 writeU8(os, diggable);
1650 writeU8(os, climbable);
1651 writeU8(os, buildable_to);
1652 os<<serializeString(""); // legacy: used to be metadata_name
1653 writeU8(os, liquid_type);
1654 os<<serializeString(liquid_alternative_flowing);
1655 os<<serializeString(liquid_alternative_source);
1656 writeU8(os, liquid_viscosity);
1657 writeU8(os, light_source);
1658 writeU32(os, damage_per_second);
1659 node_box.serialize(os, protocol_version);
1660 selection_box.serialize(os, protocol_version);
1661 writeU8(os, legacy_facedir_simple);
1662 writeU8(os, legacy_wallmounted);
1663 serializeSimpleSoundSpec(sound_footstep, os);
1664 serializeSimpleSoundSpec(sound_dig, os);
1665 serializeSimpleSoundSpec(sound_dug, os);
1667 else if (protocol_version > 13 && protocol_version < 24) {
1668 writeU8(os, 6); // version
1669 os<<serializeString(name);
1670 writeU16(os, groups.size());
1671 for (ItemGroupList::const_iterator
1672 i = groups.begin(); i != groups.end(); ++i) {
1673 os<<serializeString(i->first);
1674 writeS16(os, i->second);
1676 writeU8(os, drawtype);
1677 writeF1000(os, compatible_visual_scale);
1679 for (u32 i = 0; i < 6; i++)
1680 tiledef[i].serialize(os, protocol_version);
1681 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1683 for (u32 i = 0; i < 2; i++)
1684 tiledef_special[i].serialize(os, protocol_version);
1686 writeU8(os, post_effect_color.getAlpha());
1687 writeU8(os, post_effect_color.getRed());
1688 writeU8(os, post_effect_color.getGreen());
1689 writeU8(os, post_effect_color.getBlue());
1690 writeU8(os, param_type);
1691 writeU8(os, compatible_param_type_2);
1692 writeU8(os, is_ground_content);
1693 writeU8(os, light_propagates);
1694 writeU8(os, sunlight_propagates);
1695 writeU8(os, walkable);
1696 writeU8(os, pointable);
1697 writeU8(os, diggable);
1698 writeU8(os, climbable);
1699 writeU8(os, buildable_to);
1700 os<<serializeString(""); // legacy: used to be metadata_name
1701 writeU8(os, liquid_type);
1702 os<<serializeString(liquid_alternative_flowing);
1703 os<<serializeString(liquid_alternative_source);
1704 writeU8(os, liquid_viscosity);
1705 writeU8(os, liquid_renewable);
1706 writeU8(os, light_source);
1707 writeU32(os, damage_per_second);
1708 node_box.serialize(os, protocol_version);
1709 selection_box.serialize(os, protocol_version);
1710 writeU8(os, legacy_facedir_simple);
1711 writeU8(os, legacy_wallmounted);
1712 serializeSimpleSoundSpec(sound_footstep, os);
1713 serializeSimpleSoundSpec(sound_dig, os);
1714 serializeSimpleSoundSpec(sound_dug, os);
1715 writeU8(os, rightclickable);
1716 writeU8(os, drowning);
1717 writeU8(os, leveled);
1718 writeU8(os, liquid_range);
1720 else if(protocol_version >= 24 && protocol_version < 30) {
1721 writeU8(os, protocol_version < 27 ? 7 : 8);
1723 os << serializeString(name);
1724 writeU16(os, groups.size());
1725 for (ItemGroupList::const_iterator i = groups.begin();
1726 i != groups.end(); ++i) {
1727 os << serializeString(i->first);
1728 writeS16(os, i->second);
1730 writeU8(os, drawtype);
1731 writeF1000(os, compatible_visual_scale);
1733 for (u32 i = 0; i < 6; i++)
1734 tiledef[i].serialize(os, protocol_version);
1735 writeU8(os, CF_SPECIAL_COUNT);
1736 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1737 tiledef_special[i].serialize(os, protocol_version);
1739 writeU8(os, post_effect_color.getAlpha());
1740 writeU8(os, post_effect_color.getRed());
1741 writeU8(os, post_effect_color.getGreen());
1742 writeU8(os, post_effect_color.getBlue());
1743 writeU8(os, param_type);
1744 writeU8(os, compatible_param_type_2);
1745 writeU8(os, is_ground_content);
1746 writeU8(os, light_propagates);
1747 writeU8(os, sunlight_propagates);
1748 writeU8(os, walkable);
1749 writeU8(os, pointable);
1750 writeU8(os, diggable);
1751 writeU8(os, climbable);
1752 writeU8(os, buildable_to);
1753 os << serializeString(""); // legacy: used to be metadata_name
1754 writeU8(os, liquid_type);
1755 os << serializeString(liquid_alternative_flowing);
1756 os << serializeString(liquid_alternative_source);
1757 writeU8(os, liquid_viscosity);
1758 writeU8(os, liquid_renewable);
1759 writeU8(os, light_source);
1760 writeU32(os, damage_per_second);
1761 node_box.serialize(os, protocol_version);
1762 selection_box.serialize(os, protocol_version);
1763 writeU8(os, legacy_facedir_simple);
1764 writeU8(os, legacy_wallmounted);
1765 serializeSimpleSoundSpec(sound_footstep, os);
1766 serializeSimpleSoundSpec(sound_dig, os);
1767 serializeSimpleSoundSpec(sound_dug, os);
1768 writeU8(os, rightclickable);
1769 writeU8(os, drowning);
1770 writeU8(os, leveled);
1771 writeU8(os, liquid_range);
1772 writeU8(os, waving);
1773 os << serializeString(mesh);
1774 collision_box.serialize(os, protocol_version);
1775 writeU8(os, floodable);
1776 writeU16(os, connects_to_ids.size());
1777 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1778 i != connects_to_ids.end(); ++i)
1780 writeU8(os, connect_sides);
1782 throw SerializationError("ContentFeatures::serialize(): "
1783 "Unsupported version requested");
1786 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1788 if (version == 5) // In PROTOCOL_VERSION 13
1790 name = deSerializeString(is);
1792 u32 groups_size = readU16(is);
1793 for(u32 i=0; i<groups_size; i++){
1794 std::string name = deSerializeString(is);
1795 int value = readS16(is);
1796 groups[name] = value;
1798 drawtype = (enum NodeDrawType)readU8(is);
1800 visual_scale = readF1000(is);
1801 if (readU8(is) != 6)
1802 throw SerializationError("unsupported tile count");
1803 for (u32 i = 0; i < 6; i++)
1804 tiledef[i].deSerialize(is, version, drawtype);
1805 if (readU8(is) != CF_SPECIAL_COUNT)
1806 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1807 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1808 tiledef_special[i].deSerialize(is, version, drawtype);
1810 post_effect_color.setAlpha(readU8(is));
1811 post_effect_color.setRed(readU8(is));
1812 post_effect_color.setGreen(readU8(is));
1813 post_effect_color.setBlue(readU8(is));
1814 param_type = (enum ContentParamType)readU8(is);
1815 param_type_2 = (enum ContentParamType2)readU8(is);
1816 is_ground_content = readU8(is);
1817 light_propagates = readU8(is);
1818 sunlight_propagates = readU8(is);
1819 walkable = readU8(is);
1820 pointable = readU8(is);
1821 diggable = readU8(is);
1822 climbable = readU8(is);
1823 buildable_to = readU8(is);
1824 deSerializeString(is); // legacy: used to be metadata_name
1825 liquid_type = (enum LiquidType)readU8(is);
1826 liquid_alternative_flowing = deSerializeString(is);
1827 liquid_alternative_source = deSerializeString(is);
1828 liquid_viscosity = readU8(is);
1829 light_source = readU8(is);
1830 light_source = MYMIN(light_source, LIGHT_MAX);
1831 damage_per_second = readU32(is);
1832 node_box.deSerialize(is);
1833 selection_box.deSerialize(is);
1834 legacy_facedir_simple = readU8(is);
1835 legacy_wallmounted = readU8(is);
1836 deSerializeSimpleSoundSpec(sound_footstep, is);
1837 deSerializeSimpleSoundSpec(sound_dig, is);
1838 deSerializeSimpleSoundSpec(sound_dug, is);
1839 } else if (version == 6) {
1840 name = deSerializeString(is);
1842 u32 groups_size = readU16(is);
1843 for (u32 i = 0; i < groups_size; i++) {
1844 std::string name = deSerializeString(is);
1845 int value = readS16(is);
1846 groups[name] = value;
1848 drawtype = (enum NodeDrawType)readU8(is);
1849 visual_scale = readF1000(is);
1850 if (readU8(is) != 6)
1851 throw SerializationError("unsupported tile count");
1852 for (u32 i = 0; i < 6; i++)
1853 tiledef[i].deSerialize(is, version, drawtype);
1854 // CF_SPECIAL_COUNT in version 6 = 2
1855 if (readU8(is) != 2)
1856 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1857 for (u32 i = 0; i < 2; i++)
1858 tiledef_special[i].deSerialize(is, version, drawtype);
1860 post_effect_color.setAlpha(readU8(is));
1861 post_effect_color.setRed(readU8(is));
1862 post_effect_color.setGreen(readU8(is));
1863 post_effect_color.setBlue(readU8(is));
1864 param_type = (enum ContentParamType)readU8(is);
1865 param_type_2 = (enum ContentParamType2)readU8(is);
1866 is_ground_content = readU8(is);
1867 light_propagates = readU8(is);
1868 sunlight_propagates = readU8(is);
1869 walkable = readU8(is);
1870 pointable = readU8(is);
1871 diggable = readU8(is);
1872 climbable = readU8(is);
1873 buildable_to = readU8(is);
1874 deSerializeString(is); // legacy: used to be metadata_name
1875 liquid_type = (enum LiquidType)readU8(is);
1876 liquid_alternative_flowing = deSerializeString(is);
1877 liquid_alternative_source = deSerializeString(is);
1878 liquid_viscosity = readU8(is);
1879 liquid_renewable = readU8(is);
1880 light_source = readU8(is);
1881 damage_per_second = readU32(is);
1882 node_box.deSerialize(is);
1883 selection_box.deSerialize(is);
1884 legacy_facedir_simple = readU8(is);
1885 legacy_wallmounted = readU8(is);
1886 deSerializeSimpleSoundSpec(sound_footstep, is);
1887 deSerializeSimpleSoundSpec(sound_dig, is);
1888 deSerializeSimpleSoundSpec(sound_dug, is);
1889 rightclickable = readU8(is);
1890 drowning = readU8(is);
1891 leveled = readU8(is);
1892 liquid_range = readU8(is);
1893 } else if (version == 7 || version == 8){
1894 name = deSerializeString(is);
1896 u32 groups_size = readU16(is);
1897 for (u32 i = 0; i < groups_size; i++) {
1898 std::string name = deSerializeString(is);
1899 int value = readS16(is);
1900 groups[name] = value;
1902 drawtype = (enum NodeDrawType) readU8(is);
1904 visual_scale = readF1000(is);
1905 if (readU8(is) != 6)
1906 throw SerializationError("unsupported tile count");
1907 for (u32 i = 0; i < 6; i++)
1908 tiledef[i].deSerialize(is, version, drawtype);
1909 if (readU8(is) != CF_SPECIAL_COUNT)
1910 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1911 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1912 tiledef_special[i].deSerialize(is, version, drawtype);
1914 post_effect_color.setAlpha(readU8(is));
1915 post_effect_color.setRed(readU8(is));
1916 post_effect_color.setGreen(readU8(is));
1917 post_effect_color.setBlue(readU8(is));
1918 param_type = (enum ContentParamType) readU8(is);
1919 param_type_2 = (enum ContentParamType2) readU8(is);
1920 is_ground_content = readU8(is);
1921 light_propagates = readU8(is);
1922 sunlight_propagates = readU8(is);
1923 walkable = readU8(is);
1924 pointable = readU8(is);
1925 diggable = readU8(is);
1926 climbable = readU8(is);
1927 buildable_to = readU8(is);
1928 deSerializeString(is); // legacy: used to be metadata_name
1929 liquid_type = (enum LiquidType) readU8(is);
1930 liquid_alternative_flowing = deSerializeString(is);
1931 liquid_alternative_source = deSerializeString(is);
1932 liquid_viscosity = readU8(is);
1933 liquid_renewable = readU8(is);
1934 light_source = readU8(is);
1935 light_source = MYMIN(light_source, LIGHT_MAX);
1936 damage_per_second = readU32(is);
1937 node_box.deSerialize(is);
1938 selection_box.deSerialize(is);
1939 legacy_facedir_simple = readU8(is);
1940 legacy_wallmounted = readU8(is);
1941 deSerializeSimpleSoundSpec(sound_footstep, is);
1942 deSerializeSimpleSoundSpec(sound_dig, is);
1943 deSerializeSimpleSoundSpec(sound_dug, is);
1944 rightclickable = readU8(is);
1945 drowning = readU8(is);
1946 leveled = readU8(is);
1947 liquid_range = readU8(is);
1948 waving = readU8(is);
1950 mesh = deSerializeString(is);
1951 collision_box.deSerialize(is);
1952 floodable = readU8(is);
1953 u16 connects_to_size = readU16(is);
1954 connects_to_ids.clear();
1955 for (u16 i = 0; i < connects_to_size; i++)
1956 connects_to_ids.insert(readU16(is));
1957 connect_sides = readU8(is);
1958 } catch (SerializationError &e) {};
1960 throw SerializationError("unsupported ContentFeatures version");
1965 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1967 return m_node_registration_complete;
1971 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1973 m_node_registration_complete = completed;
1977 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1980 if (m_node_registration_complete)
1981 nr->nodeResolveInternal();
1983 m_pending_resolve_callbacks.push_back(nr);
1987 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1989 size_t len = m_pending_resolve_callbacks.size();
1990 for (size_t i = 0; i != len; i++) {
1991 if (nr != m_pending_resolve_callbacks[i])
1995 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1996 m_pending_resolve_callbacks.resize(len);
2004 void CNodeDefManager::runNodeResolveCallbacks()
2006 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
2007 NodeResolver *nr = m_pending_resolve_callbacks[i];
2008 nr->nodeResolveInternal();
2011 m_pending_resolve_callbacks.clear();
2015 void CNodeDefManager::resetNodeResolveState()
2017 m_node_registration_complete = false;
2018 m_pending_resolve_callbacks.clear();
2021 void CNodeDefManager::mapNodeboxConnections()
2023 for (u32 i = 0; i < m_content_features.size(); i++) {
2024 ContentFeatures *f = &m_content_features[i];
2025 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
2027 for (std::vector<std::string>::iterator it = f->connects_to.begin();
2028 it != f->connects_to.end(); ++it) {
2029 getIds(*it, f->connects_to_ids);
2034 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
2036 const ContentFeatures &f1 = get(from);
2038 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
2041 // lookup target in connected set
2042 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
2045 const ContentFeatures &f2 = get(to);
2047 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
2048 // ignores actually looking if back connection exists
2049 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
2051 // does to node declare usable faces?
2052 if (f2.connect_sides > 0) {
2053 if ((f2.param_type_2 == CPT2_FACEDIR ||
2054 f2.param_type_2 == CPT2_COLORED_FACEDIR)
2055 && (connect_face >= 4)) {
2056 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2057 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2059 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2061 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
2062 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2064 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2065 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2066 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
2068 return (f2.connect_sides
2069 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
2071 return (f2.connect_sides & connect_face);
2073 // the target is just a regular node, so connect no matter back connection
2081 NodeResolver::NodeResolver()
2084 m_nodenames_idx = 0;
2085 m_nnlistsizes_idx = 0;
2086 m_resolve_done = false;
2088 m_nodenames.reserve(16);
2089 m_nnlistsizes.reserve(4);
2093 NodeResolver::~NodeResolver()
2095 if (!m_resolve_done && m_ndef)
2096 m_ndef->cancelNodeResolveCallback(this);
2100 void NodeResolver::nodeResolveInternal()
2102 m_nodenames_idx = 0;
2103 m_nnlistsizes_idx = 0;
2106 m_resolve_done = true;
2108 m_nodenames.clear();
2109 m_nnlistsizes.clear();
2113 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
2114 const std::string &node_alt, content_t c_fallback)
2116 if (m_nodenames_idx == m_nodenames.size()) {
2117 *result_out = c_fallback;
2118 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2123 std::string name = m_nodenames[m_nodenames_idx++];
2125 bool success = m_ndef->getId(name, c);
2126 if (!success && node_alt != "") {
2128 success = m_ndef->getId(name, c);
2132 errorstream << "NodeResolver: failed to resolve node name '" << name
2133 << "'." << std::endl;
2142 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
2143 bool all_required, content_t c_fallback)
2145 bool success = true;
2147 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
2148 errorstream << "NodeResolver: no more node lists" << std::endl;
2152 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
2155 if (m_nodenames_idx == m_nodenames.size()) {
2156 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2161 std::string &name = m_nodenames[m_nodenames_idx++];
2163 if (name.substr(0,6) != "group:") {
2164 if (m_ndef->getId(name, c)) {
2165 result_out->push_back(c);
2166 } else if (all_required) {
2167 errorstream << "NodeResolver: failed to resolve node name '"
2168 << name << "'." << std::endl;
2169 result_out->push_back(c_fallback);
2173 std::set<content_t> cids;
2174 std::set<content_t>::iterator it;
2175 m_ndef->getIds(name, cids);
2176 for (it = cids.begin(); it != cids.end(); ++it)
2177 result_out->push_back(*it);