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 >= 30)
194 else if (protocol_version >= 29)
196 else if (protocol_version >= 26)
198 else if (protocol_version >= 17)
202 os<<serializeString(name);
203 animation.serialize(os, protocol_version);
204 if (protocol_version >= 17)
205 writeU8(os, backface_culling);
206 if (protocol_version >= 26) {
207 writeU8(os, tileable_horizontal);
208 writeU8(os, tileable_vertical);
210 if (protocol_version >= 30) {
211 writeU8(os, has_color);
213 writeU8(os, color.getRed());
214 writeU8(os, color.getGreen());
215 writeU8(os, color.getBlue());
220 void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
222 int version = readU8(is);
223 name = deSerializeString(is);
224 animation.deSerialize(is, version >= 3 ? 29 : 26);
226 backface_culling = readU8(is);
228 tileable_horizontal = readU8(is);
229 tileable_vertical = readU8(is);
232 has_color = readU8(is);
234 color.setRed(readU8(is));
235 color.setGreen(readU8(is));
236 color.setBlue(readU8(is));
240 if ((contenfeatures_version < 8) &&
241 ((drawtype == NDT_MESH) ||
242 (drawtype == NDT_FIRELIKE) ||
243 (drawtype == NDT_LIQUID) ||
244 (drawtype == NDT_PLANTLIKE)))
245 backface_culling = false;
250 SimpleSoundSpec serialization
253 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
256 os<<serializeString(ss.name);
257 writeF1000(os, ss.gain);
259 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
261 ss.name = deSerializeString(is);
262 ss.gain = readF1000(is);
265 void TextureSettings::readSettings()
267 connected_glass = g_settings->getBool("connected_glass");
268 opaque_water = g_settings->getBool("opaque_water");
269 bool enable_shaders = g_settings->getBool("enable_shaders");
270 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
271 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
272 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
273 enable_minimap = g_settings->getBool("enable_minimap");
274 std::string leaves_style_str = g_settings->get("leaves_style");
276 use_normal_texture = enable_shaders &&
277 (enable_bumpmapping || enable_parallax_occlusion);
278 if (leaves_style_str == "fancy") {
279 leaves_style = LEAVES_FANCY;
280 } else if (leaves_style_str == "simple") {
281 leaves_style = LEAVES_SIMPLE;
283 leaves_style = LEAVES_OPAQUE;
291 ContentFeatures::ContentFeatures()
296 ContentFeatures::~ContentFeatures()
300 void ContentFeatures::reset()
307 visual_solidness = 0;
308 backface_culling = true;
311 has_on_construct = false;
312 has_on_destruct = false;
313 has_after_destruct = false;
317 NOTE: Most of this is always overridden by the default values given
322 // Unknown nodes can be dug
323 groups["dig_immediate"] = 2;
324 drawtype = NDT_NORMAL;
327 for(u32 i = 0; i < 24; i++)
329 minimap_color = video::SColor(0, 0, 0, 0);
332 for(u32 i = 0; i < 6; i++)
333 tiledef[i] = TileDef();
334 for(u16 j = 0; j < CF_SPECIAL_COUNT; j++)
335 tiledef_special[j] = TileDef();
337 post_effect_color = video::SColor(0, 0, 0, 0);
338 param_type = CPT_NONE;
339 param_type_2 = CPT2_NONE;
340 is_ground_content = false;
341 light_propagates = false;
342 sunlight_propagates = false;
347 buildable_to = false;
349 rightclickable = true;
351 liquid_type = LIQUID_NONE;
352 liquid_alternative_flowing = "";
353 liquid_alternative_source = "";
354 liquid_viscosity = 0;
355 liquid_renewable = true;
356 liquid_range = LIQUID_LEVEL_MAX+1;
359 damage_per_second = 0;
360 node_box = NodeBox();
361 selection_box = NodeBox();
362 collision_box = NodeBox();
364 legacy_facedir_simple = false;
365 legacy_wallmounted = false;
366 sound_footstep = SimpleSoundSpec();
367 sound_dig = SimpleSoundSpec("__group");
368 sound_dug = SimpleSoundSpec();
370 connects_to_ids.clear();
372 color = video::SColor(0xFFFFFFFF);
377 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
379 if (protocol_version < 30) {
380 serializeOld(os, protocol_version);
388 os << serializeString(name);
389 writeU16(os, groups.size());
390 for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end();
392 os << serializeString(i->first);
393 writeS16(os, i->second);
395 writeU8(os, param_type);
396 writeU8(os, param_type_2);
399 writeU8(os, drawtype);
400 os << serializeString(mesh);
401 writeF1000(os, visual_scale);
403 for (u32 i = 0; i < 6; i++)
404 tiledef[i].serialize(os, protocol_version);
405 writeU8(os, CF_SPECIAL_COUNT);
406 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
407 tiledef_special[i].serialize(os, protocol_version);
410 writeU8(os, color.getRed());
411 writeU8(os, color.getGreen());
412 writeU8(os, color.getBlue());
413 os << serializeString(palette_name);
415 writeU8(os, connect_sides);
416 writeU16(os, connects_to_ids.size());
417 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
418 i != connects_to_ids.end(); ++i)
420 writeU8(os, post_effect_color.getAlpha());
421 writeU8(os, post_effect_color.getRed());
422 writeU8(os, post_effect_color.getGreen());
423 writeU8(os, post_effect_color.getBlue());
424 writeU8(os, leveled);
427 writeU8(os, light_propagates);
428 writeU8(os, sunlight_propagates);
429 writeU8(os, light_source);
432 writeU8(os, is_ground_content);
435 writeU8(os, walkable);
436 writeU8(os, pointable);
437 writeU8(os, diggable);
438 writeU8(os, climbable);
439 writeU8(os, buildable_to);
440 writeU8(os, rightclickable);
441 writeU32(os, damage_per_second);
444 writeU8(os, liquid_type);
445 os << serializeString(liquid_alternative_flowing);
446 os << serializeString(liquid_alternative_source);
447 writeU8(os, liquid_viscosity);
448 writeU8(os, liquid_renewable);
449 writeU8(os, liquid_range);
450 writeU8(os, drowning);
451 writeU8(os, floodable);
454 node_box.serialize(os, protocol_version);
455 selection_box.serialize(os, protocol_version);
456 collision_box.serialize(os, protocol_version);
459 serializeSimpleSoundSpec(sound_footstep, os);
460 serializeSimpleSoundSpec(sound_dig, os);
461 serializeSimpleSoundSpec(sound_dug, os);
464 writeU8(os, legacy_facedir_simple);
465 writeU8(os, legacy_wallmounted);
468 void ContentFeatures::correctAlpha()
470 if (alpha == 0 || alpha == 255)
473 for (u32 i = 0; i < 6; i++) {
475 s << tiledef[i].name << "^[noalpha^[opacity:" << ((int)alpha);
476 tiledef[i].name = s.str();
479 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
481 s << tiledef_special[i].name << "^[noalpha^[opacity:" << ((int)alpha);
482 tiledef_special[i].name = s.str();
486 void ContentFeatures::deSerialize(std::istream &is)
489 int version = readU8(is);
491 deSerializeOld(is, version);
493 } else if (version > 9) {
494 throw SerializationError("unsupported ContentFeatures version");
498 name = deSerializeString(is);
500 u32 groups_size = readU16(is);
501 for (u32 i = 0; i < groups_size; i++) {
502 std::string name = deSerializeString(is);
503 int value = readS16(is);
504 groups[name] = value;
506 param_type = (enum ContentParamType) readU8(is);
507 param_type_2 = (enum ContentParamType2) readU8(is);
510 drawtype = (enum NodeDrawType) readU8(is);
511 mesh = deSerializeString(is);
512 visual_scale = readF1000(is);
514 throw SerializationError("unsupported tile count");
515 for (u32 i = 0; i < 6; i++)
516 tiledef[i].deSerialize(is, version, drawtype);
517 if (readU8(is) != CF_SPECIAL_COUNT)
518 throw SerializationError("unsupported CF_SPECIAL_COUNT");
519 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
520 tiledef_special[i].deSerialize(is, version, drawtype);
522 color.setRed(readU8(is));
523 color.setGreen(readU8(is));
524 color.setBlue(readU8(is));
525 palette_name = deSerializeString(is);
527 connect_sides = readU8(is);
528 u16 connects_to_size = readU16(is);
529 connects_to_ids.clear();
530 for (u16 i = 0; i < connects_to_size; i++)
531 connects_to_ids.insert(readU16(is));
532 post_effect_color.setAlpha(readU8(is));
533 post_effect_color.setRed(readU8(is));
534 post_effect_color.setGreen(readU8(is));
535 post_effect_color.setBlue(readU8(is));
536 leveled = readU8(is);
539 light_propagates = readU8(is);
540 sunlight_propagates = readU8(is);
541 light_source = readU8(is);
542 light_source = MYMIN(light_source, LIGHT_MAX);
545 is_ground_content = readU8(is);
548 walkable = readU8(is);
549 pointable = readU8(is);
550 diggable = readU8(is);
551 climbable = readU8(is);
552 buildable_to = readU8(is);
553 rightclickable = readU8(is);
554 damage_per_second = readU32(is);
557 liquid_type = (enum LiquidType) readU8(is);
558 liquid_alternative_flowing = deSerializeString(is);
559 liquid_alternative_source = deSerializeString(is);
560 liquid_viscosity = readU8(is);
561 liquid_renewable = readU8(is);
562 liquid_range = readU8(is);
563 drowning = readU8(is);
564 floodable = readU8(is);
567 node_box.deSerialize(is);
568 selection_box.deSerialize(is);
569 collision_box.deSerialize(is);
572 deSerializeSimpleSoundSpec(sound_footstep, is);
573 deSerializeSimpleSoundSpec(sound_dig, is);
574 deSerializeSimpleSoundSpec(sound_dug, is);
576 // read legacy properties
577 legacy_facedir_simple = readU8(is);
578 legacy_wallmounted = readU8(is);
582 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
583 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
584 bool backface_culling, u8 material_type)
586 tile->shader_id = shader_id;
587 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
588 tile->material_type = material_type;
590 // Normal texture and shader flags texture
591 if (use_normal_texture) {
592 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
594 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
597 tile->material_flags = 0;
598 if (backface_culling)
599 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
600 if (tiledef->animation.type != TAT_NONE)
601 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
602 if (tiledef->tileable_horizontal)
603 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
604 if (tiledef->tileable_vertical)
605 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
608 tile->has_color = tiledef->has_color;
609 if (tiledef->has_color)
610 tile->color = tiledef->color;
614 // Animation parameters
616 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
618 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
619 &frame_count, &frame_length_ms, NULL);
620 tile->animation_frame_count = frame_count;
621 tile->animation_frame_length_ms = frame_length_ms;
624 if (frame_count == 1) {
625 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
627 std::ostringstream os(std::ios::binary);
628 tile->frames.resize(frame_count);
630 for (int i = 0; i < frame_count; i++) {
636 tiledef->animation.getTextureModifer(os,
637 tile->texture->getOriginalSize(), i);
639 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
640 if (tile->normal_texture)
641 frame.normal_texture = tsrc->getNormalTexture(os.str());
642 frame.flags_texture = tile->flags_texture;
643 tile->frames[i] = frame;
650 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
651 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
653 // minimap pixel color - the average color of a texture
654 if (tsettings.enable_minimap && tiledef[0].name != "")
655 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
657 // Figure out the actual tiles to use
659 for (u32 j = 0; j < 6; j++) {
660 tdef[j] = tiledef[j];
661 if (tdef[j].name == "")
662 tdef[j].name = "unknown_node.png";
665 bool is_liquid = false;
666 bool is_water_surface = false;
668 u8 material_type = (alpha == 255) ?
669 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
680 assert(liquid_type == LIQUID_SOURCE);
681 if (tsettings.opaque_water)
686 case NDT_FLOWINGLIQUID:
687 assert(liquid_type == LIQUID_FLOWING);
689 if (tsettings.opaque_water)
695 visual_solidness = 1;
697 case NDT_GLASSLIKE_FRAMED:
699 visual_solidness = 1;
701 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
703 visual_solidness = 1;
704 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
708 visual_solidness = 1;
710 case NDT_ALLFACES_OPTIONAL:
711 if (tsettings.leaves_style == LEAVES_FANCY) {
712 drawtype = NDT_ALLFACES;
714 visual_solidness = 1;
715 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
716 for (u32 j = 0; j < 6; j++) {
717 if (tiledef_special[j].name != "")
718 tdef[j].name = tiledef_special[j].name;
720 drawtype = NDT_GLASSLIKE;
722 visual_solidness = 1;
724 drawtype = NDT_NORMAL;
726 for (u32 i = 0; i < 6; i++)
727 tdef[i].name += std::string("^[noalpha");
730 material_type = TILE_MATERIAL_WAVING_LEAVES;
735 material_type = TILE_MATERIAL_WAVING_PLANTS;
753 material_type = (alpha == 255) ?
754 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
755 if (name == "default:water_source")
756 is_water_surface = true;
759 // Vertex alpha is no longer supported, correct if necessary.
763 for (u16 j = 0; j < 6; j++) {
764 tile_shader[j] = shdsrc->getShader("nodes_shader",
765 material_type, drawtype);
768 if (is_water_surface) {
769 tile_shader[0] = shdsrc->getShader("water_surface_shader",
770 material_type, drawtype);
773 // Tiles (fill in f->tiles[])
774 for (u16 j = 0; j < 6; j++) {
775 fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j],
776 tsettings.use_normal_texture,
777 tiledef[j].backface_culling, material_type);
780 // Special tiles (fill in f->special_tiles[])
781 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
782 fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j],
783 tile_shader[j], tsettings.use_normal_texture,
784 tiledef_special[j].backface_culling, material_type);
787 if ((drawtype == NDT_MESH) && (mesh != "")) {
789 // Read the mesh and apply scale
790 mesh_ptr[0] = client->getMesh(mesh);
792 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
793 scaleMesh(mesh_ptr[0], scale);
794 recalculateBoundingBox(mesh_ptr[0]);
795 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
797 } else if ((drawtype == NDT_NODEBOX) &&
798 ((node_box.type == NODEBOX_REGULAR) ||
799 (node_box.type == NODEBOX_FIXED)) &&
800 (!node_box.fixed.empty())) {
801 //Convert regular nodebox nodes to meshnodes
802 //Change the drawtype and apply scale
804 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
805 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
806 scaleMesh(mesh_ptr[0], scale);
807 recalculateBoundingBox(mesh_ptr[0]);
808 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
811 //Cache 6dfacedir and wallmounted rotated clones of meshes
812 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
813 (param_type_2 == CPT2_FACEDIR
814 || param_type_2 == CPT2_COLORED_FACEDIR)) {
815 for (u16 j = 1; j < 24; j++) {
816 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
817 rotateMeshBy6dFacedir(mesh_ptr[j], j);
818 recalculateBoundingBox(mesh_ptr[j]);
819 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
821 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
822 && (param_type_2 == CPT2_WALLMOUNTED ||
823 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
824 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
825 for (u16 j = 1; j < 6; j++) {
826 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
827 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
828 recalculateBoundingBox(mesh_ptr[j]);
829 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
831 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
832 recalculateBoundingBox(mesh_ptr[0]);
833 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
842 class CNodeDefManager: public IWritableNodeDefManager {
845 virtual ~CNodeDefManager();
847 virtual IWritableNodeDefManager *clone();
848 inline virtual const ContentFeatures& get(content_t c) const;
849 inline virtual const ContentFeatures& get(const MapNode &n) const;
850 virtual bool getId(const std::string &name, content_t &result) const;
851 virtual content_t getId(const std::string &name) const;
852 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
853 virtual const ContentFeatures& get(const std::string &name) const;
854 content_t allocateId();
855 virtual content_t set(const std::string &name, const ContentFeatures &def);
856 virtual content_t allocateDummy(const std::string &name);
857 virtual void removeNode(const std::string &name);
858 virtual void updateAliases(IItemDefManager *idef);
859 virtual void applyTextureOverrides(const std::string &override_filepath);
860 //! Returns a palette or NULL if not found. Only on client.
861 std::vector<video::SColor> *getPalette(const ContentFeatures &f,
862 const IGameDef *gamedef);
863 virtual void updateTextures(IGameDef *gamedef,
864 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
865 void *progress_cbk_args);
866 void serialize(std::ostream &os, u16 protocol_version) const;
867 void deSerialize(std::istream &is);
869 inline virtual bool getNodeRegistrationStatus() const;
870 inline virtual void setNodeRegistrationStatus(bool completed);
872 virtual void pendNodeResolve(NodeResolver *nr);
873 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
874 virtual void runNodeResolveCallbacks();
875 virtual void resetNodeResolveState();
876 virtual void mapNodeboxConnections();
877 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
878 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
880 return m_selection_box_int_union;
884 void addNameIdMapping(content_t i, std::string name);
886 * Recalculates m_selection_box_int_union based on
887 * m_selection_box_union.
889 void fixSelectionBoxIntUnion();
891 // Features indexed by id
892 std::vector<ContentFeatures> m_content_features;
894 // A mapping for fast converting back and forth between names and ids
895 NameIdMapping m_name_id_mapping;
897 // Like m_name_id_mapping, but only from names to ids, and includes
898 // item aliases too. Updated by updateAliases()
899 // Note: Not serialized.
901 UNORDERED_MAP<std::string, content_t> m_name_id_mapping_with_aliases;
903 // A mapping from groups to a list of content_ts (and their levels)
904 // that belong to it. Necessary for a direct lookup in getIds().
905 // Note: Not serialized.
906 UNORDERED_MAP<std::string, GroupItems> m_group_to_items;
908 // Next possibly free id
911 // Maps image file names to loaded palettes.
912 UNORDERED_MAP<std::string, std::vector<video::SColor> > m_palettes;
914 // NodeResolvers to callback once node registration has ended
915 std::vector<NodeResolver *> m_pending_resolve_callbacks;
917 // True when all nodes have been registered
918 bool m_node_registration_complete;
920 //! The union of all nodes' selection boxes.
921 aabb3f m_selection_box_union;
923 * The smallest box in node coordinates that
924 * contains all nodes' selection boxes.
926 core::aabbox3d<s16> m_selection_box_int_union;
930 CNodeDefManager::CNodeDefManager()
936 CNodeDefManager::~CNodeDefManager()
939 for (u32 i = 0; i < m_content_features.size(); i++) {
940 ContentFeatures *f = &m_content_features[i];
941 for (u32 j = 0; j < 24; j++) {
943 f->mesh_ptr[j]->drop();
950 void CNodeDefManager::clear()
952 m_content_features.clear();
953 m_name_id_mapping.clear();
954 m_name_id_mapping_with_aliases.clear();
955 m_group_to_items.clear();
957 m_selection_box_union.reset(0,0,0);
958 m_selection_box_int_union.reset(0,0,0);
960 resetNodeResolveState();
962 u32 initial_length = 0;
963 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
964 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
965 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
966 m_content_features.resize(initial_length);
968 // Set CONTENT_UNKNOWN
972 // Insert directly into containers
973 content_t c = CONTENT_UNKNOWN;
974 m_content_features[c] = f;
975 addNameIdMapping(c, f.name);
982 f.drawtype = NDT_AIRLIKE;
983 f.param_type = CPT_LIGHT;
984 f.light_propagates = true;
985 f.sunlight_propagates = true;
989 f.buildable_to = true;
991 f.is_ground_content = true;
992 // Insert directly into containers
993 content_t c = CONTENT_AIR;
994 m_content_features[c] = f;
995 addNameIdMapping(c, f.name);
998 // Set CONTENT_IGNORE
1002 f.drawtype = NDT_AIRLIKE;
1003 f.param_type = CPT_NONE;
1004 f.light_propagates = false;
1005 f.sunlight_propagates = false;
1007 f.pointable = false;
1009 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1010 f.is_ground_content = true;
1011 // Insert directly into containers
1012 content_t c = CONTENT_IGNORE;
1013 m_content_features[c] = f;
1014 addNameIdMapping(c, f.name);
1019 IWritableNodeDefManager *CNodeDefManager::clone()
1021 CNodeDefManager *mgr = new CNodeDefManager();
1027 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1029 return c < m_content_features.size()
1030 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1034 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1036 return get(n.getContent());
1040 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1042 UNORDERED_MAP<std::string, content_t>::const_iterator
1043 i = m_name_id_mapping_with_aliases.find(name);
1044 if(i == m_name_id_mapping_with_aliases.end())
1051 content_t CNodeDefManager::getId(const std::string &name) const
1053 content_t id = CONTENT_IGNORE;
1059 bool CNodeDefManager::getIds(const std::string &name,
1060 std::set<content_t> &result) const
1062 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1063 if (name.substr(0,6) != "group:") {
1064 content_t id = CONTENT_IGNORE;
1065 bool exists = getId(name, id);
1070 std::string group = name.substr(6);
1072 UNORDERED_MAP<std::string, GroupItems>::const_iterator
1073 i = m_group_to_items.find(group);
1074 if (i == m_group_to_items.end())
1077 const GroupItems &items = i->second;
1078 for (GroupItems::const_iterator j = items.begin();
1079 j != items.end(); ++j) {
1080 if ((*j).second != 0)
1081 result.insert((*j).first);
1083 //printf("getIds: %dus\n", t.stop());
1088 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1090 content_t id = CONTENT_UNKNOWN;
1096 // returns CONTENT_IGNORE if no free ID found
1097 content_t CNodeDefManager::allocateId()
1099 for (content_t id = m_next_id;
1100 id >= m_next_id; // overflow?
1102 while (id >= m_content_features.size()) {
1103 m_content_features.push_back(ContentFeatures());
1105 const ContentFeatures &f = m_content_features[id];
1111 // If we arrive here, an overflow occurred in id.
1112 // That means no ID was found
1113 return CONTENT_IGNORE;
1118 * Returns the smallest box that contains all boxes
1119 * in the vector. Box_union is expanded.
1120 * @param[in] boxes the vector containing the boxes
1121 * @param[in, out] box_union the union of the arguments
1123 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1125 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1126 it != boxes.end(); ++it) {
1127 box_union->addInternalBox(*it);
1133 * Returns a box that contains the nodebox in every case.
1134 * The argument node_union is expanded.
1135 * @param[in] nodebox the nodebox to be measured
1136 * @param[in] features used to decide whether the nodebox
1138 * @param[in, out] box_union the union of the arguments
1140 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1143 switch(nodebox.type) {
1145 case NODEBOX_LEVELED: {
1147 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1148 boxVectorUnion(nodebox.fixed, &half_processed);
1149 // Set leveled boxes to maximal
1150 if (nodebox.type == NODEBOX_LEVELED) {
1151 half_processed.MaxEdge.Y = +BS / 2;
1153 if (features.param_type_2 == CPT2_FACEDIR ||
1154 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1155 // Get maximal coordinate
1157 fabsf(half_processed.MinEdge.X),
1158 fabsf(half_processed.MinEdge.Y),
1159 fabsf(half_processed.MinEdge.Z),
1160 fabsf(half_processed.MaxEdge.X),
1161 fabsf(half_processed.MaxEdge.Y),
1162 fabsf(half_processed.MaxEdge.Z) };
1164 for (int i = 0; i < 6; i++) {
1165 if (max < coords[i]) {
1169 // Add the union of all possible rotated boxes
1170 box_union->addInternalPoint(-max, -max, -max);
1171 box_union->addInternalPoint(+max, +max, +max);
1173 box_union->addInternalBox(half_processed);
1177 case NODEBOX_WALLMOUNTED: {
1179 box_union->addInternalBox(nodebox.wall_top);
1180 box_union->addInternalBox(nodebox.wall_bottom);
1181 // Find maximal coordinate in the X-Z plane
1183 fabsf(nodebox.wall_side.MinEdge.X),
1184 fabsf(nodebox.wall_side.MinEdge.Z),
1185 fabsf(nodebox.wall_side.MaxEdge.X),
1186 fabsf(nodebox.wall_side.MaxEdge.Z) };
1188 for (int i = 0; i < 4; i++) {
1189 if (max < coords[i]) {
1193 // Add the union of all possible rotated boxes
1194 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1195 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1198 case NODEBOX_CONNECTED: {
1199 // Add all possible connected boxes
1200 boxVectorUnion(nodebox.fixed, box_union);
1201 boxVectorUnion(nodebox.connect_top, box_union);
1202 boxVectorUnion(nodebox.connect_bottom, box_union);
1203 boxVectorUnion(nodebox.connect_front, box_union);
1204 boxVectorUnion(nodebox.connect_left, box_union);
1205 boxVectorUnion(nodebox.connect_back, box_union);
1206 boxVectorUnion(nodebox.connect_right, box_union);
1211 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1212 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1218 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1220 m_selection_box_int_union.MinEdge.X = floorf(
1221 m_selection_box_union.MinEdge.X / BS + 0.5f);
1222 m_selection_box_int_union.MinEdge.Y = floorf(
1223 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1224 m_selection_box_int_union.MinEdge.Z = floorf(
1225 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1226 m_selection_box_int_union.MaxEdge.X = ceilf(
1227 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1228 m_selection_box_int_union.MaxEdge.Y = ceilf(
1229 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1230 m_selection_box_int_union.MaxEdge.Z = ceilf(
1231 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1235 // IWritableNodeDefManager
1236 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1240 assert(name == def.name);
1242 // Don't allow redefining ignore (but allow air and unknown)
1243 if (name == "ignore") {
1244 warningstream << "NodeDefManager: Ignoring "
1245 "CONTENT_IGNORE redefinition"<<std::endl;
1246 return CONTENT_IGNORE;
1249 content_t id = CONTENT_IGNORE;
1250 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1253 if (id == CONTENT_IGNORE) {
1254 warningstream << "NodeDefManager: Absolute "
1255 "limit reached" << std::endl;
1256 return CONTENT_IGNORE;
1258 assert(id != CONTENT_IGNORE);
1259 addNameIdMapping(id, name);
1261 m_content_features[id] = def;
1262 verbosestream << "NodeDefManager: registering content id \"" << id
1263 << "\": name=\"" << def.name << "\""<<std::endl;
1265 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1266 fixSelectionBoxIntUnion();
1267 // Add this content to the list of all groups it belongs to
1268 // FIXME: This should remove a node from groups it no longer
1269 // belongs to when a node is re-registered
1270 for (ItemGroupList::const_iterator i = def.groups.begin();
1271 i != def.groups.end(); ++i) {
1272 std::string group_name = i->first;
1274 UNORDERED_MAP<std::string, GroupItems>::iterator
1275 j = m_group_to_items.find(group_name);
1276 if (j == m_group_to_items.end()) {
1277 m_group_to_items[group_name].push_back(
1278 std::make_pair(id, i->second));
1280 GroupItems &items = j->second;
1281 items.push_back(std::make_pair(id, i->second));
1288 content_t CNodeDefManager::allocateDummy(const std::string &name)
1290 assert(name != ""); // Pre-condition
1293 return set(name, f);
1297 void CNodeDefManager::removeNode(const std::string &name)
1302 // Erase name from name ID mapping
1303 content_t id = CONTENT_IGNORE;
1304 if (m_name_id_mapping.getId(name, id)) {
1305 m_name_id_mapping.eraseName(name);
1306 m_name_id_mapping_with_aliases.erase(name);
1309 // Erase node content from all groups it belongs to
1310 for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
1311 m_group_to_items.begin();
1312 iter_groups != m_group_to_items.end();) {
1313 GroupItems &items = iter_groups->second;
1314 for (GroupItems::iterator iter_groupitems = items.begin();
1315 iter_groupitems != items.end();) {
1316 if (iter_groupitems->first == id)
1317 items.erase(iter_groupitems++);
1322 // Check if group is empty
1323 if (items.size() == 0)
1324 m_group_to_items.erase(iter_groups++);
1331 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1333 std::set<std::string> all = idef->getAll();
1334 m_name_id_mapping_with_aliases.clear();
1335 for (std::set<std::string>::iterator
1336 i = all.begin(); i != all.end(); ++i) {
1337 std::string name = *i;
1338 std::string convert_to = idef->getAlias(name);
1340 if (m_name_id_mapping.getId(convert_to, id)) {
1341 m_name_id_mapping_with_aliases.insert(
1342 std::make_pair(name, id));
1347 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1349 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1350 "overrides to textures from " << override_filepath << std::endl;
1352 std::ifstream infile(override_filepath.c_str());
1355 while (std::getline(infile, line)) {
1357 if (trim(line) == "")
1359 std::vector<std::string> splitted = str_split(line, ' ');
1360 if (splitted.size() != 3) {
1361 errorstream << override_filepath
1362 << ":" << line_c << " Could not apply texture override \""
1363 << line << "\": Syntax error" << std::endl;
1368 if (!getId(splitted[0], id))
1369 continue; // Ignore unknown node
1371 ContentFeatures &nodedef = m_content_features[id];
1373 if (splitted[1] == "top")
1374 nodedef.tiledef[0].name = splitted[2];
1375 else if (splitted[1] == "bottom")
1376 nodedef.tiledef[1].name = splitted[2];
1377 else if (splitted[1] == "right")
1378 nodedef.tiledef[2].name = splitted[2];
1379 else if (splitted[1] == "left")
1380 nodedef.tiledef[3].name = splitted[2];
1381 else if (splitted[1] == "back")
1382 nodedef.tiledef[4].name = splitted[2];
1383 else if (splitted[1] == "front")
1384 nodedef.tiledef[5].name = splitted[2];
1385 else if (splitted[1] == "all" || splitted[1] == "*")
1386 for (int i = 0; i < 6; i++)
1387 nodedef.tiledef[i].name = splitted[2];
1388 else if (splitted[1] == "sides")
1389 for (int i = 2; i < 6; i++)
1390 nodedef.tiledef[i].name = splitted[2];
1392 errorstream << override_filepath
1393 << ":" << line_c << " Could not apply texture override \""
1394 << line << "\": Unknown node side \""
1395 << splitted[1] << "\"" << std::endl;
1401 std::vector<video::SColor> *CNodeDefManager::getPalette(
1402 const ContentFeatures &f, const IGameDef *gamedef)
1405 // This works because colors always use the most significant bits
1406 // of param2. If you add a new colored type which uses param2
1407 // in a more advanced way, you should change this code, too.
1408 u32 palette_pixels = 0;
1409 switch (f.param_type_2) {
1411 palette_pixels = 256;
1413 case CPT2_COLORED_FACEDIR:
1416 case CPT2_COLORED_WALLMOUNTED:
1417 palette_pixels = 32;
1422 // This many param2 values will have the same color
1423 u32 step = 256 / palette_pixels;
1424 const std::string &name = f.palette_name;
1427 Client *client = (Client *) gamedef;
1428 ITextureSource *tsrc = client->tsrc();
1430 UNORDERED_MAP<std::string, std::vector<video::SColor> >::iterator it =
1431 m_palettes.find(name);
1432 if (it == m_palettes.end()) {
1434 if (!tsrc->isKnownSourceImage(name)) {
1435 warningstream << "CNodeDefManager::getPalette(): palette \"" << name
1436 << "\" could not be loaded." << std::endl;
1439 video::IImage *img = tsrc->generateImage(name);
1440 std::vector<video::SColor> new_palette;
1441 u32 w = img->getDimension().Width;
1442 u32 h = img->getDimension().Height;
1443 // Real area of the image
1445 if (area != palette_pixels)
1446 warningstream << "CNodeDefManager::getPalette(): the "
1447 << "specified palette image \"" << name << "\" does not "
1448 << "contain exactly " << palette_pixels
1449 << " pixels." << std::endl;
1450 if (area > palette_pixels)
1451 area = palette_pixels;
1452 // For each pixel in the image
1453 for (u32 i = 0; i < area; i++) {
1454 video::SColor c = img->getPixel(i % w, i / w);
1455 // Fill in palette with 'step' colors
1456 for (u32 j = 0; j < step; j++)
1457 new_palette.push_back(c);
1460 // Fill in remaining elements
1461 while (new_palette.size() < 256)
1462 new_palette.push_back(video::SColor(0xFFFFFFFF));
1463 m_palettes[name] = new_palette;
1464 it = m_palettes.find(name);
1466 if (it != m_palettes.end())
1467 return &((*it).second);
1473 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1474 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1475 void *progress_callback_args)
1478 infostream << "CNodeDefManager::updateTextures(): Updating "
1479 "textures in node definitions" << std::endl;
1481 Client *client = (Client *)gamedef;
1482 ITextureSource *tsrc = client->tsrc();
1483 IShaderSource *shdsrc = client->getShaderSource();
1484 scene::ISceneManager* smgr = client->getSceneManager();
1485 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
1486 TextureSettings tsettings;
1487 tsettings.readSettings();
1490 u32 size = m_content_features.size();
1492 for (u32 i = 0; i < size; i++) {
1493 ContentFeatures *f = &(m_content_features[i]);
1494 f->palette = getPalette(*f, gamedef);
1495 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1496 progress_callback(progress_callback_args, i, size);
1501 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1503 writeU8(os, 1); // version
1505 std::ostringstream os2(std::ios::binary);
1506 for (u32 i = 0; i < m_content_features.size(); i++) {
1507 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1508 || i == CONTENT_UNKNOWN)
1510 const ContentFeatures *f = &m_content_features[i];
1514 // Wrap it in a string to allow different lengths without
1515 // strict version incompatibilities
1516 std::ostringstream wrapper_os(std::ios::binary);
1517 f->serialize(wrapper_os, protocol_version);
1518 os2<<serializeString(wrapper_os.str());
1520 // must not overflow
1521 u16 next = count + 1;
1522 FATAL_ERROR_IF(next < count, "Overflow");
1525 writeU16(os, count);
1526 os << serializeLongString(os2.str());
1530 void CNodeDefManager::deSerialize(std::istream &is)
1533 int version = readU8(is);
1535 throw SerializationError("unsupported NodeDefinitionManager version");
1536 u16 count = readU16(is);
1537 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1539 for (u16 n = 0; n < count; n++) {
1540 u16 i = readU16(is2);
1542 // Read it from the string wrapper
1543 std::string wrapper = deSerializeString(is2);
1544 std::istringstream wrapper_is(wrapper, std::ios::binary);
1545 f.deSerialize(wrapper_is);
1547 // Check error conditions
1548 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1549 warningstream << "NodeDefManager::deSerialize(): "
1550 "not changing builtin node " << i << std::endl;
1554 warningstream << "NodeDefManager::deSerialize(): "
1555 "received empty name" << std::endl;
1561 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1562 warningstream << "NodeDefManager::deSerialize(): "
1563 "already defined with different ID: " << f.name << std::endl;
1567 // All is ok, add node definition with the requested ID
1568 if (i >= m_content_features.size())
1569 m_content_features.resize((u32)(i) + 1);
1570 m_content_features[i] = f;
1571 addNameIdMapping(i, f.name);
1572 verbosestream << "deserialized " << f.name << std::endl;
1574 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1575 fixSelectionBoxIntUnion();
1580 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1582 m_name_id_mapping.set(i, name);
1583 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1587 IWritableNodeDefManager *createNodeDefManager()
1589 return new CNodeDefManager();
1593 //// Serialization of old ContentFeatures formats
1594 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1596 u8 compatible_param_type_2 = param_type_2;
1597 if ((protocol_version < 28)
1598 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1599 compatible_param_type_2 = CPT2_NONE;
1600 else if (protocol_version < 30) {
1601 if (compatible_param_type_2 == CPT2_COLOR)
1602 compatible_param_type_2 = CPT2_NONE;
1603 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1604 compatible_param_type_2 = CPT2_FACEDIR;
1605 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1606 compatible_param_type_2 = CPT2_WALLMOUNTED;
1609 if (protocol_version == 13)
1611 writeU8(os, 5); // version
1612 os<<serializeString(name);
1613 writeU16(os, groups.size());
1614 for (ItemGroupList::const_iterator
1615 i = groups.begin(); i != groups.end(); ++i) {
1616 os<<serializeString(i->first);
1617 writeS16(os, i->second);
1619 writeU8(os, drawtype);
1620 writeF1000(os, visual_scale);
1622 for (u32 i = 0; i < 6; i++)
1623 tiledef[i].serialize(os, protocol_version);
1624 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1626 for (u32 i = 0; i < 2; i++)
1627 tiledef_special[i].serialize(os, protocol_version);
1629 writeU8(os, post_effect_color.getAlpha());
1630 writeU8(os, post_effect_color.getRed());
1631 writeU8(os, post_effect_color.getGreen());
1632 writeU8(os, post_effect_color.getBlue());
1633 writeU8(os, param_type);
1634 writeU8(os, compatible_param_type_2);
1635 writeU8(os, is_ground_content);
1636 writeU8(os, light_propagates);
1637 writeU8(os, sunlight_propagates);
1638 writeU8(os, walkable);
1639 writeU8(os, pointable);
1640 writeU8(os, diggable);
1641 writeU8(os, climbable);
1642 writeU8(os, buildable_to);
1643 os<<serializeString(""); // legacy: used to be metadata_name
1644 writeU8(os, liquid_type);
1645 os<<serializeString(liquid_alternative_flowing);
1646 os<<serializeString(liquid_alternative_source);
1647 writeU8(os, liquid_viscosity);
1648 writeU8(os, light_source);
1649 writeU32(os, damage_per_second);
1650 node_box.serialize(os, protocol_version);
1651 selection_box.serialize(os, protocol_version);
1652 writeU8(os, legacy_facedir_simple);
1653 writeU8(os, legacy_wallmounted);
1654 serializeSimpleSoundSpec(sound_footstep, os);
1655 serializeSimpleSoundSpec(sound_dig, os);
1656 serializeSimpleSoundSpec(sound_dug, os);
1658 else if (protocol_version > 13 && protocol_version < 24) {
1659 writeU8(os, 6); // version
1660 os<<serializeString(name);
1661 writeU16(os, groups.size());
1662 for (ItemGroupList::const_iterator
1663 i = groups.begin(); i != groups.end(); ++i) {
1664 os<<serializeString(i->first);
1665 writeS16(os, i->second);
1667 writeU8(os, drawtype);
1668 writeF1000(os, visual_scale);
1670 for (u32 i = 0; i < 6; i++)
1671 tiledef[i].serialize(os, protocol_version);
1672 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1674 for (u32 i = 0; i < 2; i++)
1675 tiledef_special[i].serialize(os, protocol_version);
1677 writeU8(os, post_effect_color.getAlpha());
1678 writeU8(os, post_effect_color.getRed());
1679 writeU8(os, post_effect_color.getGreen());
1680 writeU8(os, post_effect_color.getBlue());
1681 writeU8(os, param_type);
1682 writeU8(os, compatible_param_type_2);
1683 writeU8(os, is_ground_content);
1684 writeU8(os, light_propagates);
1685 writeU8(os, sunlight_propagates);
1686 writeU8(os, walkable);
1687 writeU8(os, pointable);
1688 writeU8(os, diggable);
1689 writeU8(os, climbable);
1690 writeU8(os, buildable_to);
1691 os<<serializeString(""); // legacy: used to be metadata_name
1692 writeU8(os, liquid_type);
1693 os<<serializeString(liquid_alternative_flowing);
1694 os<<serializeString(liquid_alternative_source);
1695 writeU8(os, liquid_viscosity);
1696 writeU8(os, liquid_renewable);
1697 writeU8(os, light_source);
1698 writeU32(os, damage_per_second);
1699 node_box.serialize(os, protocol_version);
1700 selection_box.serialize(os, protocol_version);
1701 writeU8(os, legacy_facedir_simple);
1702 writeU8(os, legacy_wallmounted);
1703 serializeSimpleSoundSpec(sound_footstep, os);
1704 serializeSimpleSoundSpec(sound_dig, os);
1705 serializeSimpleSoundSpec(sound_dug, os);
1706 writeU8(os, rightclickable);
1707 writeU8(os, drowning);
1708 writeU8(os, leveled);
1709 writeU8(os, liquid_range);
1711 else if(protocol_version >= 24 && protocol_version < 30) {
1712 writeU8(os, protocol_version < 27 ? 7 : 8);
1714 os << serializeString(name);
1715 writeU16(os, groups.size());
1716 for (ItemGroupList::const_iterator i = groups.begin();
1717 i != groups.end(); ++i) {
1718 os << serializeString(i->first);
1719 writeS16(os, i->second);
1721 writeU8(os, drawtype);
1722 writeF1000(os, visual_scale);
1724 for (u32 i = 0; i < 6; i++)
1725 tiledef[i].serialize(os, protocol_version);
1726 writeU8(os, CF_SPECIAL_COUNT);
1727 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1728 tiledef_special[i].serialize(os, protocol_version);
1730 writeU8(os, post_effect_color.getAlpha());
1731 writeU8(os, post_effect_color.getRed());
1732 writeU8(os, post_effect_color.getGreen());
1733 writeU8(os, post_effect_color.getBlue());
1734 writeU8(os, param_type);
1735 writeU8(os, compatible_param_type_2);
1736 writeU8(os, is_ground_content);
1737 writeU8(os, light_propagates);
1738 writeU8(os, sunlight_propagates);
1739 writeU8(os, walkable);
1740 writeU8(os, pointable);
1741 writeU8(os, diggable);
1742 writeU8(os, climbable);
1743 writeU8(os, buildable_to);
1744 os << serializeString(""); // legacy: used to be metadata_name
1745 writeU8(os, liquid_type);
1746 os << serializeString(liquid_alternative_flowing);
1747 os << serializeString(liquid_alternative_source);
1748 writeU8(os, liquid_viscosity);
1749 writeU8(os, liquid_renewable);
1750 writeU8(os, light_source);
1751 writeU32(os, damage_per_second);
1752 node_box.serialize(os, protocol_version);
1753 selection_box.serialize(os, protocol_version);
1754 writeU8(os, legacy_facedir_simple);
1755 writeU8(os, legacy_wallmounted);
1756 serializeSimpleSoundSpec(sound_footstep, os);
1757 serializeSimpleSoundSpec(sound_dig, os);
1758 serializeSimpleSoundSpec(sound_dug, os);
1759 writeU8(os, rightclickable);
1760 writeU8(os, drowning);
1761 writeU8(os, leveled);
1762 writeU8(os, liquid_range);
1763 writeU8(os, waving);
1764 os << serializeString(mesh);
1765 collision_box.serialize(os, protocol_version);
1766 writeU8(os, floodable);
1767 writeU16(os, connects_to_ids.size());
1768 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1769 i != connects_to_ids.end(); ++i)
1771 writeU8(os, connect_sides);
1773 throw SerializationError("ContentFeatures::serialize(): "
1774 "Unsupported version requested");
1777 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1779 if (version == 5) // In PROTOCOL_VERSION 13
1781 name = deSerializeString(is);
1783 u32 groups_size = readU16(is);
1784 for(u32 i=0; i<groups_size; i++){
1785 std::string name = deSerializeString(is);
1786 int value = readS16(is);
1787 groups[name] = value;
1789 drawtype = (enum NodeDrawType)readU8(is);
1791 visual_scale = readF1000(is);
1792 if (readU8(is) != 6)
1793 throw SerializationError("unsupported tile count");
1794 for (u32 i = 0; i < 6; i++)
1795 tiledef[i].deSerialize(is, version, drawtype);
1796 if (readU8(is) != CF_SPECIAL_COUNT)
1797 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1798 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1799 tiledef_special[i].deSerialize(is, version, drawtype);
1801 post_effect_color.setAlpha(readU8(is));
1802 post_effect_color.setRed(readU8(is));
1803 post_effect_color.setGreen(readU8(is));
1804 post_effect_color.setBlue(readU8(is));
1805 param_type = (enum ContentParamType)readU8(is);
1806 param_type_2 = (enum ContentParamType2)readU8(is);
1807 is_ground_content = readU8(is);
1808 light_propagates = readU8(is);
1809 sunlight_propagates = readU8(is);
1810 walkable = readU8(is);
1811 pointable = readU8(is);
1812 diggable = readU8(is);
1813 climbable = readU8(is);
1814 buildable_to = readU8(is);
1815 deSerializeString(is); // legacy: used to be metadata_name
1816 liquid_type = (enum LiquidType)readU8(is);
1817 liquid_alternative_flowing = deSerializeString(is);
1818 liquid_alternative_source = deSerializeString(is);
1819 liquid_viscosity = readU8(is);
1820 light_source = readU8(is);
1821 light_source = MYMIN(light_source, LIGHT_MAX);
1822 damage_per_second = readU32(is);
1823 node_box.deSerialize(is);
1824 selection_box.deSerialize(is);
1825 legacy_facedir_simple = readU8(is);
1826 legacy_wallmounted = readU8(is);
1827 deSerializeSimpleSoundSpec(sound_footstep, is);
1828 deSerializeSimpleSoundSpec(sound_dig, is);
1829 deSerializeSimpleSoundSpec(sound_dug, is);
1830 } else if (version == 6) {
1831 name = deSerializeString(is);
1833 u32 groups_size = readU16(is);
1834 for (u32 i = 0; i < groups_size; i++) {
1835 std::string name = deSerializeString(is);
1836 int value = readS16(is);
1837 groups[name] = value;
1839 drawtype = (enum NodeDrawType)readU8(is);
1840 visual_scale = readF1000(is);
1841 if (readU8(is) != 6)
1842 throw SerializationError("unsupported tile count");
1843 for (u32 i = 0; i < 6; i++)
1844 tiledef[i].deSerialize(is, version, drawtype);
1845 // CF_SPECIAL_COUNT in version 6 = 2
1846 if (readU8(is) != 2)
1847 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1848 for (u32 i = 0; i < 2; i++)
1849 tiledef_special[i].deSerialize(is, version, drawtype);
1851 post_effect_color.setAlpha(readU8(is));
1852 post_effect_color.setRed(readU8(is));
1853 post_effect_color.setGreen(readU8(is));
1854 post_effect_color.setBlue(readU8(is));
1855 param_type = (enum ContentParamType)readU8(is);
1856 param_type_2 = (enum ContentParamType2)readU8(is);
1857 is_ground_content = readU8(is);
1858 light_propagates = readU8(is);
1859 sunlight_propagates = readU8(is);
1860 walkable = readU8(is);
1861 pointable = readU8(is);
1862 diggable = readU8(is);
1863 climbable = readU8(is);
1864 buildable_to = readU8(is);
1865 deSerializeString(is); // legacy: used to be metadata_name
1866 liquid_type = (enum LiquidType)readU8(is);
1867 liquid_alternative_flowing = deSerializeString(is);
1868 liquid_alternative_source = deSerializeString(is);
1869 liquid_viscosity = readU8(is);
1870 liquid_renewable = readU8(is);
1871 light_source = readU8(is);
1872 damage_per_second = readU32(is);
1873 node_box.deSerialize(is);
1874 selection_box.deSerialize(is);
1875 legacy_facedir_simple = readU8(is);
1876 legacy_wallmounted = readU8(is);
1877 deSerializeSimpleSoundSpec(sound_footstep, is);
1878 deSerializeSimpleSoundSpec(sound_dig, is);
1879 deSerializeSimpleSoundSpec(sound_dug, is);
1880 rightclickable = readU8(is);
1881 drowning = readU8(is);
1882 leveled = readU8(is);
1883 liquid_range = readU8(is);
1884 } else if (version == 7 || version == 8){
1885 name = deSerializeString(is);
1887 u32 groups_size = readU16(is);
1888 for (u32 i = 0; i < groups_size; i++) {
1889 std::string name = deSerializeString(is);
1890 int value = readS16(is);
1891 groups[name] = value;
1893 drawtype = (enum NodeDrawType) readU8(is);
1895 visual_scale = readF1000(is);
1896 if (readU8(is) != 6)
1897 throw SerializationError("unsupported tile count");
1898 for (u32 i = 0; i < 6; i++)
1899 tiledef[i].deSerialize(is, version, drawtype);
1900 if (readU8(is) != CF_SPECIAL_COUNT)
1901 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1902 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1903 tiledef_special[i].deSerialize(is, version, drawtype);
1905 post_effect_color.setAlpha(readU8(is));
1906 post_effect_color.setRed(readU8(is));
1907 post_effect_color.setGreen(readU8(is));
1908 post_effect_color.setBlue(readU8(is));
1909 param_type = (enum ContentParamType) readU8(is);
1910 param_type_2 = (enum ContentParamType2) readU8(is);
1911 is_ground_content = readU8(is);
1912 light_propagates = readU8(is);
1913 sunlight_propagates = readU8(is);
1914 walkable = readU8(is);
1915 pointable = readU8(is);
1916 diggable = readU8(is);
1917 climbable = readU8(is);
1918 buildable_to = readU8(is);
1919 deSerializeString(is); // legacy: used to be metadata_name
1920 liquid_type = (enum LiquidType) readU8(is);
1921 liquid_alternative_flowing = deSerializeString(is);
1922 liquid_alternative_source = deSerializeString(is);
1923 liquid_viscosity = readU8(is);
1924 liquid_renewable = readU8(is);
1925 light_source = readU8(is);
1926 light_source = MYMIN(light_source, LIGHT_MAX);
1927 damage_per_second = readU32(is);
1928 node_box.deSerialize(is);
1929 selection_box.deSerialize(is);
1930 legacy_facedir_simple = readU8(is);
1931 legacy_wallmounted = readU8(is);
1932 deSerializeSimpleSoundSpec(sound_footstep, is);
1933 deSerializeSimpleSoundSpec(sound_dig, is);
1934 deSerializeSimpleSoundSpec(sound_dug, is);
1935 rightclickable = readU8(is);
1936 drowning = readU8(is);
1937 leveled = readU8(is);
1938 liquid_range = readU8(is);
1939 waving = readU8(is);
1941 mesh = deSerializeString(is);
1942 collision_box.deSerialize(is);
1943 floodable = readU8(is);
1944 u16 connects_to_size = readU16(is);
1945 connects_to_ids.clear();
1946 for (u16 i = 0; i < connects_to_size; i++)
1947 connects_to_ids.insert(readU16(is));
1948 connect_sides = readU8(is);
1949 } catch (SerializationError &e) {};
1951 throw SerializationError("unsupported ContentFeatures version");
1956 inline bool CNodeDefManager::getNodeRegistrationStatus() const
1958 return m_node_registration_complete;
1962 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1964 m_node_registration_complete = completed;
1968 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1971 if (m_node_registration_complete)
1972 nr->nodeResolveInternal();
1974 m_pending_resolve_callbacks.push_back(nr);
1978 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1980 size_t len = m_pending_resolve_callbacks.size();
1981 for (size_t i = 0; i != len; i++) {
1982 if (nr != m_pending_resolve_callbacks[i])
1986 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1987 m_pending_resolve_callbacks.resize(len);
1995 void CNodeDefManager::runNodeResolveCallbacks()
1997 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1998 NodeResolver *nr = m_pending_resolve_callbacks[i];
1999 nr->nodeResolveInternal();
2002 m_pending_resolve_callbacks.clear();
2006 void CNodeDefManager::resetNodeResolveState()
2008 m_node_registration_complete = false;
2009 m_pending_resolve_callbacks.clear();
2012 void CNodeDefManager::mapNodeboxConnections()
2014 for (u32 i = 0; i < m_content_features.size(); i++) {
2015 ContentFeatures *f = &m_content_features[i];
2016 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
2018 for (std::vector<std::string>::iterator it = f->connects_to.begin();
2019 it != f->connects_to.end(); ++it) {
2020 getIds(*it, f->connects_to_ids);
2025 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
2027 const ContentFeatures &f1 = get(from);
2029 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
2032 // lookup target in connected set
2033 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
2036 const ContentFeatures &f2 = get(to);
2038 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
2039 // ignores actually looking if back connection exists
2040 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
2042 // does to node declare usable faces?
2043 if (f2.connect_sides > 0) {
2044 if ((f2.param_type_2 == CPT2_FACEDIR ||
2045 f2.param_type_2 == CPT2_COLORED_FACEDIR)
2046 && (connect_face >= 4)) {
2047 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2048 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2050 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2052 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
2053 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2055 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2056 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2057 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
2059 return (f2.connect_sides
2060 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
2062 return (f2.connect_sides & connect_face);
2064 // the target is just a regular node, so connect no matter back connection
2072 NodeResolver::NodeResolver()
2075 m_nodenames_idx = 0;
2076 m_nnlistsizes_idx = 0;
2077 m_resolve_done = false;
2079 m_nodenames.reserve(16);
2080 m_nnlistsizes.reserve(4);
2084 NodeResolver::~NodeResolver()
2086 if (!m_resolve_done && m_ndef)
2087 m_ndef->cancelNodeResolveCallback(this);
2091 void NodeResolver::nodeResolveInternal()
2093 m_nodenames_idx = 0;
2094 m_nnlistsizes_idx = 0;
2097 m_resolve_done = true;
2099 m_nodenames.clear();
2100 m_nnlistsizes.clear();
2104 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
2105 const std::string &node_alt, content_t c_fallback)
2107 if (m_nodenames_idx == m_nodenames.size()) {
2108 *result_out = c_fallback;
2109 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2114 std::string name = m_nodenames[m_nodenames_idx++];
2116 bool success = m_ndef->getId(name, c);
2117 if (!success && node_alt != "") {
2119 success = m_ndef->getId(name, c);
2123 errorstream << "NodeResolver: failed to resolve node name '" << name
2124 << "'." << std::endl;
2133 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
2134 bool all_required, content_t c_fallback)
2136 bool success = true;
2138 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
2139 errorstream << "NodeResolver: no more node lists" << std::endl;
2143 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
2146 if (m_nodenames_idx == m_nodenames.size()) {
2147 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2152 std::string &name = m_nodenames[m_nodenames_idx++];
2154 if (name.substr(0,6) != "group:") {
2155 if (m_ndef->getId(name, c)) {
2156 result_out->push_back(c);
2157 } else if (all_required) {
2158 errorstream << "NodeResolver: failed to resolve node name '"
2159 << name << "'." << std::endl;
2160 result_out->push_back(c_fallback);
2164 std::set<content_t> cids;
2165 std::set<content_t>::iterator it;
2166 m_ndef->getIds(name, cids);
2167 for (it = cids.begin(); it != cids.end(); ++it)
2168 result_out->push_back(*it);