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<<")"
45 MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
53 m_modified(MOD_STATE_WRITE_NEEDED),
54 m_modified_reason("initial"),
55 m_modified_reason_too_long(false),
56 is_underground(false),
57 m_lighting_expired(true),
58 m_day_night_differs(false),
59 m_day_night_differs_expired(true),
61 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
62 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
80 //JMutexAutoLock lock(mesh_mutex);
94 bool MapBlock::isValidPositionParent(v3s16 p)
96 if(isValidPosition(p))
101 return m_parent->isValidPosition(getPosRelative() + p);
105 MapNode MapBlock::getNodeParent(v3s16 p)
107 if(isValidPosition(p) == false)
109 return m_parent->getNode(getPosRelative() + p);
114 throw InvalidPositionException();
115 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
119 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
121 if(isValidPosition(p) == false)
123 m_parent->setNode(getPosRelative() + p, n);
128 throw InvalidPositionException();
129 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
133 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
135 if(isValidPosition(p) == false)
138 return m_parent->getNode(getPosRelative() + p);
140 catch(InvalidPositionException &e)
142 return MapNode(CONTENT_IGNORE);
149 return MapNode(CONTENT_IGNORE);
151 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
156 Propagates sunlight down through the block.
157 Doesn't modify nodes that are not affected by sunlight.
159 Returns false if sunlight at bottom block is invalid.
160 Returns true if sunlight at bottom block is valid.
161 Returns true if bottom block doesn't exist.
163 If there is a block above, continues from it.
164 If there is no block above, assumes there is sunlight, unless
165 is_underground is set or highest node is water.
167 All sunlighted nodes are added to light_sources.
169 if remove_light==true, sets non-sunlighted nodes black.
171 if black_air_left!=NULL, it is set to true if non-sunlighted
172 air is left in block.
174 bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
175 bool remove_light, bool *black_air_left)
177 INodeDefManager *nodemgr = m_gamedef->ndef();
179 // Whether the sunlight at the top of the bottom block is valid
180 bool block_below_is_valid = true;
182 v3s16 pos_relative = getPosRelative();
184 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
186 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
189 bool no_sunlight = false;
190 bool no_top_block = false;
191 // Check if node above block has sunlight
193 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
194 if(n.getContent() == CONTENT_IGNORE)
197 no_sunlight = is_underground;
199 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
204 catch(InvalidPositionException &e)
208 // NOTE: This makes over-ground roofed places sunlighted
209 // Assume sunlight, unless is_underground==true
216 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
217 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
222 // NOTE: As of now, this just would make everything dark.
224 //no_sunlight = true;
227 #if 0 // Doesn't work; nothing gets light.
228 bool no_sunlight = true;
229 bool no_top_block = false;
230 // Check if node above block has sunlight
232 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
233 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
238 catch(InvalidPositionException &e)
244 /*std::cout<<"("<<x<<","<<z<<"): "
245 <<"no_top_block="<<no_top_block
246 <<", is_underground="<<is_underground
247 <<", no_sunlight="<<no_sunlight
250 s16 y = MAP_BLOCKSIZE-1;
252 // This makes difference to diminishing in water.
253 bool stopped_to_solid_object = false;
255 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
260 MapNode &n = getNodeRef(pos);
262 if(current_light == 0)
266 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
268 // Do nothing: Sunlight is continued
270 else if(nodemgr->get(n).light_propagates == false)
272 // A solid object is on the way.
273 stopped_to_solid_object = true;
281 current_light = diminish_light(current_light);
284 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
286 if(current_light > old_light || remove_light)
288 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
291 if(diminish_light(current_light) != 0)
293 light_sources.insert(pos_relative + pos);
296 if(current_light == 0 && stopped_to_solid_object)
300 *black_air_left = true;
305 // Whether or not the block below should see LIGHT_SUN
306 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
309 If the block below hasn't already been marked invalid:
311 Check if the node below the block has proper sunlight at top.
312 If not, the block below is invalid.
314 Ignore non-transparent nodes as they always have no light
318 if(block_below_is_valid)
320 MapNode n = getNodeParent(v3s16(x, -1, z));
321 if(nodemgr->get(n).light_propagates)
323 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
324 && sunlight_should_go_down == false)
325 block_below_is_valid = false;
326 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
327 && sunlight_should_go_down == true)
328 block_below_is_valid = false;
332 catch(InvalidPositionException &e)
334 /*std::cout<<"InvalidBlockException for bottom block node"
336 // Just no block below, no need to panic.
341 return block_below_is_valid;
345 void MapBlock::copyTo(VoxelManipulator &dst)
347 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
348 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
350 // Copy from data to VoxelManipulator
351 dst.copyFrom(data, data_area, v3s16(0,0,0),
352 getPosRelative(), data_size);
355 void MapBlock::copyFrom(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 VoxelManipulator to data
361 dst.copyTo(data, data_area, v3s16(0,0,0),
362 getPosRelative(), data_size);
365 void MapBlock::actuallyUpdateDayNightDiff()
367 INodeDefManager *nodemgr = m_gamedef->ndef();
368 // Running this function un-expires m_day_night_differs
369 m_day_night_differs_expired = false;
373 m_day_night_differs = false;
377 bool differs = false;
380 Check if any lighting value differs
382 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
384 MapNode &n = data[i];
385 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
393 If some lighting values differ, check if the whole thing is
394 just air. If it is, differ = false
398 bool only_air = true;
399 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
401 MapNode &n = data[i];
402 if(n.getContent() != CONTENT_AIR)
412 // Set member variable
413 m_day_night_differs = differs;
416 void MapBlock::expireDayNightDiff()
418 //INodeDefManager *nodemgr = m_gamedef->ndef();
421 m_day_night_differs = false;
422 m_day_night_differs_expired = false;
426 m_day_night_differs_expired = true;
429 s16 MapBlock::getGroundLevel(v2s16 p2d)
435 s16 y = MAP_BLOCKSIZE-1;
438 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
439 if(m_gamedef->ndef()->get(n).walkable)
441 if(y == MAP_BLOCKSIZE-1)
449 catch(InvalidPositionException &e)
458 // List relevant id-name pairs for ids in the block using nodedef
459 // Renumbers the content IDs (starting at 0 and incrementing
460 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
461 INodeDefManager *nodedef)
463 std::map<content_t, content_t> mapping;
464 std::set<content_t> unknown_contents;
465 content_t id_counter = 0;
466 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
468 content_t global_id = nodes[i].getContent();
469 content_t id = CONTENT_IGNORE;
471 // Try to find an existing mapping
472 std::map<content_t, content_t>::iterator j = mapping.find(global_id);
473 if(j != mapping.end())
479 // We have to assign a new mapping
481 mapping.insert(std::make_pair(global_id, id));
483 const ContentFeatures &f = nodedef->get(global_id);
484 const std::string &name = f.name;
486 unknown_contents.insert(global_id);
488 nimap->set(id, name);
491 // Update the MapNode
492 nodes[i].setContent(id);
494 for(std::set<content_t>::const_iterator
495 i = unknown_contents.begin();
496 i != unknown_contents.end(); i++){
497 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
498 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
501 // Correct ids in the block to match nodedef based on names.
502 // Unknown ones are added to nodedef.
503 // Will not update itself to match id-name pairs in nodedef.
504 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
507 INodeDefManager *nodedef = gamedef->ndef();
508 // This means the block contains incorrect ids, and we contain
509 // the information to convert those to names.
510 // nodedef contains information to convert our names to globally
512 std::set<content_t> unnamed_contents;
513 std::set<std::string> unallocatable_contents;
514 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
516 content_t local_id = nodes[i].getContent();
518 bool found = nimap->getName(local_id, name);
520 unnamed_contents.insert(local_id);
524 found = nodedef->getId(name, global_id);
526 global_id = gamedef->allocateUnknownNodeId(name);
527 if(global_id == CONTENT_IGNORE){
528 unallocatable_contents.insert(name);
532 nodes[i].setContent(global_id);
534 for(std::set<content_t>::const_iterator
535 i = unnamed_contents.begin();
536 i != unnamed_contents.end(); i++){
537 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
538 <<"Block contains id "<<(*i)
539 <<" with no name mapping"<<std::endl;
541 for(std::set<std::string>::const_iterator
542 i = unallocatable_contents.begin();
543 i != unallocatable_contents.end(); i++){
544 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
545 <<"Could not allocate global id for node name \""
546 <<(*i)<<"\""<<std::endl;
550 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
552 if(!ser_ver_supported(version))
553 throw VersionMismatchException("ERROR: MapBlock format not supported");
557 throw SerializationError("ERROR: Not writing dummy block.");
560 // Can't do this anymore; we have 16-bit dynamically allocated node IDs
561 // in memory; conversion just won't work in this direction.
563 throw SerializationError("MapBlock::serialize: serialization to "
564 "version < 24 not possible");
570 if(getDayNightDiff())
572 if(m_lighting_expired)
574 if(m_generated == false)
582 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
585 MapNode *tmp_nodes = new MapNode[nodecount];
586 for(u32 i=0; i<nodecount; i++)
587 tmp_nodes[i] = data[i];
588 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
590 u8 content_width = 2;
592 writeU8(os, content_width);
593 writeU8(os, params_width);
594 MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
595 content_width, params_width, true);
600 u8 content_width = 2;
602 writeU8(os, content_width);
603 writeU8(os, params_width);
604 MapNode::serializeBulk(os, version, data, nodecount,
605 content_width, params_width, true);
611 std::ostringstream oss(std::ios_base::binary);
612 m_node_metadata.serialize(oss);
613 compressZlib(oss.str(), os);
616 Data that goes to disk, but not the network
622 m_node_timers.serialize(os, version);
626 m_static_objects.serialize(os);
629 writeU32(os, getTimestamp());
631 // Write block-specific node definition id mapping
636 m_node_timers.serialize(os, version);
641 void MapBlock::serializeNetworkSpecific(std::ostream &os, u16 net_proto_version)
645 throw SerializationError("ERROR: Not writing dummy block.");
648 if(net_proto_version >= 21){
650 writeU8(os, version);
651 writeF1000(os, heat);
652 writeF1000(os, humidity);
656 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
658 if(!ser_ver_supported(version))
659 throw VersionMismatchException("ERROR: MapBlock format not supported");
661 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
663 m_day_night_differs_expired = false;
667 deSerialize_pre22(is, version, disk);
671 u8 flags = readU8(is);
672 is_underground = (flags & 0x01) ? true : false;
673 m_day_night_differs = (flags & 0x02) ? true : false;
674 m_lighting_expired = (flags & 0x04) ? true : false;
675 m_generated = (flags & 0x08) ? false : true;
680 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
681 <<": Bulk node data"<<std::endl);
682 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
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);
705 content_nodemeta_deserialize_legacy(iss,
706 &m_node_metadata, &m_node_timers,
709 catch(SerializationError &e)
711 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
712 <<" while deserializing node metadata at ("
713 <<PP(getPos())<<": "<<e.what()<<std::endl;
717 Data that is only on disk
727 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
728 <<": Node timers (ver==24)"<<std::endl);
729 m_node_timers.deSerialize(is, version);
733 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
734 <<": Static objects"<<std::endl);
735 m_static_objects.deSerialize(is);
738 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
739 <<": Timestamp"<<std::endl);
740 setTimestamp(readU32(is));
741 m_disk_timestamp = m_timestamp;
743 // Dynamically re-set ids based on node names
744 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
745 <<": NameIdMapping"<<std::endl);
747 nimap.deSerialize(is);
748 correctBlockNodeIds(&nimap, data, m_gamedef);
751 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
752 <<": Node timers (ver>=25)"<<std::endl);
753 m_node_timers.deSerialize(is, version);
757 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
758 <<": Done."<<std::endl);
761 void MapBlock::deSerializeNetworkSpecific(std::istream &is)
764 int version = readU8(is);
766 // throw SerializationError("unsupported MapBlock version");
768 heat = readF1000(is);
769 humidity = readF1000(is);
772 catch(SerializationError &e)
774 errorstream<<"WARNING: MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
775 <<": "<<e.what()<<std::endl;
783 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
785 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
787 // Initialize default flags
788 is_underground = false;
789 m_day_night_differs = false;
790 m_lighting_expired = false;
793 // Make a temporary buffer
794 u32 ser_length = MapNode::serializedLength(version);
795 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
797 // These have no compression
798 if(version <= 3 || version == 5 || version == 6)
803 throw SerializationError
804 ("MapBlock::deSerialize: no enough input data");
805 is_underground = tmp;
806 is.read((char*)*databuf_nodelist, nodecount * ser_length);
807 if((u32)is.gcount() != nodecount * ser_length)
808 throw SerializationError
809 ("MapBlock::deSerialize: no enough input data");
811 else if(version <= 10)
814 is.read((char*)&t8, 1);
818 // Uncompress and set material data
819 std::ostringstream os(std::ios_base::binary);
820 decompress(is, os, version);
821 std::string s = os.str();
822 if(s.size() != nodecount)
823 throw SerializationError
824 ("MapBlock::deSerialize: invalid format");
825 for(u32 i=0; i<s.size(); i++)
827 databuf_nodelist[i*ser_length] = s[i];
831 // Uncompress and set param data
832 std::ostringstream os(std::ios_base::binary);
833 decompress(is, os, version);
834 std::string s = os.str();
835 if(s.size() != nodecount)
836 throw SerializationError
837 ("MapBlock::deSerialize: invalid format");
838 for(u32 i=0; i<s.size(); i++)
840 databuf_nodelist[i*ser_length + 1] = s[i];
846 // Uncompress and set param2 data
847 std::ostringstream os(std::ios_base::binary);
848 decompress(is, os, version);
849 std::string s = os.str();
850 if(s.size() != nodecount)
851 throw SerializationError
852 ("MapBlock::deSerialize: invalid format");
853 for(u32 i=0; i<s.size(); i++)
855 databuf_nodelist[i*ser_length + 2] = s[i];
859 // All other versions (newest)
863 is.read((char*)&flags, 1);
864 is_underground = (flags & 0x01) ? true : false;
865 m_day_night_differs = (flags & 0x02) ? true : false;
866 m_lighting_expired = (flags & 0x04) ? true : false;
868 m_generated = (flags & 0x08) ? false : true;
871 std::ostringstream os(std::ios_base::binary);
872 decompress(is, os, version);
873 std::string s = os.str();
874 if(s.size() != nodecount*3)
875 throw SerializationError
876 ("MapBlock::deSerialize: decompress resulted in size"
877 " other than nodecount*3");
879 // deserialize nodes from buffer
880 for(u32 i=0; i<nodecount; i++)
882 databuf_nodelist[i*ser_length] = s[i];
883 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
884 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
896 std::string data = deSerializeString(is);
897 std::istringstream iss(data, std::ios_base::binary);
898 content_nodemeta_deserialize_legacy(iss,
899 &m_node_metadata, &m_node_timers,
904 //std::string data = deSerializeLongString(is);
905 std::ostringstream oss(std::ios_base::binary);
906 decompressZlib(is, oss);
907 std::istringstream iss(oss.str(), std::ios_base::binary);
908 content_nodemeta_deserialize_legacy(iss,
909 &m_node_metadata, &m_node_timers,
913 catch(SerializationError &e)
915 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
916 <<" while deserializing node metadata"<<std::endl;
921 // Deserialize node data
922 for(u32 i=0; i<nodecount; i++)
924 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
930 Versions up from 9 have block objects. (DEPRECATED)
933 u16 count = readU16(is);
934 // Not supported and length not known if count is not 0
936 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
937 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
943 Versions up from 15 have static objects.
946 m_static_objects.deSerialize(is);
950 setTimestamp(readU32(is));
951 m_disk_timestamp = m_timestamp;
953 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
956 // Dynamically re-set ids based on node names
958 // If supported, read node definition id mapping
960 nimap.deSerialize(is);
961 // Else set the legacy mapping
963 content_mapnode_get_name_id_mapping(&nimap);
965 correctBlockNodeIds(&nimap, data, m_gamedef);
969 // Legacy data changes
970 // This code has to convert from pre-22 to post-22 format.
971 INodeDefManager *nodedef = m_gamedef->ndef();
972 for(u32 i=0; i<nodecount; i++)
974 const ContentFeatures &f = nodedef->get(data[i].getContent());
976 if(nodedef->getId("default:stone") == data[i].getContent()
977 && data[i].getParam1() == 1)
979 data[i].setContent(nodedef->getId("default:stone_with_coal"));
980 data[i].setParam1(0);
982 else if(nodedef->getId("default:stone") == data[i].getContent()
983 && data[i].getParam1() == 2)
985 data[i].setContent(nodedef->getId("default:stone_with_iron"));
986 data[i].setParam1(0);
989 if(f.legacy_facedir_simple)
991 data[i].setParam2(data[i].getParam1());
992 data[i].setParam1(0);
995 if(f.legacy_wallmounted)
997 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
998 u8 dir_old_format = data[i].getParam2();
999 u8 dir_new_format = 0;
1000 for(u8 j=0; j<8; j++)
1002 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1008 data[i].setParam2(dir_new_format);
1015 Get a quick string to describe what a block actually contains
1017 std::string analyze_block(MapBlock *block)
1022 std::ostringstream desc;
1024 v3s16 p = block->getPos();
1026 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1029 switch(block->getModified())
1031 case MOD_STATE_CLEAN:
1034 case MOD_STATE_WRITE_AT_UNLOAD:
1035 desc<<"WRITE_AT_UNLOAD, ";
1037 case MOD_STATE_WRITE_NEEDED:
1038 desc<<"WRITE_NEEDED, ";
1041 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1044 if(block->isGenerated())
1045 desc<<"is_gen [X], ";
1047 desc<<"is_gen [ ], ";
1049 if(block->getIsUnderground())
1050 desc<<"is_ug [X], ";
1052 desc<<"is_ug [ ], ";
1054 if(block->getLightingExpired())
1055 desc<<"lighting_exp [X], ";
1057 desc<<"lighting_exp [ ], ";
1059 if(block->isDummy())
1065 bool full_ignore = true;
1066 bool some_ignore = false;
1067 bool full_air = true;
1068 bool some_air = false;
1069 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1070 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1071 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1074 MapNode n = block->getNode(p);
1075 content_t c = n.getContent();
1076 if(c == CONTENT_IGNORE)
1079 full_ignore = false;
1080 if(c == CONTENT_AIR)
1088 std::ostringstream ss;
1091 ss<<"IGNORE (full), ";
1092 else if(some_ignore)
1100 if(ss.str().size()>=2)
1101 desc<<ss.str().substr(0, ss.str().size()-2);
1106 return desc.str().substr(0, desc.str().size()-2);