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 (const aabb3f &nodebox : fixed) {
82 writeV3F1000(os, nodebox.MinEdge);
83 writeV3F1000(os, nodebox.MaxEdge);
86 case NODEBOX_WALLMOUNTED:
89 writeV3F1000(os, wall_top.MinEdge);
90 writeV3F1000(os, wall_top.MaxEdge);
91 writeV3F1000(os, wall_bottom.MinEdge);
92 writeV3F1000(os, wall_bottom.MaxEdge);
93 writeV3F1000(os, wall_side.MinEdge);
94 writeV3F1000(os, wall_side.MaxEdge);
96 case NODEBOX_CONNECTED:
98 // send old clients nodes that can't be walked through
100 writeU8(os, NODEBOX_FIXED);
103 writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2));
104 writeV3F1000(os, v3f(BS/2, BS/2, BS/2));
108 #define WRITEBOX(box) \
109 writeU16(os, (box).size()); \
110 for (const aabb3f &i: (box)) { \
111 writeV3F1000(os, i.MinEdge); \
112 writeV3F1000(os, i.MaxEdge); \
116 WRITEBOX(connect_top);
117 WRITEBOX(connect_bottom);
118 WRITEBOX(connect_front);
119 WRITEBOX(connect_left);
120 WRITEBOX(connect_back);
121 WRITEBOX(connect_right);
130 void NodeBox::deSerialize(std::istream &is)
132 int version = readU8(is);
133 if (version < 1 || version > 3)
134 throw SerializationError("unsupported NodeBox version");
138 type = (enum NodeBoxType)readU8(is);
140 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
142 u16 fixed_count = readU16(is);
146 box.MinEdge = readV3F1000(is);
147 box.MaxEdge = readV3F1000(is);
148 fixed.push_back(box);
151 else if(type == NODEBOX_WALLMOUNTED)
153 wall_top.MinEdge = readV3F1000(is);
154 wall_top.MaxEdge = readV3F1000(is);
155 wall_bottom.MinEdge = readV3F1000(is);
156 wall_bottom.MaxEdge = readV3F1000(is);
157 wall_side.MinEdge = readV3F1000(is);
158 wall_side.MaxEdge = readV3F1000(is);
160 else if (type == NODEBOX_CONNECTED)
162 #define READBOXES(box) { \
163 count = readU16(is); \
164 (box).reserve(count); \
166 v3f min = readV3F1000(is); \
167 v3f max = readV3F1000(is); \
168 (box).emplace_back(min, max); }; }
173 READBOXES(connect_top);
174 READBOXES(connect_bottom);
175 READBOXES(connect_front);
176 READBOXES(connect_left);
177 READBOXES(connect_back);
178 READBOXES(connect_right);
186 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
188 if (protocol_version >= 30)
190 else if (protocol_version >= 29)
192 else if (protocol_version >= 26)
197 os << serializeString(name);
198 animation.serialize(os, protocol_version);
199 writeU8(os, backface_culling);
200 if (protocol_version >= 26) {
201 writeU8(os, tileable_horizontal);
202 writeU8(os, tileable_vertical);
204 if (protocol_version >= 30) {
205 writeU8(os, has_color);
207 writeU8(os, color.getRed());
208 writeU8(os, color.getGreen());
209 writeU8(os, color.getBlue());
214 void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
216 int version = readU8(is);
217 name = deSerializeString(is);
218 animation.deSerialize(is, version >= 3 ? 29 : 26);
220 backface_culling = readU8(is);
222 tileable_horizontal = readU8(is);
223 tileable_vertical = readU8(is);
226 has_color = readU8(is);
228 color.setRed(readU8(is));
229 color.setGreen(readU8(is));
230 color.setBlue(readU8(is));
234 if ((contenfeatures_version < 8) &&
235 ((drawtype == NDT_MESH) ||
236 (drawtype == NDT_FIRELIKE) ||
237 (drawtype == NDT_LIQUID) ||
238 (drawtype == NDT_PLANTLIKE)))
239 backface_culling = false;
244 SimpleSoundSpec serialization
247 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
248 std::ostream &os, u8 version)
250 os<<serializeString(ss.name);
251 writeF1000(os, ss.gain);
254 writeF1000(os, ss.pitch);
256 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is, u8 version)
258 ss.name = deSerializeString(is);
259 ss.gain = readF1000(is);
262 ss.pitch = 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 void ContentFeatures::reset()
308 visual_solidness = 0;
309 backface_culling = true;
312 has_on_construct = false;
313 has_on_destruct = false;
314 has_after_destruct = false;
318 NOTE: Most of this is always overridden by the default values given
323 // Unknown nodes can be dug
324 groups["dig_immediate"] = 2;
325 drawtype = NDT_NORMAL;
328 for (auto &i : mesh_ptr)
330 minimap_color = video::SColor(0, 0, 0, 0);
333 for (auto &i : tiledef)
335 for (auto &j : tiledef_special)
338 post_effect_color = video::SColor(0, 0, 0, 0);
339 param_type = CPT_NONE;
340 param_type_2 = CPT2_NONE;
341 is_ground_content = false;
342 light_propagates = false;
343 sunlight_propagates = false;
348 buildable_to = false;
350 rightclickable = true;
352 liquid_type = LIQUID_NONE;
353 liquid_alternative_flowing = "";
354 liquid_alternative_source = "";
355 liquid_viscosity = 0;
356 liquid_renewable = true;
357 liquid_range = LIQUID_LEVEL_MAX+1;
360 damage_per_second = 0;
361 node_box = NodeBox();
362 selection_box = NodeBox();
363 collision_box = NodeBox();
365 legacy_facedir_simple = false;
366 legacy_wallmounted = false;
367 sound_footstep = SimpleSoundSpec();
368 sound_dig = SimpleSoundSpec("__group");
369 sound_dug = SimpleSoundSpec();
371 connects_to_ids.clear();
373 color = video::SColor(0xFFFFFFFF);
378 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
380 if (protocol_version < 31) {
381 serializeOld(os, protocol_version);
386 u8 version = (protocol_version >= 34) ? 11 : 10;
387 writeU8(os, version);
390 os << serializeString(name);
391 writeU16(os, groups.size());
392 for (const auto &group : groups) {
393 os << serializeString(group.first);
394 writeS16(os, group.second);
396 writeU8(os, param_type);
397 writeU8(os, param_type_2);
400 writeU8(os, drawtype);
401 os << serializeString(mesh);
402 writeF1000(os, visual_scale);
404 for (const TileDef &td : tiledef)
405 td.serialize(os, protocol_version);
406 for (const TileDef &td : tiledef_overlay)
407 td.serialize(os, protocol_version);
408 writeU8(os, CF_SPECIAL_COUNT);
409 for (const TileDef &td : tiledef_special) {
410 td.serialize(os, protocol_version);
413 writeU8(os, color.getRed());
414 writeU8(os, color.getGreen());
415 writeU8(os, color.getBlue());
416 os << serializeString(palette_name);
418 writeU8(os, connect_sides);
419 writeU16(os, connects_to_ids.size());
420 for (u16 connects_to_id : connects_to_ids)
421 writeU16(os, connects_to_id);
422 writeU8(os, post_effect_color.getAlpha());
423 writeU8(os, post_effect_color.getRed());
424 writeU8(os, post_effect_color.getGreen());
425 writeU8(os, post_effect_color.getBlue());
426 writeU8(os, leveled);
429 writeU8(os, light_propagates);
430 writeU8(os, sunlight_propagates);
431 writeU8(os, light_source);
434 writeU8(os, is_ground_content);
437 writeU8(os, walkable);
438 writeU8(os, pointable);
439 writeU8(os, diggable);
440 writeU8(os, climbable);
441 writeU8(os, buildable_to);
442 writeU8(os, rightclickable);
443 writeU32(os, damage_per_second);
446 writeU8(os, liquid_type);
447 os << serializeString(liquid_alternative_flowing);
448 os << serializeString(liquid_alternative_source);
449 writeU8(os, liquid_viscosity);
450 writeU8(os, liquid_renewable);
451 writeU8(os, liquid_range);
452 writeU8(os, drowning);
453 writeU8(os, floodable);
456 node_box.serialize(os, protocol_version);
457 selection_box.serialize(os, protocol_version);
458 collision_box.serialize(os, protocol_version);
461 serializeSimpleSoundSpec(sound_footstep, os, version);
462 serializeSimpleSoundSpec(sound_dig, os, version);
463 serializeSimpleSoundSpec(sound_dug, os, version);
466 writeU8(os, legacy_facedir_simple);
467 writeU8(os, legacy_wallmounted);
470 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
472 // alpha == 0 means that the node is using texture alpha
473 if (alpha == 0 || alpha == 255)
476 for (int i = 0; i < length; i++) {
477 if (tiles[i].name.empty())
480 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
481 tiles[i].name = s.str();
485 void ContentFeatures::deSerialize(std::istream &is)
488 int version = readU8(is);
490 deSerializeOld(is, version);
495 throw SerializationError("unsupported ContentFeatures version");
499 name = deSerializeString(is);
501 u32 groups_size = readU16(is);
502 for (u32 i = 0; i < groups_size; i++) {
503 std::string name = deSerializeString(is);
504 int value = readS16(is);
505 groups[name] = value;
507 param_type = (enum ContentParamType) readU8(is);
508 param_type_2 = (enum ContentParamType2) readU8(is);
511 drawtype = (enum NodeDrawType) readU8(is);
512 mesh = deSerializeString(is);
513 visual_scale = readF1000(is);
515 throw SerializationError("unsupported tile count");
516 for (TileDef &td : tiledef)
517 td.deSerialize(is, version, drawtype);
519 for (TileDef &td : tiledef_overlay)
520 td.deSerialize(is, version, drawtype);
521 if (readU8(is) != CF_SPECIAL_COUNT)
522 throw SerializationError("unsupported CF_SPECIAL_COUNT");
523 for (TileDef &td : tiledef_special)
524 td.deSerialize(is, version, drawtype);
526 color.setRed(readU8(is));
527 color.setGreen(readU8(is));
528 color.setBlue(readU8(is));
529 palette_name = deSerializeString(is);
531 connect_sides = readU8(is);
532 u16 connects_to_size = readU16(is);
533 connects_to_ids.clear();
534 for (u16 i = 0; i < connects_to_size; i++)
535 connects_to_ids.insert(readU16(is));
536 post_effect_color.setAlpha(readU8(is));
537 post_effect_color.setRed(readU8(is));
538 post_effect_color.setGreen(readU8(is));
539 post_effect_color.setBlue(readU8(is));
540 leveled = readU8(is);
543 light_propagates = readU8(is);
544 sunlight_propagates = readU8(is);
545 light_source = readU8(is);
546 light_source = MYMIN(light_source, LIGHT_MAX);
549 is_ground_content = readU8(is);
552 walkable = readU8(is);
553 pointable = readU8(is);
554 diggable = readU8(is);
555 climbable = readU8(is);
556 buildable_to = readU8(is);
557 rightclickable = readU8(is);
558 damage_per_second = readU32(is);
561 liquid_type = (enum LiquidType) readU8(is);
562 liquid_alternative_flowing = deSerializeString(is);
563 liquid_alternative_source = deSerializeString(is);
564 liquid_viscosity = readU8(is);
565 liquid_renewable = readU8(is);
566 liquid_range = readU8(is);
567 drowning = readU8(is);
568 floodable = readU8(is);
571 node_box.deSerialize(is);
572 selection_box.deSerialize(is);
573 collision_box.deSerialize(is);
576 deSerializeSimpleSoundSpec(sound_footstep, is, version);
577 deSerializeSimpleSoundSpec(sound_dig, is, version);
578 deSerializeSimpleSoundSpec(sound_dug, is, version);
580 // read legacy properties
581 legacy_facedir_simple = readU8(is);
582 legacy_wallmounted = readU8(is);
586 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
587 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
588 bool backface_culling, u8 material_type)
590 tile->shader_id = shader_id;
591 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
592 tile->material_type = material_type;
594 // Normal texture and shader flags texture
595 if (use_normal_texture) {
596 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
598 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
601 tile->material_flags = 0;
602 if (backface_culling)
603 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
604 if (tiledef->animation.type != TAT_NONE)
605 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
606 if (tiledef->tileable_horizontal)
607 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
608 if (tiledef->tileable_vertical)
609 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
612 tile->has_color = tiledef->has_color;
613 if (tiledef->has_color)
614 tile->color = tiledef->color;
618 // Animation parameters
620 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
622 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
623 &frame_count, &frame_length_ms, NULL);
624 tile->animation_frame_count = frame_count;
625 tile->animation_frame_length_ms = frame_length_ms;
628 if (frame_count == 1) {
629 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
631 std::ostringstream os(std::ios::binary);
633 tile->frames = std::make_shared<std::vector<FrameSpec>>();
635 tile->frames->resize(frame_count);
637 for (int i = 0; i < frame_count; i++) {
643 tiledef->animation.getTextureModifer(os,
644 tile->texture->getOriginalSize(), i);
646 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
647 if (tile->normal_texture)
648 frame.normal_texture = tsrc->getNormalTexture(os.str());
649 frame.flags_texture = tile->flags_texture;
650 (*tile->frames)[i] = frame;
657 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
658 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
660 // minimap pixel color - the average color of a texture
661 if (tsettings.enable_minimap && !tiledef[0].name.empty())
662 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
664 // Figure out the actual tiles to use
666 for (u32 j = 0; j < 6; j++) {
667 tdef[j] = tiledef[j];
668 if (tdef[j].name.empty())
669 tdef[j].name = "unknown_node.png";
671 // also the overlay tiles
672 TileDef tdef_overlay[6];
673 for (u32 j = 0; j < 6; j++)
674 tdef_overlay[j] = tiledef_overlay[j];
675 // also the special tiles
676 TileDef tdef_spec[6];
677 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
678 tdef_spec[j] = tiledef_special[j];
680 bool is_liquid = false;
682 u8 material_type = (alpha == 255) ?
683 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
688 material_type = (alpha == 255) ?
689 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
696 assert(liquid_type == LIQUID_SOURCE);
697 if (tsettings.opaque_water)
702 case NDT_FLOWINGLIQUID:
703 assert(liquid_type == LIQUID_FLOWING);
705 if (tsettings.opaque_water)
711 visual_solidness = 1;
713 case NDT_GLASSLIKE_FRAMED:
715 visual_solidness = 1;
717 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
719 visual_solidness = 1;
720 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
724 visual_solidness = 1;
726 case NDT_ALLFACES_OPTIONAL:
727 if (tsettings.leaves_style == LEAVES_FANCY) {
728 drawtype = NDT_ALLFACES;
730 visual_solidness = 1;
731 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
732 for (u32 j = 0; j < 6; j++) {
733 if (!tdef_spec[j].name.empty())
734 tdef[j].name = tdef_spec[j].name;
736 drawtype = NDT_GLASSLIKE;
738 visual_solidness = 1;
740 drawtype = NDT_NORMAL;
742 for (TileDef &td : tdef)
743 td.name += std::string("^[noalpha");
746 material_type = TILE_MATERIAL_WAVING_LEAVES;
751 material_type = TILE_MATERIAL_WAVING_PLANTS;
760 material_type = TILE_MATERIAL_WAVING_PLANTS;
761 else if (waving == 2)
762 material_type = TILE_MATERIAL_WAVING_LEAVES;
770 case NDT_PLANTLIKE_ROOTED:
776 // Vertex alpha is no longer supported, correct if necessary.
777 correctAlpha(tdef, 6);
778 correctAlpha(tdef_overlay, 6);
779 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
780 material_type = (alpha == 255) ?
781 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
784 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
786 u8 overlay_material = material_type;
787 if (overlay_material == TILE_MATERIAL_OPAQUE)
788 overlay_material = TILE_MATERIAL_BASIC;
789 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
790 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
792 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
794 // Tiles (fill in f->tiles[])
795 for (u16 j = 0; j < 6; j++) {
796 fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader,
797 tsettings.use_normal_texture,
798 tdef[j].backface_culling, material_type);
799 if (!tdef_overlay[j].name.empty())
800 fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j],
801 overlay_shader, tsettings.use_normal_texture,
802 tdef[j].backface_culling, overlay_material);
805 u8 special_material = material_type;
806 if (drawtype == NDT_PLANTLIKE_ROOTED) {
808 special_material = TILE_MATERIAL_WAVING_PLANTS;
809 else if (waving == 2)
810 special_material = TILE_MATERIAL_WAVING_LEAVES;
812 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
814 // Special tiles (fill in f->special_tiles[])
815 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
816 fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j],
817 special_shader, tsettings.use_normal_texture,
818 tdef_spec[j].backface_culling, special_material);
821 if (param_type_2 == CPT2_COLOR ||
822 param_type_2 == CPT2_COLORED_FACEDIR ||
823 param_type_2 == CPT2_COLORED_WALLMOUNTED)
824 palette = tsrc->getPalette(palette_name);
826 if (drawtype == NDT_MESH && !mesh.empty()) {
828 // Read the mesh and apply scale
829 mesh_ptr[0] = client->getMesh(mesh);
831 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
832 scaleMesh(mesh_ptr[0], scale);
833 recalculateBoundingBox(mesh_ptr[0]);
834 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
836 } else if ((drawtype == NDT_NODEBOX) &&
837 ((node_box.type == NODEBOX_REGULAR) ||
838 (node_box.type == NODEBOX_FIXED)) &&
839 (!node_box.fixed.empty())) {
840 //Convert regular nodebox nodes to meshnodes
841 //Change the drawtype and apply scale
843 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
844 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
845 scaleMesh(mesh_ptr[0], scale);
846 recalculateBoundingBox(mesh_ptr[0]);
847 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
850 //Cache 6dfacedir and wallmounted rotated clones of meshes
851 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
852 (param_type_2 == CPT2_FACEDIR
853 || param_type_2 == CPT2_COLORED_FACEDIR)) {
854 for (u16 j = 1; j < 24; j++) {
855 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
856 rotateMeshBy6dFacedir(mesh_ptr[j], j);
857 recalculateBoundingBox(mesh_ptr[j]);
858 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
860 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
861 && (param_type_2 == CPT2_WALLMOUNTED ||
862 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
863 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
864 for (u16 j = 1; j < 6; j++) {
865 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
866 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
867 recalculateBoundingBox(mesh_ptr[j]);
868 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
870 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
871 recalculateBoundingBox(mesh_ptr[0]);
872 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
881 class CNodeDefManager: public IWritableNodeDefManager {
884 virtual ~CNodeDefManager();
887 inline virtual const ContentFeatures& get(content_t c) const;
888 inline virtual const ContentFeatures& get(const MapNode &n) const;
889 virtual bool getId(const std::string &name, content_t &result) const;
890 virtual content_t getId(const std::string &name) const;
891 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
892 virtual const ContentFeatures& get(const std::string &name) const;
893 content_t allocateId();
894 virtual content_t set(const std::string &name, const ContentFeatures &def);
895 virtual content_t allocateDummy(const std::string &name);
896 virtual void removeNode(const std::string &name);
897 virtual void updateAliases(IItemDefManager *idef);
898 virtual void applyTextureOverrides(const std::string &override_filepath);
899 virtual void updateTextures(IGameDef *gamedef,
900 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
901 void *progress_cbk_args);
902 void serialize(std::ostream &os, u16 protocol_version) const;
903 void deSerialize(std::istream &is);
905 inline virtual void setNodeRegistrationStatus(bool completed);
907 virtual void pendNodeResolve(NodeResolver *nr);
908 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
909 virtual void runNodeResolveCallbacks();
910 virtual void resetNodeResolveState();
911 virtual void mapNodeboxConnections();
912 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
913 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
915 return m_selection_box_int_union;
919 void addNameIdMapping(content_t i, std::string name);
921 * Recalculates m_selection_box_int_union based on
922 * m_selection_box_union.
924 void fixSelectionBoxIntUnion();
926 // Features indexed by id
927 std::vector<ContentFeatures> m_content_features;
929 // A mapping for fast converting back and forth between names and ids
930 NameIdMapping m_name_id_mapping;
932 // Like m_name_id_mapping, but only from names to ids, and includes
933 // item aliases too. Updated by updateAliases()
934 // Note: Not serialized.
936 std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases;
938 // A mapping from groups to a list of content_ts (and their levels)
939 // that belong to it. Necessary for a direct lookup in getIds().
940 // Note: Not serialized.
941 std::unordered_map<std::string, GroupItems> m_group_to_items;
943 // Next possibly free id
946 // NodeResolvers to callback once node registration has ended
947 std::vector<NodeResolver *> m_pending_resolve_callbacks;
949 // True when all nodes have been registered
950 bool m_node_registration_complete;
952 //! The union of all nodes' selection boxes.
953 aabb3f m_selection_box_union;
955 * The smallest box in node coordinates that
956 * contains all nodes' selection boxes.
958 core::aabbox3d<s16> m_selection_box_int_union;
962 CNodeDefManager::CNodeDefManager()
968 CNodeDefManager::~CNodeDefManager()
971 for (ContentFeatures &f : m_content_features) {
972 for (auto &j : f.mesh_ptr) {
981 void CNodeDefManager::clear()
983 m_content_features.clear();
984 m_name_id_mapping.clear();
985 m_name_id_mapping_with_aliases.clear();
986 m_group_to_items.clear();
988 m_selection_box_union.reset(0,0,0);
989 m_selection_box_int_union.reset(0,0,0);
991 resetNodeResolveState();
993 u32 initial_length = 0;
994 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
995 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
996 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
997 m_content_features.resize(initial_length);
999 // Set CONTENT_UNKNOWN
1003 // Insert directly into containers
1004 content_t c = CONTENT_UNKNOWN;
1005 m_content_features[c] = f;
1006 addNameIdMapping(c, f.name);
1013 f.drawtype = NDT_AIRLIKE;
1014 f.param_type = CPT_LIGHT;
1015 f.light_propagates = true;
1016 f.sunlight_propagates = true;
1018 f.pointable = false;
1020 f.buildable_to = true;
1022 f.is_ground_content = true;
1023 // Insert directly into containers
1024 content_t c = CONTENT_AIR;
1025 m_content_features[c] = f;
1026 addNameIdMapping(c, f.name);
1029 // Set CONTENT_IGNORE
1033 f.drawtype = NDT_AIRLIKE;
1034 f.param_type = CPT_NONE;
1035 f.light_propagates = false;
1036 f.sunlight_propagates = false;
1038 f.pointable = false;
1040 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1041 f.is_ground_content = true;
1042 // Insert directly into containers
1043 content_t c = CONTENT_IGNORE;
1044 m_content_features[c] = f;
1045 addNameIdMapping(c, f.name);
1050 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1052 return c < m_content_features.size()
1053 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1057 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1059 return get(n.getContent());
1063 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1065 std::unordered_map<std::string, content_t>::const_iterator
1066 i = m_name_id_mapping_with_aliases.find(name);
1067 if(i == m_name_id_mapping_with_aliases.end())
1074 content_t CNodeDefManager::getId(const std::string &name) const
1076 content_t id = CONTENT_IGNORE;
1082 bool CNodeDefManager::getIds(const std::string &name,
1083 std::set<content_t> &result) const
1085 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1086 if (name.substr(0,6) != "group:") {
1087 content_t id = CONTENT_IGNORE;
1088 bool exists = getId(name, id);
1093 std::string group = name.substr(6);
1095 std::unordered_map<std::string, GroupItems>::const_iterator
1096 i = m_group_to_items.find(group);
1097 if (i == m_group_to_items.end())
1100 const GroupItems &items = i->second;
1101 for (const auto &item : items) {
1102 if (item.second != 0)
1103 result.insert(item.first);
1105 //printf("getIds: %dus\n", t.stop());
1110 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1112 content_t id = CONTENT_UNKNOWN;
1118 // returns CONTENT_IGNORE if no free ID found
1119 content_t CNodeDefManager::allocateId()
1121 for (content_t id = m_next_id;
1122 id >= m_next_id; // overflow?
1124 while (id >= m_content_features.size()) {
1125 m_content_features.emplace_back();
1127 const ContentFeatures &f = m_content_features[id];
1128 if (f.name.empty()) {
1133 // If we arrive here, an overflow occurred in id.
1134 // That means no ID was found
1135 return CONTENT_IGNORE;
1140 * Returns the smallest box that contains all boxes
1141 * in the vector. Box_union is expanded.
1142 * @param[in] boxes the vector containing the boxes
1143 * @param[in, out] box_union the union of the arguments
1145 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1147 for (const aabb3f &box : boxes) {
1148 box_union->addInternalBox(box);
1154 * Returns a box that contains the nodebox in every case.
1155 * The argument node_union is expanded.
1156 * @param[in] nodebox the nodebox to be measured
1157 * @param[in] features used to decide whether the nodebox
1159 * @param[in, out] box_union the union of the arguments
1161 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1164 switch(nodebox.type) {
1166 case NODEBOX_LEVELED: {
1168 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1169 boxVectorUnion(nodebox.fixed, &half_processed);
1170 // Set leveled boxes to maximal
1171 if (nodebox.type == NODEBOX_LEVELED) {
1172 half_processed.MaxEdge.Y = +BS / 2;
1174 if (features.param_type_2 == CPT2_FACEDIR ||
1175 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1176 // Get maximal coordinate
1178 fabsf(half_processed.MinEdge.X),
1179 fabsf(half_processed.MinEdge.Y),
1180 fabsf(half_processed.MinEdge.Z),
1181 fabsf(half_processed.MaxEdge.X),
1182 fabsf(half_processed.MaxEdge.Y),
1183 fabsf(half_processed.MaxEdge.Z) };
1185 for (float coord : coords) {
1190 // Add the union of all possible rotated boxes
1191 box_union->addInternalPoint(-max, -max, -max);
1192 box_union->addInternalPoint(+max, +max, +max);
1194 box_union->addInternalBox(half_processed);
1198 case NODEBOX_WALLMOUNTED: {
1200 box_union->addInternalBox(nodebox.wall_top);
1201 box_union->addInternalBox(nodebox.wall_bottom);
1202 // Find maximal coordinate in the X-Z plane
1204 fabsf(nodebox.wall_side.MinEdge.X),
1205 fabsf(nodebox.wall_side.MinEdge.Z),
1206 fabsf(nodebox.wall_side.MaxEdge.X),
1207 fabsf(nodebox.wall_side.MaxEdge.Z) };
1209 for (float coord : coords) {
1214 // Add the union of all possible rotated boxes
1215 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1216 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1219 case NODEBOX_CONNECTED: {
1220 // Add all possible connected boxes
1221 boxVectorUnion(nodebox.fixed, box_union);
1222 boxVectorUnion(nodebox.connect_top, box_union);
1223 boxVectorUnion(nodebox.connect_bottom, box_union);
1224 boxVectorUnion(nodebox.connect_front, box_union);
1225 boxVectorUnion(nodebox.connect_left, box_union);
1226 boxVectorUnion(nodebox.connect_back, box_union);
1227 boxVectorUnion(nodebox.connect_right, box_union);
1232 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1233 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1239 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1241 m_selection_box_int_union.MinEdge.X = floorf(
1242 m_selection_box_union.MinEdge.X / BS + 0.5f);
1243 m_selection_box_int_union.MinEdge.Y = floorf(
1244 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1245 m_selection_box_int_union.MinEdge.Z = floorf(
1246 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1247 m_selection_box_int_union.MaxEdge.X = ceilf(
1248 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1249 m_selection_box_int_union.MaxEdge.Y = ceilf(
1250 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1251 m_selection_box_int_union.MaxEdge.Z = ceilf(
1252 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1256 // IWritableNodeDefManager
1257 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1261 assert(name == def.name);
1263 // Don't allow redefining ignore (but allow air and unknown)
1264 if (name == "ignore") {
1265 warningstream << "NodeDefManager: Ignoring "
1266 "CONTENT_IGNORE redefinition"<<std::endl;
1267 return CONTENT_IGNORE;
1270 content_t id = CONTENT_IGNORE;
1271 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1274 if (id == CONTENT_IGNORE) {
1275 warningstream << "NodeDefManager: Absolute "
1276 "limit reached" << std::endl;
1277 return CONTENT_IGNORE;
1279 assert(id != CONTENT_IGNORE);
1280 addNameIdMapping(id, name);
1282 m_content_features[id] = def;
1283 verbosestream << "NodeDefManager: registering content id \"" << id
1284 << "\": name=\"" << def.name << "\""<<std::endl;
1286 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1287 fixSelectionBoxIntUnion();
1288 // Add this content to the list of all groups it belongs to
1289 // FIXME: This should remove a node from groups it no longer
1290 // belongs to when a node is re-registered
1291 for (const auto &group : def.groups) {
1292 const std::string &group_name = group.first;
1294 std::unordered_map<std::string, GroupItems>::iterator
1295 j = m_group_to_items.find(group_name);
1296 if (j == m_group_to_items.end()) {
1297 m_group_to_items[group_name].emplace_back(id, group.second);
1299 GroupItems &items = j->second;
1300 items.emplace_back(id, group.second);
1307 content_t CNodeDefManager::allocateDummy(const std::string &name)
1309 assert(name != ""); // Pre-condition
1312 return set(name, f);
1316 void CNodeDefManager::removeNode(const std::string &name)
1321 // Erase name from name ID mapping
1322 content_t id = CONTENT_IGNORE;
1323 if (m_name_id_mapping.getId(name, id)) {
1324 m_name_id_mapping.eraseName(name);
1325 m_name_id_mapping_with_aliases.erase(name);
1328 // Erase node content from all groups it belongs to
1329 for (std::unordered_map<std::string, GroupItems>::iterator iter_groups =
1330 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1331 GroupItems &items = iter_groups->second;
1332 for (GroupItems::iterator iter_groupitems = items.begin();
1333 iter_groupitems != items.end();) {
1334 if (iter_groupitems->first == id)
1335 items.erase(iter_groupitems++);
1340 // Check if group is empty
1342 m_group_to_items.erase(iter_groups++);
1349 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1351 std::set<std::string> all;
1353 m_name_id_mapping_with_aliases.clear();
1354 for (const std::string &name : all) {
1355 const std::string &convert_to = idef->getAlias(name);
1357 if (m_name_id_mapping.getId(convert_to, id)) {
1358 m_name_id_mapping_with_aliases.insert(
1359 std::make_pair(name, id));
1364 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1366 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1367 "overrides to textures from " << override_filepath << std::endl;
1369 std::ifstream infile(override_filepath.c_str());
1372 while (std::getline(infile, line)) {
1374 if (trim(line).empty())
1376 std::vector<std::string> splitted = str_split(line, ' ');
1377 if (splitted.size() != 3) {
1378 errorstream << override_filepath
1379 << ":" << line_c << " Could not apply texture override \""
1380 << line << "\": Syntax error" << std::endl;
1385 if (!getId(splitted[0], id))
1386 continue; // Ignore unknown node
1388 ContentFeatures &nodedef = m_content_features[id];
1390 if (splitted[1] == "top")
1391 nodedef.tiledef[0].name = splitted[2];
1392 else if (splitted[1] == "bottom")
1393 nodedef.tiledef[1].name = splitted[2];
1394 else if (splitted[1] == "right")
1395 nodedef.tiledef[2].name = splitted[2];
1396 else if (splitted[1] == "left")
1397 nodedef.tiledef[3].name = splitted[2];
1398 else if (splitted[1] == "back")
1399 nodedef.tiledef[4].name = splitted[2];
1400 else if (splitted[1] == "front")
1401 nodedef.tiledef[5].name = splitted[2];
1402 else if (splitted[1] == "all" || splitted[1] == "*")
1403 for (TileDef &i : nodedef.tiledef)
1404 i.name = splitted[2];
1405 else if (splitted[1] == "sides")
1406 for (int i = 2; i < 6; i++)
1407 nodedef.tiledef[i].name = splitted[2];
1409 errorstream << override_filepath
1410 << ":" << line_c << " Could not apply texture override \""
1411 << line << "\": Unknown node side \""
1412 << splitted[1] << "\"" << std::endl;
1418 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1419 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1420 void *progress_callback_args)
1423 infostream << "CNodeDefManager::updateTextures(): Updating "
1424 "textures in node definitions" << std::endl;
1426 Client *client = (Client *)gamedef;
1427 ITextureSource *tsrc = client->tsrc();
1428 IShaderSource *shdsrc = client->getShaderSource();
1429 scene::IMeshManipulator *meshmanip =
1430 RenderingEngine::get_scene_manager()->getMeshManipulator();
1431 TextureSettings tsettings;
1432 tsettings.readSettings();
1434 u32 size = m_content_features.size();
1436 for (u32 i = 0; i < size; i++) {
1437 ContentFeatures *f = &(m_content_features[i]);
1438 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1439 progress_callback(progress_callback_args, i, size);
1444 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1446 writeU8(os, 1); // version
1448 std::ostringstream os2(std::ios::binary);
1449 for (u32 i = 0; i < m_content_features.size(); i++) {
1450 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1451 || i == CONTENT_UNKNOWN)
1453 const ContentFeatures *f = &m_content_features[i];
1454 if (f->name.empty())
1457 // Wrap it in a string to allow different lengths without
1458 // strict version incompatibilities
1459 std::ostringstream wrapper_os(std::ios::binary);
1460 f->serialize(wrapper_os, protocol_version);
1461 os2<<serializeString(wrapper_os.str());
1463 // must not overflow
1464 u16 next = count + 1;
1465 FATAL_ERROR_IF(next < count, "Overflow");
1468 writeU16(os, count);
1469 os << serializeLongString(os2.str());
1473 void CNodeDefManager::deSerialize(std::istream &is)
1476 int version = readU8(is);
1478 throw SerializationError("unsupported NodeDefinitionManager version");
1479 u16 count = readU16(is);
1480 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1482 for (u16 n = 0; n < count; n++) {
1483 u16 i = readU16(is2);
1485 // Read it from the string wrapper
1486 std::string wrapper = deSerializeString(is2);
1487 std::istringstream wrapper_is(wrapper, std::ios::binary);
1488 f.deSerialize(wrapper_is);
1490 // Check error conditions
1491 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1492 warningstream << "NodeDefManager::deSerialize(): "
1493 "not changing builtin node " << i << std::endl;
1496 if (f.name.empty()) {
1497 warningstream << "NodeDefManager::deSerialize(): "
1498 "received empty name" << std::endl;
1504 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1505 warningstream << "NodeDefManager::deSerialize(): "
1506 "already defined with different ID: " << f.name << std::endl;
1510 // All is ok, add node definition with the requested ID
1511 if (i >= m_content_features.size())
1512 m_content_features.resize((u32)(i) + 1);
1513 m_content_features[i] = f;
1514 addNameIdMapping(i, f.name);
1515 verbosestream << "deserialized " << f.name << std::endl;
1517 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1518 fixSelectionBoxIntUnion();
1523 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1525 m_name_id_mapping.set(i, name);
1526 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1530 IWritableNodeDefManager *createNodeDefManager()
1532 return new CNodeDefManager();
1536 //// Serialization of old ContentFeatures formats
1537 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1539 u8 compatible_param_type_2 = param_type_2;
1540 if ((protocol_version < 28)
1541 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1542 compatible_param_type_2 = CPT2_NONE;
1543 else if (protocol_version < 30) {
1544 if (compatible_param_type_2 == CPT2_COLOR)
1545 compatible_param_type_2 = CPT2_NONE;
1546 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1547 compatible_param_type_2 = CPT2_FACEDIR;
1548 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1549 compatible_param_type_2 = CPT2_WALLMOUNTED;
1552 float compatible_visual_scale = visual_scale;
1553 if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1554 compatible_visual_scale = sqrt(visual_scale);
1556 TileDef compatible_tiles[6];
1557 for (u8 i = 0; i < 6; i++) {
1558 compatible_tiles[i] = tiledef[i];
1559 if (!tiledef_overlay[i].name.empty()) {
1560 std::stringstream s;
1561 s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name
1563 compatible_tiles[i].name = s.str();
1568 if (protocol_version < 31) {
1569 writeU8(os, protocol_version < 27 ? 7 : 8);
1571 os << serializeString(name);
1572 writeU16(os, groups.size());
1573 for (const auto &group : groups) {
1574 os << serializeString(group.first);
1575 writeS16(os, group.second);
1577 writeU8(os, drawtype);
1578 writeF1000(os, compatible_visual_scale);
1580 for (const auto &compatible_tile : compatible_tiles)
1581 compatible_tile.serialize(os, protocol_version);
1582 writeU8(os, CF_SPECIAL_COUNT);
1583 for (const TileDef &i : tiledef_special)
1584 i.serialize(os, protocol_version);
1586 writeU8(os, post_effect_color.getAlpha());
1587 writeU8(os, post_effect_color.getRed());
1588 writeU8(os, post_effect_color.getGreen());
1589 writeU8(os, post_effect_color.getBlue());
1590 writeU8(os, param_type);
1591 writeU8(os, compatible_param_type_2);
1592 writeU8(os, is_ground_content);
1593 writeU8(os, light_propagates);
1594 writeU8(os, sunlight_propagates);
1595 writeU8(os, walkable);
1596 writeU8(os, pointable);
1597 writeU8(os, diggable);
1598 writeU8(os, climbable);
1599 writeU8(os, buildable_to);
1600 os << serializeString(""); // legacy: used to be metadata_name
1601 writeU8(os, liquid_type);
1602 os << serializeString(liquid_alternative_flowing);
1603 os << serializeString(liquid_alternative_source);
1604 writeU8(os, liquid_viscosity);
1605 writeU8(os, liquid_renewable);
1606 writeU8(os, light_source);
1607 writeU32(os, damage_per_second);
1608 node_box.serialize(os, protocol_version);
1609 selection_box.serialize(os, protocol_version);
1610 writeU8(os, legacy_facedir_simple);
1611 writeU8(os, legacy_wallmounted);
1612 serializeSimpleSoundSpec(sound_footstep, os, 10);
1613 serializeSimpleSoundSpec(sound_dig, os, 10);
1614 serializeSimpleSoundSpec(sound_dug, os, 10);
1615 writeU8(os, rightclickable);
1616 writeU8(os, drowning);
1617 writeU8(os, leveled);
1618 writeU8(os, liquid_range);
1619 writeU8(os, waving);
1620 os << serializeString(mesh);
1621 collision_box.serialize(os, protocol_version);
1622 writeU8(os, floodable);
1623 writeU16(os, connects_to_ids.size());
1624 for (content_t connects_to_id : connects_to_ids)
1625 writeU16(os, connects_to_id);
1626 writeU8(os, connect_sides);
1628 throw SerializationError("ContentFeatures::serialize(): "
1629 "Unsupported version requested");
1633 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1635 if (version == 5) // In PROTOCOL_VERSION 13
1637 name = deSerializeString(is);
1639 u32 groups_size = readU16(is);
1640 for(u32 i=0; i<groups_size; i++){
1641 std::string name = deSerializeString(is);
1642 int value = readS16(is);
1643 groups[name] = value;
1645 drawtype = (enum NodeDrawType)readU8(is);
1647 visual_scale = readF1000(is);
1648 if (readU8(is) != 6)
1649 throw SerializationError("unsupported tile count");
1650 for (TileDef &i : tiledef)
1651 i.deSerialize(is, version, drawtype);
1652 if (readU8(is) != CF_SPECIAL_COUNT)
1653 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1654 for (TileDef &i : tiledef_special)
1655 i.deSerialize(is, version, drawtype);
1657 post_effect_color.setAlpha(readU8(is));
1658 post_effect_color.setRed(readU8(is));
1659 post_effect_color.setGreen(readU8(is));
1660 post_effect_color.setBlue(readU8(is));
1661 param_type = (enum ContentParamType)readU8(is);
1662 param_type_2 = (enum ContentParamType2)readU8(is);
1663 is_ground_content = readU8(is);
1664 light_propagates = readU8(is);
1665 sunlight_propagates = readU8(is);
1666 walkable = readU8(is);
1667 pointable = readU8(is);
1668 diggable = readU8(is);
1669 climbable = readU8(is);
1670 buildable_to = readU8(is);
1671 deSerializeString(is); // legacy: used to be metadata_name
1672 liquid_type = (enum LiquidType)readU8(is);
1673 liquid_alternative_flowing = deSerializeString(is);
1674 liquid_alternative_source = deSerializeString(is);
1675 liquid_viscosity = readU8(is);
1676 light_source = readU8(is);
1677 light_source = MYMIN(light_source, LIGHT_MAX);
1678 damage_per_second = readU32(is);
1679 node_box.deSerialize(is);
1680 selection_box.deSerialize(is);
1681 legacy_facedir_simple = readU8(is);
1682 legacy_wallmounted = readU8(is);
1683 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1684 deSerializeSimpleSoundSpec(sound_dig, is, version);
1685 deSerializeSimpleSoundSpec(sound_dug, is, version);
1686 } else if (version == 6) {
1687 name = deSerializeString(is);
1689 u32 groups_size = readU16(is);
1690 for (u32 i = 0; i < groups_size; i++) {
1691 std::string name = deSerializeString(is);
1692 int value = readS16(is);
1693 groups[name] = value;
1695 drawtype = (enum NodeDrawType)readU8(is);
1696 visual_scale = readF1000(is);
1697 if (readU8(is) != 6)
1698 throw SerializationError("unsupported tile count");
1699 for (TileDef &i : tiledef)
1700 i.deSerialize(is, version, drawtype);
1701 // CF_SPECIAL_COUNT in version 6 = 2
1702 if (readU8(is) != 2)
1703 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1704 for (u32 i = 0; i < 2; i++)
1705 tiledef_special[i].deSerialize(is, version, drawtype);
1707 post_effect_color.setAlpha(readU8(is));
1708 post_effect_color.setRed(readU8(is));
1709 post_effect_color.setGreen(readU8(is));
1710 post_effect_color.setBlue(readU8(is));
1711 param_type = (enum ContentParamType)readU8(is);
1712 param_type_2 = (enum ContentParamType2)readU8(is);
1713 is_ground_content = readU8(is);
1714 light_propagates = readU8(is);
1715 sunlight_propagates = readU8(is);
1716 walkable = readU8(is);
1717 pointable = readU8(is);
1718 diggable = readU8(is);
1719 climbable = readU8(is);
1720 buildable_to = readU8(is);
1721 deSerializeString(is); // legacy: used to be metadata_name
1722 liquid_type = (enum LiquidType)readU8(is);
1723 liquid_alternative_flowing = deSerializeString(is);
1724 liquid_alternative_source = deSerializeString(is);
1725 liquid_viscosity = readU8(is);
1726 liquid_renewable = readU8(is);
1727 light_source = readU8(is);
1728 damage_per_second = readU32(is);
1729 node_box.deSerialize(is);
1730 selection_box.deSerialize(is);
1731 legacy_facedir_simple = readU8(is);
1732 legacy_wallmounted = readU8(is);
1733 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1734 deSerializeSimpleSoundSpec(sound_dig, is, version);
1735 deSerializeSimpleSoundSpec(sound_dug, is, version);
1736 rightclickable = readU8(is);
1737 drowning = readU8(is);
1738 leveled = readU8(is);
1739 liquid_range = readU8(is);
1740 } else if (version == 7 || version == 8){
1741 name = deSerializeString(is);
1743 u32 groups_size = readU16(is);
1744 for (u32 i = 0; i < groups_size; i++) {
1745 std::string name = deSerializeString(is);
1746 int value = readS16(is);
1747 groups[name] = value;
1749 drawtype = (enum NodeDrawType) readU8(is);
1751 visual_scale = readF1000(is);
1752 if (readU8(is) != 6)
1753 throw SerializationError("unsupported tile count");
1754 for (TileDef &i : tiledef)
1755 i.deSerialize(is, version, drawtype);
1756 if (readU8(is) != CF_SPECIAL_COUNT)
1757 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1758 for (TileDef &i : tiledef_special)
1759 i.deSerialize(is, version, drawtype);
1761 post_effect_color.setAlpha(readU8(is));
1762 post_effect_color.setRed(readU8(is));
1763 post_effect_color.setGreen(readU8(is));
1764 post_effect_color.setBlue(readU8(is));
1765 param_type = (enum ContentParamType) readU8(is);
1766 param_type_2 = (enum ContentParamType2) readU8(is);
1767 is_ground_content = readU8(is);
1768 light_propagates = readU8(is);
1769 sunlight_propagates = readU8(is);
1770 walkable = readU8(is);
1771 pointable = readU8(is);
1772 diggable = readU8(is);
1773 climbable = readU8(is);
1774 buildable_to = readU8(is);
1775 deSerializeString(is); // legacy: used to be metadata_name
1776 liquid_type = (enum LiquidType) readU8(is);
1777 liquid_alternative_flowing = deSerializeString(is);
1778 liquid_alternative_source = deSerializeString(is);
1779 liquid_viscosity = readU8(is);
1780 liquid_renewable = readU8(is);
1781 light_source = readU8(is);
1782 light_source = MYMIN(light_source, LIGHT_MAX);
1783 damage_per_second = readU32(is);
1784 node_box.deSerialize(is);
1785 selection_box.deSerialize(is);
1786 legacy_facedir_simple = readU8(is);
1787 legacy_wallmounted = readU8(is);
1788 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1789 deSerializeSimpleSoundSpec(sound_dig, is, version);
1790 deSerializeSimpleSoundSpec(sound_dug, is, version);
1791 rightclickable = readU8(is);
1792 drowning = readU8(is);
1793 leveled = readU8(is);
1794 liquid_range = readU8(is);
1795 waving = readU8(is);
1797 mesh = deSerializeString(is);
1798 collision_box.deSerialize(is);
1799 floodable = readU8(is);
1800 u16 connects_to_size = readU16(is);
1801 connects_to_ids.clear();
1802 for (u16 i = 0; i < connects_to_size; i++)
1803 connects_to_ids.insert(readU16(is));
1804 connect_sides = readU8(is);
1805 } catch (SerializationError &e) {};
1807 throw SerializationError("unsupported ContentFeatures version");
1811 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1813 m_node_registration_complete = completed;
1817 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1820 if (m_node_registration_complete)
1821 nr->nodeResolveInternal();
1823 m_pending_resolve_callbacks.push_back(nr);
1827 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1829 size_t len = m_pending_resolve_callbacks.size();
1830 for (size_t i = 0; i != len; i++) {
1831 if (nr != m_pending_resolve_callbacks[i])
1835 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1836 m_pending_resolve_callbacks.resize(len);
1844 void CNodeDefManager::runNodeResolveCallbacks()
1846 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1847 NodeResolver *nr = m_pending_resolve_callbacks[i];
1848 nr->nodeResolveInternal();
1851 m_pending_resolve_callbacks.clear();
1855 void CNodeDefManager::resetNodeResolveState()
1857 m_node_registration_complete = false;
1858 m_pending_resolve_callbacks.clear();
1861 void CNodeDefManager::mapNodeboxConnections()
1863 for (ContentFeatures &f : m_content_features) {
1864 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1867 for (std::vector<std::string>::const_iterator it = f.connects_to.begin();
1868 it != f.connects_to.end(); ++it) {
1869 getIds(*it, f.connects_to_ids);
1874 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1876 const ContentFeatures &f1 = get(from);
1878 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1881 // lookup target in connected set
1882 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1885 const ContentFeatures &f2 = get(to);
1887 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1888 // ignores actually looking if back connection exists
1889 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1891 // does to node declare usable faces?
1892 if (f2.connect_sides > 0) {
1893 if ((f2.param_type_2 == CPT2_FACEDIR ||
1894 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1895 && (connect_face >= 4)) {
1896 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1897 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1899 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1901 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1902 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1904 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1905 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1906 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1908 return (f2.connect_sides
1909 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1911 return (f2.connect_sides & connect_face);
1913 // the target is just a regular node, so connect no matter back connection
1921 NodeResolver::NodeResolver()
1923 m_nodenames.reserve(16);
1924 m_nnlistsizes.reserve(4);
1928 NodeResolver::~NodeResolver()
1930 if (!m_resolve_done && m_ndef)
1931 m_ndef->cancelNodeResolveCallback(this);
1935 void NodeResolver::nodeResolveInternal()
1937 m_nodenames_idx = 0;
1938 m_nnlistsizes_idx = 0;
1941 m_resolve_done = true;
1943 m_nodenames.clear();
1944 m_nnlistsizes.clear();
1948 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1949 const std::string &node_alt, content_t c_fallback)
1951 if (m_nodenames_idx == m_nodenames.size()) {
1952 *result_out = c_fallback;
1953 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1958 std::string name = m_nodenames[m_nodenames_idx++];
1960 bool success = m_ndef->getId(name, c);
1961 if (!success && !node_alt.empty()) {
1963 success = m_ndef->getId(name, c);
1967 errorstream << "NodeResolver: failed to resolve node name '" << name
1968 << "'." << std::endl;
1977 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1978 bool all_required, content_t c_fallback)
1980 bool success = true;
1982 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1983 errorstream << "NodeResolver: no more node lists" << std::endl;
1987 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1990 if (m_nodenames_idx == m_nodenames.size()) {
1991 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1996 std::string &name = m_nodenames[m_nodenames_idx++];
1998 if (name.substr(0,6) != "group:") {
1999 if (m_ndef->getId(name, c)) {
2000 result_out->push_back(c);
2001 } else if (all_required) {
2002 errorstream << "NodeResolver: failed to resolve node name '"
2003 << name << "'." << std::endl;
2004 result_out->push_back(c_fallback);
2008 std::set<content_t> cids;
2009 std::set<content_t>::iterator it;
2010 m_ndef->getIds(name, cids);
2011 for (it = cids.begin(); it != cids.end(); ++it)
2012 result_out->push_back(*it);