3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include "nodemetadata.h"
29 #include "nameidmapping.h"
30 #include "content_mapnode.h" // For legacy name-id mapping
31 #include "content_nodemeta.h" // For legacy deserialization
32 #include "serialization.h"
34 #include "mapblock_mesh.h"
36 #include "util/string.h"
37 #include "util/serialize.h"
38 #include "util/basic_macros.h"
40 static const char *modified_reason_strings[] = {
49 "NodeMetaRef::reportMetadataChange",
51 "Timestamp expired (step)",
53 "removeRemovedObjects/remove",
54 "removeRemovedObjects/deactivate",
55 "Stored list cleared in activateObjects due to overflow",
56 "deactivateFarObjects: Static data moved in",
57 "deactivateFarObjects: Static data moved out",
58 "deactivateFarObjects: Static data changed considerably",
59 "finishBlockMake: expireDayNightDiff",
68 MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
71 m_pos_relative(pos * MAP_BLOCKSIZE),
73 m_modified(MOD_STATE_WRITE_NEEDED),
74 m_modified_reason(MOD_REASON_INITIAL),
75 is_underground(false),
76 m_lighting_expired(true),
77 m_day_night_differs(false),
78 m_day_night_differs_expired(true),
80 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
81 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
98 //MutexAutoLock lock(mesh_mutex);
112 bool MapBlock::isValidPositionParent(v3s16 p)
114 if(isValidPosition(p))
119 return m_parent->isValidPosition(getPosRelative() + p);
123 MapNode MapBlock::getNodeParent(v3s16 p, bool *is_valid_position)
125 if (isValidPosition(p) == false)
126 return m_parent->getNodeNoEx(getPosRelative() + p, is_valid_position);
129 if (is_valid_position)
130 *is_valid_position = false;
131 return MapNode(CONTENT_IGNORE);
133 if (is_valid_position)
134 *is_valid_position = true;
135 return data[p.Z * zstride + p.Y * ystride + p.X];
138 std::string MapBlock::getModifiedReasonString()
142 const u32 ubound = MYMIN(sizeof(m_modified_reason) * CHAR_BIT,
143 ARRLEN(modified_reason_strings));
145 for (u32 i = 0; i != ubound; i++) {
146 if ((m_modified_reason & (1 << i)) == 0)
149 reason += modified_reason_strings[i];
153 if (reason.length() > 2)
154 reason.resize(reason.length() - 2);
160 Propagates sunlight down through the block.
161 Doesn't modify nodes that are not affected by sunlight.
163 Returns false if sunlight at bottom block is invalid.
164 Returns true if sunlight at bottom block is valid.
165 Returns true if bottom block doesn't exist.
167 If there is a block above, continues from it.
168 If there is no block above, assumes there is sunlight, unless
169 is_underground is set or highest node is water.
171 All sunlighted nodes are added to light_sources.
173 if remove_light==true, sets non-sunlighted nodes black.
175 if black_air_left!=NULL, it is set to true if non-sunlighted
176 air is left in block.
178 bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
179 bool remove_light, bool *black_air_left)
181 INodeDefManager *nodemgr = m_gamedef->ndef();
183 // Whether the sunlight at the top of the bottom block is valid
184 bool block_below_is_valid = true;
186 v3s16 pos_relative = getPosRelative();
188 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
190 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
193 bool no_sunlight = false;
194 //bool no_top_block = false;
196 // Check if node above block has sunlight
198 bool is_valid_position;
199 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z),
201 if (is_valid_position)
203 if(n.getContent() == CONTENT_IGNORE)
206 no_sunlight = is_underground;
208 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
215 //no_top_block = true;
217 // NOTE: This makes over-ground roofed places sunlighted
218 // Assume sunlight, unless is_underground==true
225 MapNode n = getNodeNoEx(v3s16(x, MAP_BLOCKSIZE-1, z));
226 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
231 // NOTE: As of now, this just would make everything dark.
233 //no_sunlight = true;
236 #if 0 // Doesn't work; nothing gets light.
237 bool no_sunlight = true;
238 bool no_top_block = false;
239 // Check if node above block has sunlight
241 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
242 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
247 catch(InvalidPositionException &e)
253 /*std::cout<<"("<<x<<","<<z<<"): "
254 <<"no_top_block="<<no_top_block
255 <<", is_underground="<<is_underground
256 <<", no_sunlight="<<no_sunlight
259 s16 y = MAP_BLOCKSIZE-1;
261 // This makes difference to diminishing in water.
262 bool stopped_to_solid_object = false;
264 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
269 MapNode &n = getNodeRef(pos);
271 if(current_light == 0)
275 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
277 // Do nothing: Sunlight is continued
279 else if(nodemgr->get(n).light_propagates == false)
281 // A solid object is on the way.
282 stopped_to_solid_object = true;
290 current_light = diminish_light(current_light);
293 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
295 if(current_light > old_light || remove_light)
297 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
300 if(diminish_light(current_light) != 0)
302 light_sources.insert(pos_relative + pos);
305 if(current_light == 0 && stopped_to_solid_object)
309 *black_air_left = true;
314 // Whether or not the block below should see LIGHT_SUN
315 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
318 If the block below hasn't already been marked invalid:
320 Check if the node below the block has proper sunlight at top.
321 If not, the block below is invalid.
323 Ignore non-transparent nodes as they always have no light
326 if(block_below_is_valid)
328 MapNode n = getNodeParent(v3s16(x, -1, z), &is_valid_position);
329 if (is_valid_position) {
330 if(nodemgr->get(n).light_propagates)
332 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
333 && sunlight_should_go_down == false)
334 block_below_is_valid = false;
335 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
336 && sunlight_should_go_down == true)
337 block_below_is_valid = false;
342 /*std::cout<<"InvalidBlockException for bottom block node"
344 // Just no block below, no need to panic.
350 return block_below_is_valid;
354 void MapBlock::copyTo(VoxelManipulator &dst)
356 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
357 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
359 // Copy from data to VoxelManipulator
360 dst.copyFrom(data, data_area, v3s16(0,0,0),
361 getPosRelative(), data_size);
364 void MapBlock::copyFrom(VoxelManipulator &dst)
366 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
367 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
369 // Copy from VoxelManipulator to data
370 dst.copyTo(data, data_area, v3s16(0,0,0),
371 getPosRelative(), data_size);
374 void MapBlock::actuallyUpdateDayNightDiff()
376 INodeDefManager *nodemgr = m_gamedef->ndef();
378 // Running this function un-expires m_day_night_differs
379 m_day_night_differs_expired = false;
382 m_day_night_differs = false;
389 Check if any lighting value differs
391 for (u32 i = 0; i < nodecount; i++) {
392 MapNode &n = data[i];
394 differs = !n.isLightDayNightEq(nodemgr);
400 If some lighting values differ, check if the whole thing is
401 just air. If it is just air, differs = false
404 bool only_air = true;
405 for (u32 i = 0; i < nodecount; i++) {
406 MapNode &n = data[i];
407 if (n.getContent() != CONTENT_AIR) {
416 // Set member variable
417 m_day_night_differs = differs;
420 void MapBlock::expireDayNightDiff()
422 //INodeDefManager *nodemgr = m_gamedef->ndef();
425 m_day_night_differs = false;
426 m_day_night_differs_expired = false;
430 m_day_night_differs_expired = true;
433 s16 MapBlock::getGroundLevel(v2s16 p2d)
439 s16 y = MAP_BLOCKSIZE-1;
442 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
443 if(m_gamedef->ndef()->get(n).walkable)
445 if(y == MAP_BLOCKSIZE-1)
453 catch(InvalidPositionException &e)
462 // List relevant id-name pairs for ids in the block using nodedef
463 // Renumbers the content IDs (starting at 0 and incrementing
464 // use static memory requires about 65535 * sizeof(int) ram in order to be
465 // sure we can handle all content ids. But it's absolutely worth it as it's
466 // a speedup of 4 for one of the major time consuming functions on storing
468 static content_t getBlockNodeIdMapping_mapping[USHRT_MAX + 1];
469 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
470 INodeDefManager *nodedef)
472 memset(getBlockNodeIdMapping_mapping, 0xFF, (USHRT_MAX + 1) * sizeof(content_t));
474 std::set<content_t> unknown_contents;
475 content_t id_counter = 0;
476 for (u32 i = 0; i < MapBlock::nodecount; i++) {
477 content_t global_id = nodes[i].getContent();
478 content_t id = CONTENT_IGNORE;
480 // Try to find an existing mapping
481 if (getBlockNodeIdMapping_mapping[global_id] != 0xFFFF) {
482 id = getBlockNodeIdMapping_mapping[global_id];
486 // We have to assign a new mapping
488 getBlockNodeIdMapping_mapping[global_id] = id;
490 const ContentFeatures &f = nodedef->get(global_id);
491 const std::string &name = f.name;
493 unknown_contents.insert(global_id);
495 nimap->set(id, name);
498 // Update the MapNode
499 nodes[i].setContent(id);
501 for(std::set<content_t>::const_iterator
502 i = unknown_contents.begin();
503 i != unknown_contents.end(); ++i){
504 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
505 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
508 // Correct ids in the block to match nodedef based on names.
509 // Unknown ones are added to nodedef.
510 // Will not update itself to match id-name pairs in nodedef.
511 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
514 INodeDefManager *nodedef = gamedef->ndef();
515 // This means the block contains incorrect ids, and we contain
516 // the information to convert those to names.
517 // nodedef contains information to convert our names to globally
519 std::set<content_t> unnamed_contents;
520 std::set<std::string> unallocatable_contents;
521 for (u32 i = 0; i < MapBlock::nodecount; i++) {
522 content_t local_id = nodes[i].getContent();
524 bool found = nimap->getName(local_id, name);
526 unnamed_contents.insert(local_id);
530 found = nodedef->getId(name, global_id);
532 global_id = gamedef->allocateUnknownNodeId(name);
533 if(global_id == CONTENT_IGNORE){
534 unallocatable_contents.insert(name);
538 nodes[i].setContent(global_id);
540 for(std::set<content_t>::const_iterator
541 i = unnamed_contents.begin();
542 i != unnamed_contents.end(); ++i){
543 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
544 <<"Block contains id "<<(*i)
545 <<" with no name mapping"<<std::endl;
547 for(std::set<std::string>::const_iterator
548 i = unallocatable_contents.begin();
549 i != unallocatable_contents.end(); ++i){
550 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
551 <<"Could not allocate global id for node name \""
552 <<(*i)<<"\""<<std::endl;
556 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
558 if(!ser_ver_supported(version))
559 throw VersionMismatchException("ERROR: MapBlock format not supported");
563 throw SerializationError("ERROR: Not writing dummy block.");
566 FATAL_ERROR_IF(version < SER_FMT_VER_LOWEST_WRITE, "Serialisation version error");
572 if(getDayNightDiff())
574 if(m_lighting_expired)
576 if(m_generated == false)
586 MapNode *tmp_nodes = new MapNode[nodecount];
587 for(u32 i=0; i<nodecount; i++)
588 tmp_nodes[i] = data[i];
589 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
591 u8 content_width = 2;
593 writeU8(os, content_width);
594 writeU8(os, params_width);
595 MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
596 content_width, params_width, true);
601 u8 content_width = 2;
603 writeU8(os, content_width);
604 writeU8(os, params_width);
605 MapNode::serializeBulk(os, version, data, nodecount,
606 content_width, params_width, true);
612 std::ostringstream oss(std::ios_base::binary);
613 m_node_metadata.serialize(oss);
614 compressZlib(oss.str(), os);
617 Data that goes to disk, but not the network
623 m_node_timers.serialize(os, version);
627 m_static_objects.serialize(os);
630 writeU32(os, getTimestamp());
632 // Write block-specific node definition id mapping
637 m_node_timers.serialize(os, version);
642 void MapBlock::serializeNetworkSpecific(std::ostream &os, u16 net_proto_version)
646 throw SerializationError("ERROR: Not writing dummy block.");
649 if(net_proto_version >= 21){
651 writeU8(os, version);
652 writeF1000(os, 0); // deprecated heat
653 writeF1000(os, 0); // deprecated humidity
657 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
659 if(!ser_ver_supported(version))
660 throw VersionMismatchException("ERROR: MapBlock format not supported");
662 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
664 m_day_night_differs_expired = false;
668 deSerialize_pre22(is, version, disk);
672 u8 flags = readU8(is);
673 is_underground = (flags & 0x01) ? true : false;
674 m_day_night_differs = (flags & 0x02) ? true : false;
675 m_lighting_expired = (flags & 0x04) ? true : false;
676 m_generated = (flags & 0x08) ? false : true;
681 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
682 <<": Bulk node data"<<std::endl);
683 u8 content_width = readU8(is);
684 u8 params_width = readU8(is);
685 if(content_width != 1 && content_width != 2)
686 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
687 if(params_width != 2)
688 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
689 MapNode::deSerializeBulk(is, version, data, nodecount,
690 content_width, params_width, true);
695 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
696 <<": Node metadata"<<std::endl);
699 std::ostringstream oss(std::ios_base::binary);
700 decompressZlib(is, oss);
701 std::istringstream iss(oss.str(), std::ios_base::binary);
703 m_node_metadata.deSerialize(iss, m_gamedef->idef());
705 content_nodemeta_deserialize_legacy(iss,
706 &m_node_metadata, &m_node_timers,
708 } catch(SerializationError &e) {
709 warningstream<<"MapBlock::deSerialize(): Ignoring an error"
710 <<" while deserializing node metadata at ("
711 <<PP(getPos())<<": "<<e.what()<<std::endl;
715 Data that is only on disk
725 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
726 <<": Node timers (ver==24)"<<std::endl);
727 m_node_timers.deSerialize(is, version);
731 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
732 <<": Static objects"<<std::endl);
733 m_static_objects.deSerialize(is);
736 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
737 <<": Timestamp"<<std::endl);
738 setTimestamp(readU32(is));
739 m_disk_timestamp = m_timestamp;
741 // Dynamically re-set ids based on node names
742 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
743 <<": NameIdMapping"<<std::endl);
745 nimap.deSerialize(is);
746 correctBlockNodeIds(&nimap, data, m_gamedef);
749 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
750 <<": Node timers (ver>=25)"<<std::endl);
751 m_node_timers.deSerialize(is, version);
755 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
756 <<": Done."<<std::endl);
759 void MapBlock::deSerializeNetworkSpecific(std::istream &is)
762 int version = readU8(is);
764 // throw SerializationError("unsupported MapBlock version");
766 readF1000(is); // deprecated heat
767 readF1000(is); // deprecated humidity
770 catch(SerializationError &e)
772 warningstream<<"MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
773 <<": "<<e.what()<<std::endl;
781 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
783 // Initialize default flags
784 is_underground = false;
785 m_day_night_differs = false;
786 m_lighting_expired = false;
789 // Make a temporary buffer
790 u32 ser_length = MapNode::serializedLength(version);
791 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
793 // These have no compression
794 if (version <= 3 || version == 5 || version == 6) {
797 if (is.gcount() != 1)
798 throw SerializationError(std::string(FUNCTION_NAME)
799 + ": not enough input data");
800 is_underground = tmp;
801 is.read((char *)*databuf_nodelist, nodecount * ser_length);
802 if ((u32)is.gcount() != nodecount * ser_length)
803 throw SerializationError(std::string(FUNCTION_NAME)
804 + ": not enough input data");
805 } else if (version <= 10) {
807 is.read((char *)&t8, 1);
811 // Uncompress and set material data
812 std::ostringstream os(std::ios_base::binary);
813 decompress(is, os, version);
814 std::string s = os.str();
815 if (s.size() != nodecount)
816 throw SerializationError(std::string(FUNCTION_NAME)
817 + ": not enough input data");
818 for (u32 i = 0; i < s.size(); i++) {
819 databuf_nodelist[i*ser_length] = s[i];
823 // Uncompress and set param data
824 std::ostringstream os(std::ios_base::binary);
825 decompress(is, os, version);
826 std::string s = os.str();
827 if (s.size() != nodecount)
828 throw SerializationError(std::string(FUNCTION_NAME)
829 + ": not enough input data");
830 for (u32 i = 0; i < s.size(); i++) {
831 databuf_nodelist[i*ser_length + 1] = s[i];
836 // Uncompress and set param2 data
837 std::ostringstream os(std::ios_base::binary);
838 decompress(is, os, version);
839 std::string s = os.str();
840 if (s.size() != nodecount)
841 throw SerializationError(std::string(FUNCTION_NAME)
842 + ": not enough input data");
843 for (u32 i = 0; i < s.size(); i++) {
844 databuf_nodelist[i*ser_length + 2] = s[i];
847 } else { // All other versions (10 to 21)
849 is.read((char*)&flags, 1);
850 is_underground = (flags & 0x01) ? true : false;
851 m_day_night_differs = (flags & 0x02) ? true : false;
852 m_lighting_expired = (flags & 0x04) ? true : false;
854 m_generated = (flags & 0x08) ? false : true;
857 std::ostringstream os(std::ios_base::binary);
858 decompress(is, os, version);
859 std::string s = os.str();
860 if (s.size() != nodecount * 3)
861 throw SerializationError(std::string(FUNCTION_NAME)
862 + ": decompress resulted in size other than nodecount*3");
864 // deserialize nodes from buffer
865 for (u32 i = 0; i < nodecount; i++) {
866 databuf_nodelist[i*ser_length] = s[i];
867 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
868 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
878 std::string data = deSerializeString(is);
879 std::istringstream iss(data, std::ios_base::binary);
880 content_nodemeta_deserialize_legacy(iss,
881 &m_node_metadata, &m_node_timers,
884 //std::string data = deSerializeLongString(is);
885 std::ostringstream oss(std::ios_base::binary);
886 decompressZlib(is, oss);
887 std::istringstream iss(oss.str(), std::ios_base::binary);
888 content_nodemeta_deserialize_legacy(iss,
889 &m_node_metadata, &m_node_timers,
892 } catch(SerializationError &e) {
893 warningstream<<"MapBlock::deSerialize(): Ignoring an error"
894 <<" while deserializing node metadata"<<std::endl;
899 // Deserialize node data
900 for (u32 i = 0; i < nodecount; i++) {
901 data[i].deSerialize(&databuf_nodelist[i * ser_length], version);
906 Versions up from 9 have block objects. (DEPRECATED)
909 u16 count = readU16(is);
910 // Not supported and length not known if count is not 0
912 warningstream<<"MapBlock::deSerialize_pre22(): "
913 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
919 Versions up from 15 have static objects.
922 m_static_objects.deSerialize(is);
926 setTimestamp(readU32(is));
927 m_disk_timestamp = m_timestamp;
929 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
932 // Dynamically re-set ids based on node names
934 // If supported, read node definition id mapping
936 nimap.deSerialize(is);
937 // Else set the legacy mapping
939 content_mapnode_get_name_id_mapping(&nimap);
941 correctBlockNodeIds(&nimap, data, m_gamedef);
945 // Legacy data changes
946 // This code has to convert from pre-22 to post-22 format.
947 INodeDefManager *nodedef = m_gamedef->ndef();
948 for(u32 i=0; i<nodecount; i++)
950 const ContentFeatures &f = nodedef->get(data[i].getContent());
952 if(nodedef->getId("default:stone") == data[i].getContent()
953 && data[i].getParam1() == 1)
955 data[i].setContent(nodedef->getId("default:stone_with_coal"));
956 data[i].setParam1(0);
958 else if(nodedef->getId("default:stone") == data[i].getContent()
959 && data[i].getParam1() == 2)
961 data[i].setContent(nodedef->getId("default:stone_with_iron"));
962 data[i].setParam1(0);
965 if(f.legacy_facedir_simple)
967 data[i].setParam2(data[i].getParam1());
968 data[i].setParam1(0);
971 if(f.legacy_wallmounted)
973 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
974 u8 dir_old_format = data[i].getParam2();
975 u8 dir_new_format = 0;
976 for(u8 j=0; j<8; j++)
978 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
984 data[i].setParam2(dir_new_format);
991 Get a quick string to describe what a block actually contains
993 std::string analyze_block(MapBlock *block)
998 std::ostringstream desc;
1000 v3s16 p = block->getPos();
1002 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1005 switch(block->getModified())
1007 case MOD_STATE_CLEAN:
1010 case MOD_STATE_WRITE_AT_UNLOAD:
1011 desc<<"WRITE_AT_UNLOAD, ";
1013 case MOD_STATE_WRITE_NEEDED:
1014 desc<<"WRITE_NEEDED, ";
1017 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1020 if(block->isGenerated())
1021 desc<<"is_gen [X], ";
1023 desc<<"is_gen [ ], ";
1025 if(block->getIsUnderground())
1026 desc<<"is_ug [X], ";
1028 desc<<"is_ug [ ], ";
1030 if(block->getLightingExpired())
1031 desc<<"lighting_exp [X], ";
1033 desc<<"lighting_exp [ ], ";
1035 if(block->isDummy())
1041 bool full_ignore = true;
1042 bool some_ignore = false;
1043 bool full_air = true;
1044 bool some_air = false;
1045 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1046 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1047 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1050 MapNode n = block->getNodeNoEx(p);
1051 content_t c = n.getContent();
1052 if(c == CONTENT_IGNORE)
1055 full_ignore = false;
1056 if(c == CONTENT_AIR)
1064 std::ostringstream ss;
1067 ss<<"IGNORE (full), ";
1068 else if(some_ignore)
1076 if(ss.str().size()>=2)
1077 desc<<ss.str().substr(0, ss.str().size()-2);
1082 return desc.str().substr(0, desc.str().size()-2);