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.
27 #include "client/renderingengine.h"
28 #include "client/tile.h"
29 #include <IMeshManipulator.h>
33 #include "nameidmapping.h"
34 #include "util/numeric.h"
35 #include "util/serialize.h"
36 #include "exceptions.h"
40 #include <fstream> // Used in applyTextureOverrides()
48 type = NODEBOX_REGULAR;
51 // default is sign/ladder-like
52 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
53 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
54 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
55 // no default for other parts
57 connect_bottom.clear();
58 connect_front.clear();
61 connect_right.clear();
64 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
68 if (protocol_version >= 27)
76 writeU8(os, NODEBOX_FIXED);
80 writeU16(os, fixed.size());
81 for (std::vector<aabb3f>::const_iterator
83 i != fixed.end(); ++i)
85 writeV3F1000(os, i->MinEdge);
86 writeV3F1000(os, i->MaxEdge);
89 case NODEBOX_WALLMOUNTED:
92 writeV3F1000(os, wall_top.MinEdge);
93 writeV3F1000(os, wall_top.MaxEdge);
94 writeV3F1000(os, wall_bottom.MinEdge);
95 writeV3F1000(os, wall_bottom.MaxEdge);
96 writeV3F1000(os, wall_side.MinEdge);
97 writeV3F1000(os, wall_side.MaxEdge);
99 case NODEBOX_CONNECTED:
101 // send old clients nodes that can't be walked through
103 writeU8(os, NODEBOX_FIXED);
106 writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2));
107 writeV3F1000(os, v3f(BS/2, BS/2, BS/2));
111 #define WRITEBOX(box) do { \
112 writeU16(os, (box).size()); \
113 for (std::vector<aabb3f>::const_iterator \
115 i != (box).end(); ++i) { \
116 writeV3F1000(os, i->MinEdge); \
117 writeV3F1000(os, i->MaxEdge); \
121 WRITEBOX(connect_top);
122 WRITEBOX(connect_bottom);
123 WRITEBOX(connect_front);
124 WRITEBOX(connect_left);
125 WRITEBOX(connect_back);
126 WRITEBOX(connect_right);
135 void NodeBox::deSerialize(std::istream &is)
137 int version = readU8(is);
138 if (version < 1 || version > 3)
139 throw SerializationError("unsupported NodeBox version");
143 type = (enum NodeBoxType)readU8(is);
145 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
147 u16 fixed_count = readU16(is);
151 box.MinEdge = readV3F1000(is);
152 box.MaxEdge = readV3F1000(is);
153 fixed.push_back(box);
156 else if(type == NODEBOX_WALLMOUNTED)
158 wall_top.MinEdge = readV3F1000(is);
159 wall_top.MaxEdge = readV3F1000(is);
160 wall_bottom.MinEdge = readV3F1000(is);
161 wall_bottom.MaxEdge = readV3F1000(is);
162 wall_side.MinEdge = readV3F1000(is);
163 wall_side.MaxEdge = readV3F1000(is);
165 else if (type == NODEBOX_CONNECTED)
167 #define READBOXES(box) do { \
168 count = readU16(is); \
169 (box).reserve(count); \
171 v3f min = readV3F1000(is); \
172 v3f max = readV3F1000(is); \
173 (box).push_back(aabb3f(min, max)); }; } while (0)
178 READBOXES(connect_top);
179 READBOXES(connect_bottom);
180 READBOXES(connect_front);
181 READBOXES(connect_left);
182 READBOXES(connect_back);
183 READBOXES(connect_right);
191 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
193 if (protocol_version >= 30)
195 else if (protocol_version >= 29)
197 else if (protocol_version >= 26)
202 os << serializeString(name);
203 animation.serialize(os, protocol_version);
204 writeU8(os, backface_culling);
205 if (protocol_version >= 26) {
206 writeU8(os, tileable_horizontal);
207 writeU8(os, tileable_vertical);
209 if (protocol_version >= 30) {
210 writeU8(os, has_color);
212 writeU8(os, color.getRed());
213 writeU8(os, color.getGreen());
214 writeU8(os, color.getBlue());
219 void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
221 int version = readU8(is);
222 name = deSerializeString(is);
223 animation.deSerialize(is, version >= 3 ? 29 : 26);
225 backface_culling = readU8(is);
227 tileable_horizontal = readU8(is);
228 tileable_vertical = readU8(is);
231 has_color = readU8(is);
233 color.setRed(readU8(is));
234 color.setGreen(readU8(is));
235 color.setBlue(readU8(is));
239 if ((contenfeatures_version < 8) &&
240 ((drawtype == NDT_MESH) ||
241 (drawtype == NDT_FIRELIKE) ||
242 (drawtype == NDT_LIQUID) ||
243 (drawtype == NDT_PLANTLIKE)))
244 backface_culling = false;
249 SimpleSoundSpec serialization
252 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
253 std::ostream &os, u8 version)
255 os<<serializeString(ss.name);
256 writeF1000(os, ss.gain);
259 writeF1000(os, ss.pitch);
261 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is, u8 version)
263 ss.name = deSerializeString(is);
264 ss.gain = readF1000(is);
267 ss.pitch = readF1000(is);
270 void TextureSettings::readSettings()
272 connected_glass = g_settings->getBool("connected_glass");
273 opaque_water = g_settings->getBool("opaque_water");
274 bool enable_shaders = g_settings->getBool("enable_shaders");
275 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
276 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
277 bool smooth_lighting = g_settings->getBool("smooth_lighting");
278 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
279 enable_minimap = g_settings->getBool("enable_minimap");
280 std::string leaves_style_str = g_settings->get("leaves_style");
282 // Mesh cache is not supported in combination with smooth lighting
284 enable_mesh_cache = false;
286 use_normal_texture = enable_shaders &&
287 (enable_bumpmapping || enable_parallax_occlusion);
288 if (leaves_style_str == "fancy") {
289 leaves_style = LEAVES_FANCY;
290 } else if (leaves_style_str == "simple") {
291 leaves_style = LEAVES_SIMPLE;
293 leaves_style = LEAVES_OPAQUE;
301 ContentFeatures::ContentFeatures()
306 ContentFeatures::~ContentFeatures()
310 void ContentFeatures::reset()
317 visual_solidness = 0;
318 backface_culling = true;
321 has_on_construct = false;
322 has_on_destruct = false;
323 has_after_destruct = false;
327 NOTE: Most of this is always overridden by the default values given
332 // Unknown nodes can be dug
333 groups["dig_immediate"] = 2;
334 drawtype = NDT_NORMAL;
337 for(u32 i = 0; i < 24; i++)
339 minimap_color = video::SColor(0, 0, 0, 0);
342 for(u32 i = 0; i < 6; i++)
343 tiledef[i] = TileDef();
344 for(u16 j = 0; j < CF_SPECIAL_COUNT; j++)
345 tiledef_special[j] = TileDef();
347 post_effect_color = video::SColor(0, 0, 0, 0);
348 param_type = CPT_NONE;
349 param_type_2 = CPT2_NONE;
350 is_ground_content = false;
351 light_propagates = false;
352 sunlight_propagates = false;
357 buildable_to = false;
359 rightclickable = true;
361 liquid_type = LIQUID_NONE;
362 liquid_alternative_flowing = "";
363 liquid_alternative_source = "";
364 liquid_viscosity = 0;
365 liquid_renewable = true;
366 liquid_range = LIQUID_LEVEL_MAX+1;
369 damage_per_second = 0;
370 node_box = NodeBox();
371 selection_box = NodeBox();
372 collision_box = NodeBox();
374 legacy_facedir_simple = false;
375 legacy_wallmounted = false;
376 sound_footstep = SimpleSoundSpec();
377 sound_dig = SimpleSoundSpec("__group");
378 sound_dug = SimpleSoundSpec();
380 connects_to_ids.clear();
382 color = video::SColor(0xFFFFFFFF);
387 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
389 if (protocol_version < 31) {
390 serializeOld(os, protocol_version);
395 u8 version = (protocol_version >= 34) ? 11 : 10;
396 writeU8(os, version);
399 os << serializeString(name);
400 writeU16(os, groups.size());
401 for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end();
403 os << serializeString(i->first);
404 writeS16(os, i->second);
406 writeU8(os, param_type);
407 writeU8(os, param_type_2);
410 writeU8(os, drawtype);
411 os << serializeString(mesh);
412 writeF1000(os, visual_scale);
414 for (u32 i = 0; i < 6; i++)
415 tiledef[i].serialize(os, protocol_version);
416 for (u32 i = 0; i < 6; i++)
417 tiledef_overlay[i].serialize(os, protocol_version);
418 writeU8(os, CF_SPECIAL_COUNT);
419 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
420 tiledef_special[i].serialize(os, protocol_version);
423 writeU8(os, color.getRed());
424 writeU8(os, color.getGreen());
425 writeU8(os, color.getBlue());
426 os << serializeString(palette_name);
428 writeU8(os, connect_sides);
429 writeU16(os, connects_to_ids.size());
430 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
431 i != connects_to_ids.end(); ++i)
433 writeU8(os, post_effect_color.getAlpha());
434 writeU8(os, post_effect_color.getRed());
435 writeU8(os, post_effect_color.getGreen());
436 writeU8(os, post_effect_color.getBlue());
437 writeU8(os, leveled);
440 writeU8(os, light_propagates);
441 writeU8(os, sunlight_propagates);
442 writeU8(os, light_source);
445 writeU8(os, is_ground_content);
448 writeU8(os, walkable);
449 writeU8(os, pointable);
450 writeU8(os, diggable);
451 writeU8(os, climbable);
452 writeU8(os, buildable_to);
453 writeU8(os, rightclickable);
454 writeU32(os, damage_per_second);
457 writeU8(os, liquid_type);
458 os << serializeString(liquid_alternative_flowing);
459 os << serializeString(liquid_alternative_source);
460 writeU8(os, liquid_viscosity);
461 writeU8(os, liquid_renewable);
462 writeU8(os, liquid_range);
463 writeU8(os, drowning);
464 writeU8(os, floodable);
467 node_box.serialize(os, protocol_version);
468 selection_box.serialize(os, protocol_version);
469 collision_box.serialize(os, protocol_version);
472 serializeSimpleSoundSpec(sound_footstep, os, version);
473 serializeSimpleSoundSpec(sound_dig, os, version);
474 serializeSimpleSoundSpec(sound_dug, os, version);
477 writeU8(os, legacy_facedir_simple);
478 writeU8(os, legacy_wallmounted);
481 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
483 // alpha == 0 means that the node is using texture alpha
484 if (alpha == 0 || alpha == 255)
487 for (int i = 0; i < length; i++) {
488 if (tiles[i].name == "")
491 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
492 tiles[i].name = s.str();
496 void ContentFeatures::deSerialize(std::istream &is)
499 int version = readU8(is);
501 deSerializeOld(is, version);
503 } else if (version > 11) {
504 throw SerializationError("unsupported ContentFeatures version");
508 name = deSerializeString(is);
510 u32 groups_size = readU16(is);
511 for (u32 i = 0; i < groups_size; i++) {
512 std::string name = deSerializeString(is);
513 int value = readS16(is);
514 groups[name] = value;
516 param_type = (enum ContentParamType) readU8(is);
517 param_type_2 = (enum ContentParamType2) readU8(is);
520 drawtype = (enum NodeDrawType) readU8(is);
521 mesh = deSerializeString(is);
522 visual_scale = readF1000(is);
524 throw SerializationError("unsupported tile count");
525 for (u32 i = 0; i < 6; i++)
526 tiledef[i].deSerialize(is, version, drawtype);
528 for (u32 i = 0; i < 6; i++)
529 tiledef_overlay[i].deSerialize(is, version, drawtype);
530 if (readU8(is) != CF_SPECIAL_COUNT)
531 throw SerializationError("unsupported CF_SPECIAL_COUNT");
532 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
533 tiledef_special[i].deSerialize(is, version, drawtype);
535 color.setRed(readU8(is));
536 color.setGreen(readU8(is));
537 color.setBlue(readU8(is));
538 palette_name = deSerializeString(is);
540 connect_sides = readU8(is);
541 u16 connects_to_size = readU16(is);
542 connects_to_ids.clear();
543 for (u16 i = 0; i < connects_to_size; i++)
544 connects_to_ids.insert(readU16(is));
545 post_effect_color.setAlpha(readU8(is));
546 post_effect_color.setRed(readU8(is));
547 post_effect_color.setGreen(readU8(is));
548 post_effect_color.setBlue(readU8(is));
549 leveled = readU8(is);
552 light_propagates = readU8(is);
553 sunlight_propagates = readU8(is);
554 light_source = readU8(is);
555 light_source = MYMIN(light_source, LIGHT_MAX);
558 is_ground_content = readU8(is);
561 walkable = readU8(is);
562 pointable = readU8(is);
563 diggable = readU8(is);
564 climbable = readU8(is);
565 buildable_to = readU8(is);
566 rightclickable = readU8(is);
567 damage_per_second = readU32(is);
570 liquid_type = (enum LiquidType) readU8(is);
571 liquid_alternative_flowing = deSerializeString(is);
572 liquid_alternative_source = deSerializeString(is);
573 liquid_viscosity = readU8(is);
574 liquid_renewable = readU8(is);
575 liquid_range = readU8(is);
576 drowning = readU8(is);
577 floodable = readU8(is);
580 node_box.deSerialize(is);
581 selection_box.deSerialize(is);
582 collision_box.deSerialize(is);
585 deSerializeSimpleSoundSpec(sound_footstep, is, version);
586 deSerializeSimpleSoundSpec(sound_dig, is, version);
587 deSerializeSimpleSoundSpec(sound_dug, is, version);
589 // read legacy properties
590 legacy_facedir_simple = readU8(is);
591 legacy_wallmounted = readU8(is);
595 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
596 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
597 bool backface_culling, u8 material_type)
599 tile->shader_id = shader_id;
600 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
601 tile->material_type = material_type;
603 // Normal texture and shader flags texture
604 if (use_normal_texture) {
605 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
607 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
610 tile->material_flags = 0;
611 if (backface_culling)
612 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
613 if (tiledef->animation.type != TAT_NONE)
614 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
615 if (tiledef->tileable_horizontal)
616 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
617 if (tiledef->tileable_vertical)
618 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
621 tile->has_color = tiledef->has_color;
622 if (tiledef->has_color)
623 tile->color = tiledef->color;
627 // Animation parameters
629 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
631 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
632 &frame_count, &frame_length_ms, NULL);
633 tile->animation_frame_count = frame_count;
634 tile->animation_frame_length_ms = frame_length_ms;
637 if (frame_count == 1) {
638 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
640 std::ostringstream os(std::ios::binary);
642 tile->frames = std::make_shared<std::vector<FrameSpec>>();
644 tile->frames->resize(frame_count);
646 for (int i = 0; i < frame_count; i++) {
652 tiledef->animation.getTextureModifer(os,
653 tile->texture->getOriginalSize(), i);
655 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
656 if (tile->normal_texture)
657 frame.normal_texture = tsrc->getNormalTexture(os.str());
658 frame.flags_texture = tile->flags_texture;
659 (*tile->frames)[i] = frame;
666 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
667 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
669 // minimap pixel color - the average color of a texture
670 if (tsettings.enable_minimap && tiledef[0].name != "")
671 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
673 // Figure out the actual tiles to use
675 for (u32 j = 0; j < 6; j++) {
676 tdef[j] = tiledef[j];
677 if (tdef[j].name == "")
678 tdef[j].name = "unknown_node.png";
680 // also the overlay tiles
681 TileDef tdef_overlay[6];
682 for (u32 j = 0; j < 6; j++)
683 tdef_overlay[j] = tiledef_overlay[j];
684 // also the special tiles
685 TileDef tdef_spec[6];
686 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
687 tdef_spec[j] = tiledef_special[j];
689 bool is_liquid = false;
691 u8 material_type = (alpha == 255) ?
692 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
697 material_type = (alpha == 255) ?
698 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
705 assert(liquid_type == LIQUID_SOURCE);
706 if (tsettings.opaque_water)
711 case NDT_FLOWINGLIQUID:
712 assert(liquid_type == LIQUID_FLOWING);
714 if (tsettings.opaque_water)
720 visual_solidness = 1;
722 case NDT_GLASSLIKE_FRAMED:
724 visual_solidness = 1;
726 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
728 visual_solidness = 1;
729 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
733 visual_solidness = 1;
735 case NDT_ALLFACES_OPTIONAL:
736 if (tsettings.leaves_style == LEAVES_FANCY) {
737 drawtype = NDT_ALLFACES;
739 visual_solidness = 1;
740 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
741 for (u32 j = 0; j < 6; j++) {
742 if (tdef_spec[j].name != "")
743 tdef[j].name = tdef_spec[j].name;
745 drawtype = NDT_GLASSLIKE;
747 visual_solidness = 1;
749 drawtype = NDT_NORMAL;
751 for (u32 i = 0; i < 6; i++)
752 tdef[i].name += std::string("^[noalpha");
755 material_type = TILE_MATERIAL_WAVING_LEAVES;
760 material_type = TILE_MATERIAL_WAVING_PLANTS;
769 material_type = TILE_MATERIAL_WAVING_PLANTS;
770 else if (waving == 2)
771 material_type = TILE_MATERIAL_WAVING_LEAVES;
779 case NDT_PLANTLIKE_ROOTED:
785 // Vertex alpha is no longer supported, correct if necessary.
786 correctAlpha(tdef, 6);
787 correctAlpha(tdef_overlay, 6);
788 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
789 material_type = (alpha == 255) ?
790 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
793 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
795 u8 overlay_material = material_type;
796 if (overlay_material == TILE_MATERIAL_OPAQUE)
797 overlay_material = TILE_MATERIAL_BASIC;
798 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
799 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
801 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
803 // Tiles (fill in f->tiles[])
804 for (u16 j = 0; j < 6; j++) {
805 fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader,
806 tsettings.use_normal_texture,
807 tdef[j].backface_culling, material_type);
808 if (tdef_overlay[j].name != "")
809 fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j],
810 overlay_shader, tsettings.use_normal_texture,
811 tdef[j].backface_culling, overlay_material);
814 u8 special_material = material_type;
815 if (drawtype == NDT_PLANTLIKE_ROOTED) {
817 special_material = TILE_MATERIAL_WAVING_PLANTS;
818 else if (waving == 2)
819 special_material = TILE_MATERIAL_WAVING_LEAVES;
821 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
823 // Special tiles (fill in f->special_tiles[])
824 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
825 fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j],
826 special_shader, tsettings.use_normal_texture,
827 tdef_spec[j].backface_culling, special_material);
830 if (param_type_2 == CPT2_COLOR ||
831 param_type_2 == CPT2_COLORED_FACEDIR ||
832 param_type_2 == CPT2_COLORED_WALLMOUNTED)
833 palette = tsrc->getPalette(palette_name);
835 if ((drawtype == NDT_MESH) && (mesh != "")) {
837 // Read the mesh and apply scale
838 mesh_ptr[0] = client->getMesh(mesh);
840 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
841 scaleMesh(mesh_ptr[0], scale);
842 recalculateBoundingBox(mesh_ptr[0]);
843 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
845 } else if ((drawtype == NDT_NODEBOX) &&
846 ((node_box.type == NODEBOX_REGULAR) ||
847 (node_box.type == NODEBOX_FIXED)) &&
848 (!node_box.fixed.empty())) {
849 //Convert regular nodebox nodes to meshnodes
850 //Change the drawtype and apply scale
852 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
853 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
854 scaleMesh(mesh_ptr[0], scale);
855 recalculateBoundingBox(mesh_ptr[0]);
856 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
859 //Cache 6dfacedir and wallmounted rotated clones of meshes
860 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
861 (param_type_2 == CPT2_FACEDIR
862 || param_type_2 == CPT2_COLORED_FACEDIR)) {
863 for (u16 j = 1; j < 24; j++) {
864 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
865 rotateMeshBy6dFacedir(mesh_ptr[j], j);
866 recalculateBoundingBox(mesh_ptr[j]);
867 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
869 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
870 && (param_type_2 == CPT2_WALLMOUNTED ||
871 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
872 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
873 for (u16 j = 1; j < 6; j++) {
874 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
875 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
876 recalculateBoundingBox(mesh_ptr[j]);
877 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
879 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
880 recalculateBoundingBox(mesh_ptr[0]);
881 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
890 class CNodeDefManager: public IWritableNodeDefManager {
893 virtual ~CNodeDefManager();
895 virtual IWritableNodeDefManager *clone();
896 inline virtual const ContentFeatures& get(content_t c) const;
897 inline virtual const ContentFeatures& get(const MapNode &n) const;
898 virtual bool getId(const std::string &name, content_t &result) const;
899 virtual content_t getId(const std::string &name) const;
900 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
901 virtual const ContentFeatures& get(const std::string &name) const;
902 content_t allocateId();
903 virtual content_t set(const std::string &name, const ContentFeatures &def);
904 virtual content_t allocateDummy(const std::string &name);
905 virtual void removeNode(const std::string &name);
906 virtual void updateAliases(IItemDefManager *idef);
907 virtual void applyTextureOverrides(const std::string &override_filepath);
908 virtual void updateTextures(IGameDef *gamedef,
909 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
910 void *progress_cbk_args);
911 void serialize(std::ostream &os, u16 protocol_version) const;
912 void deSerialize(std::istream &is);
914 inline virtual void setNodeRegistrationStatus(bool completed);
916 virtual void pendNodeResolve(NodeResolver *nr);
917 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
918 virtual void runNodeResolveCallbacks();
919 virtual void resetNodeResolveState();
920 virtual void mapNodeboxConnections();
921 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
922 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
924 return m_selection_box_int_union;
928 void addNameIdMapping(content_t i, std::string name);
930 * Recalculates m_selection_box_int_union based on
931 * m_selection_box_union.
933 void fixSelectionBoxIntUnion();
935 // Features indexed by id
936 std::vector<ContentFeatures> m_content_features;
938 // A mapping for fast converting back and forth between names and ids
939 NameIdMapping m_name_id_mapping;
941 // Like m_name_id_mapping, but only from names to ids, and includes
942 // item aliases too. Updated by updateAliases()
943 // Note: Not serialized.
945 std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases;
947 // A mapping from groups to a list of content_ts (and their levels)
948 // that belong to it. Necessary for a direct lookup in getIds().
949 // Note: Not serialized.
950 std::unordered_map<std::string, GroupItems> m_group_to_items;
952 // Next possibly free id
955 // NodeResolvers to callback once node registration has ended
956 std::vector<NodeResolver *> m_pending_resolve_callbacks;
958 // True when all nodes have been registered
959 bool m_node_registration_complete;
961 //! The union of all nodes' selection boxes.
962 aabb3f m_selection_box_union;
964 * The smallest box in node coordinates that
965 * contains all nodes' selection boxes.
967 core::aabbox3d<s16> m_selection_box_int_union;
971 CNodeDefManager::CNodeDefManager()
977 CNodeDefManager::~CNodeDefManager()
980 for (u32 i = 0; i < m_content_features.size(); i++) {
981 ContentFeatures *f = &m_content_features[i];
982 for (u32 j = 0; j < 24; j++) {
984 f->mesh_ptr[j]->drop();
991 void CNodeDefManager::clear()
993 m_content_features.clear();
994 m_name_id_mapping.clear();
995 m_name_id_mapping_with_aliases.clear();
996 m_group_to_items.clear();
998 m_selection_box_union.reset(0,0,0);
999 m_selection_box_int_union.reset(0,0,0);
1001 resetNodeResolveState();
1003 u32 initial_length = 0;
1004 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1005 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1006 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1007 m_content_features.resize(initial_length);
1009 // Set CONTENT_UNKNOWN
1013 // Insert directly into containers
1014 content_t c = CONTENT_UNKNOWN;
1015 m_content_features[c] = f;
1016 addNameIdMapping(c, f.name);
1023 f.drawtype = NDT_AIRLIKE;
1024 f.param_type = CPT_LIGHT;
1025 f.light_propagates = true;
1026 f.sunlight_propagates = true;
1028 f.pointable = false;
1030 f.buildable_to = true;
1032 f.is_ground_content = true;
1033 // Insert directly into containers
1034 content_t c = CONTENT_AIR;
1035 m_content_features[c] = f;
1036 addNameIdMapping(c, f.name);
1039 // Set CONTENT_IGNORE
1043 f.drawtype = NDT_AIRLIKE;
1044 f.param_type = CPT_NONE;
1045 f.light_propagates = false;
1046 f.sunlight_propagates = false;
1048 f.pointable = false;
1050 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1051 f.is_ground_content = true;
1052 // Insert directly into containers
1053 content_t c = CONTENT_IGNORE;
1054 m_content_features[c] = f;
1055 addNameIdMapping(c, f.name);
1060 IWritableNodeDefManager *CNodeDefManager::clone()
1062 CNodeDefManager *mgr = new CNodeDefManager();
1068 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1070 return c < m_content_features.size()
1071 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1075 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1077 return get(n.getContent());
1081 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1083 std::unordered_map<std::string, content_t>::const_iterator
1084 i = m_name_id_mapping_with_aliases.find(name);
1085 if(i == m_name_id_mapping_with_aliases.end())
1092 content_t CNodeDefManager::getId(const std::string &name) const
1094 content_t id = CONTENT_IGNORE;
1100 bool CNodeDefManager::getIds(const std::string &name,
1101 std::set<content_t> &result) const
1103 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1104 if (name.substr(0,6) != "group:") {
1105 content_t id = CONTENT_IGNORE;
1106 bool exists = getId(name, id);
1111 std::string group = name.substr(6);
1113 std::unordered_map<std::string, GroupItems>::const_iterator
1114 i = m_group_to_items.find(group);
1115 if (i == m_group_to_items.end())
1118 const GroupItems &items = i->second;
1119 for (GroupItems::const_iterator j = items.begin();
1120 j != items.end(); ++j) {
1121 if ((*j).second != 0)
1122 result.insert((*j).first);
1124 //printf("getIds: %dus\n", t.stop());
1129 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1131 content_t id = CONTENT_UNKNOWN;
1137 // returns CONTENT_IGNORE if no free ID found
1138 content_t CNodeDefManager::allocateId()
1140 for (content_t id = m_next_id;
1141 id >= m_next_id; // overflow?
1143 while (id >= m_content_features.size()) {
1144 m_content_features.push_back(ContentFeatures());
1146 const ContentFeatures &f = m_content_features[id];
1152 // If we arrive here, an overflow occurred in id.
1153 // That means no ID was found
1154 return CONTENT_IGNORE;
1159 * Returns the smallest box that contains all boxes
1160 * in the vector. Box_union is expanded.
1161 * @param[in] boxes the vector containing the boxes
1162 * @param[in, out] box_union the union of the arguments
1164 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1166 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1167 it != boxes.end(); ++it) {
1168 box_union->addInternalBox(*it);
1174 * Returns a box that contains the nodebox in every case.
1175 * The argument node_union is expanded.
1176 * @param[in] nodebox the nodebox to be measured
1177 * @param[in] features used to decide whether the nodebox
1179 * @param[in, out] box_union the union of the arguments
1181 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1184 switch(nodebox.type) {
1186 case NODEBOX_LEVELED: {
1188 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1189 boxVectorUnion(nodebox.fixed, &half_processed);
1190 // Set leveled boxes to maximal
1191 if (nodebox.type == NODEBOX_LEVELED) {
1192 half_processed.MaxEdge.Y = +BS / 2;
1194 if (features.param_type_2 == CPT2_FACEDIR ||
1195 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1196 // Get maximal coordinate
1198 fabsf(half_processed.MinEdge.X),
1199 fabsf(half_processed.MinEdge.Y),
1200 fabsf(half_processed.MinEdge.Z),
1201 fabsf(half_processed.MaxEdge.X),
1202 fabsf(half_processed.MaxEdge.Y),
1203 fabsf(half_processed.MaxEdge.Z) };
1205 for (int i = 0; i < 6; i++) {
1206 if (max < coords[i]) {
1210 // Add the union of all possible rotated boxes
1211 box_union->addInternalPoint(-max, -max, -max);
1212 box_union->addInternalPoint(+max, +max, +max);
1214 box_union->addInternalBox(half_processed);
1218 case NODEBOX_WALLMOUNTED: {
1220 box_union->addInternalBox(nodebox.wall_top);
1221 box_union->addInternalBox(nodebox.wall_bottom);
1222 // Find maximal coordinate in the X-Z plane
1224 fabsf(nodebox.wall_side.MinEdge.X),
1225 fabsf(nodebox.wall_side.MinEdge.Z),
1226 fabsf(nodebox.wall_side.MaxEdge.X),
1227 fabsf(nodebox.wall_side.MaxEdge.Z) };
1229 for (int i = 0; i < 4; i++) {
1230 if (max < coords[i]) {
1234 // Add the union of all possible rotated boxes
1235 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1236 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1239 case NODEBOX_CONNECTED: {
1240 // Add all possible connected boxes
1241 boxVectorUnion(nodebox.fixed, box_union);
1242 boxVectorUnion(nodebox.connect_top, box_union);
1243 boxVectorUnion(nodebox.connect_bottom, box_union);
1244 boxVectorUnion(nodebox.connect_front, box_union);
1245 boxVectorUnion(nodebox.connect_left, box_union);
1246 boxVectorUnion(nodebox.connect_back, box_union);
1247 boxVectorUnion(nodebox.connect_right, box_union);
1252 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1253 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1259 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1261 m_selection_box_int_union.MinEdge.X = floorf(
1262 m_selection_box_union.MinEdge.X / BS + 0.5f);
1263 m_selection_box_int_union.MinEdge.Y = floorf(
1264 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1265 m_selection_box_int_union.MinEdge.Z = floorf(
1266 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1267 m_selection_box_int_union.MaxEdge.X = ceilf(
1268 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1269 m_selection_box_int_union.MaxEdge.Y = ceilf(
1270 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1271 m_selection_box_int_union.MaxEdge.Z = ceilf(
1272 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1276 // IWritableNodeDefManager
1277 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1281 assert(name == def.name);
1283 // Don't allow redefining ignore (but allow air and unknown)
1284 if (name == "ignore") {
1285 warningstream << "NodeDefManager: Ignoring "
1286 "CONTENT_IGNORE redefinition"<<std::endl;
1287 return CONTENT_IGNORE;
1290 content_t id = CONTENT_IGNORE;
1291 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1294 if (id == CONTENT_IGNORE) {
1295 warningstream << "NodeDefManager: Absolute "
1296 "limit reached" << std::endl;
1297 return CONTENT_IGNORE;
1299 assert(id != CONTENT_IGNORE);
1300 addNameIdMapping(id, name);
1302 m_content_features[id] = def;
1303 verbosestream << "NodeDefManager: registering content id \"" << id
1304 << "\": name=\"" << def.name << "\""<<std::endl;
1306 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1307 fixSelectionBoxIntUnion();
1308 // Add this content to the list of all groups it belongs to
1309 // FIXME: This should remove a node from groups it no longer
1310 // belongs to when a node is re-registered
1311 for (ItemGroupList::const_iterator i = def.groups.begin();
1312 i != def.groups.end(); ++i) {
1313 std::string group_name = i->first;
1315 std::unordered_map<std::string, GroupItems>::iterator
1316 j = m_group_to_items.find(group_name);
1317 if (j == m_group_to_items.end()) {
1318 m_group_to_items[group_name].push_back(
1319 std::make_pair(id, i->second));
1321 GroupItems &items = j->second;
1322 items.push_back(std::make_pair(id, i->second));
1329 content_t CNodeDefManager::allocateDummy(const std::string &name)
1331 assert(name != ""); // Pre-condition
1334 return set(name, f);
1338 void CNodeDefManager::removeNode(const std::string &name)
1343 // Erase name from name ID mapping
1344 content_t id = CONTENT_IGNORE;
1345 if (m_name_id_mapping.getId(name, id)) {
1346 m_name_id_mapping.eraseName(name);
1347 m_name_id_mapping_with_aliases.erase(name);
1350 // Erase node content from all groups it belongs to
1351 for (std::unordered_map<std::string, GroupItems>::iterator iter_groups =
1352 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1353 GroupItems &items = iter_groups->second;
1354 for (GroupItems::iterator iter_groupitems = items.begin();
1355 iter_groupitems != items.end();) {
1356 if (iter_groupitems->first == id)
1357 items.erase(iter_groupitems++);
1362 // Check if group is empty
1363 if (items.size() == 0)
1364 m_group_to_items.erase(iter_groups++);
1371 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1373 std::set<std::string> all;
1375 m_name_id_mapping_with_aliases.clear();
1376 for (std::set<std::string>::const_iterator
1377 i = all.begin(); i != all.end(); ++i) {
1378 const std::string &name = *i;
1379 const std::string &convert_to = idef->getAlias(name);
1381 if (m_name_id_mapping.getId(convert_to, id)) {
1382 m_name_id_mapping_with_aliases.insert(
1383 std::make_pair(name, id));
1388 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1390 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1391 "overrides to textures from " << override_filepath << std::endl;
1393 std::ifstream infile(override_filepath.c_str());
1396 while (std::getline(infile, line)) {
1398 if (trim(line) == "")
1400 std::vector<std::string> splitted = str_split(line, ' ');
1401 if (splitted.size() != 3) {
1402 errorstream << override_filepath
1403 << ":" << line_c << " Could not apply texture override \""
1404 << line << "\": Syntax error" << std::endl;
1409 if (!getId(splitted[0], id))
1410 continue; // Ignore unknown node
1412 ContentFeatures &nodedef = m_content_features[id];
1414 if (splitted[1] == "top")
1415 nodedef.tiledef[0].name = splitted[2];
1416 else if (splitted[1] == "bottom")
1417 nodedef.tiledef[1].name = splitted[2];
1418 else if (splitted[1] == "right")
1419 nodedef.tiledef[2].name = splitted[2];
1420 else if (splitted[1] == "left")
1421 nodedef.tiledef[3].name = splitted[2];
1422 else if (splitted[1] == "back")
1423 nodedef.tiledef[4].name = splitted[2];
1424 else if (splitted[1] == "front")
1425 nodedef.tiledef[5].name = splitted[2];
1426 else if (splitted[1] == "all" || splitted[1] == "*")
1427 for (int i = 0; i < 6; i++)
1428 nodedef.tiledef[i].name = splitted[2];
1429 else if (splitted[1] == "sides")
1430 for (int i = 2; i < 6; i++)
1431 nodedef.tiledef[i].name = splitted[2];
1433 errorstream << override_filepath
1434 << ":" << line_c << " Could not apply texture override \""
1435 << line << "\": Unknown node side \""
1436 << splitted[1] << "\"" << std::endl;
1442 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1443 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1444 void *progress_callback_args)
1447 infostream << "CNodeDefManager::updateTextures(): Updating "
1448 "textures in node definitions" << std::endl;
1450 Client *client = (Client *)gamedef;
1451 ITextureSource *tsrc = client->tsrc();
1452 IShaderSource *shdsrc = client->getShaderSource();
1453 scene::IMeshManipulator *meshmanip =
1454 RenderingEngine::get_scene_manager()->getMeshManipulator();
1455 TextureSettings tsettings;
1456 tsettings.readSettings();
1458 u32 size = m_content_features.size();
1460 for (u32 i = 0; i < size; i++) {
1461 ContentFeatures *f = &(m_content_features[i]);
1462 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1463 progress_callback(progress_callback_args, i, size);
1468 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1470 writeU8(os, 1); // version
1472 std::ostringstream os2(std::ios::binary);
1473 for (u32 i = 0; i < m_content_features.size(); i++) {
1474 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1475 || i == CONTENT_UNKNOWN)
1477 const ContentFeatures *f = &m_content_features[i];
1481 // Wrap it in a string to allow different lengths without
1482 // strict version incompatibilities
1483 std::ostringstream wrapper_os(std::ios::binary);
1484 f->serialize(wrapper_os, protocol_version);
1485 os2<<serializeString(wrapper_os.str());
1487 // must not overflow
1488 u16 next = count + 1;
1489 FATAL_ERROR_IF(next < count, "Overflow");
1492 writeU16(os, count);
1493 os << serializeLongString(os2.str());
1497 void CNodeDefManager::deSerialize(std::istream &is)
1500 int version = readU8(is);
1502 throw SerializationError("unsupported NodeDefinitionManager version");
1503 u16 count = readU16(is);
1504 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1506 for (u16 n = 0; n < count; n++) {
1507 u16 i = readU16(is2);
1509 // Read it from the string wrapper
1510 std::string wrapper = deSerializeString(is2);
1511 std::istringstream wrapper_is(wrapper, std::ios::binary);
1512 f.deSerialize(wrapper_is);
1514 // Check error conditions
1515 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1516 warningstream << "NodeDefManager::deSerialize(): "
1517 "not changing builtin node " << i << std::endl;
1521 warningstream << "NodeDefManager::deSerialize(): "
1522 "received empty name" << std::endl;
1528 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1529 warningstream << "NodeDefManager::deSerialize(): "
1530 "already defined with different ID: " << f.name << std::endl;
1534 // All is ok, add node definition with the requested ID
1535 if (i >= m_content_features.size())
1536 m_content_features.resize((u32)(i) + 1);
1537 m_content_features[i] = f;
1538 addNameIdMapping(i, f.name);
1539 verbosestream << "deserialized " << f.name << std::endl;
1541 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1542 fixSelectionBoxIntUnion();
1547 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1549 m_name_id_mapping.set(i, name);
1550 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1554 IWritableNodeDefManager *createNodeDefManager()
1556 return new CNodeDefManager();
1560 //// Serialization of old ContentFeatures formats
1561 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1563 u8 compatible_param_type_2 = param_type_2;
1564 if ((protocol_version < 28)
1565 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1566 compatible_param_type_2 = CPT2_NONE;
1567 else if (protocol_version < 30) {
1568 if (compatible_param_type_2 == CPT2_COLOR)
1569 compatible_param_type_2 = CPT2_NONE;
1570 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1571 compatible_param_type_2 = CPT2_FACEDIR;
1572 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1573 compatible_param_type_2 = CPT2_WALLMOUNTED;
1576 float compatible_visual_scale = visual_scale;
1577 if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1578 compatible_visual_scale = sqrt(visual_scale);
1580 TileDef compatible_tiles[6];
1581 for (u8 i = 0; i < 6; i++) {
1582 compatible_tiles[i] = tiledef[i];
1583 if (tiledef_overlay[i].name != "") {
1584 std::stringstream s;
1585 s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name
1587 compatible_tiles[i].name = s.str();
1592 if (protocol_version < 31) {
1593 writeU8(os, protocol_version < 27 ? 7 : 8);
1595 os << serializeString(name);
1596 writeU16(os, groups.size());
1597 for (ItemGroupList::const_iterator i = groups.begin();
1598 i != groups.end(); ++i) {
1599 os << serializeString(i->first);
1600 writeS16(os, i->second);
1602 writeU8(os, drawtype);
1603 writeF1000(os, compatible_visual_scale);
1605 for (u32 i = 0; i < 6; i++)
1606 compatible_tiles[i].serialize(os, protocol_version);
1607 writeU8(os, CF_SPECIAL_COUNT);
1608 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1609 tiledef_special[i].serialize(os, protocol_version);
1611 writeU8(os, post_effect_color.getAlpha());
1612 writeU8(os, post_effect_color.getRed());
1613 writeU8(os, post_effect_color.getGreen());
1614 writeU8(os, post_effect_color.getBlue());
1615 writeU8(os, param_type);
1616 writeU8(os, compatible_param_type_2);
1617 writeU8(os, is_ground_content);
1618 writeU8(os, light_propagates);
1619 writeU8(os, sunlight_propagates);
1620 writeU8(os, walkable);
1621 writeU8(os, pointable);
1622 writeU8(os, diggable);
1623 writeU8(os, climbable);
1624 writeU8(os, buildable_to);
1625 os << serializeString(""); // legacy: used to be metadata_name
1626 writeU8(os, liquid_type);
1627 os << serializeString(liquid_alternative_flowing);
1628 os << serializeString(liquid_alternative_source);
1629 writeU8(os, liquid_viscosity);
1630 writeU8(os, liquid_renewable);
1631 writeU8(os, light_source);
1632 writeU32(os, damage_per_second);
1633 node_box.serialize(os, protocol_version);
1634 selection_box.serialize(os, protocol_version);
1635 writeU8(os, legacy_facedir_simple);
1636 writeU8(os, legacy_wallmounted);
1637 serializeSimpleSoundSpec(sound_footstep, os, 10);
1638 serializeSimpleSoundSpec(sound_dig, os, 10);
1639 serializeSimpleSoundSpec(sound_dug, os, 10);
1640 writeU8(os, rightclickable);
1641 writeU8(os, drowning);
1642 writeU8(os, leveled);
1643 writeU8(os, liquid_range);
1644 writeU8(os, waving);
1645 os << serializeString(mesh);
1646 collision_box.serialize(os, protocol_version);
1647 writeU8(os, floodable);
1648 writeU16(os, connects_to_ids.size());
1649 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1650 i != connects_to_ids.end(); ++i)
1652 writeU8(os, connect_sides);
1654 throw SerializationError("ContentFeatures::serialize(): "
1655 "Unsupported version requested");
1659 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1661 if (version == 5) // In PROTOCOL_VERSION 13
1663 name = deSerializeString(is);
1665 u32 groups_size = readU16(is);
1666 for(u32 i=0; i<groups_size; i++){
1667 std::string name = deSerializeString(is);
1668 int value = readS16(is);
1669 groups[name] = value;
1671 drawtype = (enum NodeDrawType)readU8(is);
1673 visual_scale = readF1000(is);
1674 if (readU8(is) != 6)
1675 throw SerializationError("unsupported tile count");
1676 for (u32 i = 0; i < 6; i++)
1677 tiledef[i].deSerialize(is, version, drawtype);
1678 if (readU8(is) != CF_SPECIAL_COUNT)
1679 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1680 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1681 tiledef_special[i].deSerialize(is, version, drawtype);
1683 post_effect_color.setAlpha(readU8(is));
1684 post_effect_color.setRed(readU8(is));
1685 post_effect_color.setGreen(readU8(is));
1686 post_effect_color.setBlue(readU8(is));
1687 param_type = (enum ContentParamType)readU8(is);
1688 param_type_2 = (enum ContentParamType2)readU8(is);
1689 is_ground_content = readU8(is);
1690 light_propagates = readU8(is);
1691 sunlight_propagates = readU8(is);
1692 walkable = readU8(is);
1693 pointable = readU8(is);
1694 diggable = readU8(is);
1695 climbable = readU8(is);
1696 buildable_to = readU8(is);
1697 deSerializeString(is); // legacy: used to be metadata_name
1698 liquid_type = (enum LiquidType)readU8(is);
1699 liquid_alternative_flowing = deSerializeString(is);
1700 liquid_alternative_source = deSerializeString(is);
1701 liquid_viscosity = readU8(is);
1702 light_source = readU8(is);
1703 light_source = MYMIN(light_source, LIGHT_MAX);
1704 damage_per_second = readU32(is);
1705 node_box.deSerialize(is);
1706 selection_box.deSerialize(is);
1707 legacy_facedir_simple = readU8(is);
1708 legacy_wallmounted = readU8(is);
1709 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1710 deSerializeSimpleSoundSpec(sound_dig, is, version);
1711 deSerializeSimpleSoundSpec(sound_dug, is, version);
1712 } else if (version == 6) {
1713 name = deSerializeString(is);
1715 u32 groups_size = readU16(is);
1716 for (u32 i = 0; i < groups_size; i++) {
1717 std::string name = deSerializeString(is);
1718 int value = readS16(is);
1719 groups[name] = value;
1721 drawtype = (enum NodeDrawType)readU8(is);
1722 visual_scale = readF1000(is);
1723 if (readU8(is) != 6)
1724 throw SerializationError("unsupported tile count");
1725 for (u32 i = 0; i < 6; i++)
1726 tiledef[i].deSerialize(is, version, drawtype);
1727 // CF_SPECIAL_COUNT in version 6 = 2
1728 if (readU8(is) != 2)
1729 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1730 for (u32 i = 0; i < 2; i++)
1731 tiledef_special[i].deSerialize(is, version, drawtype);
1733 post_effect_color.setAlpha(readU8(is));
1734 post_effect_color.setRed(readU8(is));
1735 post_effect_color.setGreen(readU8(is));
1736 post_effect_color.setBlue(readU8(is));
1737 param_type = (enum ContentParamType)readU8(is);
1738 param_type_2 = (enum ContentParamType2)readU8(is);
1739 is_ground_content = readU8(is);
1740 light_propagates = readU8(is);
1741 sunlight_propagates = readU8(is);
1742 walkable = readU8(is);
1743 pointable = readU8(is);
1744 diggable = readU8(is);
1745 climbable = readU8(is);
1746 buildable_to = readU8(is);
1747 deSerializeString(is); // legacy: used to be metadata_name
1748 liquid_type = (enum LiquidType)readU8(is);
1749 liquid_alternative_flowing = deSerializeString(is);
1750 liquid_alternative_source = deSerializeString(is);
1751 liquid_viscosity = readU8(is);
1752 liquid_renewable = readU8(is);
1753 light_source = readU8(is);
1754 damage_per_second = readU32(is);
1755 node_box.deSerialize(is);
1756 selection_box.deSerialize(is);
1757 legacy_facedir_simple = readU8(is);
1758 legacy_wallmounted = readU8(is);
1759 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1760 deSerializeSimpleSoundSpec(sound_dig, is, version);
1761 deSerializeSimpleSoundSpec(sound_dug, is, version);
1762 rightclickable = readU8(is);
1763 drowning = readU8(is);
1764 leveled = readU8(is);
1765 liquid_range = readU8(is);
1766 } else if (version == 7 || version == 8){
1767 name = deSerializeString(is);
1769 u32 groups_size = readU16(is);
1770 for (u32 i = 0; i < groups_size; i++) {
1771 std::string name = deSerializeString(is);
1772 int value = readS16(is);
1773 groups[name] = value;
1775 drawtype = (enum NodeDrawType) readU8(is);
1777 visual_scale = readF1000(is);
1778 if (readU8(is) != 6)
1779 throw SerializationError("unsupported tile count");
1780 for (u32 i = 0; i < 6; i++)
1781 tiledef[i].deSerialize(is, version, drawtype);
1782 if (readU8(is) != CF_SPECIAL_COUNT)
1783 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1784 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1785 tiledef_special[i].deSerialize(is, version, drawtype);
1787 post_effect_color.setAlpha(readU8(is));
1788 post_effect_color.setRed(readU8(is));
1789 post_effect_color.setGreen(readU8(is));
1790 post_effect_color.setBlue(readU8(is));
1791 param_type = (enum ContentParamType) readU8(is);
1792 param_type_2 = (enum ContentParamType2) readU8(is);
1793 is_ground_content = readU8(is);
1794 light_propagates = readU8(is);
1795 sunlight_propagates = readU8(is);
1796 walkable = readU8(is);
1797 pointable = readU8(is);
1798 diggable = readU8(is);
1799 climbable = readU8(is);
1800 buildable_to = readU8(is);
1801 deSerializeString(is); // legacy: used to be metadata_name
1802 liquid_type = (enum LiquidType) readU8(is);
1803 liquid_alternative_flowing = deSerializeString(is);
1804 liquid_alternative_source = deSerializeString(is);
1805 liquid_viscosity = readU8(is);
1806 liquid_renewable = readU8(is);
1807 light_source = readU8(is);
1808 light_source = MYMIN(light_source, LIGHT_MAX);
1809 damage_per_second = readU32(is);
1810 node_box.deSerialize(is);
1811 selection_box.deSerialize(is);
1812 legacy_facedir_simple = readU8(is);
1813 legacy_wallmounted = readU8(is);
1814 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1815 deSerializeSimpleSoundSpec(sound_dig, is, version);
1816 deSerializeSimpleSoundSpec(sound_dug, is, version);
1817 rightclickable = readU8(is);
1818 drowning = readU8(is);
1819 leveled = readU8(is);
1820 liquid_range = readU8(is);
1821 waving = readU8(is);
1823 mesh = deSerializeString(is);
1824 collision_box.deSerialize(is);
1825 floodable = readU8(is);
1826 u16 connects_to_size = readU16(is);
1827 connects_to_ids.clear();
1828 for (u16 i = 0; i < connects_to_size; i++)
1829 connects_to_ids.insert(readU16(is));
1830 connect_sides = readU8(is);
1831 } catch (SerializationError &e) {};
1833 throw SerializationError("unsupported ContentFeatures version");
1837 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1839 m_node_registration_complete = completed;
1843 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1846 if (m_node_registration_complete)
1847 nr->nodeResolveInternal();
1849 m_pending_resolve_callbacks.push_back(nr);
1853 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1855 size_t len = m_pending_resolve_callbacks.size();
1856 for (size_t i = 0; i != len; i++) {
1857 if (nr != m_pending_resolve_callbacks[i])
1861 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1862 m_pending_resolve_callbacks.resize(len);
1870 void CNodeDefManager::runNodeResolveCallbacks()
1872 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1873 NodeResolver *nr = m_pending_resolve_callbacks[i];
1874 nr->nodeResolveInternal();
1877 m_pending_resolve_callbacks.clear();
1881 void CNodeDefManager::resetNodeResolveState()
1883 m_node_registration_complete = false;
1884 m_pending_resolve_callbacks.clear();
1887 void CNodeDefManager::mapNodeboxConnections()
1889 for (u32 i = 0; i < m_content_features.size(); i++) {
1890 ContentFeatures *f = &m_content_features[i];
1891 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1893 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1894 it != f->connects_to.end(); ++it) {
1895 getIds(*it, f->connects_to_ids);
1900 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1902 const ContentFeatures &f1 = get(from);
1904 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1907 // lookup target in connected set
1908 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1911 const ContentFeatures &f2 = get(to);
1913 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1914 // ignores actually looking if back connection exists
1915 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1917 // does to node declare usable faces?
1918 if (f2.connect_sides > 0) {
1919 if ((f2.param_type_2 == CPT2_FACEDIR ||
1920 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1921 && (connect_face >= 4)) {
1922 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1923 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1925 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1927 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1928 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1930 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1931 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1932 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1934 return (f2.connect_sides
1935 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1937 return (f2.connect_sides & connect_face);
1939 // the target is just a regular node, so connect no matter back connection
1947 NodeResolver::NodeResolver()
1949 m_nodenames.reserve(16);
1950 m_nnlistsizes.reserve(4);
1954 NodeResolver::~NodeResolver()
1956 if (!m_resolve_done && m_ndef)
1957 m_ndef->cancelNodeResolveCallback(this);
1961 void NodeResolver::nodeResolveInternal()
1963 m_nodenames_idx = 0;
1964 m_nnlistsizes_idx = 0;
1967 m_resolve_done = true;
1969 m_nodenames.clear();
1970 m_nnlistsizes.clear();
1974 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1975 const std::string &node_alt, content_t c_fallback)
1977 if (m_nodenames_idx == m_nodenames.size()) {
1978 *result_out = c_fallback;
1979 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1984 std::string name = m_nodenames[m_nodenames_idx++];
1986 bool success = m_ndef->getId(name, c);
1987 if (!success && node_alt != "") {
1989 success = m_ndef->getId(name, c);
1993 errorstream << "NodeResolver: failed to resolve node name '" << name
1994 << "'." << std::endl;
2003 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
2004 bool all_required, content_t c_fallback)
2006 bool success = true;
2008 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
2009 errorstream << "NodeResolver: no more node lists" << std::endl;
2013 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
2016 if (m_nodenames_idx == m_nodenames.size()) {
2017 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2022 std::string &name = m_nodenames[m_nodenames_idx++];
2024 if (name.substr(0,6) != "group:") {
2025 if (m_ndef->getId(name, c)) {
2026 result_out->push_back(c);
2027 } else if (all_required) {
2028 errorstream << "NodeResolver: failed to resolve node name '"
2029 << name << "'." << std::endl;
2030 result_out->push_back(c_fallback);
2034 std::set<content_t> cids;
2035 std::set<content_t>::iterator it;
2036 m_ndef->getIds(name, cids);
2037 for (it = cids.begin(); it != cids.end(); ++it)
2038 result_out->push_back(*it);