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):
48 weather_update_time(0),
52 m_modified(MOD_STATE_WRITE_NEEDED),
53 m_modified_reason("initial"),
54 m_modified_reason_too_long(false),
55 is_underground(false),
56 m_lighting_expired(true),
57 m_day_night_differs(false),
58 m_day_night_differs_expired(true),
60 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
61 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
79 //JMutexAutoLock lock(mesh_mutex);
93 bool MapBlock::isValidPositionParent(v3s16 p)
95 if(isValidPosition(p))
100 return m_parent->isValidPosition(getPosRelative() + p);
104 MapNode MapBlock::getNodeParent(v3s16 p)
106 if(isValidPosition(p) == false)
108 return m_parent->getNode(getPosRelative() + p);
113 throw InvalidPositionException();
114 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
118 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
120 if(isValidPosition(p) == false)
122 m_parent->setNode(getPosRelative() + p, n);
127 throw InvalidPositionException();
128 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
132 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
134 if(isValidPosition(p) == false)
137 return m_parent->getNode(getPosRelative() + p);
139 catch(InvalidPositionException &e)
141 return MapNode(CONTENT_IGNORE);
148 return MapNode(CONTENT_IGNORE);
150 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
155 Propagates sunlight down through the block.
156 Doesn't modify nodes that are not affected by sunlight.
158 Returns false if sunlight at bottom block is invalid.
159 Returns true if sunlight at bottom block is valid.
160 Returns true if bottom block doesn't exist.
162 If there is a block above, continues from it.
163 If there is no block above, assumes there is sunlight, unless
164 is_underground is set or highest node is water.
166 All sunlighted nodes are added to light_sources.
168 if remove_light==true, sets non-sunlighted nodes black.
170 if black_air_left!=NULL, it is set to true if non-sunlighted
171 air is left in block.
173 bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
174 bool remove_light, bool *black_air_left)
176 INodeDefManager *nodemgr = m_gamedef->ndef();
178 // Whether the sunlight at the top of the bottom block is valid
179 bool block_below_is_valid = true;
181 v3s16 pos_relative = getPosRelative();
183 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
185 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
188 bool no_sunlight = false;
189 bool no_top_block = false;
190 // Check if node above block has sunlight
192 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
193 if(n.getContent() == CONTENT_IGNORE)
196 no_sunlight = is_underground;
198 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
203 catch(InvalidPositionException &e)
207 // NOTE: This makes over-ground roofed places sunlighted
208 // Assume sunlight, unless is_underground==true
215 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
216 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
221 // NOTE: As of now, this just would make everything dark.
223 //no_sunlight = true;
226 #if 0 // Doesn't work; nothing gets light.
227 bool no_sunlight = true;
228 bool no_top_block = false;
229 // Check if node above block has sunlight
231 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
232 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
237 catch(InvalidPositionException &e)
243 /*std::cout<<"("<<x<<","<<z<<"): "
244 <<"no_top_block="<<no_top_block
245 <<", is_underground="<<is_underground
246 <<", no_sunlight="<<no_sunlight
249 s16 y = MAP_BLOCKSIZE-1;
251 // This makes difference to diminishing in water.
252 bool stopped_to_solid_object = false;
254 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
259 MapNode &n = getNodeRef(pos);
261 if(current_light == 0)
265 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
267 // Do nothing: Sunlight is continued
269 else if(nodemgr->get(n).light_propagates == false)
271 // A solid object is on the way.
272 stopped_to_solid_object = true;
280 current_light = diminish_light(current_light);
283 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
285 if(current_light > old_light || remove_light)
287 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
290 if(diminish_light(current_light) != 0)
292 light_sources.insert(pos_relative + pos);
295 if(current_light == 0 && stopped_to_solid_object)
299 *black_air_left = true;
304 // Whether or not the block below should see LIGHT_SUN
305 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
308 If the block below hasn't already been marked invalid:
310 Check if the node below the block has proper sunlight at top.
311 If not, the block below is invalid.
313 Ignore non-transparent nodes as they always have no light
317 if(block_below_is_valid)
319 MapNode n = getNodeParent(v3s16(x, -1, z));
320 if(nodemgr->get(n).light_propagates)
322 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
323 && sunlight_should_go_down == false)
324 block_below_is_valid = false;
325 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
326 && sunlight_should_go_down == true)
327 block_below_is_valid = false;
331 catch(InvalidPositionException &e)
333 /*std::cout<<"InvalidBlockException for bottom block node"
335 // Just no block below, no need to panic.
340 return block_below_is_valid;
344 void MapBlock::copyTo(VoxelManipulator &dst)
346 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
347 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
349 // Copy from data to VoxelManipulator
350 dst.copyFrom(data, data_area, v3s16(0,0,0),
351 getPosRelative(), data_size);
354 void MapBlock::copyFrom(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 VoxelManipulator to data
360 dst.copyTo(data, data_area, v3s16(0,0,0),
361 getPosRelative(), data_size);
364 void MapBlock::actuallyUpdateDayNightDiff()
366 INodeDefManager *nodemgr = m_gamedef->ndef();
367 // Running this function un-expires m_day_night_differs
368 m_day_night_differs_expired = false;
372 m_day_night_differs = false;
376 bool differs = false;
379 Check if any lighting value differs
381 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
383 MapNode &n = data[i];
384 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
392 If some lighting values differ, check if the whole thing is
393 just air. If it is, differ = false
397 bool only_air = true;
398 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
400 MapNode &n = data[i];
401 if(n.getContent() != CONTENT_AIR)
411 // Set member variable
412 m_day_night_differs = differs;
415 void MapBlock::expireDayNightDiff()
417 //INodeDefManager *nodemgr = m_gamedef->ndef();
420 m_day_night_differs = false;
421 m_day_night_differs_expired = false;
425 m_day_night_differs_expired = true;
428 s16 MapBlock::getGroundLevel(v2s16 p2d)
434 s16 y = MAP_BLOCKSIZE-1;
437 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
438 if(m_gamedef->ndef()->get(n).walkable)
440 if(y == MAP_BLOCKSIZE-1)
448 catch(InvalidPositionException &e)
457 // List relevant id-name pairs for ids in the block using nodedef
458 // Renumbers the content IDs (starting at 0 and incrementing
459 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
460 INodeDefManager *nodedef)
462 std::map<content_t, content_t> mapping;
463 std::set<content_t> unknown_contents;
464 content_t id_counter = 0;
465 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
467 content_t global_id = nodes[i].getContent();
468 content_t id = CONTENT_IGNORE;
470 // Try to find an existing mapping
471 std::map<content_t, content_t>::iterator j = mapping.find(global_id);
472 if(j != mapping.end())
478 // We have to assign a new mapping
480 mapping.insert(std::make_pair(global_id, id));
482 const ContentFeatures &f = nodedef->get(global_id);
483 const std::string &name = f.name;
485 unknown_contents.insert(global_id);
487 nimap->set(id, name);
490 // Update the MapNode
491 nodes[i].setContent(id);
493 for(std::set<content_t>::const_iterator
494 i = unknown_contents.begin();
495 i != unknown_contents.end(); i++){
496 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
497 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
500 // Correct ids in the block to match nodedef based on names.
501 // Unknown ones are added to nodedef.
502 // Will not update itself to match id-name pairs in nodedef.
503 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
506 INodeDefManager *nodedef = gamedef->ndef();
507 // This means the block contains incorrect ids, and we contain
508 // the information to convert those to names.
509 // nodedef contains information to convert our names to globally
511 std::set<content_t> unnamed_contents;
512 std::set<std::string> unallocatable_contents;
513 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
515 content_t local_id = nodes[i].getContent();
517 bool found = nimap->getName(local_id, name);
519 unnamed_contents.insert(local_id);
523 found = nodedef->getId(name, global_id);
525 global_id = gamedef->allocateUnknownNodeId(name);
526 if(global_id == CONTENT_IGNORE){
527 unallocatable_contents.insert(name);
531 nodes[i].setContent(global_id);
533 for(std::set<content_t>::const_iterator
534 i = unnamed_contents.begin();
535 i != unnamed_contents.end(); i++){
536 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
537 <<"Block contains id "<<(*i)
538 <<" with no name mapping"<<std::endl;
540 for(std::set<std::string>::const_iterator
541 i = unallocatable_contents.begin();
542 i != unallocatable_contents.end(); i++){
543 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
544 <<"Could not allocate global id for node name \""
545 <<(*i)<<"\""<<std::endl;
549 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
551 if(!ser_ver_supported(version))
552 throw VersionMismatchException("ERROR: MapBlock format not supported");
556 throw SerializationError("ERROR: Not writing dummy block.");
559 // Can't do this anymore; we have 16-bit dynamically allocated node IDs
560 // in memory; conversion just won't work in this direction.
562 throw SerializationError("MapBlock::serialize: serialization to "
563 "version < 24 not possible");
569 if(getDayNightDiff())
571 if(m_lighting_expired)
573 if(m_generated == false)
581 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
584 MapNode *tmp_nodes = new MapNode[nodecount];
585 for(u32 i=0; i<nodecount; i++)
586 tmp_nodes[i] = data[i];
587 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
589 u8 content_width = 2;
591 writeU8(os, content_width);
592 writeU8(os, params_width);
593 MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
594 content_width, params_width, true);
599 u8 content_width = 2;
601 writeU8(os, content_width);
602 writeU8(os, params_width);
603 MapNode::serializeBulk(os, version, data, nodecount,
604 content_width, params_width, true);
610 std::ostringstream oss(std::ios_base::binary);
611 m_node_metadata.serialize(oss);
612 compressZlib(oss.str(), os);
615 Data that goes to disk, but not the network
621 m_node_timers.serialize(os, version);
625 m_static_objects.serialize(os);
628 writeU32(os, getTimestamp());
630 // Write block-specific node definition id mapping
635 m_node_timers.serialize(os, version);
640 void MapBlock::serializeNetworkSpecific(std::ostream &os, u16 net_proto_version)
644 throw SerializationError("ERROR: Not writing dummy block.");
647 if(net_proto_version >= 21){
649 writeU8(os, version);
650 writeF1000(os, heat);
651 writeF1000(os, humidity);
655 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
657 if(!ser_ver_supported(version))
658 throw VersionMismatchException("ERROR: MapBlock format not supported");
660 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
662 m_day_night_differs_expired = false;
666 deSerialize_pre22(is, version, disk);
670 u8 flags = readU8(is);
671 is_underground = (flags & 0x01) ? true : false;
672 m_day_night_differs = (flags & 0x02) ? true : false;
673 m_lighting_expired = (flags & 0x04) ? true : false;
674 m_generated = (flags & 0x08) ? false : true;
679 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
680 <<": Bulk node data"<<std::endl);
681 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
682 u8 content_width = readU8(is);
683 u8 params_width = readU8(is);
684 if(content_width != 1 && content_width != 2)
685 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
686 if(params_width != 2)
687 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
688 MapNode::deSerializeBulk(is, version, data, nodecount,
689 content_width, params_width, true);
694 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
695 <<": Node metadata"<<std::endl);
698 std::ostringstream oss(std::ios_base::binary);
699 decompressZlib(is, oss);
700 std::istringstream iss(oss.str(), std::ios_base::binary);
702 m_node_metadata.deSerialize(iss, m_gamedef);
704 content_nodemeta_deserialize_legacy(iss,
705 &m_node_metadata, &m_node_timers,
708 catch(SerializationError &e)
710 errorstream<<"WARNING: 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 heat = readF1000(is);
768 humidity = readF1000(is);
771 catch(SerializationError &e)
773 errorstream<<"WARNING: MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
774 <<": "<<e.what()<<std::endl;
782 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
784 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
786 // Initialize default flags
787 is_underground = false;
788 m_day_night_differs = false;
789 m_lighting_expired = false;
792 // Make a temporary buffer
793 u32 ser_length = MapNode::serializedLength(version);
794 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
796 // These have no compression
797 if(version <= 3 || version == 5 || version == 6)
802 throw SerializationError
803 ("MapBlock::deSerialize: no enough input data");
804 is_underground = tmp;
805 is.read((char*)*databuf_nodelist, nodecount * ser_length);
806 if((u32)is.gcount() != nodecount * ser_length)
807 throw SerializationError
808 ("MapBlock::deSerialize: no enough input data");
810 else if(version <= 10)
813 is.read((char*)&t8, 1);
817 // Uncompress and set material data
818 std::ostringstream os(std::ios_base::binary);
819 decompress(is, os, version);
820 std::string s = os.str();
821 if(s.size() != nodecount)
822 throw SerializationError
823 ("MapBlock::deSerialize: invalid format");
824 for(u32 i=0; i<s.size(); i++)
826 databuf_nodelist[i*ser_length] = s[i];
830 // Uncompress and set param data
831 std::ostringstream os(std::ios_base::binary);
832 decompress(is, os, version);
833 std::string s = os.str();
834 if(s.size() != nodecount)
835 throw SerializationError
836 ("MapBlock::deSerialize: invalid format");
837 for(u32 i=0; i<s.size(); i++)
839 databuf_nodelist[i*ser_length + 1] = s[i];
845 // Uncompress and set param2 data
846 std::ostringstream os(std::ios_base::binary);
847 decompress(is, os, version);
848 std::string s = os.str();
849 if(s.size() != nodecount)
850 throw SerializationError
851 ("MapBlock::deSerialize: invalid format");
852 for(u32 i=0; i<s.size(); i++)
854 databuf_nodelist[i*ser_length + 2] = s[i];
858 // All other versions (newest)
862 is.read((char*)&flags, 1);
863 is_underground = (flags & 0x01) ? true : false;
864 m_day_night_differs = (flags & 0x02) ? true : false;
865 m_lighting_expired = (flags & 0x04) ? true : false;
867 m_generated = (flags & 0x08) ? false : true;
870 std::ostringstream os(std::ios_base::binary);
871 decompress(is, os, version);
872 std::string s = os.str();
873 if(s.size() != nodecount*3)
874 throw SerializationError
875 ("MapBlock::deSerialize: decompress resulted in size"
876 " other than nodecount*3");
878 // deserialize nodes from buffer
879 for(u32 i=0; i<nodecount; i++)
881 databuf_nodelist[i*ser_length] = s[i];
882 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
883 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
895 std::string data = deSerializeString(is);
896 std::istringstream iss(data, std::ios_base::binary);
897 content_nodemeta_deserialize_legacy(iss,
898 &m_node_metadata, &m_node_timers,
903 //std::string data = deSerializeLongString(is);
904 std::ostringstream oss(std::ios_base::binary);
905 decompressZlib(is, oss);
906 std::istringstream iss(oss.str(), std::ios_base::binary);
907 content_nodemeta_deserialize_legacy(iss,
908 &m_node_metadata, &m_node_timers,
912 catch(SerializationError &e)
914 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
915 <<" while deserializing node metadata"<<std::endl;
920 // Deserialize node data
921 for(u32 i=0; i<nodecount; i++)
923 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
929 Versions up from 9 have block objects. (DEPRECATED)
932 u16 count = readU16(is);
933 // Not supported and length not known if count is not 0
935 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
936 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
942 Versions up from 15 have static objects.
945 m_static_objects.deSerialize(is);
949 setTimestamp(readU32(is));
950 m_disk_timestamp = m_timestamp;
952 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
955 // Dynamically re-set ids based on node names
957 // If supported, read node definition id mapping
959 nimap.deSerialize(is);
960 // Else set the legacy mapping
962 content_mapnode_get_name_id_mapping(&nimap);
964 correctBlockNodeIds(&nimap, data, m_gamedef);
968 // Legacy data changes
969 // This code has to convert from pre-22 to post-22 format.
970 INodeDefManager *nodedef = m_gamedef->ndef();
971 for(u32 i=0; i<nodecount; i++)
973 const ContentFeatures &f = nodedef->get(data[i].getContent());
975 if(nodedef->getId("default:stone") == data[i].getContent()
976 && data[i].getParam1() == 1)
978 data[i].setContent(nodedef->getId("default:stone_with_coal"));
979 data[i].setParam1(0);
981 else if(nodedef->getId("default:stone") == data[i].getContent()
982 && data[i].getParam1() == 2)
984 data[i].setContent(nodedef->getId("default:stone_with_iron"));
985 data[i].setParam1(0);
988 if(f.legacy_facedir_simple)
990 data[i].setParam2(data[i].getParam1());
991 data[i].setParam1(0);
994 if(f.legacy_wallmounted)
996 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
997 u8 dir_old_format = data[i].getParam2();
998 u8 dir_new_format = 0;
999 for(u8 j=0; j<8; j++)
1001 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1007 data[i].setParam2(dir_new_format);
1014 Get a quick string to describe what a block actually contains
1016 std::string analyze_block(MapBlock *block)
1021 std::ostringstream desc;
1023 v3s16 p = block->getPos();
1025 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1028 switch(block->getModified())
1030 case MOD_STATE_CLEAN:
1033 case MOD_STATE_WRITE_AT_UNLOAD:
1034 desc<<"WRITE_AT_UNLOAD, ";
1036 case MOD_STATE_WRITE_NEEDED:
1037 desc<<"WRITE_NEEDED, ";
1040 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1043 if(block->isGenerated())
1044 desc<<"is_gen [X], ";
1046 desc<<"is_gen [ ], ";
1048 if(block->getIsUnderground())
1049 desc<<"is_ug [X], ";
1051 desc<<"is_ug [ ], ";
1053 if(block->getLightingExpired())
1054 desc<<"lighting_exp [X], ";
1056 desc<<"lighting_exp [ ], ";
1058 if(block->isDummy())
1064 bool full_ignore = true;
1065 bool some_ignore = false;
1066 bool full_air = true;
1067 bool some_air = false;
1068 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1069 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1070 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1073 MapNode n = block->getNode(p);
1074 content_t c = n.getContent();
1075 if(c == CONTENT_IGNORE)
1078 full_ignore = false;
1079 if(c == CONTENT_AIR)
1087 std::ostringstream ss;
1090 ss<<"IGNORE (full), ";
1091 else if(some_ignore)
1099 if(ss.str().size()>=2)
1100 desc<<ss.str().substr(0, ss.str().size()-2);
1105 return desc.str().substr(0, desc.str().size()-2);