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.
26 #include "client/renderingengine.h"
27 #include "client/tile.h"
28 #include <IMeshManipulator.h>
32 #include "nameidmapping.h"
33 #include "util/numeric.h"
34 #include "util/serialize.h"
35 #include "exceptions.h"
39 #include <fstream> // Used in applyTextureOverrides()
47 type = NODEBOX_REGULAR;
50 // default is sign/ladder-like
51 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
52 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
53 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
54 // no default for other parts
56 connect_bottom.clear();
57 connect_front.clear();
60 connect_right.clear();
63 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
67 if (protocol_version >= 27)
75 writeU8(os, NODEBOX_FIXED);
79 writeU16(os, fixed.size());
80 for (std::vector<aabb3f>::const_iterator
82 i != fixed.end(); ++i)
84 writeV3F1000(os, i->MinEdge);
85 writeV3F1000(os, i->MaxEdge);
88 case NODEBOX_WALLMOUNTED:
91 writeV3F1000(os, wall_top.MinEdge);
92 writeV3F1000(os, wall_top.MaxEdge);
93 writeV3F1000(os, wall_bottom.MinEdge);
94 writeV3F1000(os, wall_bottom.MaxEdge);
95 writeV3F1000(os, wall_side.MinEdge);
96 writeV3F1000(os, wall_side.MaxEdge);
98 case NODEBOX_CONNECTED:
100 // send old clients nodes that can't be walked through
102 writeU8(os, NODEBOX_FIXED);
105 writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2));
106 writeV3F1000(os, v3f(BS/2, BS/2, BS/2));
110 #define WRITEBOX(box) do { \
111 writeU16(os, (box).size()); \
112 for (std::vector<aabb3f>::const_iterator \
114 i != (box).end(); ++i) { \
115 writeV3F1000(os, i->MinEdge); \
116 writeV3F1000(os, i->MaxEdge); \
120 WRITEBOX(connect_top);
121 WRITEBOX(connect_bottom);
122 WRITEBOX(connect_front);
123 WRITEBOX(connect_left);
124 WRITEBOX(connect_back);
125 WRITEBOX(connect_right);
134 void NodeBox::deSerialize(std::istream &is)
136 int version = readU8(is);
137 if (version < 1 || version > 3)
138 throw SerializationError("unsupported NodeBox version");
142 type = (enum NodeBoxType)readU8(is);
144 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
146 u16 fixed_count = readU16(is);
150 box.MinEdge = readV3F1000(is);
151 box.MaxEdge = readV3F1000(is);
152 fixed.push_back(box);
155 else if(type == NODEBOX_WALLMOUNTED)
157 wall_top.MinEdge = readV3F1000(is);
158 wall_top.MaxEdge = readV3F1000(is);
159 wall_bottom.MinEdge = readV3F1000(is);
160 wall_bottom.MaxEdge = readV3F1000(is);
161 wall_side.MinEdge = readV3F1000(is);
162 wall_side.MaxEdge = readV3F1000(is);
164 else if (type == NODEBOX_CONNECTED)
166 #define READBOXES(box) do { \
167 count = readU16(is); \
168 (box).reserve(count); \
170 v3f min = readV3F1000(is); \
171 v3f max = readV3F1000(is); \
172 (box).push_back(aabb3f(min, max)); }; } while (0)
177 READBOXES(connect_top);
178 READBOXES(connect_bottom);
179 READBOXES(connect_front);
180 READBOXES(connect_left);
181 READBOXES(connect_back);
182 READBOXES(connect_right);
190 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
192 if (protocol_version >= 30)
194 else if (protocol_version >= 29)
196 else if (protocol_version >= 26)
201 os << serializeString(name);
202 animation.serialize(os, protocol_version);
203 writeU8(os, backface_culling);
204 if (protocol_version >= 26) {
205 writeU8(os, tileable_horizontal);
206 writeU8(os, tileable_vertical);
208 if (protocol_version >= 30) {
209 writeU8(os, has_color);
211 writeU8(os, color.getRed());
212 writeU8(os, color.getGreen());
213 writeU8(os, color.getBlue());
218 void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
220 int version = readU8(is);
221 name = deSerializeString(is);
222 animation.deSerialize(is, version >= 3 ? 29 : 26);
224 backface_culling = readU8(is);
226 tileable_horizontal = readU8(is);
227 tileable_vertical = readU8(is);
230 has_color = readU8(is);
232 color.setRed(readU8(is));
233 color.setGreen(readU8(is));
234 color.setBlue(readU8(is));
238 if ((contenfeatures_version < 8) &&
239 ((drawtype == NDT_MESH) ||
240 (drawtype == NDT_FIRELIKE) ||
241 (drawtype == NDT_LIQUID) ||
242 (drawtype == NDT_PLANTLIKE)))
243 backface_culling = false;
248 SimpleSoundSpec serialization
251 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
252 std::ostream &os, u8 version)
254 os<<serializeString(ss.name);
255 writeF1000(os, ss.gain);
258 writeF1000(os, ss.pitch);
260 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is, u8 version)
262 ss.name = deSerializeString(is);
263 ss.gain = readF1000(is);
266 ss.pitch = readF1000(is);
269 void TextureSettings::readSettings()
271 connected_glass = g_settings->getBool("connected_glass");
272 opaque_water = g_settings->getBool("opaque_water");
273 bool enable_shaders = g_settings->getBool("enable_shaders");
274 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
275 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
276 bool smooth_lighting = g_settings->getBool("smooth_lighting");
277 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
278 enable_minimap = g_settings->getBool("enable_minimap");
279 std::string leaves_style_str = g_settings->get("leaves_style");
281 // Mesh cache is not supported in combination with smooth lighting
283 enable_mesh_cache = false;
285 use_normal_texture = enable_shaders &&
286 (enable_bumpmapping || enable_parallax_occlusion);
287 if (leaves_style_str == "fancy") {
288 leaves_style = LEAVES_FANCY;
289 } else if (leaves_style_str == "simple") {
290 leaves_style = LEAVES_SIMPLE;
292 leaves_style = LEAVES_OPAQUE;
300 ContentFeatures::ContentFeatures()
305 ContentFeatures::~ContentFeatures()
309 void ContentFeatures::reset()
316 visual_solidness = 0;
317 backface_culling = true;
320 has_on_construct = false;
321 has_on_destruct = false;
322 has_after_destruct = false;
326 NOTE: Most of this is always overridden by the default values given
331 // Unknown nodes can be dug
332 groups["dig_immediate"] = 2;
333 drawtype = NDT_NORMAL;
336 for(u32 i = 0; i < 24; i++)
338 minimap_color = video::SColor(0, 0, 0, 0);
341 for(u32 i = 0; i < 6; i++)
342 tiledef[i] = TileDef();
343 for(u16 j = 0; j < CF_SPECIAL_COUNT; j++)
344 tiledef_special[j] = TileDef();
346 post_effect_color = video::SColor(0, 0, 0, 0);
347 param_type = CPT_NONE;
348 param_type_2 = CPT2_NONE;
349 is_ground_content = false;
350 light_propagates = false;
351 sunlight_propagates = false;
356 buildable_to = false;
358 rightclickable = true;
360 liquid_type = LIQUID_NONE;
361 liquid_alternative_flowing = "";
362 liquid_alternative_source = "";
363 liquid_viscosity = 0;
364 liquid_renewable = true;
365 liquid_range = LIQUID_LEVEL_MAX+1;
368 damage_per_second = 0;
369 node_box = NodeBox();
370 selection_box = NodeBox();
371 collision_box = NodeBox();
373 legacy_facedir_simple = false;
374 legacy_wallmounted = false;
375 sound_footstep = SimpleSoundSpec();
376 sound_dig = SimpleSoundSpec("__group");
377 sound_dug = SimpleSoundSpec();
379 connects_to_ids.clear();
381 color = video::SColor(0xFFFFFFFF);
386 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
388 if (protocol_version < 31) {
389 serializeOld(os, protocol_version);
394 u8 version = (protocol_version >= 34) ? 11 : 10;
395 writeU8(os, version);
398 os << serializeString(name);
399 writeU16(os, groups.size());
400 for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end();
402 os << serializeString(i->first);
403 writeS16(os, i->second);
405 writeU8(os, param_type);
406 writeU8(os, param_type_2);
409 writeU8(os, drawtype);
410 os << serializeString(mesh);
411 writeF1000(os, visual_scale);
413 for (u32 i = 0; i < 6; i++)
414 tiledef[i].serialize(os, protocol_version);
415 for (u32 i = 0; i < 6; i++)
416 tiledef_overlay[i].serialize(os, protocol_version);
417 writeU8(os, CF_SPECIAL_COUNT);
418 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
419 tiledef_special[i].serialize(os, protocol_version);
422 writeU8(os, color.getRed());
423 writeU8(os, color.getGreen());
424 writeU8(os, color.getBlue());
425 os << serializeString(palette_name);
427 writeU8(os, connect_sides);
428 writeU16(os, connects_to_ids.size());
429 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
430 i != connects_to_ids.end(); ++i)
432 writeU8(os, post_effect_color.getAlpha());
433 writeU8(os, post_effect_color.getRed());
434 writeU8(os, post_effect_color.getGreen());
435 writeU8(os, post_effect_color.getBlue());
436 writeU8(os, leveled);
439 writeU8(os, light_propagates);
440 writeU8(os, sunlight_propagates);
441 writeU8(os, light_source);
444 writeU8(os, is_ground_content);
447 writeU8(os, walkable);
448 writeU8(os, pointable);
449 writeU8(os, diggable);
450 writeU8(os, climbable);
451 writeU8(os, buildable_to);
452 writeU8(os, rightclickable);
453 writeU32(os, damage_per_second);
456 writeU8(os, liquid_type);
457 os << serializeString(liquid_alternative_flowing);
458 os << serializeString(liquid_alternative_source);
459 writeU8(os, liquid_viscosity);
460 writeU8(os, liquid_renewable);
461 writeU8(os, liquid_range);
462 writeU8(os, drowning);
463 writeU8(os, floodable);
466 node_box.serialize(os, protocol_version);
467 selection_box.serialize(os, protocol_version);
468 collision_box.serialize(os, protocol_version);
471 serializeSimpleSoundSpec(sound_footstep, os, version);
472 serializeSimpleSoundSpec(sound_dig, os, version);
473 serializeSimpleSoundSpec(sound_dug, os, version);
476 writeU8(os, legacy_facedir_simple);
477 writeU8(os, legacy_wallmounted);
480 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
482 // alpha == 0 means that the node is using texture alpha
483 if (alpha == 0 || alpha == 255)
486 for (int i = 0; i < length; i++) {
487 if (tiles[i].name == "")
490 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
491 tiles[i].name = s.str();
495 void ContentFeatures::deSerialize(std::istream &is)
498 int version = readU8(is);
500 deSerializeOld(is, version);
502 } else if (version > 11) {
503 throw SerializationError("unsupported ContentFeatures version");
507 name = deSerializeString(is);
509 u32 groups_size = readU16(is);
510 for (u32 i = 0; i < groups_size; i++) {
511 std::string name = deSerializeString(is);
512 int value = readS16(is);
513 groups[name] = value;
515 param_type = (enum ContentParamType) readU8(is);
516 param_type_2 = (enum ContentParamType2) readU8(is);
519 drawtype = (enum NodeDrawType) readU8(is);
520 mesh = deSerializeString(is);
521 visual_scale = readF1000(is);
523 throw SerializationError("unsupported tile count");
524 for (u32 i = 0; i < 6; i++)
525 tiledef[i].deSerialize(is, version, drawtype);
527 for (u32 i = 0; i < 6; i++)
528 tiledef_overlay[i].deSerialize(is, version, drawtype);
529 if (readU8(is) != CF_SPECIAL_COUNT)
530 throw SerializationError("unsupported CF_SPECIAL_COUNT");
531 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
532 tiledef_special[i].deSerialize(is, version, drawtype);
534 color.setRed(readU8(is));
535 color.setGreen(readU8(is));
536 color.setBlue(readU8(is));
537 palette_name = deSerializeString(is);
539 connect_sides = readU8(is);
540 u16 connects_to_size = readU16(is);
541 connects_to_ids.clear();
542 for (u16 i = 0; i < connects_to_size; i++)
543 connects_to_ids.insert(readU16(is));
544 post_effect_color.setAlpha(readU8(is));
545 post_effect_color.setRed(readU8(is));
546 post_effect_color.setGreen(readU8(is));
547 post_effect_color.setBlue(readU8(is));
548 leveled = readU8(is);
551 light_propagates = readU8(is);
552 sunlight_propagates = readU8(is);
553 light_source = readU8(is);
554 light_source = MYMIN(light_source, LIGHT_MAX);
557 is_ground_content = readU8(is);
560 walkable = readU8(is);
561 pointable = readU8(is);
562 diggable = readU8(is);
563 climbable = readU8(is);
564 buildable_to = readU8(is);
565 rightclickable = readU8(is);
566 damage_per_second = readU32(is);
569 liquid_type = (enum LiquidType) readU8(is);
570 liquid_alternative_flowing = deSerializeString(is);
571 liquid_alternative_source = deSerializeString(is);
572 liquid_viscosity = readU8(is);
573 liquid_renewable = readU8(is);
574 liquid_range = readU8(is);
575 drowning = readU8(is);
576 floodable = readU8(is);
579 node_box.deSerialize(is);
580 selection_box.deSerialize(is);
581 collision_box.deSerialize(is);
584 deSerializeSimpleSoundSpec(sound_footstep, is, version);
585 deSerializeSimpleSoundSpec(sound_dig, is, version);
586 deSerializeSimpleSoundSpec(sound_dug, is, version);
588 // read legacy properties
589 legacy_facedir_simple = readU8(is);
590 legacy_wallmounted = readU8(is);
594 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
595 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
596 bool backface_culling, u8 material_type)
598 tile->shader_id = shader_id;
599 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
600 tile->material_type = material_type;
602 // Normal texture and shader flags texture
603 if (use_normal_texture) {
604 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
606 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
609 tile->material_flags = 0;
610 if (backface_culling)
611 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
612 if (tiledef->animation.type != TAT_NONE)
613 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
614 if (tiledef->tileable_horizontal)
615 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
616 if (tiledef->tileable_vertical)
617 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
620 tile->has_color = tiledef->has_color;
621 if (tiledef->has_color)
622 tile->color = tiledef->color;
626 // Animation parameters
628 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
630 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
631 &frame_count, &frame_length_ms, NULL);
632 tile->animation_frame_count = frame_count;
633 tile->animation_frame_length_ms = frame_length_ms;
636 if (frame_count == 1) {
637 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
639 std::ostringstream os(std::ios::binary);
641 tile->frames = std::make_shared<std::vector<FrameSpec>>();
643 tile->frames->resize(frame_count);
645 for (int i = 0; i < frame_count; i++) {
651 tiledef->animation.getTextureModifer(os,
652 tile->texture->getOriginalSize(), i);
654 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
655 if (tile->normal_texture)
656 frame.normal_texture = tsrc->getNormalTexture(os.str());
657 frame.flags_texture = tile->flags_texture;
658 (*tile->frames)[i] = frame;
665 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
666 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
668 // minimap pixel color - the average color of a texture
669 if (tsettings.enable_minimap && tiledef[0].name != "")
670 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
672 // Figure out the actual tiles to use
674 for (u32 j = 0; j < 6; j++) {
675 tdef[j] = tiledef[j];
676 if (tdef[j].name == "")
677 tdef[j].name = "unknown_node.png";
679 // also the overlay tiles
680 TileDef tdef_overlay[6];
681 for (u32 j = 0; j < 6; j++)
682 tdef_overlay[j] = tiledef_overlay[j];
683 // also the special tiles
684 TileDef tdef_spec[6];
685 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
686 tdef_spec[j] = tiledef_special[j];
688 bool is_liquid = false;
690 u8 material_type = (alpha == 255) ?
691 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
696 material_type = (alpha == 255) ?
697 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
704 assert(liquid_type == LIQUID_SOURCE);
705 if (tsettings.opaque_water)
710 case NDT_FLOWINGLIQUID:
711 assert(liquid_type == LIQUID_FLOWING);
713 if (tsettings.opaque_water)
719 visual_solidness = 1;
721 case NDT_GLASSLIKE_FRAMED:
723 visual_solidness = 1;
725 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
727 visual_solidness = 1;
728 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
732 visual_solidness = 1;
734 case NDT_ALLFACES_OPTIONAL:
735 if (tsettings.leaves_style == LEAVES_FANCY) {
736 drawtype = NDT_ALLFACES;
738 visual_solidness = 1;
739 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
740 for (u32 j = 0; j < 6; j++) {
741 if (tdef_spec[j].name != "")
742 tdef[j].name = tdef_spec[j].name;
744 drawtype = NDT_GLASSLIKE;
746 visual_solidness = 1;
748 drawtype = NDT_NORMAL;
750 for (u32 i = 0; i < 6; i++)
751 tdef[i].name += std::string("^[noalpha");
754 material_type = TILE_MATERIAL_WAVING_LEAVES;
759 material_type = TILE_MATERIAL_WAVING_PLANTS;
768 material_type = TILE_MATERIAL_WAVING_PLANTS;
769 else if (waving == 2)
770 material_type = TILE_MATERIAL_WAVING_LEAVES;
778 case NDT_PLANTLIKE_ROOTED:
784 // Vertex alpha is no longer supported, correct if necessary.
785 correctAlpha(tdef, 6);
786 correctAlpha(tdef_overlay, 6);
787 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
788 material_type = (alpha == 255) ?
789 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
792 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
794 u8 overlay_material = material_type;
795 if (overlay_material == TILE_MATERIAL_OPAQUE)
796 overlay_material = TILE_MATERIAL_BASIC;
797 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
798 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
800 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
802 // Tiles (fill in f->tiles[])
803 for (u16 j = 0; j < 6; j++) {
804 fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader,
805 tsettings.use_normal_texture,
806 tdef[j].backface_culling, material_type);
807 if (tdef_overlay[j].name != "")
808 fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j],
809 overlay_shader, tsettings.use_normal_texture,
810 tdef[j].backface_culling, overlay_material);
813 u8 special_material = material_type;
814 if (drawtype == NDT_PLANTLIKE_ROOTED) {
816 special_material = TILE_MATERIAL_WAVING_PLANTS;
817 else if (waving == 2)
818 special_material = TILE_MATERIAL_WAVING_LEAVES;
820 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
822 // Special tiles (fill in f->special_tiles[])
823 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
824 fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j],
825 special_shader, tsettings.use_normal_texture,
826 tdef_spec[j].backface_culling, special_material);
829 if (param_type_2 == CPT2_COLOR ||
830 param_type_2 == CPT2_COLORED_FACEDIR ||
831 param_type_2 == CPT2_COLORED_WALLMOUNTED)
832 palette = tsrc->getPalette(palette_name);
834 if ((drawtype == NDT_MESH) && (mesh != "")) {
836 // Read the mesh and apply scale
837 mesh_ptr[0] = client->getMesh(mesh);
839 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
840 scaleMesh(mesh_ptr[0], scale);
841 recalculateBoundingBox(mesh_ptr[0]);
842 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
844 } else if ((drawtype == NDT_NODEBOX) &&
845 ((node_box.type == NODEBOX_REGULAR) ||
846 (node_box.type == NODEBOX_FIXED)) &&
847 (!node_box.fixed.empty())) {
848 //Convert regular nodebox nodes to meshnodes
849 //Change the drawtype and apply scale
851 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
852 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
853 scaleMesh(mesh_ptr[0], scale);
854 recalculateBoundingBox(mesh_ptr[0]);
855 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
858 //Cache 6dfacedir and wallmounted rotated clones of meshes
859 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
860 (param_type_2 == CPT2_FACEDIR
861 || param_type_2 == CPT2_COLORED_FACEDIR)) {
862 for (u16 j = 1; j < 24; j++) {
863 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
864 rotateMeshBy6dFacedir(mesh_ptr[j], j);
865 recalculateBoundingBox(mesh_ptr[j]);
866 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
868 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
869 && (param_type_2 == CPT2_WALLMOUNTED ||
870 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
871 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
872 for (u16 j = 1; j < 6; j++) {
873 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
874 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
875 recalculateBoundingBox(mesh_ptr[j]);
876 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
878 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
879 recalculateBoundingBox(mesh_ptr[0]);
880 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
889 class CNodeDefManager: public IWritableNodeDefManager {
892 virtual ~CNodeDefManager();
894 virtual IWritableNodeDefManager *clone();
895 inline virtual const ContentFeatures& get(content_t c) const;
896 inline virtual const ContentFeatures& get(const MapNode &n) const;
897 virtual bool getId(const std::string &name, content_t &result) const;
898 virtual content_t getId(const std::string &name) const;
899 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
900 virtual const ContentFeatures& get(const std::string &name) const;
901 content_t allocateId();
902 virtual content_t set(const std::string &name, const ContentFeatures &def);
903 virtual content_t allocateDummy(const std::string &name);
904 virtual void removeNode(const std::string &name);
905 virtual void updateAliases(IItemDefManager *idef);
906 virtual void applyTextureOverrides(const std::string &override_filepath);
907 virtual void updateTextures(IGameDef *gamedef,
908 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
909 void *progress_cbk_args);
910 void serialize(std::ostream &os, u16 protocol_version) const;
911 void deSerialize(std::istream &is);
913 inline virtual void setNodeRegistrationStatus(bool completed);
915 virtual void pendNodeResolve(NodeResolver *nr);
916 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
917 virtual void runNodeResolveCallbacks();
918 virtual void resetNodeResolveState();
919 virtual void mapNodeboxConnections();
920 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
921 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
923 return m_selection_box_int_union;
927 void addNameIdMapping(content_t i, std::string name);
929 * Recalculates m_selection_box_int_union based on
930 * m_selection_box_union.
932 void fixSelectionBoxIntUnion();
934 // Features indexed by id
935 std::vector<ContentFeatures> m_content_features;
937 // A mapping for fast converting back and forth between names and ids
938 NameIdMapping m_name_id_mapping;
940 // Like m_name_id_mapping, but only from names to ids, and includes
941 // item aliases too. Updated by updateAliases()
942 // Note: Not serialized.
944 std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases;
946 // A mapping from groups to a list of content_ts (and their levels)
947 // that belong to it. Necessary for a direct lookup in getIds().
948 // Note: Not serialized.
949 std::unordered_map<std::string, GroupItems> m_group_to_items;
951 // Next possibly free id
954 // NodeResolvers to callback once node registration has ended
955 std::vector<NodeResolver *> m_pending_resolve_callbacks;
957 // True when all nodes have been registered
958 bool m_node_registration_complete;
960 //! The union of all nodes' selection boxes.
961 aabb3f m_selection_box_union;
963 * The smallest box in node coordinates that
964 * contains all nodes' selection boxes.
966 core::aabbox3d<s16> m_selection_box_int_union;
970 CNodeDefManager::CNodeDefManager()
976 CNodeDefManager::~CNodeDefManager()
979 for (u32 i = 0; i < m_content_features.size(); i++) {
980 ContentFeatures *f = &m_content_features[i];
981 for (u32 j = 0; j < 24; j++) {
983 f->mesh_ptr[j]->drop();
990 void CNodeDefManager::clear()
992 m_content_features.clear();
993 m_name_id_mapping.clear();
994 m_name_id_mapping_with_aliases.clear();
995 m_group_to_items.clear();
997 m_selection_box_union.reset(0,0,0);
998 m_selection_box_int_union.reset(0,0,0);
1000 resetNodeResolveState();
1002 u32 initial_length = 0;
1003 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1004 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1005 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1006 m_content_features.resize(initial_length);
1008 // Set CONTENT_UNKNOWN
1012 // Insert directly into containers
1013 content_t c = CONTENT_UNKNOWN;
1014 m_content_features[c] = f;
1015 addNameIdMapping(c, f.name);
1022 f.drawtype = NDT_AIRLIKE;
1023 f.param_type = CPT_LIGHT;
1024 f.light_propagates = true;
1025 f.sunlight_propagates = true;
1027 f.pointable = false;
1029 f.buildable_to = true;
1031 f.is_ground_content = true;
1032 // Insert directly into containers
1033 content_t c = CONTENT_AIR;
1034 m_content_features[c] = f;
1035 addNameIdMapping(c, f.name);
1038 // Set CONTENT_IGNORE
1042 f.drawtype = NDT_AIRLIKE;
1043 f.param_type = CPT_NONE;
1044 f.light_propagates = false;
1045 f.sunlight_propagates = false;
1047 f.pointable = false;
1049 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1050 f.is_ground_content = true;
1051 // Insert directly into containers
1052 content_t c = CONTENT_IGNORE;
1053 m_content_features[c] = f;
1054 addNameIdMapping(c, f.name);
1059 IWritableNodeDefManager *CNodeDefManager::clone()
1061 CNodeDefManager *mgr = new CNodeDefManager();
1067 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1069 return c < m_content_features.size()
1070 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1074 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1076 return get(n.getContent());
1080 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1082 std::unordered_map<std::string, content_t>::const_iterator
1083 i = m_name_id_mapping_with_aliases.find(name);
1084 if(i == m_name_id_mapping_with_aliases.end())
1091 content_t CNodeDefManager::getId(const std::string &name) const
1093 content_t id = CONTENT_IGNORE;
1099 bool CNodeDefManager::getIds(const std::string &name,
1100 std::set<content_t> &result) const
1102 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1103 if (name.substr(0,6) != "group:") {
1104 content_t id = CONTENT_IGNORE;
1105 bool exists = getId(name, id);
1110 std::string group = name.substr(6);
1112 std::unordered_map<std::string, GroupItems>::const_iterator
1113 i = m_group_to_items.find(group);
1114 if (i == m_group_to_items.end())
1117 const GroupItems &items = i->second;
1118 for (GroupItems::const_iterator j = items.begin();
1119 j != items.end(); ++j) {
1120 if ((*j).second != 0)
1121 result.insert((*j).first);
1123 //printf("getIds: %dus\n", t.stop());
1128 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1130 content_t id = CONTENT_UNKNOWN;
1136 // returns CONTENT_IGNORE if no free ID found
1137 content_t CNodeDefManager::allocateId()
1139 for (content_t id = m_next_id;
1140 id >= m_next_id; // overflow?
1142 while (id >= m_content_features.size()) {
1143 m_content_features.push_back(ContentFeatures());
1145 const ContentFeatures &f = m_content_features[id];
1151 // If we arrive here, an overflow occurred in id.
1152 // That means no ID was found
1153 return CONTENT_IGNORE;
1158 * Returns the smallest box that contains all boxes
1159 * in the vector. Box_union is expanded.
1160 * @param[in] boxes the vector containing the boxes
1161 * @param[in, out] box_union the union of the arguments
1163 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1165 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1166 it != boxes.end(); ++it) {
1167 box_union->addInternalBox(*it);
1173 * Returns a box that contains the nodebox in every case.
1174 * The argument node_union is expanded.
1175 * @param[in] nodebox the nodebox to be measured
1176 * @param[in] features used to decide whether the nodebox
1178 * @param[in, out] box_union the union of the arguments
1180 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1183 switch(nodebox.type) {
1185 case NODEBOX_LEVELED: {
1187 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1188 boxVectorUnion(nodebox.fixed, &half_processed);
1189 // Set leveled boxes to maximal
1190 if (nodebox.type == NODEBOX_LEVELED) {
1191 half_processed.MaxEdge.Y = +BS / 2;
1193 if (features.param_type_2 == CPT2_FACEDIR ||
1194 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1195 // Get maximal coordinate
1197 fabsf(half_processed.MinEdge.X),
1198 fabsf(half_processed.MinEdge.Y),
1199 fabsf(half_processed.MinEdge.Z),
1200 fabsf(half_processed.MaxEdge.X),
1201 fabsf(half_processed.MaxEdge.Y),
1202 fabsf(half_processed.MaxEdge.Z) };
1204 for (int i = 0; i < 6; i++) {
1205 if (max < coords[i]) {
1209 // Add the union of all possible rotated boxes
1210 box_union->addInternalPoint(-max, -max, -max);
1211 box_union->addInternalPoint(+max, +max, +max);
1213 box_union->addInternalBox(half_processed);
1217 case NODEBOX_WALLMOUNTED: {
1219 box_union->addInternalBox(nodebox.wall_top);
1220 box_union->addInternalBox(nodebox.wall_bottom);
1221 // Find maximal coordinate in the X-Z plane
1223 fabsf(nodebox.wall_side.MinEdge.X),
1224 fabsf(nodebox.wall_side.MinEdge.Z),
1225 fabsf(nodebox.wall_side.MaxEdge.X),
1226 fabsf(nodebox.wall_side.MaxEdge.Z) };
1228 for (int i = 0; i < 4; i++) {
1229 if (max < coords[i]) {
1233 // Add the union of all possible rotated boxes
1234 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1235 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1238 case NODEBOX_CONNECTED: {
1239 // Add all possible connected boxes
1240 boxVectorUnion(nodebox.fixed, box_union);
1241 boxVectorUnion(nodebox.connect_top, box_union);
1242 boxVectorUnion(nodebox.connect_bottom, box_union);
1243 boxVectorUnion(nodebox.connect_front, box_union);
1244 boxVectorUnion(nodebox.connect_left, box_union);
1245 boxVectorUnion(nodebox.connect_back, box_union);
1246 boxVectorUnion(nodebox.connect_right, box_union);
1251 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1252 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1258 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1260 m_selection_box_int_union.MinEdge.X = floorf(
1261 m_selection_box_union.MinEdge.X / BS + 0.5f);
1262 m_selection_box_int_union.MinEdge.Y = floorf(
1263 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1264 m_selection_box_int_union.MinEdge.Z = floorf(
1265 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1266 m_selection_box_int_union.MaxEdge.X = ceilf(
1267 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1268 m_selection_box_int_union.MaxEdge.Y = ceilf(
1269 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1270 m_selection_box_int_union.MaxEdge.Z = ceilf(
1271 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1275 // IWritableNodeDefManager
1276 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1280 assert(name == def.name);
1282 // Don't allow redefining ignore (but allow air and unknown)
1283 if (name == "ignore") {
1284 warningstream << "NodeDefManager: Ignoring "
1285 "CONTENT_IGNORE redefinition"<<std::endl;
1286 return CONTENT_IGNORE;
1289 content_t id = CONTENT_IGNORE;
1290 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1293 if (id == CONTENT_IGNORE) {
1294 warningstream << "NodeDefManager: Absolute "
1295 "limit reached" << std::endl;
1296 return CONTENT_IGNORE;
1298 assert(id != CONTENT_IGNORE);
1299 addNameIdMapping(id, name);
1301 m_content_features[id] = def;
1302 verbosestream << "NodeDefManager: registering content id \"" << id
1303 << "\": name=\"" << def.name << "\""<<std::endl;
1305 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1306 fixSelectionBoxIntUnion();
1307 // Add this content to the list of all groups it belongs to
1308 // FIXME: This should remove a node from groups it no longer
1309 // belongs to when a node is re-registered
1310 for (ItemGroupList::const_iterator i = def.groups.begin();
1311 i != def.groups.end(); ++i) {
1312 std::string group_name = i->first;
1314 std::unordered_map<std::string, GroupItems>::iterator
1315 j = m_group_to_items.find(group_name);
1316 if (j == m_group_to_items.end()) {
1317 m_group_to_items[group_name].push_back(
1318 std::make_pair(id, i->second));
1320 GroupItems &items = j->second;
1321 items.push_back(std::make_pair(id, i->second));
1328 content_t CNodeDefManager::allocateDummy(const std::string &name)
1330 assert(name != ""); // Pre-condition
1333 return set(name, f);
1337 void CNodeDefManager::removeNode(const std::string &name)
1342 // Erase name from name ID mapping
1343 content_t id = CONTENT_IGNORE;
1344 if (m_name_id_mapping.getId(name, id)) {
1345 m_name_id_mapping.eraseName(name);
1346 m_name_id_mapping_with_aliases.erase(name);
1349 // Erase node content from all groups it belongs to
1350 for (std::unordered_map<std::string, GroupItems>::iterator iter_groups =
1351 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1352 GroupItems &items = iter_groups->second;
1353 for (GroupItems::iterator iter_groupitems = items.begin();
1354 iter_groupitems != items.end();) {
1355 if (iter_groupitems->first == id)
1356 items.erase(iter_groupitems++);
1361 // Check if group is empty
1362 if (items.size() == 0)
1363 m_group_to_items.erase(iter_groups++);
1370 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1372 std::set<std::string> all;
1374 m_name_id_mapping_with_aliases.clear();
1375 for (std::set<std::string>::const_iterator
1376 i = all.begin(); i != all.end(); ++i) {
1377 const std::string &name = *i;
1378 const std::string &convert_to = idef->getAlias(name);
1380 if (m_name_id_mapping.getId(convert_to, id)) {
1381 m_name_id_mapping_with_aliases.insert(
1382 std::make_pair(name, id));
1387 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1389 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1390 "overrides to textures from " << override_filepath << std::endl;
1392 std::ifstream infile(override_filepath.c_str());
1395 while (std::getline(infile, line)) {
1397 if (trim(line) == "")
1399 std::vector<std::string> splitted = str_split(line, ' ');
1400 if (splitted.size() != 3) {
1401 errorstream << override_filepath
1402 << ":" << line_c << " Could not apply texture override \""
1403 << line << "\": Syntax error" << std::endl;
1408 if (!getId(splitted[0], id))
1409 continue; // Ignore unknown node
1411 ContentFeatures &nodedef = m_content_features[id];
1413 if (splitted[1] == "top")
1414 nodedef.tiledef[0].name = splitted[2];
1415 else if (splitted[1] == "bottom")
1416 nodedef.tiledef[1].name = splitted[2];
1417 else if (splitted[1] == "right")
1418 nodedef.tiledef[2].name = splitted[2];
1419 else if (splitted[1] == "left")
1420 nodedef.tiledef[3].name = splitted[2];
1421 else if (splitted[1] == "back")
1422 nodedef.tiledef[4].name = splitted[2];
1423 else if (splitted[1] == "front")
1424 nodedef.tiledef[5].name = splitted[2];
1425 else if (splitted[1] == "all" || splitted[1] == "*")
1426 for (int i = 0; i < 6; i++)
1427 nodedef.tiledef[i].name = splitted[2];
1428 else if (splitted[1] == "sides")
1429 for (int i = 2; i < 6; i++)
1430 nodedef.tiledef[i].name = splitted[2];
1432 errorstream << override_filepath
1433 << ":" << line_c << " Could not apply texture override \""
1434 << line << "\": Unknown node side \""
1435 << splitted[1] << "\"" << std::endl;
1441 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1442 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1443 void *progress_callback_args)
1446 infostream << "CNodeDefManager::updateTextures(): Updating "
1447 "textures in node definitions" << std::endl;
1449 Client *client = (Client *)gamedef;
1450 ITextureSource *tsrc = client->tsrc();
1451 IShaderSource *shdsrc = client->getShaderSource();
1452 scene::IMeshManipulator *meshmanip =
1453 RenderingEngine::get_scene_manager()->getMeshManipulator();
1454 TextureSettings tsettings;
1455 tsettings.readSettings();
1457 u32 size = m_content_features.size();
1459 for (u32 i = 0; i < size; i++) {
1460 ContentFeatures *f = &(m_content_features[i]);
1461 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1462 progress_callback(progress_callback_args, i, size);
1467 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1469 writeU8(os, 1); // version
1471 std::ostringstream os2(std::ios::binary);
1472 for (u32 i = 0; i < m_content_features.size(); i++) {
1473 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1474 || i == CONTENT_UNKNOWN)
1476 const ContentFeatures *f = &m_content_features[i];
1480 // Wrap it in a string to allow different lengths without
1481 // strict version incompatibilities
1482 std::ostringstream wrapper_os(std::ios::binary);
1483 f->serialize(wrapper_os, protocol_version);
1484 os2<<serializeString(wrapper_os.str());
1486 // must not overflow
1487 u16 next = count + 1;
1488 FATAL_ERROR_IF(next < count, "Overflow");
1491 writeU16(os, count);
1492 os << serializeLongString(os2.str());
1496 void CNodeDefManager::deSerialize(std::istream &is)
1499 int version = readU8(is);
1501 throw SerializationError("unsupported NodeDefinitionManager version");
1502 u16 count = readU16(is);
1503 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1505 for (u16 n = 0; n < count; n++) {
1506 u16 i = readU16(is2);
1508 // Read it from the string wrapper
1509 std::string wrapper = deSerializeString(is2);
1510 std::istringstream wrapper_is(wrapper, std::ios::binary);
1511 f.deSerialize(wrapper_is);
1513 // Check error conditions
1514 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1515 warningstream << "NodeDefManager::deSerialize(): "
1516 "not changing builtin node " << i << std::endl;
1520 warningstream << "NodeDefManager::deSerialize(): "
1521 "received empty name" << std::endl;
1527 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1528 warningstream << "NodeDefManager::deSerialize(): "
1529 "already defined with different ID: " << f.name << std::endl;
1533 // All is ok, add node definition with the requested ID
1534 if (i >= m_content_features.size())
1535 m_content_features.resize((u32)(i) + 1);
1536 m_content_features[i] = f;
1537 addNameIdMapping(i, f.name);
1538 verbosestream << "deserialized " << f.name << std::endl;
1540 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1541 fixSelectionBoxIntUnion();
1546 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1548 m_name_id_mapping.set(i, name);
1549 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1553 IWritableNodeDefManager *createNodeDefManager()
1555 return new CNodeDefManager();
1559 //// Serialization of old ContentFeatures formats
1560 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1562 u8 compatible_param_type_2 = param_type_2;
1563 if ((protocol_version < 28)
1564 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1565 compatible_param_type_2 = CPT2_NONE;
1566 else if (protocol_version < 30) {
1567 if (compatible_param_type_2 == CPT2_COLOR)
1568 compatible_param_type_2 = CPT2_NONE;
1569 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1570 compatible_param_type_2 = CPT2_FACEDIR;
1571 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1572 compatible_param_type_2 = CPT2_WALLMOUNTED;
1575 float compatible_visual_scale = visual_scale;
1576 if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1577 compatible_visual_scale = sqrt(visual_scale);
1579 TileDef compatible_tiles[6];
1580 for (u8 i = 0; i < 6; i++) {
1581 compatible_tiles[i] = tiledef[i];
1582 if (tiledef_overlay[i].name != "") {
1583 std::stringstream s;
1584 s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name
1586 compatible_tiles[i].name = s.str();
1591 if (protocol_version < 31) {
1592 writeU8(os, protocol_version < 27 ? 7 : 8);
1594 os << serializeString(name);
1595 writeU16(os, groups.size());
1596 for (ItemGroupList::const_iterator i = groups.begin();
1597 i != groups.end(); ++i) {
1598 os << serializeString(i->first);
1599 writeS16(os, i->second);
1601 writeU8(os, drawtype);
1602 writeF1000(os, compatible_visual_scale);
1604 for (u32 i = 0; i < 6; i++)
1605 compatible_tiles[i].serialize(os, protocol_version);
1606 writeU8(os, CF_SPECIAL_COUNT);
1607 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1608 tiledef_special[i].serialize(os, protocol_version);
1610 writeU8(os, post_effect_color.getAlpha());
1611 writeU8(os, post_effect_color.getRed());
1612 writeU8(os, post_effect_color.getGreen());
1613 writeU8(os, post_effect_color.getBlue());
1614 writeU8(os, param_type);
1615 writeU8(os, compatible_param_type_2);
1616 writeU8(os, is_ground_content);
1617 writeU8(os, light_propagates);
1618 writeU8(os, sunlight_propagates);
1619 writeU8(os, walkable);
1620 writeU8(os, pointable);
1621 writeU8(os, diggable);
1622 writeU8(os, climbable);
1623 writeU8(os, buildable_to);
1624 os << serializeString(""); // legacy: used to be metadata_name
1625 writeU8(os, liquid_type);
1626 os << serializeString(liquid_alternative_flowing);
1627 os << serializeString(liquid_alternative_source);
1628 writeU8(os, liquid_viscosity);
1629 writeU8(os, liquid_renewable);
1630 writeU8(os, light_source);
1631 writeU32(os, damage_per_second);
1632 node_box.serialize(os, protocol_version);
1633 selection_box.serialize(os, protocol_version);
1634 writeU8(os, legacy_facedir_simple);
1635 writeU8(os, legacy_wallmounted);
1636 serializeSimpleSoundSpec(sound_footstep, os, 10);
1637 serializeSimpleSoundSpec(sound_dig, os, 10);
1638 serializeSimpleSoundSpec(sound_dug, os, 10);
1639 writeU8(os, rightclickable);
1640 writeU8(os, drowning);
1641 writeU8(os, leveled);
1642 writeU8(os, liquid_range);
1643 writeU8(os, waving);
1644 os << serializeString(mesh);
1645 collision_box.serialize(os, protocol_version);
1646 writeU8(os, floodable);
1647 writeU16(os, connects_to_ids.size());
1648 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1649 i != connects_to_ids.end(); ++i)
1651 writeU8(os, connect_sides);
1653 throw SerializationError("ContentFeatures::serialize(): "
1654 "Unsupported version requested");
1658 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1660 if (version == 5) // In PROTOCOL_VERSION 13
1662 name = deSerializeString(is);
1664 u32 groups_size = readU16(is);
1665 for(u32 i=0; i<groups_size; i++){
1666 std::string name = deSerializeString(is);
1667 int value = readS16(is);
1668 groups[name] = value;
1670 drawtype = (enum NodeDrawType)readU8(is);
1672 visual_scale = readF1000(is);
1673 if (readU8(is) != 6)
1674 throw SerializationError("unsupported tile count");
1675 for (u32 i = 0; i < 6; i++)
1676 tiledef[i].deSerialize(is, version, drawtype);
1677 if (readU8(is) != CF_SPECIAL_COUNT)
1678 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1679 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1680 tiledef_special[i].deSerialize(is, version, drawtype);
1682 post_effect_color.setAlpha(readU8(is));
1683 post_effect_color.setRed(readU8(is));
1684 post_effect_color.setGreen(readU8(is));
1685 post_effect_color.setBlue(readU8(is));
1686 param_type = (enum ContentParamType)readU8(is);
1687 param_type_2 = (enum ContentParamType2)readU8(is);
1688 is_ground_content = readU8(is);
1689 light_propagates = readU8(is);
1690 sunlight_propagates = readU8(is);
1691 walkable = readU8(is);
1692 pointable = readU8(is);
1693 diggable = readU8(is);
1694 climbable = readU8(is);
1695 buildable_to = readU8(is);
1696 deSerializeString(is); // legacy: used to be metadata_name
1697 liquid_type = (enum LiquidType)readU8(is);
1698 liquid_alternative_flowing = deSerializeString(is);
1699 liquid_alternative_source = deSerializeString(is);
1700 liquid_viscosity = readU8(is);
1701 light_source = readU8(is);
1702 light_source = MYMIN(light_source, LIGHT_MAX);
1703 damage_per_second = readU32(is);
1704 node_box.deSerialize(is);
1705 selection_box.deSerialize(is);
1706 legacy_facedir_simple = readU8(is);
1707 legacy_wallmounted = readU8(is);
1708 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1709 deSerializeSimpleSoundSpec(sound_dig, is, version);
1710 deSerializeSimpleSoundSpec(sound_dug, is, version);
1711 } else if (version == 6) {
1712 name = deSerializeString(is);
1714 u32 groups_size = readU16(is);
1715 for (u32 i = 0; i < groups_size; i++) {
1716 std::string name = deSerializeString(is);
1717 int value = readS16(is);
1718 groups[name] = value;
1720 drawtype = (enum NodeDrawType)readU8(is);
1721 visual_scale = readF1000(is);
1722 if (readU8(is) != 6)
1723 throw SerializationError("unsupported tile count");
1724 for (u32 i = 0; i < 6; i++)
1725 tiledef[i].deSerialize(is, version, drawtype);
1726 // CF_SPECIAL_COUNT in version 6 = 2
1727 if (readU8(is) != 2)
1728 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1729 for (u32 i = 0; i < 2; i++)
1730 tiledef_special[i].deSerialize(is, version, drawtype);
1732 post_effect_color.setAlpha(readU8(is));
1733 post_effect_color.setRed(readU8(is));
1734 post_effect_color.setGreen(readU8(is));
1735 post_effect_color.setBlue(readU8(is));
1736 param_type = (enum ContentParamType)readU8(is);
1737 param_type_2 = (enum ContentParamType2)readU8(is);
1738 is_ground_content = readU8(is);
1739 light_propagates = readU8(is);
1740 sunlight_propagates = readU8(is);
1741 walkable = readU8(is);
1742 pointable = readU8(is);
1743 diggable = readU8(is);
1744 climbable = readU8(is);
1745 buildable_to = readU8(is);
1746 deSerializeString(is); // legacy: used to be metadata_name
1747 liquid_type = (enum LiquidType)readU8(is);
1748 liquid_alternative_flowing = deSerializeString(is);
1749 liquid_alternative_source = deSerializeString(is);
1750 liquid_viscosity = readU8(is);
1751 liquid_renewable = readU8(is);
1752 light_source = readU8(is);
1753 damage_per_second = readU32(is);
1754 node_box.deSerialize(is);
1755 selection_box.deSerialize(is);
1756 legacy_facedir_simple = readU8(is);
1757 legacy_wallmounted = readU8(is);
1758 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1759 deSerializeSimpleSoundSpec(sound_dig, is, version);
1760 deSerializeSimpleSoundSpec(sound_dug, is, version);
1761 rightclickable = readU8(is);
1762 drowning = readU8(is);
1763 leveled = readU8(is);
1764 liquid_range = readU8(is);
1765 } else if (version == 7 || version == 8){
1766 name = deSerializeString(is);
1768 u32 groups_size = readU16(is);
1769 for (u32 i = 0; i < groups_size; i++) {
1770 std::string name = deSerializeString(is);
1771 int value = readS16(is);
1772 groups[name] = value;
1774 drawtype = (enum NodeDrawType) readU8(is);
1776 visual_scale = readF1000(is);
1777 if (readU8(is) != 6)
1778 throw SerializationError("unsupported tile count");
1779 for (u32 i = 0; i < 6; i++)
1780 tiledef[i].deSerialize(is, version, drawtype);
1781 if (readU8(is) != CF_SPECIAL_COUNT)
1782 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1783 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1784 tiledef_special[i].deSerialize(is, version, drawtype);
1786 post_effect_color.setAlpha(readU8(is));
1787 post_effect_color.setRed(readU8(is));
1788 post_effect_color.setGreen(readU8(is));
1789 post_effect_color.setBlue(readU8(is));
1790 param_type = (enum ContentParamType) readU8(is);
1791 param_type_2 = (enum ContentParamType2) readU8(is);
1792 is_ground_content = readU8(is);
1793 light_propagates = readU8(is);
1794 sunlight_propagates = readU8(is);
1795 walkable = readU8(is);
1796 pointable = readU8(is);
1797 diggable = readU8(is);
1798 climbable = readU8(is);
1799 buildable_to = readU8(is);
1800 deSerializeString(is); // legacy: used to be metadata_name
1801 liquid_type = (enum LiquidType) readU8(is);
1802 liquid_alternative_flowing = deSerializeString(is);
1803 liquid_alternative_source = deSerializeString(is);
1804 liquid_viscosity = readU8(is);
1805 liquid_renewable = readU8(is);
1806 light_source = readU8(is);
1807 light_source = MYMIN(light_source, LIGHT_MAX);
1808 damage_per_second = readU32(is);
1809 node_box.deSerialize(is);
1810 selection_box.deSerialize(is);
1811 legacy_facedir_simple = readU8(is);
1812 legacy_wallmounted = readU8(is);
1813 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1814 deSerializeSimpleSoundSpec(sound_dig, is, version);
1815 deSerializeSimpleSoundSpec(sound_dug, is, version);
1816 rightclickable = readU8(is);
1817 drowning = readU8(is);
1818 leveled = readU8(is);
1819 liquid_range = readU8(is);
1820 waving = readU8(is);
1822 mesh = deSerializeString(is);
1823 collision_box.deSerialize(is);
1824 floodable = readU8(is);
1825 u16 connects_to_size = readU16(is);
1826 connects_to_ids.clear();
1827 for (u16 i = 0; i < connects_to_size; i++)
1828 connects_to_ids.insert(readU16(is));
1829 connect_sides = readU8(is);
1830 } catch (SerializationError &e) {};
1832 throw SerializationError("unsupported ContentFeatures version");
1836 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1838 m_node_registration_complete = completed;
1842 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1845 if (m_node_registration_complete)
1846 nr->nodeResolveInternal();
1848 m_pending_resolve_callbacks.push_back(nr);
1852 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1854 size_t len = m_pending_resolve_callbacks.size();
1855 for (size_t i = 0; i != len; i++) {
1856 if (nr != m_pending_resolve_callbacks[i])
1860 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1861 m_pending_resolve_callbacks.resize(len);
1869 void CNodeDefManager::runNodeResolveCallbacks()
1871 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1872 NodeResolver *nr = m_pending_resolve_callbacks[i];
1873 nr->nodeResolveInternal();
1876 m_pending_resolve_callbacks.clear();
1880 void CNodeDefManager::resetNodeResolveState()
1882 m_node_registration_complete = false;
1883 m_pending_resolve_callbacks.clear();
1886 void CNodeDefManager::mapNodeboxConnections()
1888 for (u32 i = 0; i < m_content_features.size(); i++) {
1889 ContentFeatures *f = &m_content_features[i];
1890 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1892 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1893 it != f->connects_to.end(); ++it) {
1894 getIds(*it, f->connects_to_ids);
1899 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1901 const ContentFeatures &f1 = get(from);
1903 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1906 // lookup target in connected set
1907 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1910 const ContentFeatures &f2 = get(to);
1912 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1913 // ignores actually looking if back connection exists
1914 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1916 // does to node declare usable faces?
1917 if (f2.connect_sides > 0) {
1918 if ((f2.param_type_2 == CPT2_FACEDIR ||
1919 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1920 && (connect_face >= 4)) {
1921 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1922 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1924 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1926 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1927 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1929 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 32, 16, 8, 4 // 32 - left
1933 return (f2.connect_sides
1934 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1936 return (f2.connect_sides & connect_face);
1938 // the target is just a regular node, so connect no matter back connection
1946 NodeResolver::NodeResolver()
1948 m_nodenames.reserve(16);
1949 m_nnlistsizes.reserve(4);
1953 NodeResolver::~NodeResolver()
1955 if (!m_resolve_done && m_ndef)
1956 m_ndef->cancelNodeResolveCallback(this);
1960 void NodeResolver::nodeResolveInternal()
1962 m_nodenames_idx = 0;
1963 m_nnlistsizes_idx = 0;
1966 m_resolve_done = true;
1968 m_nodenames.clear();
1969 m_nnlistsizes.clear();
1973 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1974 const std::string &node_alt, content_t c_fallback)
1976 if (m_nodenames_idx == m_nodenames.size()) {
1977 *result_out = c_fallback;
1978 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1983 std::string name = m_nodenames[m_nodenames_idx++];
1985 bool success = m_ndef->getId(name, c);
1986 if (!success && node_alt != "") {
1988 success = m_ndef->getId(name, c);
1992 errorstream << "NodeResolver: failed to resolve node name '" << name
1993 << "'." << std::endl;
2002 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
2003 bool all_required, content_t c_fallback)
2005 bool success = true;
2007 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
2008 errorstream << "NodeResolver: no more node lists" << std::endl;
2012 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
2015 if (m_nodenames_idx == m_nodenames.size()) {
2016 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2021 std::string &name = m_nodenames[m_nodenames_idx++];
2023 if (name.substr(0,6) != "group:") {
2024 if (m_ndef->getId(name, c)) {
2025 result_out->push_back(c);
2026 } else if (all_required) {
2027 errorstream << "NodeResolver: failed to resolve node name '"
2028 << name << "'." << std::endl;
2029 result_out->push_back(c_fallback);
2033 std::set<content_t> cids;
2034 std::set<content_t>::iterator it;
2035 m_ndef->getIds(name, cids);
2036 for (it = cids.begin(); it != cids.end(); ++it)
2037 result_out->push_back(*it);