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.
28 #include "nodemetadata.h"
31 #include "nameidmapping.h"
32 #include "content_mapnode.h" // For legacy name-id mapping
33 #include "content_nodemeta.h" // For legacy deserialization
35 #include "mapblock_mesh.h"
37 #include "util/string.h"
38 #include "util/serialize.h"
40 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
46 MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
54 m_modified(MOD_STATE_WRITE_NEEDED),
55 m_modified_reason("initial"),
56 m_modified_reason_too_long(false),
57 is_underground(false),
58 m_lighting_expired(true),
59 m_day_night_differs(false),
60 m_day_night_differs_expired(true),
62 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
63 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
81 //JMutexAutoLock lock(mesh_mutex);
95 bool MapBlock::isValidPositionParent(v3s16 p)
97 if(isValidPosition(p))
102 return m_parent->isValidPosition(getPosRelative() + p);
106 MapNode MapBlock::getNodeParent(v3s16 p)
108 if(isValidPosition(p) == false)
110 return m_parent->getNode(getPosRelative() + p);
115 throw InvalidPositionException();
116 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
120 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
122 if(isValidPosition(p) == false)
124 m_parent->setNode(getPosRelative() + p, n);
129 throw InvalidPositionException();
130 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
134 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
136 if(isValidPosition(p) == false)
139 return m_parent->getNode(getPosRelative() + p);
141 catch(InvalidPositionException &e)
143 return MapNode(CONTENT_IGNORE);
150 return MapNode(CONTENT_IGNORE);
152 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
157 Propagates sunlight down through the block.
158 Doesn't modify nodes that are not affected by sunlight.
160 Returns false if sunlight at bottom block is invalid.
161 Returns true if sunlight at bottom block is valid.
162 Returns true if bottom block doesn't exist.
164 If there is a block above, continues from it.
165 If there is no block above, assumes there is sunlight, unless
166 is_underground is set or highest node is water.
168 All sunlighted nodes are added to light_sources.
170 if remove_light==true, sets non-sunlighted nodes black.
172 if black_air_left!=NULL, it is set to true if non-sunlighted
173 air is left in block.
175 bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
176 bool remove_light, bool *black_air_left)
178 INodeDefManager *nodemgr = m_gamedef->ndef();
180 // Whether the sunlight at the top of the bottom block is valid
181 bool block_below_is_valid = true;
183 v3s16 pos_relative = getPosRelative();
185 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
187 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
190 bool no_sunlight = false;
191 bool no_top_block = false;
192 // Check if node above block has sunlight
194 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
195 if(n.getContent() == CONTENT_IGNORE)
198 no_sunlight = is_underground;
200 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
205 catch(InvalidPositionException &e)
209 // NOTE: This makes over-ground roofed places sunlighted
210 // Assume sunlight, unless is_underground==true
217 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
218 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
223 // NOTE: As of now, this just would make everything dark.
225 //no_sunlight = true;
228 #if 0 // Doesn't work; nothing gets light.
229 bool no_sunlight = true;
230 bool no_top_block = false;
231 // Check if node above block has sunlight
233 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
234 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
239 catch(InvalidPositionException &e)
245 /*std::cout<<"("<<x<<","<<z<<"): "
246 <<"no_top_block="<<no_top_block
247 <<", is_underground="<<is_underground
248 <<", no_sunlight="<<no_sunlight
251 s16 y = MAP_BLOCKSIZE-1;
253 // This makes difference to diminishing in water.
254 bool stopped_to_solid_object = false;
256 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
261 MapNode &n = getNodeRef(pos);
263 if(current_light == 0)
267 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
269 // Do nothing: Sunlight is continued
271 else if(nodemgr->get(n).light_propagates == false)
273 // A solid object is on the way.
274 stopped_to_solid_object = true;
282 current_light = diminish_light(current_light);
285 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
287 if(current_light > old_light || remove_light)
289 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
292 if(diminish_light(current_light) != 0)
294 light_sources.insert(pos_relative + pos);
297 if(current_light == 0 && stopped_to_solid_object)
301 *black_air_left = true;
306 // Whether or not the block below should see LIGHT_SUN
307 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
310 If the block below hasn't already been marked invalid:
312 Check if the node below the block has proper sunlight at top.
313 If not, the block below is invalid.
315 Ignore non-transparent nodes as they always have no light
319 if(block_below_is_valid)
321 MapNode n = getNodeParent(v3s16(x, -1, z));
322 if(nodemgr->get(n).light_propagates)
324 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
325 && sunlight_should_go_down == false)
326 block_below_is_valid = false;
327 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
328 && sunlight_should_go_down == true)
329 block_below_is_valid = false;
333 catch(InvalidPositionException &e)
335 /*std::cout<<"InvalidBlockException for bottom block node"
337 // Just no block below, no need to panic.
342 return block_below_is_valid;
346 void MapBlock::copyTo(VoxelManipulator &dst)
348 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
349 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
351 // Copy from data to VoxelManipulator
352 dst.copyFrom(data, data_area, v3s16(0,0,0),
353 getPosRelative(), data_size);
356 void MapBlock::copyFrom(VoxelManipulator &dst)
358 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
359 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
361 // Copy from VoxelManipulator to data
362 dst.copyTo(data, data_area, v3s16(0,0,0),
363 getPosRelative(), data_size);
366 void MapBlock::actuallyUpdateDayNightDiff()
368 INodeDefManager *nodemgr = m_gamedef->ndef();
369 // Running this function un-expires m_day_night_differs
370 m_day_night_differs_expired = false;
374 m_day_night_differs = false;
378 bool differs = false;
381 Check if any lighting value differs
383 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
385 MapNode &n = data[i];
386 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
394 If some lighting values differ, check if the whole thing is
395 just air. If it is, differ = false
399 bool only_air = true;
400 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
402 MapNode &n = data[i];
403 if(n.getContent() != CONTENT_AIR)
413 // Set member variable
414 m_day_night_differs = differs;
417 void MapBlock::expireDayNightDiff()
419 //INodeDefManager *nodemgr = m_gamedef->ndef();
422 m_day_night_differs = false;
423 m_day_night_differs_expired = false;
427 m_day_night_differs_expired = true;
430 s16 MapBlock::getGroundLevel(v2s16 p2d)
436 s16 y = MAP_BLOCKSIZE-1;
439 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
440 if(m_gamedef->ndef()->get(n).walkable)
442 if(y == MAP_BLOCKSIZE-1)
450 catch(InvalidPositionException &e)
459 // List relevant id-name pairs for ids in the block using nodedef
460 // Renumbers the content IDs (starting at 0 and incrementing
461 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
462 INodeDefManager *nodedef)
464 std::map<content_t, content_t> mapping;
465 std::set<content_t> unknown_contents;
466 content_t id_counter = 0;
467 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
469 content_t global_id = nodes[i].getContent();
470 content_t id = CONTENT_IGNORE;
472 // Try to find an existing mapping
473 std::map<content_t, content_t>::iterator j = mapping.find(global_id);
474 if(j != mapping.end())
480 // We have to assign a new mapping
482 mapping.insert(std::make_pair(global_id, id));
484 const ContentFeatures &f = nodedef->get(global_id);
485 const std::string &name = f.name;
487 unknown_contents.insert(global_id);
489 nimap->set(id, name);
492 // Update the MapNode
493 nodes[i].setContent(id);
495 for(std::set<content_t>::const_iterator
496 i = unknown_contents.begin();
497 i != unknown_contents.end(); i++){
498 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
499 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
502 // Correct ids in the block to match nodedef based on names.
503 // Unknown ones are added to nodedef.
504 // Will not update itself to match id-name pairs in nodedef.
505 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
508 INodeDefManager *nodedef = gamedef->ndef();
509 // This means the block contains incorrect ids, and we contain
510 // the information to convert those to names.
511 // nodedef contains information to convert our names to globally
513 std::set<content_t> unnamed_contents;
514 std::set<std::string> unallocatable_contents;
515 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
517 content_t local_id = nodes[i].getContent();
519 bool found = nimap->getName(local_id, name);
521 unnamed_contents.insert(local_id);
525 found = nodedef->getId(name, global_id);
527 global_id = gamedef->allocateUnknownNodeId(name);
528 if(global_id == CONTENT_IGNORE){
529 unallocatable_contents.insert(name);
533 nodes[i].setContent(global_id);
535 for(std::set<content_t>::const_iterator
536 i = unnamed_contents.begin();
537 i != unnamed_contents.end(); i++){
538 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
539 <<"Block contains id "<<(*i)
540 <<" with no name mapping"<<std::endl;
542 for(std::set<std::string>::const_iterator
543 i = unallocatable_contents.begin();
544 i != unallocatable_contents.end(); i++){
545 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
546 <<"Could not allocate global id for node name \""
547 <<(*i)<<"\""<<std::endl;
551 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
553 if(!ser_ver_supported(version))
554 throw VersionMismatchException("ERROR: MapBlock format not supported");
558 throw SerializationError("ERROR: Not writing dummy block.");
561 // Can't do this anymore; we have 16-bit dynamically allocated node IDs
562 // in memory; conversion just won't work in this direction.
564 throw SerializationError("MapBlock::serialize: serialization to "
565 "version < 24 not possible");
571 if(getDayNightDiff())
573 if(m_lighting_expired)
575 if(m_generated == false)
583 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
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, heat);
653 writeF1000(os, 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 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
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);
706 content_nodemeta_deserialize_legacy(iss,
707 &m_node_metadata, &m_node_timers,
710 catch(SerializationError &e)
712 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
713 <<" while deserializing node metadata at ("
714 <<PP(getPos())<<": "<<e.what()<<std::endl;
718 Data that is only on disk
728 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
729 <<": Node timers (ver==24)"<<std::endl);
730 m_node_timers.deSerialize(is, version);
734 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
735 <<": Static objects"<<std::endl);
736 m_static_objects.deSerialize(is);
739 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
740 <<": Timestamp"<<std::endl);
741 setTimestamp(readU32(is));
742 m_disk_timestamp = m_timestamp;
744 // Dynamically re-set ids based on node names
745 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
746 <<": NameIdMapping"<<std::endl);
748 nimap.deSerialize(is);
749 correctBlockNodeIds(&nimap, data, m_gamedef);
752 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
753 <<": Node timers (ver>=25)"<<std::endl);
754 m_node_timers.deSerialize(is, version);
758 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
759 <<": Done."<<std::endl);
762 void MapBlock::deSerializeNetworkSpecific(std::istream &is)
765 int version = readU8(is);
767 // throw SerializationError("unsupported MapBlock version");
769 heat = readF1000(is);
770 humidity = readF1000(is);
773 catch(SerializationError &e)
775 errorstream<<"WARNING: MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
776 <<": "<<e.what()<<std::endl;
784 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
786 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
788 // Initialize default flags
789 is_underground = false;
790 m_day_night_differs = false;
791 m_lighting_expired = false;
794 // Make a temporary buffer
795 u32 ser_length = MapNode::serializedLength(version);
796 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
798 // These have no compression
799 if(version <= 3 || version == 5 || version == 6)
804 throw SerializationError
805 ("MapBlock::deSerialize: no enough input data");
806 is_underground = tmp;
807 is.read((char*)*databuf_nodelist, nodecount * ser_length);
808 if((u32)is.gcount() != nodecount * ser_length)
809 throw SerializationError
810 ("MapBlock::deSerialize: no enough input data");
812 else if(version <= 10)
815 is.read((char*)&t8, 1);
819 // Uncompress and set material data
820 std::ostringstream os(std::ios_base::binary);
821 decompress(is, os, version);
822 std::string s = os.str();
823 if(s.size() != nodecount)
824 throw SerializationError
825 ("MapBlock::deSerialize: invalid format");
826 for(u32 i=0; i<s.size(); i++)
828 databuf_nodelist[i*ser_length] = s[i];
832 // Uncompress and set param data
833 std::ostringstream os(std::ios_base::binary);
834 decompress(is, os, version);
835 std::string s = os.str();
836 if(s.size() != nodecount)
837 throw SerializationError
838 ("MapBlock::deSerialize: invalid format");
839 for(u32 i=0; i<s.size(); i++)
841 databuf_nodelist[i*ser_length + 1] = s[i];
847 // Uncompress and set param2 data
848 std::ostringstream os(std::ios_base::binary);
849 decompress(is, os, version);
850 std::string s = os.str();
851 if(s.size() != nodecount)
852 throw SerializationError
853 ("MapBlock::deSerialize: invalid format");
854 for(u32 i=0; i<s.size(); i++)
856 databuf_nodelist[i*ser_length + 2] = s[i];
860 // All other versions (newest)
864 is.read((char*)&flags, 1);
865 is_underground = (flags & 0x01) ? true : false;
866 m_day_night_differs = (flags & 0x02) ? true : false;
867 m_lighting_expired = (flags & 0x04) ? true : false;
869 m_generated = (flags & 0x08) ? false : true;
872 std::ostringstream os(std::ios_base::binary);
873 decompress(is, os, version);
874 std::string s = os.str();
875 if(s.size() != nodecount*3)
876 throw SerializationError
877 ("MapBlock::deSerialize: decompress resulted in size"
878 " other than nodecount*3");
880 // deserialize nodes from buffer
881 for(u32 i=0; i<nodecount; i++)
883 databuf_nodelist[i*ser_length] = s[i];
884 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
885 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
897 std::string data = deSerializeString(is);
898 std::istringstream iss(data, std::ios_base::binary);
899 content_nodemeta_deserialize_legacy(iss,
900 &m_node_metadata, &m_node_timers,
905 //std::string data = deSerializeLongString(is);
906 std::ostringstream oss(std::ios_base::binary);
907 decompressZlib(is, oss);
908 std::istringstream iss(oss.str(), std::ios_base::binary);
909 content_nodemeta_deserialize_legacy(iss,
910 &m_node_metadata, &m_node_timers,
914 catch(SerializationError &e)
916 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
917 <<" while deserializing node metadata"<<std::endl;
922 // Deserialize node data
923 for(u32 i=0; i<nodecount; i++)
925 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
931 Versions up from 9 have block objects. (DEPRECATED)
934 u16 count = readU16(is);
935 // Not supported and length not known if count is not 0
937 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
938 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
944 Versions up from 15 have static objects.
947 m_static_objects.deSerialize(is);
951 setTimestamp(readU32(is));
952 m_disk_timestamp = m_timestamp;
954 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
957 // Dynamically re-set ids based on node names
959 // If supported, read node definition id mapping
961 nimap.deSerialize(is);
962 // Else set the legacy mapping
964 content_mapnode_get_name_id_mapping(&nimap);
966 correctBlockNodeIds(&nimap, data, m_gamedef);
970 // Legacy data changes
971 // This code has to convert from pre-22 to post-22 format.
972 INodeDefManager *nodedef = m_gamedef->ndef();
973 for(u32 i=0; i<nodecount; i++)
975 const ContentFeatures &f = nodedef->get(data[i].getContent());
977 if(nodedef->getId("default:stone") == data[i].getContent()
978 && data[i].getParam1() == 1)
980 data[i].setContent(nodedef->getId("default:stone_with_coal"));
981 data[i].setParam1(0);
983 else if(nodedef->getId("default:stone") == data[i].getContent()
984 && data[i].getParam1() == 2)
986 data[i].setContent(nodedef->getId("default:stone_with_iron"));
987 data[i].setParam1(0);
990 if(f.legacy_facedir_simple)
992 data[i].setParam2(data[i].getParam1());
993 data[i].setParam1(0);
996 if(f.legacy_wallmounted)
998 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
999 u8 dir_old_format = data[i].getParam2();
1000 u8 dir_new_format = 0;
1001 for(u8 j=0; j<8; j++)
1003 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1009 data[i].setParam2(dir_new_format);
1016 Get a quick string to describe what a block actually contains
1018 std::string analyze_block(MapBlock *block)
1023 std::ostringstream desc;
1025 v3s16 p = block->getPos();
1027 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1030 switch(block->getModified())
1032 case MOD_STATE_CLEAN:
1035 case MOD_STATE_WRITE_AT_UNLOAD:
1036 desc<<"WRITE_AT_UNLOAD, ";
1038 case MOD_STATE_WRITE_NEEDED:
1039 desc<<"WRITE_NEEDED, ";
1042 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1045 if(block->isGenerated())
1046 desc<<"is_gen [X], ";
1048 desc<<"is_gen [ ], ";
1050 if(block->getIsUnderground())
1051 desc<<"is_ug [X], ";
1053 desc<<"is_ug [ ], ";
1055 if(block->getLightingExpired())
1056 desc<<"lighting_exp [X], ";
1058 desc<<"lighting_exp [ ], ";
1060 if(block->isDummy())
1066 bool full_ignore = true;
1067 bool some_ignore = false;
1068 bool full_air = true;
1069 bool some_air = false;
1070 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1071 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1072 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1075 MapNode n = block->getNode(p);
1076 content_t c = n.getContent();
1077 if(c == CONTENT_IGNORE)
1080 full_ignore = false;
1081 if(c == CONTENT_AIR)
1089 std::ostringstream ss;
1092 ss<<"IGNORE (full), ";
1093 else if(some_ignore)
1101 if(ss.str().size()>=2)
1102 desc<<ss.str().substr(0, ss.str().size()-2);
1107 return desc.str().substr(0, desc.str().size()-2);