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"
39 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
41 static const char *modified_reason_strings[] = {
50 "NodeMetaRef::reportMetadataChange",
52 "Timestamp expired (step)",
54 "removeRemovedObjects/remove",
55 "removeRemovedObjects/deactivate",
56 "Stored list cleared in activateObjects due to overflow",
57 "deactivateFarObjects: Static data moved in",
58 "deactivateFarObjects: Static data moved out",
59 "deactivateFarObjects: Static data changed considerably",
60 "finishBlockMake: expireDayNightDiff",
69 MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
72 m_pos_relative(pos * MAP_BLOCKSIZE),
74 m_modified(MOD_STATE_WRITE_NEEDED),
75 m_modified_reason(MOD_REASON_INITIAL),
76 is_underground(false),
77 m_lighting_expired(true),
78 m_day_night_differs(false),
79 m_day_night_differs_expired(true),
81 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
82 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
99 //MutexAutoLock lock(mesh_mutex);
113 bool MapBlock::isValidPositionParent(v3s16 p)
115 if(isValidPosition(p))
120 return m_parent->isValidPosition(getPosRelative() + p);
124 MapNode MapBlock::getNodeParent(v3s16 p, bool *is_valid_position)
126 if (isValidPosition(p) == false)
127 return m_parent->getNodeNoEx(getPosRelative() + p, is_valid_position);
130 if (is_valid_position)
131 *is_valid_position = false;
132 return MapNode(CONTENT_IGNORE);
134 if (is_valid_position)
135 *is_valid_position = true;
136 return data[p.Z * zstride + p.Y * ystride + p.X];
139 std::string MapBlock::getModifiedReasonString()
143 const u32 ubound = MYMIN(sizeof(m_modified_reason) * CHAR_BIT,
144 ARRLEN(modified_reason_strings));
146 for (u32 i = 0; i != ubound; i++) {
147 if ((m_modified_reason & (1 << i)) == 0)
150 reason += modified_reason_strings[i];
154 if (reason.length() > 2)
155 reason.resize(reason.length() - 2);
161 Propagates sunlight down through the block.
162 Doesn't modify nodes that are not affected by sunlight.
164 Returns false if sunlight at bottom block is invalid.
165 Returns true if sunlight at bottom block is valid.
166 Returns true if bottom block doesn't exist.
168 If there is a block above, continues from it.
169 If there is no block above, assumes there is sunlight, unless
170 is_underground is set or highest node is water.
172 All sunlighted nodes are added to light_sources.
174 if remove_light==true, sets non-sunlighted nodes black.
176 if black_air_left!=NULL, it is set to true if non-sunlighted
177 air is left in block.
179 bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
180 bool remove_light, bool *black_air_left)
182 INodeDefManager *nodemgr = m_gamedef->ndef();
184 // Whether the sunlight at the top of the bottom block is valid
185 bool block_below_is_valid = true;
187 v3s16 pos_relative = getPosRelative();
189 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
191 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
194 bool no_sunlight = false;
195 //bool no_top_block = false;
197 // Check if node above block has sunlight
199 bool is_valid_position;
200 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z),
202 if (is_valid_position)
204 if(n.getContent() == CONTENT_IGNORE)
207 no_sunlight = is_underground;
209 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
216 //no_top_block = true;
218 // NOTE: This makes over-ground roofed places sunlighted
219 // Assume sunlight, unless is_underground==true
226 MapNode n = getNodeNoEx(v3s16(x, MAP_BLOCKSIZE-1, z));
227 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
232 // NOTE: As of now, this just would make everything dark.
234 //no_sunlight = true;
237 #if 0 // Doesn't work; nothing gets light.
238 bool no_sunlight = true;
239 bool no_top_block = false;
240 // Check if node above block has sunlight
242 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
243 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
248 catch(InvalidPositionException &e)
254 /*std::cout<<"("<<x<<","<<z<<"): "
255 <<"no_top_block="<<no_top_block
256 <<", is_underground="<<is_underground
257 <<", no_sunlight="<<no_sunlight
260 s16 y = MAP_BLOCKSIZE-1;
262 // This makes difference to diminishing in water.
263 bool stopped_to_solid_object = false;
265 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
270 MapNode &n = getNodeRef(pos);
272 if(current_light == 0)
276 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
278 // Do nothing: Sunlight is continued
280 else if(nodemgr->get(n).light_propagates == false)
282 // A solid object is on the way.
283 stopped_to_solid_object = true;
291 current_light = diminish_light(current_light);
294 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
296 if(current_light > old_light || remove_light)
298 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
301 if(diminish_light(current_light) != 0)
303 light_sources.insert(pos_relative + pos);
306 if(current_light == 0 && stopped_to_solid_object)
310 *black_air_left = true;
315 // Whether or not the block below should see LIGHT_SUN
316 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
319 If the block below hasn't already been marked invalid:
321 Check if the node below the block has proper sunlight at top.
322 If not, the block below is invalid.
324 Ignore non-transparent nodes as they always have no light
327 if(block_below_is_valid)
329 MapNode n = getNodeParent(v3s16(x, -1, z), &is_valid_position);
330 if (is_valid_position) {
331 if(nodemgr->get(n).light_propagates)
333 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
334 && sunlight_should_go_down == false)
335 block_below_is_valid = false;
336 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
337 && sunlight_should_go_down == true)
338 block_below_is_valid = false;
343 /*std::cout<<"InvalidBlockException for bottom block node"
345 // Just no block below, no need to panic.
351 return block_below_is_valid;
355 void MapBlock::copyTo(VoxelManipulator &dst)
357 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
358 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
360 // Copy from data to VoxelManipulator
361 dst.copyFrom(data, data_area, v3s16(0,0,0),
362 getPosRelative(), data_size);
365 void MapBlock::copyFrom(VoxelManipulator &dst)
367 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
368 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
370 // Copy from VoxelManipulator to data
371 dst.copyTo(data, data_area, v3s16(0,0,0),
372 getPosRelative(), data_size);
375 void MapBlock::actuallyUpdateDayNightDiff()
377 INodeDefManager *nodemgr = m_gamedef->ndef();
379 // Running this function un-expires m_day_night_differs
380 m_day_night_differs_expired = false;
383 m_day_night_differs = false;
390 Check if any lighting value differs
392 for (u32 i = 0; i < nodecount; i++) {
393 MapNode &n = data[i];
395 differs = !n.isLightDayNightEq(nodemgr);
401 If some lighting values differ, check if the whole thing is
402 just air. If it is just air, differs = false
405 bool only_air = true;
406 for (u32 i = 0; i < nodecount; i++) {
407 MapNode &n = data[i];
408 if (n.getContent() != CONTENT_AIR) {
417 // Set member variable
418 m_day_night_differs = differs;
421 void MapBlock::expireDayNightDiff()
423 //INodeDefManager *nodemgr = m_gamedef->ndef();
426 m_day_night_differs = false;
427 m_day_night_differs_expired = false;
431 m_day_night_differs_expired = true;
434 s16 MapBlock::getGroundLevel(v2s16 p2d)
440 s16 y = MAP_BLOCKSIZE-1;
443 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
444 if(m_gamedef->ndef()->get(n).walkable)
446 if(y == MAP_BLOCKSIZE-1)
454 catch(InvalidPositionException &e)
463 // List relevant id-name pairs for ids in the block using nodedef
464 // Renumbers the content IDs (starting at 0 and incrementing
465 // use static memory requires about 65535 * sizeof(int) ram in order to be
466 // sure we can handle all content ids. But it's absolutely worth it as it's
467 // a speedup of 4 for one of the major time consuming functions on storing
469 static content_t getBlockNodeIdMapping_mapping[USHRT_MAX + 1];
470 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
471 INodeDefManager *nodedef)
473 memset(getBlockNodeIdMapping_mapping, 0xFF, (USHRT_MAX + 1) * sizeof(content_t));
475 std::set<content_t> unknown_contents;
476 content_t id_counter = 0;
477 for (u32 i = 0; i < MapBlock::nodecount; i++) {
478 content_t global_id = nodes[i].getContent();
479 content_t id = CONTENT_IGNORE;
481 // Try to find an existing mapping
482 if (getBlockNodeIdMapping_mapping[global_id] != 0xFFFF) {
483 id = getBlockNodeIdMapping_mapping[global_id];
487 // We have to assign a new mapping
489 getBlockNodeIdMapping_mapping[global_id] = id;
491 const ContentFeatures &f = nodedef->get(global_id);
492 const std::string &name = f.name;
494 unknown_contents.insert(global_id);
496 nimap->set(id, name);
499 // Update the MapNode
500 nodes[i].setContent(id);
502 for(std::set<content_t>::const_iterator
503 i = unknown_contents.begin();
504 i != unknown_contents.end(); ++i){
505 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
506 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
509 // Correct ids in the block to match nodedef based on names.
510 // Unknown ones are added to nodedef.
511 // Will not update itself to match id-name pairs in nodedef.
512 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
515 INodeDefManager *nodedef = gamedef->ndef();
516 // This means the block contains incorrect ids, and we contain
517 // the information to convert those to names.
518 // nodedef contains information to convert our names to globally
520 std::set<content_t> unnamed_contents;
521 std::set<std::string> unallocatable_contents;
522 for (u32 i = 0; i < MapBlock::nodecount; i++) {
523 content_t local_id = nodes[i].getContent();
525 bool found = nimap->getName(local_id, name);
527 unnamed_contents.insert(local_id);
531 found = nodedef->getId(name, global_id);
533 global_id = gamedef->allocateUnknownNodeId(name);
534 if(global_id == CONTENT_IGNORE){
535 unallocatable_contents.insert(name);
539 nodes[i].setContent(global_id);
541 for(std::set<content_t>::const_iterator
542 i = unnamed_contents.begin();
543 i != unnamed_contents.end(); ++i){
544 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
545 <<"Block contains id "<<(*i)
546 <<" with no name mapping"<<std::endl;
548 for(std::set<std::string>::const_iterator
549 i = unallocatable_contents.begin();
550 i != unallocatable_contents.end(); ++i){
551 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
552 <<"Could not allocate global id for node name \""
553 <<(*i)<<"\""<<std::endl;
557 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
559 if(!ser_ver_supported(version))
560 throw VersionMismatchException("ERROR: MapBlock format not supported");
564 throw SerializationError("ERROR: Not writing dummy block.");
567 FATAL_ERROR_IF(version < SER_FMT_VER_LOWEST_WRITE, "Serialisation version error");
573 if(getDayNightDiff())
575 if(m_lighting_expired)
577 if(m_generated == false)
587 MapNode *tmp_nodes = new MapNode[nodecount];
588 for(u32 i=0; i<nodecount; i++)
589 tmp_nodes[i] = data[i];
590 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
592 u8 content_width = 2;
594 writeU8(os, content_width);
595 writeU8(os, params_width);
596 MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
597 content_width, params_width, true);
602 u8 content_width = 2;
604 writeU8(os, content_width);
605 writeU8(os, params_width);
606 MapNode::serializeBulk(os, version, data, nodecount,
607 content_width, params_width, true);
613 std::ostringstream oss(std::ios_base::binary);
614 m_node_metadata.serialize(oss);
615 compressZlib(oss.str(), os);
618 Data that goes to disk, but not the network
624 m_node_timers.serialize(os, version);
628 m_static_objects.serialize(os);
631 writeU32(os, getTimestamp());
633 // Write block-specific node definition id mapping
638 m_node_timers.serialize(os, version);
643 void MapBlock::serializeNetworkSpecific(std::ostream &os, u16 net_proto_version)
647 throw SerializationError("ERROR: Not writing dummy block.");
650 if(net_proto_version >= 21){
652 writeU8(os, version);
653 writeF1000(os, 0); // deprecated heat
654 writeF1000(os, 0); // deprecated humidity
658 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
660 if(!ser_ver_supported(version))
661 throw VersionMismatchException("ERROR: MapBlock format not supported");
663 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
665 m_day_night_differs_expired = false;
669 deSerialize_pre22(is, version, disk);
673 u8 flags = readU8(is);
674 is_underground = (flags & 0x01) ? true : false;
675 m_day_night_differs = (flags & 0x02) ? true : false;
676 m_lighting_expired = (flags & 0x04) ? true : false;
677 m_generated = (flags & 0x08) ? false : true;
682 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
683 <<": Bulk node data"<<std::endl);
684 u8 content_width = readU8(is);
685 u8 params_width = readU8(is);
686 if(content_width != 1 && content_width != 2)
687 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
688 if(params_width != 2)
689 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
690 MapNode::deSerializeBulk(is, version, data, nodecount,
691 content_width, params_width, true);
696 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
697 <<": Node metadata"<<std::endl);
700 std::ostringstream oss(std::ios_base::binary);
701 decompressZlib(is, oss);
702 std::istringstream iss(oss.str(), std::ios_base::binary);
704 m_node_metadata.deSerialize(iss, m_gamedef->idef());
706 content_nodemeta_deserialize_legacy(iss,
707 &m_node_metadata, &m_node_timers,
709 } catch(SerializationError &e) {
710 warningstream<<"MapBlock::deSerialize(): Ignoring an error"
711 <<" while deserializing node metadata at ("
712 <<PP(getPos())<<": "<<e.what()<<std::endl;
716 Data that is only on disk
726 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
727 <<": Node timers (ver==24)"<<std::endl);
728 m_node_timers.deSerialize(is, version);
732 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
733 <<": Static objects"<<std::endl);
734 m_static_objects.deSerialize(is);
737 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
738 <<": Timestamp"<<std::endl);
739 setTimestamp(readU32(is));
740 m_disk_timestamp = m_timestamp;
742 // Dynamically re-set ids based on node names
743 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
744 <<": NameIdMapping"<<std::endl);
746 nimap.deSerialize(is);
747 correctBlockNodeIds(&nimap, data, m_gamedef);
750 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
751 <<": Node timers (ver>=25)"<<std::endl);
752 m_node_timers.deSerialize(is, version);
756 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
757 <<": Done."<<std::endl);
760 void MapBlock::deSerializeNetworkSpecific(std::istream &is)
763 int version = readU8(is);
765 // throw SerializationError("unsupported MapBlock version");
767 readF1000(is); // deprecated heat
768 readF1000(is); // deprecated humidity
771 catch(SerializationError &e)
773 warningstream<<"MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
774 <<": "<<e.what()<<std::endl;
782 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
784 // Initialize default flags
785 is_underground = false;
786 m_day_night_differs = false;
787 m_lighting_expired = false;
790 // Make a temporary buffer
791 u32 ser_length = MapNode::serializedLength(version);
792 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
794 // These have no compression
795 if (version <= 3 || version == 5 || version == 6) {
798 if (is.gcount() != 1)
799 throw SerializationError(std::string(FUNCTION_NAME)
800 + ": not enough input data");
801 is_underground = tmp;
802 is.read((char *)*databuf_nodelist, nodecount * ser_length);
803 if ((u32)is.gcount() != nodecount * ser_length)
804 throw SerializationError(std::string(FUNCTION_NAME)
805 + ": not enough input data");
806 } else if (version <= 10) {
808 is.read((char *)&t8, 1);
812 // Uncompress and set material data
813 std::ostringstream os(std::ios_base::binary);
814 decompress(is, os, version);
815 std::string s = os.str();
816 if (s.size() != nodecount)
817 throw SerializationError(std::string(FUNCTION_NAME)
818 + ": not enough input data");
819 for (u32 i = 0; i < s.size(); i++) {
820 databuf_nodelist[i*ser_length] = s[i];
824 // Uncompress and set param data
825 std::ostringstream os(std::ios_base::binary);
826 decompress(is, os, version);
827 std::string s = os.str();
828 if (s.size() != nodecount)
829 throw SerializationError(std::string(FUNCTION_NAME)
830 + ": not enough input data");
831 for (u32 i = 0; i < s.size(); i++) {
832 databuf_nodelist[i*ser_length + 1] = s[i];
837 // Uncompress and set param2 data
838 std::ostringstream os(std::ios_base::binary);
839 decompress(is, os, version);
840 std::string s = os.str();
841 if (s.size() != nodecount)
842 throw SerializationError(std::string(FUNCTION_NAME)
843 + ": not enough input data");
844 for (u32 i = 0; i < s.size(); i++) {
845 databuf_nodelist[i*ser_length + 2] = s[i];
848 } else { // All other versions (10 to 21)
850 is.read((char*)&flags, 1);
851 is_underground = (flags & 0x01) ? true : false;
852 m_day_night_differs = (flags & 0x02) ? true : false;
853 m_lighting_expired = (flags & 0x04) ? true : false;
855 m_generated = (flags & 0x08) ? false : true;
858 std::ostringstream os(std::ios_base::binary);
859 decompress(is, os, version);
860 std::string s = os.str();
861 if (s.size() != nodecount * 3)
862 throw SerializationError(std::string(FUNCTION_NAME)
863 + ": decompress resulted in size other than nodecount*3");
865 // deserialize nodes from buffer
866 for (u32 i = 0; i < nodecount; i++) {
867 databuf_nodelist[i*ser_length] = s[i];
868 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
869 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
879 std::string data = deSerializeString(is);
880 std::istringstream iss(data, std::ios_base::binary);
881 content_nodemeta_deserialize_legacy(iss,
882 &m_node_metadata, &m_node_timers,
885 //std::string data = deSerializeLongString(is);
886 std::ostringstream oss(std::ios_base::binary);
887 decompressZlib(is, oss);
888 std::istringstream iss(oss.str(), std::ios_base::binary);
889 content_nodemeta_deserialize_legacy(iss,
890 &m_node_metadata, &m_node_timers,
893 } catch(SerializationError &e) {
894 warningstream<<"MapBlock::deSerialize(): Ignoring an error"
895 <<" while deserializing node metadata"<<std::endl;
900 // Deserialize node data
901 for (u32 i = 0; i < nodecount; i++) {
902 data[i].deSerialize(&databuf_nodelist[i * ser_length], version);
907 Versions up from 9 have block objects. (DEPRECATED)
910 u16 count = readU16(is);
911 // Not supported and length not known if count is not 0
913 warningstream<<"MapBlock::deSerialize_pre22(): "
914 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
920 Versions up from 15 have static objects.
923 m_static_objects.deSerialize(is);
927 setTimestamp(readU32(is));
928 m_disk_timestamp = m_timestamp;
930 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
933 // Dynamically re-set ids based on node names
935 // If supported, read node definition id mapping
937 nimap.deSerialize(is);
938 // Else set the legacy mapping
940 content_mapnode_get_name_id_mapping(&nimap);
942 correctBlockNodeIds(&nimap, data, m_gamedef);
946 // Legacy data changes
947 // This code has to convert from pre-22 to post-22 format.
948 INodeDefManager *nodedef = m_gamedef->ndef();
949 for(u32 i=0; i<nodecount; i++)
951 const ContentFeatures &f = nodedef->get(data[i].getContent());
953 if(nodedef->getId("default:stone") == data[i].getContent()
954 && data[i].getParam1() == 1)
956 data[i].setContent(nodedef->getId("default:stone_with_coal"));
957 data[i].setParam1(0);
959 else if(nodedef->getId("default:stone") == data[i].getContent()
960 && data[i].getParam1() == 2)
962 data[i].setContent(nodedef->getId("default:stone_with_iron"));
963 data[i].setParam1(0);
966 if(f.legacy_facedir_simple)
968 data[i].setParam2(data[i].getParam1());
969 data[i].setParam1(0);
972 if(f.legacy_wallmounted)
974 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
975 u8 dir_old_format = data[i].getParam2();
976 u8 dir_new_format = 0;
977 for(u8 j=0; j<8; j++)
979 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
985 data[i].setParam2(dir_new_format);
992 Get a quick string to describe what a block actually contains
994 std::string analyze_block(MapBlock *block)
999 std::ostringstream desc;
1001 v3s16 p = block->getPos();
1003 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1006 switch(block->getModified())
1008 case MOD_STATE_CLEAN:
1011 case MOD_STATE_WRITE_AT_UNLOAD:
1012 desc<<"WRITE_AT_UNLOAD, ";
1014 case MOD_STATE_WRITE_NEEDED:
1015 desc<<"WRITE_NEEDED, ";
1018 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1021 if(block->isGenerated())
1022 desc<<"is_gen [X], ";
1024 desc<<"is_gen [ ], ";
1026 if(block->getIsUnderground())
1027 desc<<"is_ug [X], ";
1029 desc<<"is_ug [ ], ";
1031 if(block->getLightingExpired())
1032 desc<<"lighting_exp [X], ";
1034 desc<<"lighting_exp [ ], ";
1036 if(block->isDummy())
1042 bool full_ignore = true;
1043 bool some_ignore = false;
1044 bool full_air = true;
1045 bool some_air = false;
1046 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1047 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1048 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1051 MapNode n = block->getNodeNoEx(p);
1052 content_t c = n.getContent();
1053 if(c == CONTENT_IGNORE)
1056 full_ignore = false;
1057 if(c == CONTENT_AIR)
1065 std::ostringstream ss;
1068 ss<<"IGNORE (full), ";
1069 else if(some_ignore)
1077 if(ss.str().size()>=2)
1078 desc<<ss.str().substr(0, ss.str().size()-2);
1083 return desc.str().substr(0, desc.str().size()-2);