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);
640 tile->frames.resize(frame_count);
642 for (int i = 0; i < frame_count; i++) {
648 tiledef->animation.getTextureModifer(os,
649 tile->texture->getOriginalSize(), i);
651 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
652 if (tile->normal_texture)
653 frame.normal_texture = tsrc->getNormalTexture(os.str());
654 frame.flags_texture = tile->flags_texture;
655 tile->frames[i] = frame;
662 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
663 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
665 // minimap pixel color - the average color of a texture
666 if (tsettings.enable_minimap && tiledef[0].name != "")
667 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
669 // Figure out the actual tiles to use
671 for (u32 j = 0; j < 6; j++) {
672 tdef[j] = tiledef[j];
673 if (tdef[j].name == "")
674 tdef[j].name = "unknown_node.png";
676 // also the overlay tiles
677 TileDef tdef_overlay[6];
678 for (u32 j = 0; j < 6; j++)
679 tdef_overlay[j] = tiledef_overlay[j];
680 // also the special tiles
681 TileDef tdef_spec[6];
682 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
683 tdef_spec[j] = tiledef_special[j];
685 bool is_liquid = false;
687 u8 material_type = (alpha == 255) ?
688 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
693 material_type = (alpha == 255) ?
694 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
701 assert(liquid_type == LIQUID_SOURCE);
702 if (tsettings.opaque_water)
707 case NDT_FLOWINGLIQUID:
708 assert(liquid_type == LIQUID_FLOWING);
710 if (tsettings.opaque_water)
716 visual_solidness = 1;
718 case NDT_GLASSLIKE_FRAMED:
720 visual_solidness = 1;
722 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
724 visual_solidness = 1;
725 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
729 visual_solidness = 1;
731 case NDT_ALLFACES_OPTIONAL:
732 if (tsettings.leaves_style == LEAVES_FANCY) {
733 drawtype = NDT_ALLFACES;
735 visual_solidness = 1;
736 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
737 for (u32 j = 0; j < 6; j++) {
738 if (tdef_spec[j].name != "")
739 tdef[j].name = tdef_spec[j].name;
741 drawtype = NDT_GLASSLIKE;
743 visual_solidness = 1;
745 drawtype = NDT_NORMAL;
747 for (u32 i = 0; i < 6; i++)
748 tdef[i].name += std::string("^[noalpha");
751 material_type = TILE_MATERIAL_WAVING_LEAVES;
756 material_type = TILE_MATERIAL_WAVING_PLANTS;
765 material_type = TILE_MATERIAL_WAVING_PLANTS;
766 else if (waving == 2)
767 material_type = TILE_MATERIAL_WAVING_LEAVES;
775 case NDT_PLANTLIKE_ROOTED:
781 // Vertex alpha is no longer supported, correct if necessary.
782 correctAlpha(tdef, 6);
783 correctAlpha(tdef_overlay, 6);
784 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
785 material_type = (alpha == 255) ?
786 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
789 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
791 u8 overlay_material = material_type;
792 if (overlay_material == TILE_MATERIAL_OPAQUE)
793 overlay_material = TILE_MATERIAL_BASIC;
794 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
795 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
797 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
799 // Tiles (fill in f->tiles[])
800 for (u16 j = 0; j < 6; j++) {
801 fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader,
802 tsettings.use_normal_texture,
803 tdef[j].backface_culling, material_type);
804 if (tdef_overlay[j].name != "")
805 fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j],
806 overlay_shader, tsettings.use_normal_texture,
807 tdef[j].backface_culling, overlay_material);
810 u8 special_material = material_type;
811 if (drawtype == NDT_PLANTLIKE_ROOTED) {
813 special_material = TILE_MATERIAL_WAVING_PLANTS;
814 else if (waving == 2)
815 special_material = TILE_MATERIAL_WAVING_LEAVES;
817 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
819 // Special tiles (fill in f->special_tiles[])
820 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
821 fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j],
822 special_shader, tsettings.use_normal_texture,
823 tdef_spec[j].backface_culling, special_material);
826 if (param_type_2 == CPT2_COLOR ||
827 param_type_2 == CPT2_COLORED_FACEDIR ||
828 param_type_2 == CPT2_COLORED_WALLMOUNTED)
829 palette = tsrc->getPalette(palette_name);
831 if ((drawtype == NDT_MESH) && (mesh != "")) {
833 // Read the mesh and apply scale
834 mesh_ptr[0] = client->getMesh(mesh);
836 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
837 scaleMesh(mesh_ptr[0], scale);
838 recalculateBoundingBox(mesh_ptr[0]);
839 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
841 } else if ((drawtype == NDT_NODEBOX) &&
842 ((node_box.type == NODEBOX_REGULAR) ||
843 (node_box.type == NODEBOX_FIXED)) &&
844 (!node_box.fixed.empty())) {
845 //Convert regular nodebox nodes to meshnodes
846 //Change the drawtype and apply scale
848 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
849 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
850 scaleMesh(mesh_ptr[0], scale);
851 recalculateBoundingBox(mesh_ptr[0]);
852 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
855 //Cache 6dfacedir and wallmounted rotated clones of meshes
856 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
857 (param_type_2 == CPT2_FACEDIR
858 || param_type_2 == CPT2_COLORED_FACEDIR)) {
859 for (u16 j = 1; j < 24; j++) {
860 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
861 rotateMeshBy6dFacedir(mesh_ptr[j], j);
862 recalculateBoundingBox(mesh_ptr[j]);
863 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
865 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
866 && (param_type_2 == CPT2_WALLMOUNTED ||
867 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
868 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
869 for (u16 j = 1; j < 6; j++) {
870 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
871 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
872 recalculateBoundingBox(mesh_ptr[j]);
873 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
875 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
876 recalculateBoundingBox(mesh_ptr[0]);
877 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
886 class CNodeDefManager: public IWritableNodeDefManager {
889 virtual ~CNodeDefManager();
891 virtual IWritableNodeDefManager *clone();
892 inline virtual const ContentFeatures& get(content_t c) const;
893 inline virtual const ContentFeatures& get(const MapNode &n) const;
894 virtual bool getId(const std::string &name, content_t &result) const;
895 virtual content_t getId(const std::string &name) const;
896 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
897 virtual const ContentFeatures& get(const std::string &name) const;
898 content_t allocateId();
899 virtual content_t set(const std::string &name, const ContentFeatures &def);
900 virtual content_t allocateDummy(const std::string &name);
901 virtual void removeNode(const std::string &name);
902 virtual void updateAliases(IItemDefManager *idef);
903 virtual void applyTextureOverrides(const std::string &override_filepath);
904 virtual void updateTextures(IGameDef *gamedef,
905 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
906 void *progress_cbk_args);
907 void serialize(std::ostream &os, u16 protocol_version) const;
908 void deSerialize(std::istream &is);
910 inline virtual void setNodeRegistrationStatus(bool completed);
912 virtual void pendNodeResolve(NodeResolver *nr);
913 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
914 virtual void runNodeResolveCallbacks();
915 virtual void resetNodeResolveState();
916 virtual void mapNodeboxConnections();
917 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
918 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
920 return m_selection_box_int_union;
924 void addNameIdMapping(content_t i, std::string name);
926 * Recalculates m_selection_box_int_union based on
927 * m_selection_box_union.
929 void fixSelectionBoxIntUnion();
931 // Features indexed by id
932 std::vector<ContentFeatures> m_content_features;
934 // A mapping for fast converting back and forth between names and ids
935 NameIdMapping m_name_id_mapping;
937 // Like m_name_id_mapping, but only from names to ids, and includes
938 // item aliases too. Updated by updateAliases()
939 // Note: Not serialized.
941 std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases;
943 // A mapping from groups to a list of content_ts (and their levels)
944 // that belong to it. Necessary for a direct lookup in getIds().
945 // Note: Not serialized.
946 std::unordered_map<std::string, GroupItems> m_group_to_items;
948 // Next possibly free id
951 // NodeResolvers to callback once node registration has ended
952 std::vector<NodeResolver *> m_pending_resolve_callbacks;
954 // True when all nodes have been registered
955 bool m_node_registration_complete;
957 //! The union of all nodes' selection boxes.
958 aabb3f m_selection_box_union;
960 * The smallest box in node coordinates that
961 * contains all nodes' selection boxes.
963 core::aabbox3d<s16> m_selection_box_int_union;
967 CNodeDefManager::CNodeDefManager()
973 CNodeDefManager::~CNodeDefManager()
976 for (u32 i = 0; i < m_content_features.size(); i++) {
977 ContentFeatures *f = &m_content_features[i];
978 for (u32 j = 0; j < 24; j++) {
980 f->mesh_ptr[j]->drop();
987 void CNodeDefManager::clear()
989 m_content_features.clear();
990 m_name_id_mapping.clear();
991 m_name_id_mapping_with_aliases.clear();
992 m_group_to_items.clear();
994 m_selection_box_union.reset(0,0,0);
995 m_selection_box_int_union.reset(0,0,0);
997 resetNodeResolveState();
999 u32 initial_length = 0;
1000 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1001 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1002 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1003 m_content_features.resize(initial_length);
1005 // Set CONTENT_UNKNOWN
1009 // Insert directly into containers
1010 content_t c = CONTENT_UNKNOWN;
1011 m_content_features[c] = f;
1012 addNameIdMapping(c, f.name);
1019 f.drawtype = NDT_AIRLIKE;
1020 f.param_type = CPT_LIGHT;
1021 f.light_propagates = true;
1022 f.sunlight_propagates = true;
1024 f.pointable = false;
1026 f.buildable_to = true;
1028 f.is_ground_content = true;
1029 // Insert directly into containers
1030 content_t c = CONTENT_AIR;
1031 m_content_features[c] = f;
1032 addNameIdMapping(c, f.name);
1035 // Set CONTENT_IGNORE
1039 f.drawtype = NDT_AIRLIKE;
1040 f.param_type = CPT_NONE;
1041 f.light_propagates = false;
1042 f.sunlight_propagates = false;
1044 f.pointable = false;
1046 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1047 f.is_ground_content = true;
1048 // Insert directly into containers
1049 content_t c = CONTENT_IGNORE;
1050 m_content_features[c] = f;
1051 addNameIdMapping(c, f.name);
1056 IWritableNodeDefManager *CNodeDefManager::clone()
1058 CNodeDefManager *mgr = new CNodeDefManager();
1064 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1066 return c < m_content_features.size()
1067 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1071 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1073 return get(n.getContent());
1077 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1079 std::unordered_map<std::string, content_t>::const_iterator
1080 i = m_name_id_mapping_with_aliases.find(name);
1081 if(i == m_name_id_mapping_with_aliases.end())
1088 content_t CNodeDefManager::getId(const std::string &name) const
1090 content_t id = CONTENT_IGNORE;
1096 bool CNodeDefManager::getIds(const std::string &name,
1097 std::set<content_t> &result) const
1099 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1100 if (name.substr(0,6) != "group:") {
1101 content_t id = CONTENT_IGNORE;
1102 bool exists = getId(name, id);
1107 std::string group = name.substr(6);
1109 std::unordered_map<std::string, GroupItems>::const_iterator
1110 i = m_group_to_items.find(group);
1111 if (i == m_group_to_items.end())
1114 const GroupItems &items = i->second;
1115 for (GroupItems::const_iterator j = items.begin();
1116 j != items.end(); ++j) {
1117 if ((*j).second != 0)
1118 result.insert((*j).first);
1120 //printf("getIds: %dus\n", t.stop());
1125 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1127 content_t id = CONTENT_UNKNOWN;
1133 // returns CONTENT_IGNORE if no free ID found
1134 content_t CNodeDefManager::allocateId()
1136 for (content_t id = m_next_id;
1137 id >= m_next_id; // overflow?
1139 while (id >= m_content_features.size()) {
1140 m_content_features.push_back(ContentFeatures());
1142 const ContentFeatures &f = m_content_features[id];
1148 // If we arrive here, an overflow occurred in id.
1149 // That means no ID was found
1150 return CONTENT_IGNORE;
1155 * Returns the smallest box that contains all boxes
1156 * in the vector. Box_union is expanded.
1157 * @param[in] boxes the vector containing the boxes
1158 * @param[in, out] box_union the union of the arguments
1160 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1162 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1163 it != boxes.end(); ++it) {
1164 box_union->addInternalBox(*it);
1170 * Returns a box that contains the nodebox in every case.
1171 * The argument node_union is expanded.
1172 * @param[in] nodebox the nodebox to be measured
1173 * @param[in] features used to decide whether the nodebox
1175 * @param[in, out] box_union the union of the arguments
1177 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1180 switch(nodebox.type) {
1182 case NODEBOX_LEVELED: {
1184 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1185 boxVectorUnion(nodebox.fixed, &half_processed);
1186 // Set leveled boxes to maximal
1187 if (nodebox.type == NODEBOX_LEVELED) {
1188 half_processed.MaxEdge.Y = +BS / 2;
1190 if (features.param_type_2 == CPT2_FACEDIR ||
1191 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1192 // Get maximal coordinate
1194 fabsf(half_processed.MinEdge.X),
1195 fabsf(half_processed.MinEdge.Y),
1196 fabsf(half_processed.MinEdge.Z),
1197 fabsf(half_processed.MaxEdge.X),
1198 fabsf(half_processed.MaxEdge.Y),
1199 fabsf(half_processed.MaxEdge.Z) };
1201 for (int i = 0; i < 6; i++) {
1202 if (max < coords[i]) {
1206 // Add the union of all possible rotated boxes
1207 box_union->addInternalPoint(-max, -max, -max);
1208 box_union->addInternalPoint(+max, +max, +max);
1210 box_union->addInternalBox(half_processed);
1214 case NODEBOX_WALLMOUNTED: {
1216 box_union->addInternalBox(nodebox.wall_top);
1217 box_union->addInternalBox(nodebox.wall_bottom);
1218 // Find maximal coordinate in the X-Z plane
1220 fabsf(nodebox.wall_side.MinEdge.X),
1221 fabsf(nodebox.wall_side.MinEdge.Z),
1222 fabsf(nodebox.wall_side.MaxEdge.X),
1223 fabsf(nodebox.wall_side.MaxEdge.Z) };
1225 for (int i = 0; i < 4; i++) {
1226 if (max < coords[i]) {
1230 // Add the union of all possible rotated boxes
1231 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1232 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1235 case NODEBOX_CONNECTED: {
1236 // Add all possible connected boxes
1237 boxVectorUnion(nodebox.fixed, box_union);
1238 boxVectorUnion(nodebox.connect_top, box_union);
1239 boxVectorUnion(nodebox.connect_bottom, box_union);
1240 boxVectorUnion(nodebox.connect_front, box_union);
1241 boxVectorUnion(nodebox.connect_left, box_union);
1242 boxVectorUnion(nodebox.connect_back, box_union);
1243 boxVectorUnion(nodebox.connect_right, box_union);
1248 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1249 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1255 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1257 m_selection_box_int_union.MinEdge.X = floorf(
1258 m_selection_box_union.MinEdge.X / BS + 0.5f);
1259 m_selection_box_int_union.MinEdge.Y = floorf(
1260 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1261 m_selection_box_int_union.MinEdge.Z = floorf(
1262 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1263 m_selection_box_int_union.MaxEdge.X = ceilf(
1264 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1265 m_selection_box_int_union.MaxEdge.Y = ceilf(
1266 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1267 m_selection_box_int_union.MaxEdge.Z = ceilf(
1268 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1272 // IWritableNodeDefManager
1273 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1277 assert(name == def.name);
1279 // Don't allow redefining ignore (but allow air and unknown)
1280 if (name == "ignore") {
1281 warningstream << "NodeDefManager: Ignoring "
1282 "CONTENT_IGNORE redefinition"<<std::endl;
1283 return CONTENT_IGNORE;
1286 content_t id = CONTENT_IGNORE;
1287 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1290 if (id == CONTENT_IGNORE) {
1291 warningstream << "NodeDefManager: Absolute "
1292 "limit reached" << std::endl;
1293 return CONTENT_IGNORE;
1295 assert(id != CONTENT_IGNORE);
1296 addNameIdMapping(id, name);
1298 m_content_features[id] = def;
1299 verbosestream << "NodeDefManager: registering content id \"" << id
1300 << "\": name=\"" << def.name << "\""<<std::endl;
1302 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1303 fixSelectionBoxIntUnion();
1304 // Add this content to the list of all groups it belongs to
1305 // FIXME: This should remove a node from groups it no longer
1306 // belongs to when a node is re-registered
1307 for (ItemGroupList::const_iterator i = def.groups.begin();
1308 i != def.groups.end(); ++i) {
1309 std::string group_name = i->first;
1311 std::unordered_map<std::string, GroupItems>::iterator
1312 j = m_group_to_items.find(group_name);
1313 if (j == m_group_to_items.end()) {
1314 m_group_to_items[group_name].push_back(
1315 std::make_pair(id, i->second));
1317 GroupItems &items = j->second;
1318 items.push_back(std::make_pair(id, i->second));
1325 content_t CNodeDefManager::allocateDummy(const std::string &name)
1327 assert(name != ""); // Pre-condition
1330 return set(name, f);
1334 void CNodeDefManager::removeNode(const std::string &name)
1339 // Erase name from name ID mapping
1340 content_t id = CONTENT_IGNORE;
1341 if (m_name_id_mapping.getId(name, id)) {
1342 m_name_id_mapping.eraseName(name);
1343 m_name_id_mapping_with_aliases.erase(name);
1346 // Erase node content from all groups it belongs to
1347 for (std::unordered_map<std::string, GroupItems>::iterator iter_groups =
1348 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1349 GroupItems &items = iter_groups->second;
1350 for (GroupItems::iterator iter_groupitems = items.begin();
1351 iter_groupitems != items.end();) {
1352 if (iter_groupitems->first == id)
1353 items.erase(iter_groupitems++);
1358 // Check if group is empty
1359 if (items.size() == 0)
1360 m_group_to_items.erase(iter_groups++);
1367 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1369 std::set<std::string> all;
1371 m_name_id_mapping_with_aliases.clear();
1372 for (std::set<std::string>::const_iterator
1373 i = all.begin(); i != all.end(); ++i) {
1374 const std::string &name = *i;
1375 const std::string &convert_to = idef->getAlias(name);
1377 if (m_name_id_mapping.getId(convert_to, id)) {
1378 m_name_id_mapping_with_aliases.insert(
1379 std::make_pair(name, id));
1384 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1386 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1387 "overrides to textures from " << override_filepath << std::endl;
1389 std::ifstream infile(override_filepath.c_str());
1392 while (std::getline(infile, line)) {
1394 if (trim(line) == "")
1396 std::vector<std::string> splitted = str_split(line, ' ');
1397 if (splitted.size() != 3) {
1398 errorstream << override_filepath
1399 << ":" << line_c << " Could not apply texture override \""
1400 << line << "\": Syntax error" << std::endl;
1405 if (!getId(splitted[0], id))
1406 continue; // Ignore unknown node
1408 ContentFeatures &nodedef = m_content_features[id];
1410 if (splitted[1] == "top")
1411 nodedef.tiledef[0].name = splitted[2];
1412 else if (splitted[1] == "bottom")
1413 nodedef.tiledef[1].name = splitted[2];
1414 else if (splitted[1] == "right")
1415 nodedef.tiledef[2].name = splitted[2];
1416 else if (splitted[1] == "left")
1417 nodedef.tiledef[3].name = splitted[2];
1418 else if (splitted[1] == "back")
1419 nodedef.tiledef[4].name = splitted[2];
1420 else if (splitted[1] == "front")
1421 nodedef.tiledef[5].name = splitted[2];
1422 else if (splitted[1] == "all" || splitted[1] == "*")
1423 for (int i = 0; i < 6; i++)
1424 nodedef.tiledef[i].name = splitted[2];
1425 else if (splitted[1] == "sides")
1426 for (int i = 2; i < 6; i++)
1427 nodedef.tiledef[i].name = splitted[2];
1429 errorstream << override_filepath
1430 << ":" << line_c << " Could not apply texture override \""
1431 << line << "\": Unknown node side \""
1432 << splitted[1] << "\"" << std::endl;
1438 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1439 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1440 void *progress_callback_args)
1443 infostream << "CNodeDefManager::updateTextures(): Updating "
1444 "textures in node definitions" << std::endl;
1446 Client *client = (Client *)gamedef;
1447 ITextureSource *tsrc = client->tsrc();
1448 IShaderSource *shdsrc = client->getShaderSource();
1449 scene::IMeshManipulator *meshmanip =
1450 RenderingEngine::get_scene_manager()->getMeshManipulator();
1451 TextureSettings tsettings;
1452 tsettings.readSettings();
1454 u32 size = m_content_features.size();
1456 for (u32 i = 0; i < size; i++) {
1457 ContentFeatures *f = &(m_content_features[i]);
1458 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1459 progress_callback(progress_callback_args, i, size);
1464 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1466 writeU8(os, 1); // version
1468 std::ostringstream os2(std::ios::binary);
1469 for (u32 i = 0; i < m_content_features.size(); i++) {
1470 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1471 || i == CONTENT_UNKNOWN)
1473 const ContentFeatures *f = &m_content_features[i];
1477 // Wrap it in a string to allow different lengths without
1478 // strict version incompatibilities
1479 std::ostringstream wrapper_os(std::ios::binary);
1480 f->serialize(wrapper_os, protocol_version);
1481 os2<<serializeString(wrapper_os.str());
1483 // must not overflow
1484 u16 next = count + 1;
1485 FATAL_ERROR_IF(next < count, "Overflow");
1488 writeU16(os, count);
1489 os << serializeLongString(os2.str());
1493 void CNodeDefManager::deSerialize(std::istream &is)
1496 int version = readU8(is);
1498 throw SerializationError("unsupported NodeDefinitionManager version");
1499 u16 count = readU16(is);
1500 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1502 for (u16 n = 0; n < count; n++) {
1503 u16 i = readU16(is2);
1505 // Read it from the string wrapper
1506 std::string wrapper = deSerializeString(is2);
1507 std::istringstream wrapper_is(wrapper, std::ios::binary);
1508 f.deSerialize(wrapper_is);
1510 // Check error conditions
1511 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1512 warningstream << "NodeDefManager::deSerialize(): "
1513 "not changing builtin node " << i << std::endl;
1517 warningstream << "NodeDefManager::deSerialize(): "
1518 "received empty name" << std::endl;
1524 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1525 warningstream << "NodeDefManager::deSerialize(): "
1526 "already defined with different ID: " << f.name << std::endl;
1530 // All is ok, add node definition with the requested ID
1531 if (i >= m_content_features.size())
1532 m_content_features.resize((u32)(i) + 1);
1533 m_content_features[i] = f;
1534 addNameIdMapping(i, f.name);
1535 verbosestream << "deserialized " << f.name << std::endl;
1537 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1538 fixSelectionBoxIntUnion();
1543 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1545 m_name_id_mapping.set(i, name);
1546 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1550 IWritableNodeDefManager *createNodeDefManager()
1552 return new CNodeDefManager();
1556 //// Serialization of old ContentFeatures formats
1557 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1559 u8 compatible_param_type_2 = param_type_2;
1560 if ((protocol_version < 28)
1561 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1562 compatible_param_type_2 = CPT2_NONE;
1563 else if (protocol_version < 30) {
1564 if (compatible_param_type_2 == CPT2_COLOR)
1565 compatible_param_type_2 = CPT2_NONE;
1566 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1567 compatible_param_type_2 = CPT2_FACEDIR;
1568 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1569 compatible_param_type_2 = CPT2_WALLMOUNTED;
1572 float compatible_visual_scale = visual_scale;
1573 if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1574 compatible_visual_scale = sqrt(visual_scale);
1576 TileDef compatible_tiles[6];
1577 for (u8 i = 0; i < 6; i++) {
1578 compatible_tiles[i] = tiledef[i];
1579 if (tiledef_overlay[i].name != "") {
1580 std::stringstream s;
1581 s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name
1583 compatible_tiles[i].name = s.str();
1588 if (protocol_version < 31) {
1589 writeU8(os, protocol_version < 27 ? 7 : 8);
1591 os << serializeString(name);
1592 writeU16(os, groups.size());
1593 for (ItemGroupList::const_iterator i = groups.begin();
1594 i != groups.end(); ++i) {
1595 os << serializeString(i->first);
1596 writeS16(os, i->second);
1598 writeU8(os, drawtype);
1599 writeF1000(os, compatible_visual_scale);
1601 for (u32 i = 0; i < 6; i++)
1602 compatible_tiles[i].serialize(os, protocol_version);
1603 writeU8(os, CF_SPECIAL_COUNT);
1604 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1605 tiledef_special[i].serialize(os, protocol_version);
1607 writeU8(os, post_effect_color.getAlpha());
1608 writeU8(os, post_effect_color.getRed());
1609 writeU8(os, post_effect_color.getGreen());
1610 writeU8(os, post_effect_color.getBlue());
1611 writeU8(os, param_type);
1612 writeU8(os, compatible_param_type_2);
1613 writeU8(os, is_ground_content);
1614 writeU8(os, light_propagates);
1615 writeU8(os, sunlight_propagates);
1616 writeU8(os, walkable);
1617 writeU8(os, pointable);
1618 writeU8(os, diggable);
1619 writeU8(os, climbable);
1620 writeU8(os, buildable_to);
1621 os << serializeString(""); // legacy: used to be metadata_name
1622 writeU8(os, liquid_type);
1623 os << serializeString(liquid_alternative_flowing);
1624 os << serializeString(liquid_alternative_source);
1625 writeU8(os, liquid_viscosity);
1626 writeU8(os, liquid_renewable);
1627 writeU8(os, light_source);
1628 writeU32(os, damage_per_second);
1629 node_box.serialize(os, protocol_version);
1630 selection_box.serialize(os, protocol_version);
1631 writeU8(os, legacy_facedir_simple);
1632 writeU8(os, legacy_wallmounted);
1633 serializeSimpleSoundSpec(sound_footstep, os, 10);
1634 serializeSimpleSoundSpec(sound_dig, os, 10);
1635 serializeSimpleSoundSpec(sound_dug, os, 10);
1636 writeU8(os, rightclickable);
1637 writeU8(os, drowning);
1638 writeU8(os, leveled);
1639 writeU8(os, liquid_range);
1640 writeU8(os, waving);
1641 os << serializeString(mesh);
1642 collision_box.serialize(os, protocol_version);
1643 writeU8(os, floodable);
1644 writeU16(os, connects_to_ids.size());
1645 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1646 i != connects_to_ids.end(); ++i)
1648 writeU8(os, connect_sides);
1650 throw SerializationError("ContentFeatures::serialize(): "
1651 "Unsupported version requested");
1655 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1657 if (version == 5) // In PROTOCOL_VERSION 13
1659 name = deSerializeString(is);
1661 u32 groups_size = readU16(is);
1662 for(u32 i=0; i<groups_size; i++){
1663 std::string name = deSerializeString(is);
1664 int value = readS16(is);
1665 groups[name] = value;
1667 drawtype = (enum NodeDrawType)readU8(is);
1669 visual_scale = readF1000(is);
1670 if (readU8(is) != 6)
1671 throw SerializationError("unsupported tile count");
1672 for (u32 i = 0; i < 6; i++)
1673 tiledef[i].deSerialize(is, version, drawtype);
1674 if (readU8(is) != CF_SPECIAL_COUNT)
1675 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1676 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1677 tiledef_special[i].deSerialize(is, version, drawtype);
1679 post_effect_color.setAlpha(readU8(is));
1680 post_effect_color.setRed(readU8(is));
1681 post_effect_color.setGreen(readU8(is));
1682 post_effect_color.setBlue(readU8(is));
1683 param_type = (enum ContentParamType)readU8(is);
1684 param_type_2 = (enum ContentParamType2)readU8(is);
1685 is_ground_content = readU8(is);
1686 light_propagates = readU8(is);
1687 sunlight_propagates = readU8(is);
1688 walkable = readU8(is);
1689 pointable = readU8(is);
1690 diggable = readU8(is);
1691 climbable = readU8(is);
1692 buildable_to = readU8(is);
1693 deSerializeString(is); // legacy: used to be metadata_name
1694 liquid_type = (enum LiquidType)readU8(is);
1695 liquid_alternative_flowing = deSerializeString(is);
1696 liquid_alternative_source = deSerializeString(is);
1697 liquid_viscosity = readU8(is);
1698 light_source = readU8(is);
1699 light_source = MYMIN(light_source, LIGHT_MAX);
1700 damage_per_second = readU32(is);
1701 node_box.deSerialize(is);
1702 selection_box.deSerialize(is);
1703 legacy_facedir_simple = readU8(is);
1704 legacy_wallmounted = readU8(is);
1705 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1706 deSerializeSimpleSoundSpec(sound_dig, is, version);
1707 deSerializeSimpleSoundSpec(sound_dug, is, version);
1708 } else if (version == 6) {
1709 name = deSerializeString(is);
1711 u32 groups_size = readU16(is);
1712 for (u32 i = 0; i < groups_size; i++) {
1713 std::string name = deSerializeString(is);
1714 int value = readS16(is);
1715 groups[name] = value;
1717 drawtype = (enum NodeDrawType)readU8(is);
1718 visual_scale = readF1000(is);
1719 if (readU8(is) != 6)
1720 throw SerializationError("unsupported tile count");
1721 for (u32 i = 0; i < 6; i++)
1722 tiledef[i].deSerialize(is, version, drawtype);
1723 // CF_SPECIAL_COUNT in version 6 = 2
1724 if (readU8(is) != 2)
1725 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1726 for (u32 i = 0; i < 2; i++)
1727 tiledef_special[i].deSerialize(is, version, drawtype);
1729 post_effect_color.setAlpha(readU8(is));
1730 post_effect_color.setRed(readU8(is));
1731 post_effect_color.setGreen(readU8(is));
1732 post_effect_color.setBlue(readU8(is));
1733 param_type = (enum ContentParamType)readU8(is);
1734 param_type_2 = (enum ContentParamType2)readU8(is);
1735 is_ground_content = readU8(is);
1736 light_propagates = readU8(is);
1737 sunlight_propagates = readU8(is);
1738 walkable = readU8(is);
1739 pointable = readU8(is);
1740 diggable = readU8(is);
1741 climbable = readU8(is);
1742 buildable_to = readU8(is);
1743 deSerializeString(is); // legacy: used to be metadata_name
1744 liquid_type = (enum LiquidType)readU8(is);
1745 liquid_alternative_flowing = deSerializeString(is);
1746 liquid_alternative_source = deSerializeString(is);
1747 liquid_viscosity = readU8(is);
1748 liquid_renewable = readU8(is);
1749 light_source = readU8(is);
1750 damage_per_second = readU32(is);
1751 node_box.deSerialize(is);
1752 selection_box.deSerialize(is);
1753 legacy_facedir_simple = readU8(is);
1754 legacy_wallmounted = readU8(is);
1755 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1756 deSerializeSimpleSoundSpec(sound_dig, is, version);
1757 deSerializeSimpleSoundSpec(sound_dug, is, version);
1758 rightclickable = readU8(is);
1759 drowning = readU8(is);
1760 leveled = readU8(is);
1761 liquid_range = readU8(is);
1762 } else if (version == 7 || version == 8){
1763 name = deSerializeString(is);
1765 u32 groups_size = readU16(is);
1766 for (u32 i = 0; i < groups_size; i++) {
1767 std::string name = deSerializeString(is);
1768 int value = readS16(is);
1769 groups[name] = value;
1771 drawtype = (enum NodeDrawType) readU8(is);
1773 visual_scale = readF1000(is);
1774 if (readU8(is) != 6)
1775 throw SerializationError("unsupported tile count");
1776 for (u32 i = 0; i < 6; i++)
1777 tiledef[i].deSerialize(is, version, drawtype);
1778 if (readU8(is) != CF_SPECIAL_COUNT)
1779 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1780 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1781 tiledef_special[i].deSerialize(is, version, drawtype);
1783 post_effect_color.setAlpha(readU8(is));
1784 post_effect_color.setRed(readU8(is));
1785 post_effect_color.setGreen(readU8(is));
1786 post_effect_color.setBlue(readU8(is));
1787 param_type = (enum ContentParamType) readU8(is);
1788 param_type_2 = (enum ContentParamType2) readU8(is);
1789 is_ground_content = readU8(is);
1790 light_propagates = readU8(is);
1791 sunlight_propagates = readU8(is);
1792 walkable = readU8(is);
1793 pointable = readU8(is);
1794 diggable = readU8(is);
1795 climbable = readU8(is);
1796 buildable_to = readU8(is);
1797 deSerializeString(is); // legacy: used to be metadata_name
1798 liquid_type = (enum LiquidType) readU8(is);
1799 liquid_alternative_flowing = deSerializeString(is);
1800 liquid_alternative_source = deSerializeString(is);
1801 liquid_viscosity = readU8(is);
1802 liquid_renewable = readU8(is);
1803 light_source = readU8(is);
1804 light_source = MYMIN(light_source, LIGHT_MAX);
1805 damage_per_second = readU32(is);
1806 node_box.deSerialize(is);
1807 selection_box.deSerialize(is);
1808 legacy_facedir_simple = readU8(is);
1809 legacy_wallmounted = readU8(is);
1810 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1811 deSerializeSimpleSoundSpec(sound_dig, is, version);
1812 deSerializeSimpleSoundSpec(sound_dug, is, version);
1813 rightclickable = readU8(is);
1814 drowning = readU8(is);
1815 leveled = readU8(is);
1816 liquid_range = readU8(is);
1817 waving = readU8(is);
1819 mesh = deSerializeString(is);
1820 collision_box.deSerialize(is);
1821 floodable = readU8(is);
1822 u16 connects_to_size = readU16(is);
1823 connects_to_ids.clear();
1824 for (u16 i = 0; i < connects_to_size; i++)
1825 connects_to_ids.insert(readU16(is));
1826 connect_sides = readU8(is);
1827 } catch (SerializationError &e) {};
1829 throw SerializationError("unsupported ContentFeatures version");
1833 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1835 m_node_registration_complete = completed;
1839 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1842 if (m_node_registration_complete)
1843 nr->nodeResolveInternal();
1845 m_pending_resolve_callbacks.push_back(nr);
1849 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1851 size_t len = m_pending_resolve_callbacks.size();
1852 for (size_t i = 0; i != len; i++) {
1853 if (nr != m_pending_resolve_callbacks[i])
1857 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1858 m_pending_resolve_callbacks.resize(len);
1866 void CNodeDefManager::runNodeResolveCallbacks()
1868 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1869 NodeResolver *nr = m_pending_resolve_callbacks[i];
1870 nr->nodeResolveInternal();
1873 m_pending_resolve_callbacks.clear();
1877 void CNodeDefManager::resetNodeResolveState()
1879 m_node_registration_complete = false;
1880 m_pending_resolve_callbacks.clear();
1883 void CNodeDefManager::mapNodeboxConnections()
1885 for (u32 i = 0; i < m_content_features.size(); i++) {
1886 ContentFeatures *f = &m_content_features[i];
1887 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1889 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1890 it != f->connects_to.end(); ++it) {
1891 getIds(*it, f->connects_to_ids);
1896 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1898 const ContentFeatures &f1 = get(from);
1900 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1903 // lookup target in connected set
1904 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1907 const ContentFeatures &f2 = get(to);
1909 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1910 // ignores actually looking if back connection exists
1911 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1913 // does to node declare usable faces?
1914 if (f2.connect_sides > 0) {
1915 if ((f2.param_type_2 == CPT2_FACEDIR ||
1916 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1917 && (connect_face >= 4)) {
1918 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1919 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1921 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1923 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1924 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, 0, 0, 0, 0, 0,
1927 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1928 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1930 return (f2.connect_sides
1931 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1933 return (f2.connect_sides & connect_face);
1935 // the target is just a regular node, so connect no matter back connection
1943 NodeResolver::NodeResolver()
1945 m_nodenames.reserve(16);
1946 m_nnlistsizes.reserve(4);
1950 NodeResolver::~NodeResolver()
1952 if (!m_resolve_done && m_ndef)
1953 m_ndef->cancelNodeResolveCallback(this);
1957 void NodeResolver::nodeResolveInternal()
1959 m_nodenames_idx = 0;
1960 m_nnlistsizes_idx = 0;
1963 m_resolve_done = true;
1965 m_nodenames.clear();
1966 m_nnlistsizes.clear();
1970 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1971 const std::string &node_alt, content_t c_fallback)
1973 if (m_nodenames_idx == m_nodenames.size()) {
1974 *result_out = c_fallback;
1975 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1980 std::string name = m_nodenames[m_nodenames_idx++];
1982 bool success = m_ndef->getId(name, c);
1983 if (!success && node_alt != "") {
1985 success = m_ndef->getId(name, c);
1989 errorstream << "NodeResolver: failed to resolve node name '" << name
1990 << "'." << std::endl;
1999 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
2000 bool all_required, content_t c_fallback)
2002 bool success = true;
2004 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
2005 errorstream << "NodeResolver: no more node lists" << std::endl;
2009 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
2012 if (m_nodenames_idx == m_nodenames.size()) {
2013 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2018 std::string &name = m_nodenames[m_nodenames_idx++];
2020 if (name.substr(0,6) != "group:") {
2021 if (m_ndef->getId(name, c)) {
2022 result_out->push_back(c);
2023 } else if (all_required) {
2024 errorstream << "NodeResolver: failed to resolve node name '"
2025 << name << "'." << std::endl;
2026 result_out->push_back(c_fallback);
2030 std::set<content_t> cids;
2031 std::set<content_t>::iterator it;
2032 m_ndef->getIds(name, cids);
2033 for (it = cids.begin(); it != cids.end(); ++it)
2034 result_out->push_back(*it);