3 Copyright (C) 2010 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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
34 #include "mapblock_mesh.h"
41 MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
42 m_node_metadata(new NodeMetadataList),
46 m_modified(MOD_STATE_WRITE_NEEDED),
47 m_modified_reason("initial"),
48 m_modified_reason_too_long(false),
49 is_underground(false),
50 m_lighting_expired(true),
51 m_day_night_differs(false),
52 m_day_night_differs_expired(true),
54 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
55 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
72 //JMutexAutoLock lock(mesh_mutex);
82 delete m_node_metadata;
88 bool MapBlock::isValidPositionParent(v3s16 p)
90 if(isValidPosition(p))
95 return m_parent->isValidPosition(getPosRelative() + p);
99 MapNode MapBlock::getNodeParent(v3s16 p)
101 if(isValidPosition(p) == false)
103 return m_parent->getNode(getPosRelative() + p);
108 throw InvalidPositionException();
109 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
113 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
115 if(isValidPosition(p) == false)
117 m_parent->setNode(getPosRelative() + p, n);
122 throw InvalidPositionException();
123 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
127 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
129 if(isValidPosition(p) == false)
132 return m_parent->getNode(getPosRelative() + p);
134 catch(InvalidPositionException &e)
136 return MapNode(CONTENT_IGNORE);
143 return MapNode(CONTENT_IGNORE);
145 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
150 Propagates sunlight down through the block.
151 Doesn't modify nodes that are not affected by sunlight.
153 Returns false if sunlight at bottom block is invalid.
154 Returns true if sunlight at bottom block is valid.
155 Returns true if bottom block doesn't exist.
157 If there is a block above, continues from it.
158 If there is no block above, assumes there is sunlight, unless
159 is_underground is set or highest node is water.
161 All sunlighted nodes are added to light_sources.
163 if remove_light==true, sets non-sunlighted nodes black.
165 if black_air_left!=NULL, it is set to true if non-sunlighted
166 air is left in block.
168 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
169 bool remove_light, bool *black_air_left)
171 INodeDefManager *nodemgr = m_gamedef->ndef();
173 // Whether the sunlight at the top of the bottom block is valid
174 bool block_below_is_valid = true;
176 v3s16 pos_relative = getPosRelative();
178 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
180 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
183 bool no_sunlight = false;
184 bool no_top_block = false;
185 // Check if node above block has sunlight
187 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
188 if(n.getContent() == CONTENT_IGNORE)
191 no_sunlight = is_underground;
193 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
198 catch(InvalidPositionException &e)
202 // NOTE: This makes over-ground roofed places sunlighted
203 // Assume sunlight, unless is_underground==true
210 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
211 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
216 // NOTE: As of now, this just would make everything dark.
218 //no_sunlight = true;
221 #if 0 // Doesn't work; nothing gets light.
222 bool no_sunlight = true;
223 bool no_top_block = false;
224 // Check if node above block has sunlight
226 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
227 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
232 catch(InvalidPositionException &e)
238 /*std::cout<<"("<<x<<","<<z<<"): "
239 <<"no_top_block="<<no_top_block
240 <<", is_underground="<<is_underground
241 <<", no_sunlight="<<no_sunlight
244 s16 y = MAP_BLOCKSIZE-1;
246 // This makes difference to diminishing in water.
247 bool stopped_to_solid_object = false;
249 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
254 MapNode &n = getNodeRef(pos);
256 if(current_light == 0)
260 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
262 // Do nothing: Sunlight is continued
264 else if(nodemgr->get(n).light_propagates == false)
266 // A solid object is on the way.
267 stopped_to_solid_object = true;
275 current_light = diminish_light(current_light);
278 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
280 if(current_light > old_light || remove_light)
282 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
285 if(diminish_light(current_light) != 0)
287 light_sources.insert(pos_relative + pos, true);
290 if(current_light == 0 && stopped_to_solid_object)
294 *black_air_left = true;
299 // Whether or not the block below should see LIGHT_SUN
300 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
303 If the block below hasn't already been marked invalid:
305 Check if the node below the block has proper sunlight at top.
306 If not, the block below is invalid.
308 Ignore non-transparent nodes as they always have no light
312 if(block_below_is_valid)
314 MapNode n = getNodeParent(v3s16(x, -1, z));
315 if(nodemgr->get(n).light_propagates)
317 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
318 && sunlight_should_go_down == false)
319 block_below_is_valid = false;
320 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
321 && sunlight_should_go_down == true)
322 block_below_is_valid = false;
326 catch(InvalidPositionException &e)
328 /*std::cout<<"InvalidBlockException for bottom block node"
330 // Just no block below, no need to panic.
335 return block_below_is_valid;
339 void MapBlock::copyTo(VoxelManipulator &dst)
341 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
342 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
344 // Copy from data to VoxelManipulator
345 dst.copyFrom(data, data_area, v3s16(0,0,0),
346 getPosRelative(), data_size);
349 void MapBlock::copyFrom(VoxelManipulator &dst)
351 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
352 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
354 // Copy from VoxelManipulator to data
355 dst.copyTo(data, data_area, v3s16(0,0,0),
356 getPosRelative(), data_size);
359 void MapBlock::actuallyUpdateDayNightDiff()
361 INodeDefManager *nodemgr = m_gamedef->ndef();
362 // Running this function un-expires m_day_night_differs
363 m_day_night_differs_expired = false;
367 m_day_night_differs = false;
371 bool differs = false;
374 Check if any lighting value differs
376 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
378 MapNode &n = data[i];
379 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
387 If some lighting values differ, check if the whole thing is
388 just air. If it is, differ = false
392 bool only_air = true;
393 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
395 MapNode &n = data[i];
396 if(n.getContent() != CONTENT_AIR)
406 // Set member variable
407 m_day_night_differs = differs;
410 void MapBlock::expireDayNightDiff()
412 INodeDefManager *nodemgr = m_gamedef->ndef();
415 m_day_night_differs = false;
416 m_day_night_differs_expired = false;
420 m_day_night_differs_expired = true;
423 s16 MapBlock::getGroundLevel(v2s16 p2d)
429 s16 y = MAP_BLOCKSIZE-1;
432 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
433 if(m_gamedef->ndef()->get(n).walkable)
435 if(y == MAP_BLOCKSIZE-1)
443 catch(InvalidPositionException &e)
452 // List relevant id-name pairs for ids in the block using nodedef
453 // Renumbers the content IDs (starting at 0 and incrementing
454 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
455 INodeDefManager *nodedef)
457 std::map<content_t, content_t> mapping;
458 std::set<content_t> unknown_contents;
459 content_t id_counter = 0;
460 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
462 content_t global_id = nodes[i].getContent();
463 content_t id = CONTENT_IGNORE;
465 // Try to find an existing mapping
466 std::map<content_t, content_t>::iterator j = mapping.find(global_id);
467 if(j != mapping.end())
473 // We have to assign a new mapping
475 mapping.insert(std::make_pair(global_id, id));
477 const ContentFeatures &f = nodedef->get(global_id);
478 const std::string &name = f.name;
480 unknown_contents.insert(global_id);
482 nimap->set(id, name);
485 // Update the MapNode
486 nodes[i].setContent(id);
488 for(std::set<content_t>::const_iterator
489 i = unknown_contents.begin();
490 i != unknown_contents.end(); i++){
491 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
492 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
495 // Correct ids in the block to match nodedef based on names.
496 // Unknown ones are added to nodedef.
497 // Will not update itself to match id-name pairs in nodedef.
498 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
501 INodeDefManager *nodedef = gamedef->ndef();
502 // This means the block contains incorrect ids, and we contain
503 // the information to convert those to names.
504 // nodedef contains information to convert our names to globally
506 std::set<content_t> unnamed_contents;
507 std::set<std::string> unallocatable_contents;
508 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
510 content_t local_id = nodes[i].getContent();
512 bool found = nimap->getName(local_id, name);
514 unnamed_contents.insert(local_id);
518 found = nodedef->getId(name, global_id);
520 global_id = gamedef->allocateUnknownNodeId(name);
521 if(global_id == CONTENT_IGNORE){
522 unallocatable_contents.insert(name);
526 nodes[i].setContent(global_id);
528 for(std::set<content_t>::const_iterator
529 i = unnamed_contents.begin();
530 i != unnamed_contents.end(); i++){
531 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
532 <<"Block contains id "<<(*i)
533 <<" with no name mapping"<<std::endl;
535 for(std::set<std::string>::const_iterator
536 i = unallocatable_contents.begin();
537 i != unallocatable_contents.end(); i++){
538 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
539 <<"Could not allocate global id for node name \""
540 <<(*i)<<"\""<<std::endl;
544 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
546 if(!ser_ver_supported(version))
547 throw VersionMismatchException("ERROR: MapBlock format not supported");
551 throw SerializationError("ERROR: Not writing dummy block.");
556 serialize_pre22(os, version, disk);
564 if(getDayNightDiff())
566 if(m_lighting_expired)
568 if(m_generated == false)
576 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
579 MapNode *tmp_nodes = new MapNode[nodecount];
580 for(u32 i=0; i<nodecount; i++)
581 tmp_nodes[i] = data[i];
582 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
584 u8 content_width = 1;
585 /*u8 content_width = (nimap.size() <= 255) ? 1 : 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 = 1;
596 /*u8 content_width = 2;*/
598 writeU8(os, content_width);
599 writeU8(os, params_width);
600 MapNode::serializeBulk(os, version, data, nodecount,
601 content_width, params_width, true);
607 std::ostringstream oss(std::ios_base::binary);
608 m_node_metadata->serialize(oss);
609 compressZlib(oss.str(), os);
612 Data that goes to disk, but not the network
617 m_static_objects.serialize(os);
620 writeU32(os, getTimestamp());
622 // Write block-specific node definition id mapping
628 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
630 if(!ser_ver_supported(version))
631 throw VersionMismatchException("ERROR: MapBlock format not supported");
633 m_day_night_differs_expired = false;
637 deSerialize_pre22(is, version, disk);
641 u8 flags = readU8(is);
642 is_underground = (flags & 0x01) ? true : false;
643 m_day_night_differs = (flags & 0x02) ? true : false;
644 m_lighting_expired = (flags & 0x04) ? true : false;
645 m_generated = (flags & 0x08) ? false : true;
650 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
651 u8 content_width = readU8(is);
652 u8 params_width = readU8(is);
653 if(content_width != 1)
654 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
655 if(params_width != 2)
656 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
657 MapNode::deSerializeBulk(is, version, data, nodecount,
658 content_width, params_width, true);
665 std::ostringstream oss(std::ios_base::binary);
666 decompressZlib(is, oss);
667 std::istringstream iss(oss.str(), std::ios_base::binary);
668 m_node_metadata->deSerialize(iss, m_gamedef);
670 catch(SerializationError &e)
672 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
673 <<" while deserializing node metadata"<<std::endl;
677 Data that is only on disk
682 m_static_objects.deSerialize(is);
685 setTimestamp(readU32(is));
686 m_disk_timestamp = m_timestamp;
688 // Dynamically re-set ids based on node names
690 nimap.deSerialize(is);
691 correctBlockNodeIds(&nimap, data, m_gamedef);
699 // List relevant id-name pairs for ids in the block using nodedef
700 // Before serialization version 22
701 static void getBlockNodeIdMapping_pre22(NameIdMapping *nimap, MapNode *nodes,
702 INodeDefManager *nodedef)
704 std::set<content_t> unknown_contents;
705 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
707 content_t id = nodes[i].getContent();
708 const ContentFeatures &f = nodedef->get(id);
709 const std::string &name = f.name;
711 unknown_contents.insert(id);
713 nimap->set(id, name);
715 for(std::set<content_t>::const_iterator
716 i = unknown_contents.begin();
717 i != unknown_contents.end(); i++){
718 errorstream<<"getBlockNodeIdMapping_pre22(): IGNORING ERROR: "
719 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
722 void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
724 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
726 MapNode *tmp_data = new MapNode[nodecount];
728 // Legacy data changes
729 // This code has to change from post-22 to pre-22 format.
730 INodeDefManager *nodedef = m_gamedef->ndef();
731 for(u32 i=0; i<nodecount; i++)
733 const ContentFeatures &f = nodedef->get(tmp_data[i].getContent());
735 if(nodedef->getId("default:stone_with_coal") == tmp_data[i].getContent())
737 tmp_data[i].setContent(nodedef->getId("default:stone"));
738 tmp_data[i].setParam1(1); // MINERAL_COAL
740 else if(nodedef->getId("default:stone_with_iron") == tmp_data[i].getContent())
742 tmp_data[i].setContent(nodedef->getId("default:stone"));
743 tmp_data[i].setParam1(2); // MINERAL_IRON
746 if(f.legacy_facedir_simple)
748 tmp_data[i].setParam1(tmp_data[i].getParam2());
749 tmp_data[i].setParam2(0);
752 if(f.legacy_wallmounted)
754 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
755 u8 dir_new_format = tmp_data[i].getParam2() & 7; // lowest 3 bits
756 u8 dir_old_format = wallmounted_new_to_old[dir_new_format];
757 tmp_data[i].setParam2(dir_old_format);
762 u32 ser_length = MapNode::serializedLength(version);
763 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
764 for(u32 i=0; i<nodecount; i++)
766 tmp_data[i].serialize(&databuf_nodelist[i*ser_length], version);
771 // These have no compression
772 if(version <= 3 || version == 5 || version == 6)
774 writeU8(os, is_underground);
775 os.write((char*)*databuf_nodelist, databuf_nodelist.getSize());
777 else if(version <= 10)
781 Compress the materials and the params separately.
785 writeU8(os, is_underground);
787 // Get and compress materials
788 SharedBuffer<u8> materialdata(nodecount);
789 for(u32 i=0; i<nodecount; i++)
791 materialdata[i] = databuf_nodelist[i*ser_length];
793 compress(materialdata, os, version);
795 // Get and compress lights
796 SharedBuffer<u8> lightdata(nodecount);
797 for(u32 i=0; i<nodecount; i++)
799 lightdata[i] = databuf_nodelist[i*ser_length+1];
801 compress(lightdata, os, version);
805 // Get and compress param2
806 SharedBuffer<u8> param2data(nodecount);
807 for(u32 i=0; i<nodecount; i++)
809 param2data[i] = databuf_nodelist[i*ser_length+2];
811 compress(param2data, os, version);
814 // All other versions (newest)
821 if(getDayNightDiff())
823 if(m_lighting_expired)
827 if(m_generated == false)
836 // Create buffer with different parameters sorted
837 SharedBuffer<u8> databuf(nodecount*3);
838 for(u32 i=0; i<nodecount; i++)
840 databuf[i] = databuf_nodelist[i*ser_length];
841 databuf[i+nodecount] = databuf_nodelist[i*ser_length+1];
842 databuf[i+nodecount*2] = databuf_nodelist[i*ser_length+2];
846 Compress data to output stream
849 compress(databuf, os, version);
859 std::ostringstream oss(std::ios_base::binary);
860 m_node_metadata->serialize(oss);
861 os<<serializeString(oss.str());
863 // This will happen if the string is longer than 65535
864 catch(SerializationError &e)
866 // Use an empty string
867 os<<serializeString("");
872 std::ostringstream oss(std::ios_base::binary);
873 m_node_metadata->serialize(oss);
874 compressZlib(oss.str(), os);
875 //os<<serializeLongString(oss.str());
883 // Versions up from 9 have block objects. (DEPRECATED)
890 // Versions up from 15 have static objects.
893 m_static_objects.serialize(os);
899 writeU32(os, getTimestamp());
902 // Scan and write node definition id mapping
906 getBlockNodeIdMapping_pre22(&nimap, data, m_gamedef->ndef());
912 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
914 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
916 // Initialize default flags
917 is_underground = false;
918 m_day_night_differs = false;
919 m_lighting_expired = false;
922 // Make a temporary buffer
923 u32 ser_length = MapNode::serializedLength(version);
924 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
926 // These have no compression
927 if(version <= 3 || version == 5 || version == 6)
932 throw SerializationError
933 ("MapBlock::deSerialize: no enough input data");
934 is_underground = tmp;
935 is.read((char*)*databuf_nodelist, nodecount * ser_length);
936 if(is.gcount() != nodecount * ser_length)
937 throw SerializationError
938 ("MapBlock::deSerialize: no enough input data");
940 else if(version <= 10)
943 is.read((char*)&t8, 1);
947 // Uncompress and set material data
948 std::ostringstream os(std::ios_base::binary);
949 decompress(is, os, version);
950 std::string s = os.str();
951 if(s.size() != nodecount)
952 throw SerializationError
953 ("MapBlock::deSerialize: invalid format");
954 for(u32 i=0; i<s.size(); i++)
956 databuf_nodelist[i*ser_length] = s[i];
960 // Uncompress and set param data
961 std::ostringstream os(std::ios_base::binary);
962 decompress(is, os, version);
963 std::string s = os.str();
964 if(s.size() != nodecount)
965 throw SerializationError
966 ("MapBlock::deSerialize: invalid format");
967 for(u32 i=0; i<s.size(); i++)
969 databuf_nodelist[i*ser_length + 1] = s[i];
975 // Uncompress and set param2 data
976 std::ostringstream os(std::ios_base::binary);
977 decompress(is, os, version);
978 std::string s = os.str();
979 if(s.size() != nodecount)
980 throw SerializationError
981 ("MapBlock::deSerialize: invalid format");
982 for(u32 i=0; i<s.size(); i++)
984 databuf_nodelist[i*ser_length + 2] = s[i];
988 // All other versions (newest)
992 is.read((char*)&flags, 1);
993 is_underground = (flags & 0x01) ? true : false;
994 m_day_night_differs = (flags & 0x02) ? true : false;
995 m_lighting_expired = (flags & 0x04) ? true : false;
997 m_generated = (flags & 0x08) ? false : true;
1000 std::ostringstream os(std::ios_base::binary);
1001 decompress(is, os, version);
1002 std::string s = os.str();
1003 if(s.size() != nodecount*3)
1004 throw SerializationError
1005 ("MapBlock::deSerialize: decompress resulted in size"
1006 " other than nodecount*3");
1008 // deserialize nodes from buffer
1009 for(u32 i=0; i<nodecount; i++)
1011 databuf_nodelist[i*ser_length] = s[i];
1012 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
1013 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
1025 std::string data = deSerializeString(is);
1026 std::istringstream iss(data, std::ios_base::binary);
1027 m_node_metadata->deSerialize(iss, m_gamedef);
1031 //std::string data = deSerializeLongString(is);
1032 std::ostringstream oss(std::ios_base::binary);
1033 decompressZlib(is, oss);
1034 std::istringstream iss(oss.str(), std::ios_base::binary);
1035 m_node_metadata->deSerialize(iss, m_gamedef);
1038 catch(SerializationError &e)
1040 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
1041 <<" while deserializing node metadata"<<std::endl;
1046 // Deserialize node data
1047 for(u32 i=0; i<nodecount; i++)
1049 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
1055 Versions up from 9 have block objects. (DEPRECATED)
1058 u16 count = readU16(is);
1059 // Not supported and length not known if count is not 0
1061 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
1062 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
1068 Versions up from 15 have static objects.
1071 m_static_objects.deSerialize(is);
1075 setTimestamp(readU32(is));
1076 m_disk_timestamp = m_timestamp;
1078 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
1081 // Dynamically re-set ids based on node names
1082 NameIdMapping nimap;
1083 // If supported, read node definition id mapping
1085 nimap.deSerialize(is);
1086 // Else set the legacy mapping
1088 content_mapnode_get_name_id_mapping(&nimap);
1090 correctBlockNodeIds(&nimap, data, m_gamedef);
1094 // Legacy data changes
1095 // This code has to convert from pre-22 to post-22 format.
1096 INodeDefManager *nodedef = m_gamedef->ndef();
1097 for(u32 i=0; i<nodecount; i++)
1099 const ContentFeatures &f = nodedef->get(data[i].getContent());
1101 if(nodedef->getId("default:stone") == data[i].getContent()
1102 && data[i].getParam1() == 1)
1104 data[i].setContent(nodedef->getId("default:stone_with_coal"));
1105 data[i].setParam1(0);
1107 else if(nodedef->getId("default:stone") == data[i].getContent()
1108 && data[i].getParam1() == 2)
1110 data[i].setContent(nodedef->getId("default:stone_with_iron"));
1111 data[i].setParam1(0);
1114 if(f.legacy_facedir_simple)
1116 data[i].setParam2(data[i].getParam1());
1117 data[i].setParam1(0);
1120 if(f.legacy_wallmounted)
1122 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
1123 u8 dir_old_format = data[i].getParam2();
1124 u8 dir_new_format = 0;
1125 for(u8 j=0; j<8; j++)
1127 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1133 data[i].setParam2(dir_new_format);
1140 Get a quick string to describe what a block actually contains
1142 std::string analyze_block(MapBlock *block)
1147 std::ostringstream desc;
1149 v3s16 p = block->getPos();
1151 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1154 switch(block->getModified())
1156 case MOD_STATE_CLEAN:
1159 case MOD_STATE_WRITE_AT_UNLOAD:
1160 desc<<"WRITE_AT_UNLOAD, ";
1162 case MOD_STATE_WRITE_NEEDED:
1163 desc<<"WRITE_NEEDED, ";
1166 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1169 if(block->isGenerated())
1170 desc<<"is_gen [X], ";
1172 desc<<"is_gen [ ], ";
1174 if(block->getIsUnderground())
1175 desc<<"is_ug [X], ";
1177 desc<<"is_ug [ ], ";
1179 if(block->getLightingExpired())
1180 desc<<"lighting_exp [X], ";
1182 desc<<"lighting_exp [ ], ";
1184 if(block->isDummy())
1190 bool full_ignore = true;
1191 bool some_ignore = false;
1192 bool full_air = true;
1193 bool some_air = false;
1194 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1195 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1196 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1199 MapNode n = block->getNode(p);
1200 content_t c = n.getContent();
1201 if(c == CONTENT_IGNORE)
1204 full_ignore = false;
1205 if(c == CONTENT_AIR)
1213 std::ostringstream ss;
1216 ss<<"IGNORE (full), ";
1217 else if(some_ignore)
1225 if(ss.str().size()>=2)
1226 desc<<ss.str().substr(0, ss.str().size()-2);
1231 return desc.str().substr(0, desc.str().size()-2);