3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "client/tile.h"
27 #include <IMeshManipulator.h>
31 #include "nameidmapping.h"
32 #include "util/numeric.h"
33 #include "util/serialize.h"
34 #include "exceptions.h"
38 #include <fstream> // Used in applyTextureOverrides()
46 type = NODEBOX_REGULAR;
49 // default is sign/ladder-like
50 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
51 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
52 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
53 // no default for other parts
55 connect_bottom.clear();
56 connect_front.clear();
59 connect_right.clear();
62 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
65 if (protocol_version >= 27)
67 else if (protocol_version >= 21)
75 writeU8(os, NODEBOX_FIXED);
79 writeU16(os, fixed.size());
80 for (std::vector<aabb3f>::const_iterator
82 i != fixed.end(); ++i)
84 writeV3F1000(os, i->MinEdge);
85 writeV3F1000(os, i->MaxEdge);
88 case NODEBOX_WALLMOUNTED:
91 writeV3F1000(os, wall_top.MinEdge);
92 writeV3F1000(os, wall_top.MaxEdge);
93 writeV3F1000(os, wall_bottom.MinEdge);
94 writeV3F1000(os, wall_bottom.MaxEdge);
95 writeV3F1000(os, wall_side.MinEdge);
96 writeV3F1000(os, wall_side.MaxEdge);
98 case NODEBOX_CONNECTED:
100 // send old clients nodes that can't be walked through
102 writeU8(os, NODEBOX_FIXED);
105 writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2));
106 writeV3F1000(os, v3f(BS/2, BS/2, BS/2));
110 #define WRITEBOX(box) do { \
111 writeU16(os, (box).size()); \
112 for (std::vector<aabb3f>::const_iterator \
114 i != (box).end(); ++i) { \
115 writeV3F1000(os, i->MinEdge); \
116 writeV3F1000(os, i->MaxEdge); \
120 WRITEBOX(connect_top);
121 WRITEBOX(connect_bottom);
122 WRITEBOX(connect_front);
123 WRITEBOX(connect_left);
124 WRITEBOX(connect_back);
125 WRITEBOX(connect_right);
134 void NodeBox::deSerialize(std::istream &is)
136 int version = readU8(is);
137 if (version < 1 || version > 3)
138 throw SerializationError("unsupported NodeBox version");
142 type = (enum NodeBoxType)readU8(is);
144 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
146 u16 fixed_count = readU16(is);
150 box.MinEdge = readV3F1000(is);
151 box.MaxEdge = readV3F1000(is);
152 fixed.push_back(box);
155 else if(type == NODEBOX_WALLMOUNTED)
157 wall_top.MinEdge = readV3F1000(is);
158 wall_top.MaxEdge = readV3F1000(is);
159 wall_bottom.MinEdge = readV3F1000(is);
160 wall_bottom.MaxEdge = readV3F1000(is);
161 wall_side.MinEdge = readV3F1000(is);
162 wall_side.MaxEdge = readV3F1000(is);
164 else if (type == NODEBOX_CONNECTED)
166 #define READBOXES(box) do { \
167 count = readU16(is); \
168 (box).reserve(count); \
170 v3f min = readV3F1000(is); \
171 v3f max = readV3F1000(is); \
172 (box).push_back(aabb3f(min, max)); }; } while (0)
177 READBOXES(connect_top);
178 READBOXES(connect_bottom);
179 READBOXES(connect_front);
180 READBOXES(connect_left);
181 READBOXES(connect_back);
182 READBOXES(connect_right);
190 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
192 if (protocol_version >= 29)
194 else if (protocol_version >= 26)
196 else if (protocol_version >= 17)
200 os<<serializeString(name);
201 animation.serialize(os, protocol_version);
202 if (protocol_version >= 17)
203 writeU8(os, backface_culling);
204 if (protocol_version >= 26) {
205 writeU8(os, tileable_horizontal);
206 writeU8(os, tileable_vertical);
210 void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
212 int version = readU8(is);
213 name = deSerializeString(is);
214 animation.deSerialize(is, version >= 3 ? 29 : 26);
216 backface_culling = readU8(is);
218 tileable_horizontal = readU8(is);
219 tileable_vertical = readU8(is);
222 if ((contenfeatures_version < 8) &&
223 ((drawtype == NDT_MESH) ||
224 (drawtype == NDT_FIRELIKE) ||
225 (drawtype == NDT_LIQUID) ||
226 (drawtype == NDT_PLANTLIKE)))
227 backface_culling = false;
232 SimpleSoundSpec serialization
235 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
238 os<<serializeString(ss.name);
239 writeF1000(os, ss.gain);
241 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
243 ss.name = deSerializeString(is);
244 ss.gain = readF1000(is);
247 void TextureSettings::readSettings()
249 connected_glass = g_settings->getBool("connected_glass");
250 opaque_water = g_settings->getBool("opaque_water");
251 bool enable_shaders = g_settings->getBool("enable_shaders");
252 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
253 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
254 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
255 enable_minimap = g_settings->getBool("enable_minimap");
256 std::string leaves_style_str = g_settings->get("leaves_style");
258 use_normal_texture = enable_shaders &&
259 (enable_bumpmapping || enable_parallax_occlusion);
260 if (leaves_style_str == "fancy") {
261 leaves_style = LEAVES_FANCY;
262 } else if (leaves_style_str == "simple") {
263 leaves_style = LEAVES_SIMPLE;
265 leaves_style = LEAVES_OPAQUE;
273 ContentFeatures::ContentFeatures()
278 ContentFeatures::~ContentFeatures()
282 void ContentFeatures::reset()
289 visual_solidness = 0;
290 backface_culling = true;
293 has_on_construct = false;
294 has_on_destruct = false;
295 has_after_destruct = false;
299 NOTE: Most of this is always overridden by the default values given
304 // Unknown nodes can be dug
305 groups["dig_immediate"] = 2;
306 drawtype = NDT_NORMAL;
309 for(u32 i = 0; i < 24; i++)
311 minimap_color = video::SColor(0, 0, 0, 0);
314 for(u32 i = 0; i < 6; i++)
315 tiledef[i] = TileDef();
316 for(u16 j = 0; j < CF_SPECIAL_COUNT; j++)
317 tiledef_special[j] = TileDef();
319 post_effect_color = video::SColor(0, 0, 0, 0);
320 param_type = CPT_NONE;
321 param_type_2 = CPT2_NONE;
322 is_ground_content = false;
323 light_propagates = false;
324 sunlight_propagates = false;
329 buildable_to = false;
331 rightclickable = true;
333 liquid_type = LIQUID_NONE;
334 liquid_alternative_flowing = "";
335 liquid_alternative_source = "";
336 liquid_viscosity = 0;
337 liquid_renewable = true;
338 liquid_range = LIQUID_LEVEL_MAX+1;
341 damage_per_second = 0;
342 node_box = NodeBox();
343 selection_box = NodeBox();
344 collision_box = NodeBox();
346 legacy_facedir_simple = false;
347 legacy_wallmounted = false;
348 sound_footstep = SimpleSoundSpec();
349 sound_dig = SimpleSoundSpec("__group");
350 sound_dug = SimpleSoundSpec();
352 connects_to_ids.clear();
356 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
358 if(protocol_version < 24){
359 serializeOld(os, protocol_version);
363 writeU8(os, protocol_version < 27 ? 7 : 8);
365 os<<serializeString(name);
366 writeU16(os, groups.size());
367 for(ItemGroupList::const_iterator
368 i = groups.begin(); i != groups.end(); ++i){
369 os<<serializeString(i->first);
370 writeS16(os, i->second);
372 writeU8(os, drawtype);
373 writeF1000(os, visual_scale);
375 for(u32 i = 0; i < 6; i++)
376 tiledef[i].serialize(os, protocol_version);
377 writeU8(os, CF_SPECIAL_COUNT);
378 for(u32 i = 0; i < CF_SPECIAL_COUNT; i++){
379 tiledef_special[i].serialize(os, protocol_version);
382 writeU8(os, post_effect_color.getAlpha());
383 writeU8(os, post_effect_color.getRed());
384 writeU8(os, post_effect_color.getGreen());
385 writeU8(os, post_effect_color.getBlue());
386 writeU8(os, param_type);
387 if ((protocol_version < 28) && (param_type_2 == CPT2_MESHOPTIONS))
388 writeU8(os, CPT2_NONE);
390 writeU8(os, param_type_2);
391 writeU8(os, is_ground_content);
392 writeU8(os, light_propagates);
393 writeU8(os, sunlight_propagates);
394 writeU8(os, walkable);
395 writeU8(os, pointable);
396 writeU8(os, diggable);
397 writeU8(os, climbable);
398 writeU8(os, buildable_to);
399 os<<serializeString(""); // legacy: used to be metadata_name
400 writeU8(os, liquid_type);
401 os<<serializeString(liquid_alternative_flowing);
402 os<<serializeString(liquid_alternative_source);
403 writeU8(os, liquid_viscosity);
404 writeU8(os, liquid_renewable);
405 writeU8(os, light_source);
406 writeU32(os, damage_per_second);
407 node_box.serialize(os, protocol_version);
408 selection_box.serialize(os, protocol_version);
409 writeU8(os, legacy_facedir_simple);
410 writeU8(os, legacy_wallmounted);
411 serializeSimpleSoundSpec(sound_footstep, os);
412 serializeSimpleSoundSpec(sound_dig, os);
413 serializeSimpleSoundSpec(sound_dug, os);
414 writeU8(os, rightclickable);
415 writeU8(os, drowning);
416 writeU8(os, leveled);
417 writeU8(os, liquid_range);
419 // Stuff below should be moved to correct place in a version that otherwise changes
420 // the protocol version
421 os<<serializeString(mesh);
422 collision_box.serialize(os, protocol_version);
423 writeU8(os, floodable);
424 writeU16(os, connects_to_ids.size());
425 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
426 i != connects_to_ids.end(); ++i)
428 writeU8(os, connect_sides);
431 void ContentFeatures::deSerialize(std::istream &is)
433 int version = readU8(is);
435 deSerializeOld(is, version);
437 } else if (version > 8) {
438 throw SerializationError("unsupported ContentFeatures version");
441 name = deSerializeString(is);
443 u32 groups_size = readU16(is);
444 for(u32 i = 0; i < groups_size; i++){
445 std::string name = deSerializeString(is);
446 int value = readS16(is);
447 groups[name] = value;
449 drawtype = (enum NodeDrawType)readU8(is);
451 visual_scale = readF1000(is);
453 throw SerializationError("unsupported tile count");
454 for(u32 i = 0; i < 6; i++)
455 tiledef[i].deSerialize(is, version, drawtype);
456 if(readU8(is) != CF_SPECIAL_COUNT)
457 throw SerializationError("unsupported CF_SPECIAL_COUNT");
458 for(u32 i = 0; i < CF_SPECIAL_COUNT; i++)
459 tiledef_special[i].deSerialize(is, version, drawtype);
461 post_effect_color.setAlpha(readU8(is));
462 post_effect_color.setRed(readU8(is));
463 post_effect_color.setGreen(readU8(is));
464 post_effect_color.setBlue(readU8(is));
465 param_type = (enum ContentParamType)readU8(is);
466 param_type_2 = (enum ContentParamType2)readU8(is);
467 is_ground_content = readU8(is);
468 light_propagates = readU8(is);
469 sunlight_propagates = readU8(is);
470 walkable = readU8(is);
471 pointable = readU8(is);
472 diggable = readU8(is);
473 climbable = readU8(is);
474 buildable_to = readU8(is);
475 deSerializeString(is); // legacy: used to be metadata_name
476 liquid_type = (enum LiquidType)readU8(is);
477 liquid_alternative_flowing = deSerializeString(is);
478 liquid_alternative_source = deSerializeString(is);
479 liquid_viscosity = readU8(is);
480 liquid_renewable = readU8(is);
481 light_source = readU8(is);
482 light_source = MYMIN(light_source, LIGHT_MAX);
483 damage_per_second = readU32(is);
484 node_box.deSerialize(is);
485 selection_box.deSerialize(is);
486 legacy_facedir_simple = readU8(is);
487 legacy_wallmounted = readU8(is);
488 deSerializeSimpleSoundSpec(sound_footstep, is);
489 deSerializeSimpleSoundSpec(sound_dig, is);
490 deSerializeSimpleSoundSpec(sound_dug, is);
491 rightclickable = readU8(is);
492 drowning = readU8(is);
493 leveled = readU8(is);
494 liquid_range = readU8(is);
496 // If you add anything here, insert it primarily inside the try-catch
497 // block to not need to increase the version.
499 // Stuff below should be moved to correct place in a version that
500 // otherwise changes the protocol version
501 mesh = deSerializeString(is);
502 collision_box.deSerialize(is);
503 floodable = readU8(is);
504 u16 connects_to_size = readU16(is);
505 connects_to_ids.clear();
506 for (u16 i = 0; i < connects_to_size; i++)
507 connects_to_ids.insert(readU16(is));
508 connect_sides = readU8(is);
509 }catch(SerializationError &e) {};
513 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
514 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
515 bool backface_culling, u8 alpha, u8 material_type)
517 tile->shader_id = shader_id;
518 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
520 tile->material_type = material_type;
522 // Normal texture and shader flags texture
523 if (use_normal_texture) {
524 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
526 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
529 tile->material_flags = 0;
530 if (backface_culling)
531 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
532 if (tiledef->animation.type != TAT_NONE)
533 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
534 if (tiledef->tileable_horizontal)
535 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
536 if (tiledef->tileable_vertical)
537 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
539 // Animation parameters
541 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
543 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
544 &frame_count, &frame_length_ms);
545 tile->animation_frame_count = frame_count;
546 tile->animation_frame_length_ms = frame_length_ms;
549 if (frame_count == 1) {
550 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
552 std::ostringstream os(std::ios::binary);
553 tile->frames.resize(frame_count);
555 for (int i = 0; i < frame_count; i++) {
561 tiledef->animation.getTextureModifer(os,
562 tile->texture->getOriginalSize(), i);
564 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
565 if (tile->normal_texture)
566 frame.normal_texture = tsrc->getNormalTexture(os.str());
567 frame.flags_texture = tile->flags_texture;
568 tile->frames[i] = frame;
575 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
576 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
578 // minimap pixel color - the average color of a texture
579 if (tsettings.enable_minimap && tiledef[0].name != "")
580 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
582 // Figure out the actual tiles to use
584 for (u32 j = 0; j < 6; j++) {
585 tdef[j] = tiledef[j];
586 if (tdef[j].name == "")
587 tdef[j].name = "unknown_node.png";
590 bool is_liquid = false;
591 bool is_water_surface = false;
593 u8 material_type = (alpha == 255) ?
594 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
605 assert(liquid_type == LIQUID_SOURCE);
606 if (tsettings.opaque_water)
611 case NDT_FLOWINGLIQUID:
612 assert(liquid_type == LIQUID_FLOWING);
614 if (tsettings.opaque_water)
620 visual_solidness = 1;
622 case NDT_GLASSLIKE_FRAMED:
624 visual_solidness = 1;
626 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
628 visual_solidness = 1;
629 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
633 visual_solidness = 1;
635 case NDT_ALLFACES_OPTIONAL:
636 if (tsettings.leaves_style == LEAVES_FANCY) {
637 drawtype = NDT_ALLFACES;
639 visual_solidness = 1;
640 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
641 for (u32 j = 0; j < 6; j++) {
642 if (tiledef_special[j].name != "")
643 tdef[j].name = tiledef_special[j].name;
645 drawtype = NDT_GLASSLIKE;
647 visual_solidness = 1;
649 drawtype = NDT_NORMAL;
651 for (u32 i = 0; i < 6; i++)
652 tdef[i].name += std::string("^[noalpha");
655 material_type = TILE_MATERIAL_WAVING_LEAVES;
660 material_type = TILE_MATERIAL_WAVING_PLANTS;
678 material_type = (alpha == 255) ?
679 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
680 if (name == "default:water_source")
681 is_water_surface = true;
685 for (u16 j = 0; j < 6; j++) {
686 tile_shader[j] = shdsrc->getShader("nodes_shader",
687 material_type, drawtype);
690 if (is_water_surface) {
691 tile_shader[0] = shdsrc->getShader("water_surface_shader",
692 material_type, drawtype);
695 // Tiles (fill in f->tiles[])
696 for (u16 j = 0; j < 6; j++) {
697 fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j],
698 tsettings.use_normal_texture,
699 tiledef[j].backface_culling, alpha, material_type);
702 // Special tiles (fill in f->special_tiles[])
703 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
704 fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j],
705 tile_shader[j], tsettings.use_normal_texture,
706 tiledef_special[j].backface_culling, alpha, material_type);
709 if ((drawtype == NDT_MESH) && (mesh != "")) {
711 // Read the mesh and apply scale
712 mesh_ptr[0] = client->getMesh(mesh);
714 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
715 scaleMesh(mesh_ptr[0], scale);
716 recalculateBoundingBox(mesh_ptr[0]);
717 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
719 } else if ((drawtype == NDT_NODEBOX) &&
720 ((node_box.type == NODEBOX_REGULAR) ||
721 (node_box.type == NODEBOX_FIXED)) &&
722 (!node_box.fixed.empty())) {
723 //Convert regular nodebox nodes to meshnodes
724 //Change the drawtype and apply scale
726 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
727 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
728 scaleMesh(mesh_ptr[0], scale);
729 recalculateBoundingBox(mesh_ptr[0]);
730 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
733 //Cache 6dfacedir and wallmounted rotated clones of meshes
734 if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_FACEDIR)) {
735 for (u16 j = 1; j < 24; j++) {
736 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
737 rotateMeshBy6dFacedir(mesh_ptr[j], j);
738 recalculateBoundingBox(mesh_ptr[j]);
739 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
741 } else if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_WALLMOUNTED)) {
742 static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
743 for (u16 j = 1; j < 6; j++) {
744 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
745 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
746 recalculateBoundingBox(mesh_ptr[j]);
747 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
749 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
750 recalculateBoundingBox(mesh_ptr[0]);
751 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
760 class CNodeDefManager: public IWritableNodeDefManager {
763 virtual ~CNodeDefManager();
765 virtual IWritableNodeDefManager *clone();
766 inline virtual const ContentFeatures& get(content_t c) const;
767 inline virtual const ContentFeatures& get(const MapNode &n) const;
768 virtual bool getId(const std::string &name, content_t &result) const;
769 virtual content_t getId(const std::string &name) const;
770 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
771 virtual const ContentFeatures& get(const std::string &name) const;
772 content_t allocateId();
773 virtual content_t set(const std::string &name, const ContentFeatures &def);
774 virtual content_t allocateDummy(const std::string &name);
775 virtual void removeNode(const std::string &name);
776 virtual void updateAliases(IItemDefManager *idef);
777 virtual void applyTextureOverrides(const std::string &override_filepath);
778 virtual void updateTextures(IGameDef *gamedef,
779 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
780 void *progress_cbk_args);
781 void serialize(std::ostream &os, u16 protocol_version) const;
782 void deSerialize(std::istream &is);
784 inline virtual bool getNodeRegistrationStatus() const;
785 inline virtual void setNodeRegistrationStatus(bool completed);
787 virtual void pendNodeResolve(NodeResolver *nr);
788 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
789 virtual void runNodeResolveCallbacks();
790 virtual void resetNodeResolveState();
791 virtual void mapNodeboxConnections();
792 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
793 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
795 return m_selection_box_int_union;
799 void addNameIdMapping(content_t i, std::string name);
801 * Recalculates m_selection_box_int_union based on
802 * m_selection_box_union.
804 void fixSelectionBoxIntUnion();
806 // Features indexed by id
807 std::vector<ContentFeatures> m_content_features;
809 // A mapping for fast converting back and forth between names and ids
810 NameIdMapping m_name_id_mapping;
812 // Like m_name_id_mapping, but only from names to ids, and includes
813 // item aliases too. Updated by updateAliases()
814 // Note: Not serialized.
816 UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
818 // A mapping from groups to a list of content_ts (and their levels)
819 // that belong to it. Necessary for a direct lookup in getIds().
820 // Note: Not serialized.
821 UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
823 // Next possibly free id
826 // NodeResolvers to callback once node registration has ended
827 std::vector<NodeResolver *> m_pending_resolve_callbacks;
829 // True when all nodes have been registered
830 bool m_node_registration_complete;
832 //! The union of all nodes' selection boxes.
833 aabb3f m_selection_box_union;
835 * The smallest box in node coordinates that
836 * contains all nodes' selection boxes.
838 core::aabbox3d<s16> m_selection_box_int_union;
842 CNodeDefManager::CNodeDefManager()
848 CNodeDefManager::~CNodeDefManager()
851 for (u32 i = 0; i < m_content_features.size(); i++) {
852 ContentFeatures *f = &m_content_features[i];
853 for (u32 j = 0; j < 24; j++) {
855 f->mesh_ptr[j]->drop();
862 void CNodeDefManager::clear()
864 m_content_features.clear();
865 m_name_id_mapping.clear();
866 m_name_id_mapping_with_aliases.clear();
867 m_group_to_items.clear();
869 m_selection_box_union.reset(0,0,0);
870 m_selection_box_int_union.reset(0,0,0);
872 resetNodeResolveState();
874 u32 initial_length = 0;
875 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
876 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
877 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
878 m_content_features.resize(initial_length);
880 // Set CONTENT_UNKNOWN
884 // Insert directly into containers
885 content_t c = CONTENT_UNKNOWN;
886 m_content_features[c] = f;
887 addNameIdMapping(c, f.name);
894 f.drawtype = NDT_AIRLIKE;
895 f.param_type = CPT_LIGHT;
896 f.light_propagates = true;
897 f.sunlight_propagates = true;
901 f.buildable_to = true;
903 f.is_ground_content = true;
904 // Insert directly into containers
905 content_t c = CONTENT_AIR;
906 m_content_features[c] = f;
907 addNameIdMapping(c, f.name);
910 // Set CONTENT_IGNORE
914 f.drawtype = NDT_AIRLIKE;
915 f.param_type = CPT_NONE;
916 f.light_propagates = false;
917 f.sunlight_propagates = false;
921 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
922 f.is_ground_content = true;
923 // Insert directly into containers
924 content_t c = CONTENT_IGNORE;
925 m_content_features[c] = f;
926 addNameIdMapping(c, f.name);
931 IWritableNodeDefManager *CNodeDefManager::clone()
933 CNodeDefManager *mgr = new CNodeDefManager();
939 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
941 return c < m_content_features.size()
942 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
946 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
948 return get(n.getContent());
952 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
954 UNORDERED_MAP<std::string, content_t>::const_iterator
955 i = m_name_id_mapping_with_aliases.find(name);
956 if(i == m_name_id_mapping_with_aliases.end())
963 content_t CNodeDefManager::getId(const std::string &name) const
965 content_t id = CONTENT_IGNORE;
971 bool CNodeDefManager::getIds(const std::string &name,
972 std::set<content_t> &result) const
974 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
975 if (name.substr(0,6) != "group:") {
976 content_t id = CONTENT_IGNORE;
977 bool exists = getId(name, id);
982 std::string group = name.substr(6);
984 UNORDERED_MAP<std::string, GroupItems>::const_iterator
985 i = m_group_to_items.find(group);
986 if (i == m_group_to_items.end())
989 const GroupItems &items = i->second;
990 for (GroupItems::const_iterator j = items.begin();
991 j != items.end(); ++j) {
992 if ((*j).second != 0)
993 result.insert((*j).first);
995 //printf("getIds: %dus\n", t.stop());
1000 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1002 content_t id = CONTENT_UNKNOWN;
1008 // returns CONTENT_IGNORE if no free ID found
1009 content_t CNodeDefManager::allocateId()
1011 for (content_t id = m_next_id;
1012 id >= m_next_id; // overflow?
1014 while (id >= m_content_features.size()) {
1015 m_content_features.push_back(ContentFeatures());
1017 const ContentFeatures &f = m_content_features[id];
1023 // If we arrive here, an overflow occurred in id.
1024 // That means no ID was found
1025 return CONTENT_IGNORE;
1030 * Returns the smallest box that contains all boxes
1031 * in the vector. Box_union is expanded.
1032 * @param[in] boxes the vector containing the boxes
1033 * @param[in, out] box_union the union of the arguments
1035 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1037 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1038 it != boxes.end(); ++it) {
1039 box_union->addInternalBox(*it);
1045 * Returns a box that contains the nodebox in every case.
1046 * The argument node_union is expanded.
1047 * @param[in] nodebox the nodebox to be measured
1048 * @param[in] features used to decide whether the nodebox
1050 * @param[in, out] box_union the union of the arguments
1052 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1055 switch(nodebox.type) {
1057 case NODEBOX_LEVELED: {
1059 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1060 boxVectorUnion(nodebox.fixed, &half_processed);
1061 // Set leveled boxes to maximal
1062 if (nodebox.type == NODEBOX_LEVELED) {
1063 half_processed.MaxEdge.Y = +BS / 2;
1065 if (features.param_type_2 == CPT2_FACEDIR) {
1066 // Get maximal coordinate
1068 fabsf(half_processed.MinEdge.X),
1069 fabsf(half_processed.MinEdge.Y),
1070 fabsf(half_processed.MinEdge.Z),
1071 fabsf(half_processed.MaxEdge.X),
1072 fabsf(half_processed.MaxEdge.Y),
1073 fabsf(half_processed.MaxEdge.Z) };
1075 for (int i = 0; i < 6; i++) {
1076 if (max < coords[i]) {
1080 // Add the union of all possible rotated boxes
1081 box_union->addInternalPoint(-max, -max, -max);
1082 box_union->addInternalPoint(+max, +max, +max);
1084 box_union->addInternalBox(half_processed);
1088 case NODEBOX_WALLMOUNTED: {
1090 box_union->addInternalBox(nodebox.wall_top);
1091 box_union->addInternalBox(nodebox.wall_bottom);
1092 // Find maximal coordinate in the X-Z plane
1094 fabsf(nodebox.wall_side.MinEdge.X),
1095 fabsf(nodebox.wall_side.MinEdge.Z),
1096 fabsf(nodebox.wall_side.MaxEdge.X),
1097 fabsf(nodebox.wall_side.MaxEdge.Z) };
1099 for (int i = 0; i < 4; i++) {
1100 if (max < coords[i]) {
1104 // Add the union of all possible rotated boxes
1105 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1106 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1109 case NODEBOX_CONNECTED: {
1110 // Add all possible connected boxes
1111 boxVectorUnion(nodebox.fixed, box_union);
1112 boxVectorUnion(nodebox.connect_top, box_union);
1113 boxVectorUnion(nodebox.connect_bottom, box_union);
1114 boxVectorUnion(nodebox.connect_front, box_union);
1115 boxVectorUnion(nodebox.connect_left, box_union);
1116 boxVectorUnion(nodebox.connect_back, box_union);
1117 boxVectorUnion(nodebox.connect_right, box_union);
1122 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1123 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1129 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1131 m_selection_box_int_union.MinEdge.X = floorf(
1132 m_selection_box_union.MinEdge.X / BS + 0.5f);
1133 m_selection_box_int_union.MinEdge.Y = floorf(
1134 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1135 m_selection_box_int_union.MinEdge.Z = floorf(
1136 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1137 m_selection_box_int_union.MaxEdge.X = ceilf(
1138 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1139 m_selection_box_int_union.MaxEdge.Y = ceilf(
1140 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1141 m_selection_box_int_union.MaxEdge.Z = ceilf(
1142 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1146 // IWritableNodeDefManager
1147 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1151 assert(name == def.name);
1153 // Don't allow redefining ignore (but allow air and unknown)
1154 if (name == "ignore") {
1155 warningstream << "NodeDefManager: Ignoring "
1156 "CONTENT_IGNORE redefinition"<<std::endl;
1157 return CONTENT_IGNORE;
1160 content_t id = CONTENT_IGNORE;
1161 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1164 if (id == CONTENT_IGNORE) {
1165 warningstream << "NodeDefManager: Absolute "
1166 "limit reached" << std::endl;
1167 return CONTENT_IGNORE;
1169 assert(id != CONTENT_IGNORE);
1170 addNameIdMapping(id, name);
1172 m_content_features[id] = def;
1173 verbosestream << "NodeDefManager: registering content id \"" << id
1174 << "\": name=\"" << def.name << "\""<<std::endl;
1176 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1177 fixSelectionBoxIntUnion();
1178 // Add this content to the list of all groups it belongs to
1179 // FIXME: This should remove a node from groups it no longer
1180 // belongs to when a node is re-registered
1181 for (ItemGroupList::const_iterator i = def.groups.begin();
1182 i != def.groups.end(); ++i) {
1183 std::string group_name = i->first;
1185 UNORDERED_MAP<std::string, GroupItems>::iterator
1186 j = m_group_to_items.find(group_name);
1187 if (j == m_group_to_items.end()) {
1188 m_group_to_items[group_name].push_back(
1189 std::make_pair(id, i->second));
1191 GroupItems &items = j->second;
1192 items.push_back(std::make_pair(id, i->second));
1199 content_t CNodeDefManager::allocateDummy(const std::string &name)
1201 assert(name != ""); // Pre-condition
1204 return set(name, f);
1208 void CNodeDefManager::removeNode(const std::string &name)
1213 // Erase name from name ID mapping
1214 content_t id = CONTENT_IGNORE;
1215 if (m_name_id_mapping.getId(name, id)) {
1216 m_name_id_mapping.eraseName(name);
1217 m_name_id_mapping_with_aliases.erase(name);
1220 // Erase node content from all groups it belongs to
1221 for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1222 m_group_to_items.begin();
1223 iter_groups != m_group_to_items.end();) {
1224 GroupItems &items = iter_groups->second;
1225 for (GroupItems::iterator iter_groupitems = items.begin();
1226 iter_groupitems != items.end();) {
1227 if (iter_groupitems->first == id)
1228 items.erase(iter_groupitems++);
1233 // Check if group is empty
1234 if (items.size() == 0)
1235 m_group_to_items.erase(iter_groups++);
1242 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1244 std::set<std::string> all = idef->getAll();
1245 m_name_id_mapping_with_aliases.clear();
1246 for (std::set<std::string>::iterator
1247 i = all.begin(); i != all.end(); ++i) {
1248 std::string name = *i;
1249 std::string convert_to = idef->getAlias(name);
1251 if (m_name_id_mapping.getId(convert_to, id)) {
1252 m_name_id_mapping_with_aliases.insert(
1253 std::make_pair(name, id));
1258 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1260 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1261 "overrides to textures from " << override_filepath << std::endl;
1263 std::ifstream infile(override_filepath.c_str());
1266 while (std::getline(infile, line)) {
1268 if (trim(line) == "")
1270 std::vector<std::string> splitted = str_split(line, ' ');
1271 if (splitted.size() != 3) {
1272 errorstream << override_filepath
1273 << ":" << line_c << " Could not apply texture override \""
1274 << line << "\": Syntax error" << std::endl;
1279 if (!getId(splitted[0], id))
1280 continue; // Ignore unknown node
1282 ContentFeatures &nodedef = m_content_features[id];
1284 if (splitted[1] == "top")
1285 nodedef.tiledef[0].name = splitted[2];
1286 else if (splitted[1] == "bottom")
1287 nodedef.tiledef[1].name = splitted[2];
1288 else if (splitted[1] == "right")
1289 nodedef.tiledef[2].name = splitted[2];
1290 else if (splitted[1] == "left")
1291 nodedef.tiledef[3].name = splitted[2];
1292 else if (splitted[1] == "back")
1293 nodedef.tiledef[4].name = splitted[2];
1294 else if (splitted[1] == "front")
1295 nodedef.tiledef[5].name = splitted[2];
1296 else if (splitted[1] == "all" || splitted[1] == "*")
1297 for (int i = 0; i < 6; i++)
1298 nodedef.tiledef[i].name = splitted[2];
1299 else if (splitted[1] == "sides")
1300 for (int i = 2; i < 6; i++)
1301 nodedef.tiledef[i].name = splitted[2];
1303 errorstream << override_filepath
1304 << ":" << line_c << " Could not apply texture override \""
1305 << line << "\": Unknown node side \""
1306 << splitted[1] << "\"" << std::endl;
1312 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1313 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1314 void *progress_callback_args)
1317 infostream << "CNodeDefManager::updateTextures(): Updating "
1318 "textures in node definitions" << std::endl;
1320 Client *client = (Client *)gamedef;
1321 ITextureSource *tsrc = client->tsrc();
1322 IShaderSource *shdsrc = client->getShaderSource();
1323 scene::ISceneManager* smgr = client->getSceneManager();
1324 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1325 TextureSettings tsettings;
1326 tsettings.readSettings();
1328 u32 size = m_content_features.size();
1330 for (u32 i = 0; i < size; i++) {
1331 m_content_features[i].updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1332 progress_callback(progress_callback_args, i, size);
1337 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1339 writeU8(os, 1); // version
1341 std::ostringstream os2(std::ios::binary);
1342 for (u32 i = 0; i < m_content_features.size(); i++) {
1343 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1344 || i == CONTENT_UNKNOWN)
1346 const ContentFeatures *f = &m_content_features[i];
1350 // Wrap it in a string to allow different lengths without
1351 // strict version incompatibilities
1352 std::ostringstream wrapper_os(std::ios::binary);
1353 f->serialize(wrapper_os, protocol_version);
1354 os2<<serializeString(wrapper_os.str());
1356 // must not overflow
1357 u16 next = count + 1;
1358 FATAL_ERROR_IF(next < count, "Overflow");
1361 writeU16(os, count);
1362 os << serializeLongString(os2.str());
1366 void CNodeDefManager::deSerialize(std::istream &is)
1369 int version = readU8(is);
1371 throw SerializationError("unsupported NodeDefinitionManager version");
1372 u16 count = readU16(is);
1373 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1375 for (u16 n = 0; n < count; n++) {
1376 u16 i = readU16(is2);
1378 // Read it from the string wrapper
1379 std::string wrapper = deSerializeString(is2);
1380 std::istringstream wrapper_is(wrapper, std::ios::binary);
1381 f.deSerialize(wrapper_is);
1383 // Check error conditions
1384 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1385 warningstream << "NodeDefManager::deSerialize(): "
1386 "not changing builtin node " << i << std::endl;
1390 warningstream << "NodeDefManager::deSerialize(): "
1391 "received empty name" << std::endl;
1397 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1398 warningstream << "NodeDefManager::deSerialize(): "
1399 "already defined with different ID: " << f.name << std::endl;
1403 // All is ok, add node definition with the requested ID
1404 if (i >= m_content_features.size())
1405 m_content_features.resize((u32)(i) + 1);
1406 m_content_features[i] = f;
1407 addNameIdMapping(i, f.name);
1408 verbosestream << "deserialized " << f.name << std::endl;
1410 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1411 fixSelectionBoxIntUnion();
1416 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1418 m_name_id_mapping.set(i, name);
1419 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1423 IWritableNodeDefManager *createNodeDefManager()
1425 return new CNodeDefManager();
1429 //// Serialization of old ContentFeatures formats
1430 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1432 if (protocol_version == 13)
1434 writeU8(os, 5); // version
1435 os<<serializeString(name);
1436 writeU16(os, groups.size());
1437 for (ItemGroupList::const_iterator
1438 i = groups.begin(); i != groups.end(); ++i) {
1439 os<<serializeString(i->first);
1440 writeS16(os, i->second);
1442 writeU8(os, drawtype);
1443 writeF1000(os, visual_scale);
1445 for (u32 i = 0; i < 6; i++)
1446 tiledef[i].serialize(os, protocol_version);
1447 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1449 for (u32 i = 0; i < 2; i++)
1450 tiledef_special[i].serialize(os, protocol_version);
1452 writeU8(os, post_effect_color.getAlpha());
1453 writeU8(os, post_effect_color.getRed());
1454 writeU8(os, post_effect_color.getGreen());
1455 writeU8(os, post_effect_color.getBlue());
1456 writeU8(os, param_type);
1457 writeU8(os, param_type_2);
1458 writeU8(os, is_ground_content);
1459 writeU8(os, light_propagates);
1460 writeU8(os, sunlight_propagates);
1461 writeU8(os, walkable);
1462 writeU8(os, pointable);
1463 writeU8(os, diggable);
1464 writeU8(os, climbable);
1465 writeU8(os, buildable_to);
1466 os<<serializeString(""); // legacy: used to be metadata_name
1467 writeU8(os, liquid_type);
1468 os<<serializeString(liquid_alternative_flowing);
1469 os<<serializeString(liquid_alternative_source);
1470 writeU8(os, liquid_viscosity);
1471 writeU8(os, light_source);
1472 writeU32(os, damage_per_second);
1473 node_box.serialize(os, protocol_version);
1474 selection_box.serialize(os, protocol_version);
1475 writeU8(os, legacy_facedir_simple);
1476 writeU8(os, legacy_wallmounted);
1477 serializeSimpleSoundSpec(sound_footstep, os);
1478 serializeSimpleSoundSpec(sound_dig, os);
1479 serializeSimpleSoundSpec(sound_dug, os);
1481 else if (protocol_version > 13 && protocol_version < 24) {
1482 writeU8(os, 6); // version
1483 os<<serializeString(name);
1484 writeU16(os, groups.size());
1485 for (ItemGroupList::const_iterator
1486 i = groups.begin(); i != groups.end(); ++i) {
1487 os<<serializeString(i->first);
1488 writeS16(os, i->second);
1490 writeU8(os, drawtype);
1491 writeF1000(os, visual_scale);
1493 for (u32 i = 0; i < 6; i++)
1494 tiledef[i].serialize(os, protocol_version);
1495 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1497 for (u32 i = 0; i < 2; i++)
1498 tiledef_special[i].serialize(os, protocol_version);
1500 writeU8(os, post_effect_color.getAlpha());
1501 writeU8(os, post_effect_color.getRed());
1502 writeU8(os, post_effect_color.getGreen());
1503 writeU8(os, post_effect_color.getBlue());
1504 writeU8(os, param_type);
1505 writeU8(os, param_type_2);
1506 writeU8(os, is_ground_content);
1507 writeU8(os, light_propagates);
1508 writeU8(os, sunlight_propagates);
1509 writeU8(os, walkable);
1510 writeU8(os, pointable);
1511 writeU8(os, diggable);
1512 writeU8(os, climbable);
1513 writeU8(os, buildable_to);
1514 os<<serializeString(""); // legacy: used to be metadata_name
1515 writeU8(os, liquid_type);
1516 os<<serializeString(liquid_alternative_flowing);
1517 os<<serializeString(liquid_alternative_source);
1518 writeU8(os, liquid_viscosity);
1519 writeU8(os, liquid_renewable);
1520 writeU8(os, light_source);
1521 writeU32(os, damage_per_second);
1522 node_box.serialize(os, protocol_version);
1523 selection_box.serialize(os, protocol_version);
1524 writeU8(os, legacy_facedir_simple);
1525 writeU8(os, legacy_wallmounted);
1526 serializeSimpleSoundSpec(sound_footstep, os);
1527 serializeSimpleSoundSpec(sound_dig, os);
1528 serializeSimpleSoundSpec(sound_dug, os);
1529 writeU8(os, rightclickable);
1530 writeU8(os, drowning);
1531 writeU8(os, leveled);
1532 writeU8(os, liquid_range);
1534 throw SerializationError("ContentFeatures::serialize(): "
1535 "Unsupported version requested");
1538 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1540 if (version == 5) // In PROTOCOL_VERSION 13
1542 name = deSerializeString(is);
1544 u32 groups_size = readU16(is);
1545 for(u32 i=0; i<groups_size; i++){
1546 std::string name = deSerializeString(is);
1547 int value = readS16(is);
1548 groups[name] = value;
1550 drawtype = (enum NodeDrawType)readU8(is);
1552 visual_scale = readF1000(is);
1553 if (readU8(is) != 6)
1554 throw SerializationError("unsupported tile count");
1555 for (u32 i = 0; i < 6; i++)
1556 tiledef[i].deSerialize(is, version, drawtype);
1557 if (readU8(is) != CF_SPECIAL_COUNT)
1558 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1559 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1560 tiledef_special[i].deSerialize(is, version, drawtype);
1562 post_effect_color.setAlpha(readU8(is));
1563 post_effect_color.setRed(readU8(is));
1564 post_effect_color.setGreen(readU8(is));
1565 post_effect_color.setBlue(readU8(is));
1566 param_type = (enum ContentParamType)readU8(is);
1567 param_type_2 = (enum ContentParamType2)readU8(is);
1568 is_ground_content = readU8(is);
1569 light_propagates = readU8(is);
1570 sunlight_propagates = readU8(is);
1571 walkable = readU8(is);
1572 pointable = readU8(is);
1573 diggable = readU8(is);
1574 climbable = readU8(is);
1575 buildable_to = readU8(is);
1576 deSerializeString(is); // legacy: used to be metadata_name
1577 liquid_type = (enum LiquidType)readU8(is);
1578 liquid_alternative_flowing = deSerializeString(is);
1579 liquid_alternative_source = deSerializeString(is);
1580 liquid_viscosity = readU8(is);
1581 light_source = readU8(is);
1582 light_source = MYMIN(light_source, LIGHT_MAX);
1583 damage_per_second = readU32(is);
1584 node_box.deSerialize(is);
1585 selection_box.deSerialize(is);
1586 legacy_facedir_simple = readU8(is);
1587 legacy_wallmounted = readU8(is);
1588 deSerializeSimpleSoundSpec(sound_footstep, is);
1589 deSerializeSimpleSoundSpec(sound_dig, is);
1590 deSerializeSimpleSoundSpec(sound_dug, is);
1591 } else if (version == 6) {
1592 name = deSerializeString(is);
1594 u32 groups_size = readU16(is);
1595 for (u32 i = 0; i < groups_size; i++) {
1596 std::string name = deSerializeString(is);
1597 int value = readS16(is);
1598 groups[name] = value;
1600 drawtype = (enum NodeDrawType)readU8(is);
1601 visual_scale = readF1000(is);
1602 if (readU8(is) != 6)
1603 throw SerializationError("unsupported tile count");
1604 for (u32 i = 0; i < 6; i++)
1605 tiledef[i].deSerialize(is, version, drawtype);
1606 // CF_SPECIAL_COUNT in version 6 = 2
1607 if (readU8(is) != 2)
1608 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1609 for (u32 i = 0; i < 2; i++)
1610 tiledef_special[i].deSerialize(is, version, drawtype);
1612 post_effect_color.setAlpha(readU8(is));
1613 post_effect_color.setRed(readU8(is));
1614 post_effect_color.setGreen(readU8(is));
1615 post_effect_color.setBlue(readU8(is));
1616 param_type = (enum ContentParamType)readU8(is);
1617 param_type_2 = (enum ContentParamType2)readU8(is);
1618 is_ground_content = readU8(is);
1619 light_propagates = readU8(is);
1620 sunlight_propagates = readU8(is);
1621 walkable = readU8(is);
1622 pointable = readU8(is);
1623 diggable = readU8(is);
1624 climbable = readU8(is);
1625 buildable_to = readU8(is);
1626 deSerializeString(is); // legacy: used to be metadata_name
1627 liquid_type = (enum LiquidType)readU8(is);
1628 liquid_alternative_flowing = deSerializeString(is);
1629 liquid_alternative_source = deSerializeString(is);
1630 liquid_viscosity = readU8(is);
1631 liquid_renewable = readU8(is);
1632 light_source = readU8(is);
1633 damage_per_second = readU32(is);
1634 node_box.deSerialize(is);
1635 selection_box.deSerialize(is);
1636 legacy_facedir_simple = readU8(is);
1637 legacy_wallmounted = readU8(is);
1638 deSerializeSimpleSoundSpec(sound_footstep, is);
1639 deSerializeSimpleSoundSpec(sound_dig, is);
1640 deSerializeSimpleSoundSpec(sound_dug, is);
1641 rightclickable = readU8(is);
1642 drowning = readU8(is);
1643 leveled = readU8(is);
1644 liquid_range = readU8(is);
1646 throw SerializationError("unsupported ContentFeatures version");
1651 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1653 return m_node_registration_complete;
1657 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1659 m_node_registration_complete = completed;
1663 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1666 if (m_node_registration_complete)
1667 nr->nodeResolveInternal();
1669 m_pending_resolve_callbacks.push_back(nr);
1673 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1675 size_t len = m_pending_resolve_callbacks.size();
1676 for (size_t i = 0; i != len; i++) {
1677 if (nr != m_pending_resolve_callbacks[i])
1681 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1682 m_pending_resolve_callbacks.resize(len);
1690 void CNodeDefManager::runNodeResolveCallbacks()
1692 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1693 NodeResolver *nr = m_pending_resolve_callbacks[i];
1694 nr->nodeResolveInternal();
1697 m_pending_resolve_callbacks.clear();
1701 void CNodeDefManager::resetNodeResolveState()
1703 m_node_registration_complete = false;
1704 m_pending_resolve_callbacks.clear();
1707 void CNodeDefManager::mapNodeboxConnections()
1709 for (u32 i = 0; i < m_content_features.size(); i++) {
1710 ContentFeatures *f = &m_content_features[i];
1711 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1713 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1714 it != f->connects_to.end(); ++it) {
1715 getIds(*it, f->connects_to_ids);
1720 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1722 const ContentFeatures &f1 = get(from);
1724 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1727 // lookup target in connected set
1728 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1731 const ContentFeatures &f2 = get(to);
1733 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1734 // ignores actually looking if back connection exists
1735 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1737 // does to node declare usable faces?
1738 if (f2.connect_sides > 0) {
1739 if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) {
1740 static const u8 rot[33 * 4] = {
1741 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1742 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back
1743 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right
1744 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1745 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front
1746 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1747 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1748 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1749 32, 16, 8, 4 // 32 - left
1751 return (f2.connect_sides & rot[(connect_face * 4) + to.param2]);
1753 return (f2.connect_sides & connect_face);
1755 // the target is just a regular node, so connect no matter back connection
1763 NodeResolver::NodeResolver()
1766 m_nodenames_idx = 0;
1767 m_nnlistsizes_idx = 0;
1768 m_resolve_done = false;
1770 m_nodenames.reserve(16);
1771 m_nnlistsizes.reserve(4);
1775 NodeResolver::~NodeResolver()
1777 if (!m_resolve_done && m_ndef)
1778 m_ndef->cancelNodeResolveCallback(this);
1782 void NodeResolver::nodeResolveInternal()
1784 m_nodenames_idx = 0;
1785 m_nnlistsizes_idx = 0;
1788 m_resolve_done = true;
1790 m_nodenames.clear();
1791 m_nnlistsizes.clear();
1795 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1796 const std::string &node_alt, content_t c_fallback)
1798 if (m_nodenames_idx == m_nodenames.size()) {
1799 *result_out = c_fallback;
1800 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1805 std::string name = m_nodenames[m_nodenames_idx++];
1807 bool success = m_ndef->getId(name, c);
1808 if (!success && node_alt != "") {
1810 success = m_ndef->getId(name, c);
1814 errorstream << "NodeResolver: failed to resolve node name '" << name
1815 << "'." << std::endl;
1824 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1825 bool all_required, content_t c_fallback)
1827 bool success = true;
1829 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1830 errorstream << "NodeResolver: no more node lists" << std::endl;
1834 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1837 if (m_nodenames_idx == m_nodenames.size()) {
1838 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1843 std::string &name = m_nodenames[m_nodenames_idx++];
1845 if (name.substr(0,6) != "group:") {
1846 if (m_ndef->getId(name, c)) {
1847 result_out->push_back(c);
1848 } else if (all_required) {
1849 errorstream << "NodeResolver: failed to resolve node name '"
1850 << name << "'." << std::endl;
1851 result_out->push_back(c_fallback);
1855 std::set<content_t> cids;
1856 std::set<content_t>::iterator it;
1857 m_ndef->getIds(name, cids);
1858 for (it = cids.begin(); it != cids.end(); ++it)
1859 result_out->push_back(*it);