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 if (protocol_version == 13)
1616 writeU8(os, 5); // version
1617 os<<serializeString(name);
1618 writeU16(os, groups.size());
1619 for (ItemGroupList::const_iterator
1620 i = groups.begin(); i != groups.end(); ++i) {
1621 os<<serializeString(i->first);
1622 writeS16(os, i->second);
1624 writeU8(os, drawtype);
1625 writeF1000(os, visual_scale);
1627 for (u32 i = 0; i < 6; i++)
1628 tiledef[i].serialize(os, protocol_version);
1629 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1631 for (u32 i = 0; i < 2; i++)
1632 tiledef_special[i].serialize(os, protocol_version);
1634 writeU8(os, post_effect_color.getAlpha());
1635 writeU8(os, post_effect_color.getRed());
1636 writeU8(os, post_effect_color.getGreen());
1637 writeU8(os, post_effect_color.getBlue());
1638 writeU8(os, param_type);
1639 writeU8(os, compatible_param_type_2);
1640 writeU8(os, is_ground_content);
1641 writeU8(os, light_propagates);
1642 writeU8(os, sunlight_propagates);
1643 writeU8(os, walkable);
1644 writeU8(os, pointable);
1645 writeU8(os, diggable);
1646 writeU8(os, climbable);
1647 writeU8(os, buildable_to);
1648 os<<serializeString(""); // legacy: used to be metadata_name
1649 writeU8(os, liquid_type);
1650 os<<serializeString(liquid_alternative_flowing);
1651 os<<serializeString(liquid_alternative_source);
1652 writeU8(os, liquid_viscosity);
1653 writeU8(os, light_source);
1654 writeU32(os, damage_per_second);
1655 node_box.serialize(os, protocol_version);
1656 selection_box.serialize(os, protocol_version);
1657 writeU8(os, legacy_facedir_simple);
1658 writeU8(os, legacy_wallmounted);
1659 serializeSimpleSoundSpec(sound_footstep, os);
1660 serializeSimpleSoundSpec(sound_dig, os);
1661 serializeSimpleSoundSpec(sound_dug, os);
1663 else if (protocol_version > 13 && protocol_version < 24) {
1664 writeU8(os, 6); // version
1665 os<<serializeString(name);
1666 writeU16(os, groups.size());
1667 for (ItemGroupList::const_iterator
1668 i = groups.begin(); i != groups.end(); ++i) {
1669 os<<serializeString(i->first);
1670 writeS16(os, i->second);
1672 writeU8(os, drawtype);
1673 writeF1000(os, visual_scale);
1675 for (u32 i = 0; i < 6; i++)
1676 tiledef[i].serialize(os, protocol_version);
1677 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1679 for (u32 i = 0; i < 2; i++)
1680 tiledef_special[i].serialize(os, protocol_version);
1682 writeU8(os, post_effect_color.getAlpha());
1683 writeU8(os, post_effect_color.getRed());
1684 writeU8(os, post_effect_color.getGreen());
1685 writeU8(os, post_effect_color.getBlue());
1686 writeU8(os, param_type);
1687 writeU8(os, compatible_param_type_2);
1688 writeU8(os, is_ground_content);
1689 writeU8(os, light_propagates);
1690 writeU8(os, sunlight_propagates);
1691 writeU8(os, walkable);
1692 writeU8(os, pointable);
1693 writeU8(os, diggable);
1694 writeU8(os, climbable);
1695 writeU8(os, buildable_to);
1696 os<<serializeString(""); // legacy: used to be metadata_name
1697 writeU8(os, liquid_type);
1698 os<<serializeString(liquid_alternative_flowing);
1699 os<<serializeString(liquid_alternative_source);
1700 writeU8(os, liquid_viscosity);
1701 writeU8(os, liquid_renewable);
1702 writeU8(os, light_source);
1703 writeU32(os, damage_per_second);
1704 node_box.serialize(os, protocol_version);
1705 selection_box.serialize(os, protocol_version);
1706 writeU8(os, legacy_facedir_simple);
1707 writeU8(os, legacy_wallmounted);
1708 serializeSimpleSoundSpec(sound_footstep, os);
1709 serializeSimpleSoundSpec(sound_dig, os);
1710 serializeSimpleSoundSpec(sound_dug, os);
1711 writeU8(os, rightclickable);
1712 writeU8(os, drowning);
1713 writeU8(os, leveled);
1714 writeU8(os, liquid_range);
1716 else if(protocol_version >= 24 && protocol_version < 30) {
1717 writeU8(os, protocol_version < 27 ? 7 : 8);
1719 os << serializeString(name);
1720 writeU16(os, groups.size());
1721 for (ItemGroupList::const_iterator i = groups.begin();
1722 i != groups.end(); ++i) {
1723 os << serializeString(i->first);
1724 writeS16(os, i->second);
1726 writeU8(os, drawtype);
1727 writeF1000(os, visual_scale);
1729 for (u32 i = 0; i < 6; i++)
1730 tiledef[i].serialize(os, protocol_version);
1731 writeU8(os, CF_SPECIAL_COUNT);
1732 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1733 tiledef_special[i].serialize(os, protocol_version);
1735 writeU8(os, post_effect_color.getAlpha());
1736 writeU8(os, post_effect_color.getRed());
1737 writeU8(os, post_effect_color.getGreen());
1738 writeU8(os, post_effect_color.getBlue());
1739 writeU8(os, param_type);
1740 writeU8(os, compatible_param_type_2);
1741 writeU8(os, is_ground_content);
1742 writeU8(os, light_propagates);
1743 writeU8(os, sunlight_propagates);
1744 writeU8(os, walkable);
1745 writeU8(os, pointable);
1746 writeU8(os, diggable);
1747 writeU8(os, climbable);
1748 writeU8(os, buildable_to);
1749 os << serializeString(""); // legacy: used to be metadata_name
1750 writeU8(os, liquid_type);
1751 os << serializeString(liquid_alternative_flowing);
1752 os << serializeString(liquid_alternative_source);
1753 writeU8(os, liquid_viscosity);
1754 writeU8(os, liquid_renewable);
1755 writeU8(os, light_source);
1756 writeU32(os, damage_per_second);
1757 node_box.serialize(os, protocol_version);
1758 selection_box.serialize(os, protocol_version);
1759 writeU8(os, legacy_facedir_simple);
1760 writeU8(os, legacy_wallmounted);
1761 serializeSimpleSoundSpec(sound_footstep, os);
1762 serializeSimpleSoundSpec(sound_dig, os);
1763 serializeSimpleSoundSpec(sound_dug, os);
1764 writeU8(os, rightclickable);
1765 writeU8(os, drowning);
1766 writeU8(os, leveled);
1767 writeU8(os, liquid_range);
1768 writeU8(os, waving);
1769 os << serializeString(mesh);
1770 collision_box.serialize(os, protocol_version);
1771 writeU8(os, floodable);
1772 writeU16(os, connects_to_ids.size());
1773 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1774 i != connects_to_ids.end(); ++i)
1776 writeU8(os, connect_sides);
1778 throw SerializationError("ContentFeatures::serialize(): "
1779 "Unsupported version requested");
1782 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1784 if (version == 5) // In PROTOCOL_VERSION 13
1786 name = deSerializeString(is);
1788 u32 groups_size = readU16(is);
1789 for(u32 i=0; i<groups_size; i++){
1790 std::string name = deSerializeString(is);
1791 int value = readS16(is);
1792 groups[name] = value;
1794 drawtype = (enum NodeDrawType)readU8(is);
1796 visual_scale = readF1000(is);
1797 if (readU8(is) != 6)
1798 throw SerializationError("unsupported tile count");
1799 for (u32 i = 0; i < 6; i++)
1800 tiledef[i].deSerialize(is, version, drawtype);
1801 if (readU8(is) != CF_SPECIAL_COUNT)
1802 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1803 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1804 tiledef_special[i].deSerialize(is, version, drawtype);
1806 post_effect_color.setAlpha(readU8(is));
1807 post_effect_color.setRed(readU8(is));
1808 post_effect_color.setGreen(readU8(is));
1809 post_effect_color.setBlue(readU8(is));
1810 param_type = (enum ContentParamType)readU8(is);
1811 param_type_2 = (enum ContentParamType2)readU8(is);
1812 is_ground_content = readU8(is);
1813 light_propagates = readU8(is);
1814 sunlight_propagates = readU8(is);
1815 walkable = readU8(is);
1816 pointable = readU8(is);
1817 diggable = readU8(is);
1818 climbable = readU8(is);
1819 buildable_to = readU8(is);
1820 deSerializeString(is); // legacy: used to be metadata_name
1821 liquid_type = (enum LiquidType)readU8(is);
1822 liquid_alternative_flowing = deSerializeString(is);
1823 liquid_alternative_source = deSerializeString(is);
1824 liquid_viscosity = readU8(is);
1825 light_source = readU8(is);
1826 light_source = MYMIN(light_source, LIGHT_MAX);
1827 damage_per_second = readU32(is);
1828 node_box.deSerialize(is);
1829 selection_box.deSerialize(is);
1830 legacy_facedir_simple = readU8(is);
1831 legacy_wallmounted = readU8(is);
1832 deSerializeSimpleSoundSpec(sound_footstep, is);
1833 deSerializeSimpleSoundSpec(sound_dig, is);
1834 deSerializeSimpleSoundSpec(sound_dug, is);
1835 } else if (version == 6) {
1836 name = deSerializeString(is);
1838 u32 groups_size = readU16(is);
1839 for (u32 i = 0; i < groups_size; i++) {
1840 std::string name = deSerializeString(is);
1841 int value = readS16(is);
1842 groups[name] = value;
1844 drawtype = (enum NodeDrawType)readU8(is);
1845 visual_scale = readF1000(is);
1846 if (readU8(is) != 6)
1847 throw SerializationError("unsupported tile count");
1848 for (u32 i = 0; i < 6; i++)
1849 tiledef[i].deSerialize(is, version, drawtype);
1850 // CF_SPECIAL_COUNT in version 6 = 2
1851 if (readU8(is) != 2)
1852 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1853 for (u32 i = 0; i < 2; i++)
1854 tiledef_special[i].deSerialize(is, version, drawtype);
1856 post_effect_color.setAlpha(readU8(is));
1857 post_effect_color.setRed(readU8(is));
1858 post_effect_color.setGreen(readU8(is));
1859 post_effect_color.setBlue(readU8(is));
1860 param_type = (enum ContentParamType)readU8(is);
1861 param_type_2 = (enum ContentParamType2)readU8(is);
1862 is_ground_content = readU8(is);
1863 light_propagates = readU8(is);
1864 sunlight_propagates = readU8(is);
1865 walkable = readU8(is);
1866 pointable = readU8(is);
1867 diggable = readU8(is);
1868 climbable = readU8(is);
1869 buildable_to = readU8(is);
1870 deSerializeString(is); // legacy: used to be metadata_name
1871 liquid_type = (enum LiquidType)readU8(is);
1872 liquid_alternative_flowing = deSerializeString(is);
1873 liquid_alternative_source = deSerializeString(is);
1874 liquid_viscosity = readU8(is);
1875 liquid_renewable = readU8(is);
1876 light_source = readU8(is);
1877 damage_per_second = readU32(is);
1878 node_box.deSerialize(is);
1879 selection_box.deSerialize(is);
1880 legacy_facedir_simple = readU8(is);
1881 legacy_wallmounted = readU8(is);
1882 deSerializeSimpleSoundSpec(sound_footstep, is);
1883 deSerializeSimpleSoundSpec(sound_dig, is);
1884 deSerializeSimpleSoundSpec(sound_dug, is);
1885 rightclickable = readU8(is);
1886 drowning = readU8(is);
1887 leveled = readU8(is);
1888 liquid_range = readU8(is);
1889 } else if (version == 7 || version == 8){
1890 name = deSerializeString(is);
1892 u32 groups_size = readU16(is);
1893 for (u32 i = 0; i < groups_size; i++) {
1894 std::string name = deSerializeString(is);
1895 int value = readS16(is);
1896 groups[name] = value;
1898 drawtype = (enum NodeDrawType) readU8(is);
1900 visual_scale = readF1000(is);
1901 if (readU8(is) != 6)
1902 throw SerializationError("unsupported tile count");
1903 for (u32 i = 0; i < 6; i++)
1904 tiledef[i].deSerialize(is, version, drawtype);
1905 if (readU8(is) != CF_SPECIAL_COUNT)
1906 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1907 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1908 tiledef_special[i].deSerialize(is, version, drawtype);
1910 post_effect_color.setAlpha(readU8(is));
1911 post_effect_color.setRed(readU8(is));
1912 post_effect_color.setGreen(readU8(is));
1913 post_effect_color.setBlue(readU8(is));
1914 param_type = (enum ContentParamType) readU8(is);
1915 param_type_2 = (enum ContentParamType2) readU8(is);
1916 is_ground_content = readU8(is);
1917 light_propagates = readU8(is);
1918 sunlight_propagates = readU8(is);
1919 walkable = readU8(is);
1920 pointable = readU8(is);
1921 diggable = readU8(is);
1922 climbable = readU8(is);
1923 buildable_to = readU8(is);
1924 deSerializeString(is); // legacy: used to be metadata_name
1925 liquid_type = (enum LiquidType) readU8(is);
1926 liquid_alternative_flowing = deSerializeString(is);
1927 liquid_alternative_source = deSerializeString(is);
1928 liquid_viscosity = readU8(is);
1929 liquid_renewable = readU8(is);
1930 light_source = readU8(is);
1931 light_source = MYMIN(light_source, LIGHT_MAX);
1932 damage_per_second = readU32(is);
1933 node_box.deSerialize(is);
1934 selection_box.deSerialize(is);
1935 legacy_facedir_simple = readU8(is);
1936 legacy_wallmounted = readU8(is);
1937 deSerializeSimpleSoundSpec(sound_footstep, is);
1938 deSerializeSimpleSoundSpec(sound_dig, is);
1939 deSerializeSimpleSoundSpec(sound_dug, is);
1940 rightclickable = readU8(is);
1941 drowning = readU8(is);
1942 leveled = readU8(is);
1943 liquid_range = readU8(is);
1944 waving = readU8(is);
1946 mesh = deSerializeString(is);
1947 collision_box.deSerialize(is);
1948 floodable = readU8(is);
1949 u16 connects_to_size = readU16(is);
1950 connects_to_ids.clear();
1951 for (u16 i = 0; i < connects_to_size; i++)
1952 connects_to_ids.insert(readU16(is));
1953 connect_sides = readU8(is);
1954 } catch (SerializationError &e) {};
1956 throw SerializationError("unsupported ContentFeatures version");
1961 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1963 return m_node_registration_complete;
1967 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1969 m_node_registration_complete = completed;
1973 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1976 if (m_node_registration_complete)
1977 nr->nodeResolveInternal();
1979 m_pending_resolve_callbacks.push_back(nr);
1983 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1985 size_t len = m_pending_resolve_callbacks.size();
1986 for (size_t i = 0; i != len; i++) {
1987 if (nr != m_pending_resolve_callbacks[i])
1991 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1992 m_pending_resolve_callbacks.resize(len);
2000 void CNodeDefManager::runNodeResolveCallbacks()
2002 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
2003 NodeResolver *nr = m_pending_resolve_callbacks[i];
2004 nr->nodeResolveInternal();
2007 m_pending_resolve_callbacks.clear();
2011 void CNodeDefManager::resetNodeResolveState()
2013 m_node_registration_complete = false;
2014 m_pending_resolve_callbacks.clear();
2017 void CNodeDefManager::mapNodeboxConnections()
2019 for (u32 i = 0; i < m_content_features.size(); i++) {
2020 ContentFeatures *f = &m_content_features[i];
2021 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
2023 for (std::vector<std::string>::iterator it = f->connects_to.begin();
2024 it != f->connects_to.end(); ++it) {
2025 getIds(*it, f->connects_to_ids);
2030 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
2032 const ContentFeatures &f1 = get(from);
2034 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
2037 // lookup target in connected set
2038 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
2041 const ContentFeatures &f2 = get(to);
2043 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
2044 // ignores actually looking if back connection exists
2045 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
2047 // does to node declare usable faces?
2048 if (f2.connect_sides > 0) {
2049 if ((f2.param_type_2 == CPT2_FACEDIR ||
2050 f2.param_type_2 == CPT2_COLORED_FACEDIR)
2051 && (connect_face >= 4)) {
2052 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2053 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2055 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2057 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
2058 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2060 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0,
2062 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
2064 return (f2.connect_sides
2065 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
2067 return (f2.connect_sides & connect_face);
2069 // the target is just a regular node, so connect no matter back connection
2077 NodeResolver::NodeResolver()
2080 m_nodenames_idx = 0;
2081 m_nnlistsizes_idx = 0;
2082 m_resolve_done = false;
2084 m_nodenames.reserve(16);
2085 m_nnlistsizes.reserve(4);
2089 NodeResolver::~NodeResolver()
2091 if (!m_resolve_done && m_ndef)
2092 m_ndef->cancelNodeResolveCallback(this);
2096 void NodeResolver::nodeResolveInternal()
2098 m_nodenames_idx = 0;
2099 m_nnlistsizes_idx = 0;
2102 m_resolve_done = true;
2104 m_nodenames.clear();
2105 m_nnlistsizes.clear();
2109 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
2110 const std::string &node_alt, content_t c_fallback)
2112 if (m_nodenames_idx == m_nodenames.size()) {
2113 *result_out = c_fallback;
2114 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2119 std::string name = m_nodenames[m_nodenames_idx++];
2121 bool success = m_ndef->getId(name, c);
2122 if (!success && node_alt != "") {
2124 success = m_ndef->getId(name, c);
2128 errorstream << "NodeResolver: failed to resolve node name '" << name
2129 << "'." << std::endl;
2138 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
2139 bool all_required, content_t c_fallback)
2141 bool success = true;
2143 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
2144 errorstream << "NodeResolver: no more node lists" << std::endl;
2148 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
2151 if (m_nodenames_idx == m_nodenames.size()) {
2152 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2157 std::string &name = m_nodenames[m_nodenames_idx++];
2159 if (name.substr(0,6) != "group:") {
2160 if (m_ndef->getId(name, c)) {
2161 result_out->push_back(c);
2162 } else if (all_required) {
2163 errorstream << "NodeResolver: failed to resolve node name '"
2164 << name << "'." << std::endl;
2165 result_out->push_back(c_fallback);
2169 std::set<content_t> cids;
2170 std::set<content_t>::iterator it;
2171 m_ndef->getIds(name, cids);
2172 for (it = cids.begin(); it != cids.end(); ++it)
2173 result_out->push_back(*it);