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):
49 m_modified(MOD_STATE_WRITE_NEEDED),
50 m_modified_reason("initial"),
51 m_modified_reason_too_long(false),
52 is_underground(false),
53 m_lighting_expired(true),
54 m_day_night_differs(false),
55 m_day_night_differs_expired(true),
57 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
58 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
75 //JMutexAutoLock lock(mesh_mutex);
89 bool MapBlock::isValidPositionParent(v3s16 p)
91 if(isValidPosition(p))
96 return m_parent->isValidPosition(getPosRelative() + p);
100 MapNode MapBlock::getNodeParent(v3s16 p)
102 if(isValidPosition(p) == false)
104 return m_parent->getNode(getPosRelative() + p);
109 throw InvalidPositionException();
110 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
114 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
116 if(isValidPosition(p) == false)
118 m_parent->setNode(getPosRelative() + p, n);
123 throw InvalidPositionException();
124 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
128 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
130 if(isValidPosition(p) == false)
133 return m_parent->getNode(getPosRelative() + p);
135 catch(InvalidPositionException &e)
137 return MapNode(CONTENT_IGNORE);
144 return MapNode(CONTENT_IGNORE);
146 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
151 Propagates sunlight down through the block.
152 Doesn't modify nodes that are not affected by sunlight.
154 Returns false if sunlight at bottom block is invalid.
155 Returns true if sunlight at bottom block is valid.
156 Returns true if bottom block doesn't exist.
158 If there is a block above, continues from it.
159 If there is no block above, assumes there is sunlight, unless
160 is_underground is set or highest node is water.
162 All sunlighted nodes are added to light_sources.
164 if remove_light==true, sets non-sunlighted nodes black.
166 if black_air_left!=NULL, it is set to true if non-sunlighted
167 air is left in block.
169 bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
170 bool remove_light, bool *black_air_left)
172 INodeDefManager *nodemgr = m_gamedef->ndef();
174 // Whether the sunlight at the top of the bottom block is valid
175 bool block_below_is_valid = true;
177 v3s16 pos_relative = getPosRelative();
179 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
181 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
184 bool no_sunlight = false;
185 bool no_top_block = false;
186 // Check if node above block has sunlight
188 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
189 if(n.getContent() == CONTENT_IGNORE)
192 no_sunlight = is_underground;
194 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
199 catch(InvalidPositionException &e)
203 // NOTE: This makes over-ground roofed places sunlighted
204 // Assume sunlight, unless is_underground==true
211 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
212 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
217 // NOTE: As of now, this just would make everything dark.
219 //no_sunlight = true;
222 #if 0 // Doesn't work; nothing gets light.
223 bool no_sunlight = true;
224 bool no_top_block = false;
225 // Check if node above block has sunlight
227 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
228 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
233 catch(InvalidPositionException &e)
239 /*std::cout<<"("<<x<<","<<z<<"): "
240 <<"no_top_block="<<no_top_block
241 <<", is_underground="<<is_underground
242 <<", no_sunlight="<<no_sunlight
245 s16 y = MAP_BLOCKSIZE-1;
247 // This makes difference to diminishing in water.
248 bool stopped_to_solid_object = false;
250 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
255 MapNode &n = getNodeRef(pos);
257 if(current_light == 0)
261 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
263 // Do nothing: Sunlight is continued
265 else if(nodemgr->get(n).light_propagates == false)
267 // A solid object is on the way.
268 stopped_to_solid_object = true;
276 current_light = diminish_light(current_light);
279 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
281 if(current_light > old_light || remove_light)
283 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
286 if(diminish_light(current_light) != 0)
288 light_sources.insert(pos_relative + pos);
291 if(current_light == 0 && stopped_to_solid_object)
295 *black_air_left = true;
300 // Whether or not the block below should see LIGHT_SUN
301 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
304 If the block below hasn't already been marked invalid:
306 Check if the node below the block has proper sunlight at top.
307 If not, the block below is invalid.
309 Ignore non-transparent nodes as they always have no light
313 if(block_below_is_valid)
315 MapNode n = getNodeParent(v3s16(x, -1, z));
316 if(nodemgr->get(n).light_propagates)
318 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
319 && sunlight_should_go_down == false)
320 block_below_is_valid = false;
321 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
322 && sunlight_should_go_down == true)
323 block_below_is_valid = false;
327 catch(InvalidPositionException &e)
329 /*std::cout<<"InvalidBlockException for bottom block node"
331 // Just no block below, no need to panic.
336 return block_below_is_valid;
340 void MapBlock::copyTo(VoxelManipulator &dst)
342 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
343 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
345 // Copy from data to VoxelManipulator
346 dst.copyFrom(data, data_area, v3s16(0,0,0),
347 getPosRelative(), data_size);
350 void MapBlock::copyFrom(VoxelManipulator &dst)
352 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
353 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
355 // Copy from VoxelManipulator to data
356 dst.copyTo(data, data_area, v3s16(0,0,0),
357 getPosRelative(), data_size);
360 void MapBlock::actuallyUpdateDayNightDiff()
362 INodeDefManager *nodemgr = m_gamedef->ndef();
363 // Running this function un-expires m_day_night_differs
364 m_day_night_differs_expired = false;
368 m_day_night_differs = false;
372 bool differs = false;
375 Check if any lighting value differs
377 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
379 MapNode &n = data[i];
380 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
388 If some lighting values differ, check if the whole thing is
389 just air. If it is, differ = false
393 bool only_air = true;
394 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
396 MapNode &n = data[i];
397 if(n.getContent() != CONTENT_AIR)
407 // Set member variable
408 m_day_night_differs = differs;
411 void MapBlock::expireDayNightDiff()
413 //INodeDefManager *nodemgr = m_gamedef->ndef();
416 m_day_night_differs = false;
417 m_day_night_differs_expired = false;
421 m_day_night_differs_expired = true;
424 s16 MapBlock::getGroundLevel(v2s16 p2d)
430 s16 y = MAP_BLOCKSIZE-1;
433 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
434 if(m_gamedef->ndef()->get(n).walkable)
436 if(y == MAP_BLOCKSIZE-1)
444 catch(InvalidPositionException &e)
453 // List relevant id-name pairs for ids in the block using nodedef
454 // Renumbers the content IDs (starting at 0 and incrementing
455 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
456 INodeDefManager *nodedef)
458 std::map<content_t, content_t> mapping;
459 std::set<content_t> unknown_contents;
460 content_t id_counter = 0;
461 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
463 content_t global_id = nodes[i].getContent();
464 content_t id = CONTENT_IGNORE;
466 // Try to find an existing mapping
467 std::map<content_t, content_t>::iterator j = mapping.find(global_id);
468 if(j != mapping.end())
474 // We have to assign a new mapping
476 mapping.insert(std::make_pair(global_id, id));
478 const ContentFeatures &f = nodedef->get(global_id);
479 const std::string &name = f.name;
481 unknown_contents.insert(global_id);
483 nimap->set(id, name);
486 // Update the MapNode
487 nodes[i].setContent(id);
489 for(std::set<content_t>::const_iterator
490 i = unknown_contents.begin();
491 i != unknown_contents.end(); i++){
492 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
493 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
496 // Correct ids in the block to match nodedef based on names.
497 // Unknown ones are added to nodedef.
498 // Will not update itself to match id-name pairs in nodedef.
499 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
502 INodeDefManager *nodedef = gamedef->ndef();
503 // This means the block contains incorrect ids, and we contain
504 // the information to convert those to names.
505 // nodedef contains information to convert our names to globally
507 std::set<content_t> unnamed_contents;
508 std::set<std::string> unallocatable_contents;
509 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
511 content_t local_id = nodes[i].getContent();
513 bool found = nimap->getName(local_id, name);
515 unnamed_contents.insert(local_id);
519 found = nodedef->getId(name, global_id);
521 global_id = gamedef->allocateUnknownNodeId(name);
522 if(global_id == CONTENT_IGNORE){
523 unallocatable_contents.insert(name);
527 nodes[i].setContent(global_id);
529 for(std::set<content_t>::const_iterator
530 i = unnamed_contents.begin();
531 i != unnamed_contents.end(); i++){
532 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
533 <<"Block contains id "<<(*i)
534 <<" with no name mapping"<<std::endl;
536 for(std::set<std::string>::const_iterator
537 i = unallocatable_contents.begin();
538 i != unallocatable_contents.end(); i++){
539 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
540 <<"Could not allocate global id for node name \""
541 <<(*i)<<"\""<<std::endl;
545 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
547 if(!ser_ver_supported(version))
548 throw VersionMismatchException("ERROR: MapBlock format not supported");
552 throw SerializationError("ERROR: Not writing dummy block.");
555 // Can't do this anymore; we have 16-bit dynamically allocated node IDs
556 // in memory; conversion just won't work in this direction.
558 throw SerializationError("MapBlock::serialize: serialization to "
559 "version < 24 not possible");
565 if(getDayNightDiff())
567 if(m_lighting_expired)
569 if(m_generated == false)
577 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
580 MapNode *tmp_nodes = new MapNode[nodecount];
581 for(u32 i=0; i<nodecount; i++)
582 tmp_nodes[i] = data[i];
583 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
585 u8 content_width = 2;
587 writeU8(os, content_width);
588 writeU8(os, params_width);
589 MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
590 content_width, params_width, true);
595 u8 content_width = 2;
597 writeU8(os, content_width);
598 writeU8(os, params_width);
599 MapNode::serializeBulk(os, version, data, nodecount,
600 content_width, params_width, true);
606 std::ostringstream oss(std::ios_base::binary);
607 m_node_metadata.serialize(oss);
608 compressZlib(oss.str(), os);
611 Data that goes to disk, but not the network
617 m_node_timers.serialize(os, version);
621 m_static_objects.serialize(os);
624 writeU32(os, getTimestamp());
626 // Write block-specific node definition id mapping
631 m_node_timers.serialize(os, version);
636 void MapBlock::serializeNetworkSpecific(std::ostream &os, u16 net_proto_version)
640 throw SerializationError("ERROR: Not writing dummy block.");
643 if(net_proto_version >= 21){
645 writeU8(os, version);
646 writeF1000(os, 0); // deprecated heat
647 writeF1000(os, 0); // deprecated humidity
651 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
653 if(!ser_ver_supported(version))
654 throw VersionMismatchException("ERROR: MapBlock format not supported");
656 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
658 m_day_night_differs_expired = false;
662 deSerialize_pre22(is, version, disk);
666 u8 flags = readU8(is);
667 is_underground = (flags & 0x01) ? true : false;
668 m_day_night_differs = (flags & 0x02) ? true : false;
669 m_lighting_expired = (flags & 0x04) ? true : false;
670 m_generated = (flags & 0x08) ? false : true;
675 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
676 <<": Bulk node data"<<std::endl);
677 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
678 u8 content_width = readU8(is);
679 u8 params_width = readU8(is);
680 if(content_width != 1 && content_width != 2)
681 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
682 if(params_width != 2)
683 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
684 MapNode::deSerializeBulk(is, version, data, nodecount,
685 content_width, params_width, true);
690 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
691 <<": Node metadata"<<std::endl);
694 std::ostringstream oss(std::ios_base::binary);
695 decompressZlib(is, oss);
696 std::istringstream iss(oss.str(), std::ios_base::binary);
698 m_node_metadata.deSerialize(iss, m_gamedef);
700 content_nodemeta_deserialize_legacy(iss,
701 &m_node_metadata, &m_node_timers,
704 catch(SerializationError &e)
706 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
707 <<" while deserializing node metadata at ("
708 <<PP(getPos())<<": "<<e.what()<<std::endl;
712 Data that is only on disk
722 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
723 <<": Node timers (ver==24)"<<std::endl);
724 m_node_timers.deSerialize(is, version);
728 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
729 <<": Static objects"<<std::endl);
730 m_static_objects.deSerialize(is);
733 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
734 <<": Timestamp"<<std::endl);
735 setTimestamp(readU32(is));
736 m_disk_timestamp = m_timestamp;
738 // Dynamically re-set ids based on node names
739 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
740 <<": NameIdMapping"<<std::endl);
742 nimap.deSerialize(is);
743 correctBlockNodeIds(&nimap, data, m_gamedef);
746 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
747 <<": Node timers (ver>=25)"<<std::endl);
748 m_node_timers.deSerialize(is, version);
752 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
753 <<": Done."<<std::endl);
756 void MapBlock::deSerializeNetworkSpecific(std::istream &is)
759 int version = readU8(is);
761 // throw SerializationError("unsupported MapBlock version");
763 readF1000(is); // deprecated heat
764 readF1000(is); // deprecated humidity
767 catch(SerializationError &e)
769 errorstream<<"WARNING: MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
770 <<": "<<e.what()<<std::endl;
778 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
780 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
782 // Initialize default flags
783 is_underground = false;
784 m_day_night_differs = false;
785 m_lighting_expired = false;
788 // Make a temporary buffer
789 u32 ser_length = MapNode::serializedLength(version);
790 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
792 // These have no compression
793 if(version <= 3 || version == 5 || version == 6)
798 throw SerializationError
799 ("MapBlock::deSerialize: no 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
804 ("MapBlock::deSerialize: no enough input data");
806 else if(version <= 10)
809 is.read((char*)&t8, 1);
813 // Uncompress and set material data
814 std::ostringstream os(std::ios_base::binary);
815 decompress(is, os, version);
816 std::string s = os.str();
817 if(s.size() != nodecount)
818 throw SerializationError
819 ("MapBlock::deSerialize: invalid format");
820 for(u32 i=0; i<s.size(); i++)
822 databuf_nodelist[i*ser_length] = s[i];
826 // Uncompress and set param data
827 std::ostringstream os(std::ios_base::binary);
828 decompress(is, os, version);
829 std::string s = os.str();
830 if(s.size() != nodecount)
831 throw SerializationError
832 ("MapBlock::deSerialize: invalid format");
833 for(u32 i=0; i<s.size(); i++)
835 databuf_nodelist[i*ser_length + 1] = s[i];
841 // Uncompress and set param2 data
842 std::ostringstream os(std::ios_base::binary);
843 decompress(is, os, version);
844 std::string s = os.str();
845 if(s.size() != nodecount)
846 throw SerializationError
847 ("MapBlock::deSerialize: invalid format");
848 for(u32 i=0; i<s.size(); i++)
850 databuf_nodelist[i*ser_length + 2] = s[i];
854 // All other versions (newest)
858 is.read((char*)&flags, 1);
859 is_underground = (flags & 0x01) ? true : false;
860 m_day_night_differs = (flags & 0x02) ? true : false;
861 m_lighting_expired = (flags & 0x04) ? true : false;
863 m_generated = (flags & 0x08) ? false : true;
866 std::ostringstream os(std::ios_base::binary);
867 decompress(is, os, version);
868 std::string s = os.str();
869 if(s.size() != nodecount*3)
870 throw SerializationError
871 ("MapBlock::deSerialize: decompress resulted in size"
872 " other than nodecount*3");
874 // deserialize nodes from buffer
875 for(u32 i=0; i<nodecount; i++)
877 databuf_nodelist[i*ser_length] = s[i];
878 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
879 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
891 std::string data = deSerializeString(is);
892 std::istringstream iss(data, std::ios_base::binary);
893 content_nodemeta_deserialize_legacy(iss,
894 &m_node_metadata, &m_node_timers,
899 //std::string data = deSerializeLongString(is);
900 std::ostringstream oss(std::ios_base::binary);
901 decompressZlib(is, oss);
902 std::istringstream iss(oss.str(), std::ios_base::binary);
903 content_nodemeta_deserialize_legacy(iss,
904 &m_node_metadata, &m_node_timers,
908 catch(SerializationError &e)
910 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
911 <<" while deserializing node metadata"<<std::endl;
916 // Deserialize node data
917 for(u32 i=0; i<nodecount; i++)
919 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
925 Versions up from 9 have block objects. (DEPRECATED)
928 u16 count = readU16(is);
929 // Not supported and length not known if count is not 0
931 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
932 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
938 Versions up from 15 have static objects.
941 m_static_objects.deSerialize(is);
945 setTimestamp(readU32(is));
946 m_disk_timestamp = m_timestamp;
948 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
951 // Dynamically re-set ids based on node names
953 // If supported, read node definition id mapping
955 nimap.deSerialize(is);
956 // Else set the legacy mapping
958 content_mapnode_get_name_id_mapping(&nimap);
960 correctBlockNodeIds(&nimap, data, m_gamedef);
964 // Legacy data changes
965 // This code has to convert from pre-22 to post-22 format.
966 INodeDefManager *nodedef = m_gamedef->ndef();
967 for(u32 i=0; i<nodecount; i++)
969 const ContentFeatures &f = nodedef->get(data[i].getContent());
971 if(nodedef->getId("default:stone") == data[i].getContent()
972 && data[i].getParam1() == 1)
974 data[i].setContent(nodedef->getId("default:stone_with_coal"));
975 data[i].setParam1(0);
977 else if(nodedef->getId("default:stone") == data[i].getContent()
978 && data[i].getParam1() == 2)
980 data[i].setContent(nodedef->getId("default:stone_with_iron"));
981 data[i].setParam1(0);
984 if(f.legacy_facedir_simple)
986 data[i].setParam2(data[i].getParam1());
987 data[i].setParam1(0);
990 if(f.legacy_wallmounted)
992 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
993 u8 dir_old_format = data[i].getParam2();
994 u8 dir_new_format = 0;
995 for(u8 j=0; j<8; j++)
997 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1003 data[i].setParam2(dir_new_format);
1010 Get a quick string to describe what a block actually contains
1012 std::string analyze_block(MapBlock *block)
1017 std::ostringstream desc;
1019 v3s16 p = block->getPos();
1021 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1024 switch(block->getModified())
1026 case MOD_STATE_CLEAN:
1029 case MOD_STATE_WRITE_AT_UNLOAD:
1030 desc<<"WRITE_AT_UNLOAD, ";
1032 case MOD_STATE_WRITE_NEEDED:
1033 desc<<"WRITE_NEEDED, ";
1036 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1039 if(block->isGenerated())
1040 desc<<"is_gen [X], ";
1042 desc<<"is_gen [ ], ";
1044 if(block->getIsUnderground())
1045 desc<<"is_ug [X], ";
1047 desc<<"is_ug [ ], ";
1049 if(block->getLightingExpired())
1050 desc<<"lighting_exp [X], ";
1052 desc<<"lighting_exp [ ], ";
1054 if(block->isDummy())
1060 bool full_ignore = true;
1061 bool some_ignore = false;
1062 bool full_air = true;
1063 bool some_air = false;
1064 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1065 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1066 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1069 MapNode n = block->getNode(p);
1070 content_t c = n.getContent();
1071 if(c == CONTENT_IGNORE)
1074 full_ignore = false;
1075 if(c == CONTENT_AIR)
1083 std::ostringstream ss;
1086 ss<<"IGNORE (full), ";
1087 else if(some_ignore)
1095 if(ss.str().size()>=2)
1096 desc<<ss.str().substr(0, ss.str().size()-2);
1101 return desc.str().substr(0, desc.str().size()-2);