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
33 #include "content_nodemeta.h" // For legacy deserialization
35 #include "mapblock_mesh.h"
38 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
44 MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
48 m_modified(MOD_STATE_WRITE_NEEDED),
49 m_modified_reason("initial"),
50 m_modified_reason_too_long(false),
51 is_underground(false),
52 m_lighting_expired(true),
53 m_day_night_differs(false),
54 m_day_night_differs_expired(true),
56 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
57 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
74 //JMutexAutoLock lock(mesh_mutex);
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);
609 m_node_metadata.serialize(oss);
611 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
612 compressZlib(oss.str(), os);
615 Data that goes to disk, but not the network
621 m_node_timers.serialize(os);
624 m_static_objects.serialize(os);
627 writeU32(os, getTimestamp());
629 // Write block-specific node definition id mapping
635 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
637 if(!ser_ver_supported(version))
638 throw VersionMismatchException("ERROR: MapBlock format not supported");
640 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
642 m_day_night_differs_expired = false;
646 deSerialize_pre22(is, version, disk);
650 u8 flags = readU8(is);
651 is_underground = (flags & 0x01) ? true : false;
652 m_day_night_differs = (flags & 0x02) ? true : false;
653 m_lighting_expired = (flags & 0x04) ? true : false;
654 m_generated = (flags & 0x08) ? false : true;
659 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
660 <<": Bulk node data"<<std::endl);
661 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
662 u8 content_width = readU8(is);
663 u8 params_width = readU8(is);
664 if(content_width != 1)
665 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
666 if(params_width != 2)
667 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
668 MapNode::deSerializeBulk(is, version, data, nodecount,
669 content_width, params_width, true);
674 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
675 <<": Node metadata"<<std::endl);
678 std::ostringstream oss(std::ios_base::binary);
679 decompressZlib(is, oss);
680 std::istringstream iss(oss.str(), std::ios_base::binary);
682 m_node_metadata.deSerialize(iss, m_gamedef);
684 content_nodemeta_deserialize_legacy(iss,
685 &m_node_metadata, &m_node_timers,
688 catch(SerializationError &e)
690 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
691 <<" while deserializing node metadata at ("
692 <<PP(getPos())<<": "<<e.what()<<std::endl;
696 Data that is only on disk
702 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
703 <<": Node timers"<<std::endl);
704 m_node_timers.deSerialize(is);
708 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
709 <<": Static objects"<<std::endl);
710 m_static_objects.deSerialize(is);
713 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
714 <<": Timestamp"<<std::endl);
715 setTimestamp(readU32(is));
716 m_disk_timestamp = m_timestamp;
718 // Dynamically re-set ids based on node names
719 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
720 <<": NameIdMapping"<<std::endl);
722 nimap.deSerialize(is);
723 correctBlockNodeIds(&nimap, data, m_gamedef);
726 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
727 <<": Done."<<std::endl);
734 // List relevant id-name pairs for ids in the block using nodedef
735 // Before serialization version 22
736 static void getBlockNodeIdMapping_pre22(NameIdMapping *nimap, MapNode *nodes,
737 INodeDefManager *nodedef)
739 std::set<content_t> unknown_contents;
740 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
742 content_t id = nodes[i].getContent();
743 const ContentFeatures &f = nodedef->get(id);
744 const std::string &name = f.name;
746 unknown_contents.insert(id);
748 nimap->set(id, name);
750 for(std::set<content_t>::const_iterator
751 i = unknown_contents.begin();
752 i != unknown_contents.end(); i++){
753 errorstream<<"getBlockNodeIdMapping_pre22(): IGNORING ERROR: "
754 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
757 void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
759 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
761 MapNode *tmp_data = new MapNode[nodecount];
763 // Legacy data changes
764 // This code has to change from post-22 to pre-22 format.
765 INodeDefManager *nodedef = m_gamedef->ndef();
766 for(u32 i=0; i<nodecount; i++)
768 const ContentFeatures &f = nodedef->get(tmp_data[i].getContent());
770 if(nodedef->getId("default:stone_with_coal") == tmp_data[i].getContent())
772 tmp_data[i].setContent(nodedef->getId("default:stone"));
773 tmp_data[i].setParam1(1); // MINERAL_COAL
775 else if(nodedef->getId("default:stone_with_iron") == tmp_data[i].getContent())
777 tmp_data[i].setContent(nodedef->getId("default:stone"));
778 tmp_data[i].setParam1(2); // MINERAL_IRON
781 if(f.legacy_facedir_simple)
783 tmp_data[i].setParam1(tmp_data[i].getParam2());
784 tmp_data[i].setParam2(0);
787 if(f.legacy_wallmounted)
789 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
790 u8 dir_new_format = tmp_data[i].getParam2() & 7; // lowest 3 bits
791 u8 dir_old_format = wallmounted_new_to_old[dir_new_format];
792 tmp_data[i].setParam2(dir_old_format);
797 u32 ser_length = MapNode::serializedLength(version);
798 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
799 for(u32 i=0; i<nodecount; i++)
801 tmp_data[i].serialize(&databuf_nodelist[i*ser_length], version);
806 // These have no compression
807 if(version <= 3 || version == 5 || version == 6)
809 writeU8(os, is_underground);
810 os.write((char*)*databuf_nodelist, databuf_nodelist.getSize());
812 else if(version <= 10)
816 Compress the materials and the params separately.
820 writeU8(os, is_underground);
822 // Get and compress materials
823 SharedBuffer<u8> materialdata(nodecount);
824 for(u32 i=0; i<nodecount; i++)
826 materialdata[i] = databuf_nodelist[i*ser_length];
828 compress(materialdata, os, version);
830 // Get and compress lights
831 SharedBuffer<u8> lightdata(nodecount);
832 for(u32 i=0; i<nodecount; i++)
834 lightdata[i] = databuf_nodelist[i*ser_length+1];
836 compress(lightdata, os, version);
840 // Get and compress param2
841 SharedBuffer<u8> param2data(nodecount);
842 for(u32 i=0; i<nodecount; i++)
844 param2data[i] = databuf_nodelist[i*ser_length+2];
846 compress(param2data, os, version);
849 // All other versions (newest)
856 if(getDayNightDiff())
858 if(m_lighting_expired)
862 if(m_generated == false)
871 // Create buffer with different parameters sorted
872 SharedBuffer<u8> databuf(nodecount*3);
873 for(u32 i=0; i<nodecount; i++)
875 databuf[i] = databuf_nodelist[i*ser_length];
876 databuf[i+nodecount] = databuf_nodelist[i*ser_length+1];
877 databuf[i+nodecount*2] = databuf_nodelist[i*ser_length+2];
881 Compress data to output stream
884 compress(databuf, os, version);
894 std::ostringstream oss(std::ios_base::binary);
895 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
896 os<<serializeString(oss.str());
898 // This will happen if the string is longer than 65535
899 catch(SerializationError &e)
901 // Use an empty string
902 os<<serializeString("");
907 std::ostringstream oss(std::ios_base::binary);
908 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
909 compressZlib(oss.str(), os);
910 //os<<serializeLongString(oss.str());
918 // Versions up from 9 have block objects. (DEPRECATED)
925 // Versions up from 15 have static objects.
928 m_static_objects.serialize(os);
934 writeU32(os, getTimestamp());
937 // Scan and write node definition id mapping
941 getBlockNodeIdMapping_pre22(&nimap, data, m_gamedef->ndef());
947 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
949 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
951 // Initialize default flags
952 is_underground = false;
953 m_day_night_differs = false;
954 m_lighting_expired = false;
957 // Make a temporary buffer
958 u32 ser_length = MapNode::serializedLength(version);
959 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
961 // These have no compression
962 if(version <= 3 || version == 5 || version == 6)
967 throw SerializationError
968 ("MapBlock::deSerialize: no enough input data");
969 is_underground = tmp;
970 is.read((char*)*databuf_nodelist, nodecount * ser_length);
971 if(is.gcount() != nodecount * ser_length)
972 throw SerializationError
973 ("MapBlock::deSerialize: no enough input data");
975 else if(version <= 10)
978 is.read((char*)&t8, 1);
982 // Uncompress and set material data
983 std::ostringstream os(std::ios_base::binary);
984 decompress(is, os, version);
985 std::string s = os.str();
986 if(s.size() != nodecount)
987 throw SerializationError
988 ("MapBlock::deSerialize: invalid format");
989 for(u32 i=0; i<s.size(); i++)
991 databuf_nodelist[i*ser_length] = s[i];
995 // Uncompress and set param data
996 std::ostringstream os(std::ios_base::binary);
997 decompress(is, os, version);
998 std::string s = os.str();
999 if(s.size() != nodecount)
1000 throw SerializationError
1001 ("MapBlock::deSerialize: invalid format");
1002 for(u32 i=0; i<s.size(); i++)
1004 databuf_nodelist[i*ser_length + 1] = s[i];
1010 // Uncompress and set param2 data
1011 std::ostringstream os(std::ios_base::binary);
1012 decompress(is, os, version);
1013 std::string s = os.str();
1014 if(s.size() != nodecount)
1015 throw SerializationError
1016 ("MapBlock::deSerialize: invalid format");
1017 for(u32 i=0; i<s.size(); i++)
1019 databuf_nodelist[i*ser_length + 2] = s[i];
1023 // All other versions (newest)
1027 is.read((char*)&flags, 1);
1028 is_underground = (flags & 0x01) ? true : false;
1029 m_day_night_differs = (flags & 0x02) ? true : false;
1030 m_lighting_expired = (flags & 0x04) ? true : false;
1032 m_generated = (flags & 0x08) ? false : true;
1035 std::ostringstream os(std::ios_base::binary);
1036 decompress(is, os, version);
1037 std::string s = os.str();
1038 if(s.size() != nodecount*3)
1039 throw SerializationError
1040 ("MapBlock::deSerialize: decompress resulted in size"
1041 " other than nodecount*3");
1043 // deserialize nodes from buffer
1044 for(u32 i=0; i<nodecount; i++)
1046 databuf_nodelist[i*ser_length] = s[i];
1047 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
1048 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
1060 std::string data = deSerializeString(is);
1061 std::istringstream iss(data, std::ios_base::binary);
1062 content_nodemeta_deserialize_legacy(iss,
1063 &m_node_metadata, &m_node_timers,
1068 //std::string data = deSerializeLongString(is);
1069 std::ostringstream oss(std::ios_base::binary);
1070 decompressZlib(is, oss);
1071 std::istringstream iss(oss.str(), std::ios_base::binary);
1072 content_nodemeta_deserialize_legacy(iss,
1073 &m_node_metadata, &m_node_timers,
1077 catch(SerializationError &e)
1079 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
1080 <<" while deserializing node metadata"<<std::endl;
1085 // Deserialize node data
1086 for(u32 i=0; i<nodecount; i++)
1088 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
1094 Versions up from 9 have block objects. (DEPRECATED)
1097 u16 count = readU16(is);
1098 // Not supported and length not known if count is not 0
1100 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
1101 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
1107 Versions up from 15 have static objects.
1110 m_static_objects.deSerialize(is);
1114 setTimestamp(readU32(is));
1115 m_disk_timestamp = m_timestamp;
1117 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
1120 // Dynamically re-set ids based on node names
1121 NameIdMapping nimap;
1122 // If supported, read node definition id mapping
1124 nimap.deSerialize(is);
1125 // Else set the legacy mapping
1127 content_mapnode_get_name_id_mapping(&nimap);
1129 correctBlockNodeIds(&nimap, data, m_gamedef);
1133 // Legacy data changes
1134 // This code has to convert from pre-22 to post-22 format.
1135 INodeDefManager *nodedef = m_gamedef->ndef();
1136 for(u32 i=0; i<nodecount; i++)
1138 const ContentFeatures &f = nodedef->get(data[i].getContent());
1140 if(nodedef->getId("default:stone") == data[i].getContent()
1141 && data[i].getParam1() == 1)
1143 data[i].setContent(nodedef->getId("default:stone_with_coal"));
1144 data[i].setParam1(0);
1146 else if(nodedef->getId("default:stone") == data[i].getContent()
1147 && data[i].getParam1() == 2)
1149 data[i].setContent(nodedef->getId("default:stone_with_iron"));
1150 data[i].setParam1(0);
1153 if(f.legacy_facedir_simple)
1155 data[i].setParam2(data[i].getParam1());
1156 data[i].setParam1(0);
1159 if(f.legacy_wallmounted)
1161 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
1162 u8 dir_old_format = data[i].getParam2();
1163 u8 dir_new_format = 0;
1164 for(u8 j=0; j<8; j++)
1166 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1172 data[i].setParam2(dir_new_format);
1179 Get a quick string to describe what a block actually contains
1181 std::string analyze_block(MapBlock *block)
1186 std::ostringstream desc;
1188 v3s16 p = block->getPos();
1190 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1193 switch(block->getModified())
1195 case MOD_STATE_CLEAN:
1198 case MOD_STATE_WRITE_AT_UNLOAD:
1199 desc<<"WRITE_AT_UNLOAD, ";
1201 case MOD_STATE_WRITE_NEEDED:
1202 desc<<"WRITE_NEEDED, ";
1205 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1208 if(block->isGenerated())
1209 desc<<"is_gen [X], ";
1211 desc<<"is_gen [ ], ";
1213 if(block->getIsUnderground())
1214 desc<<"is_ug [X], ";
1216 desc<<"is_ug [ ], ";
1218 if(block->getLightingExpired())
1219 desc<<"lighting_exp [X], ";
1221 desc<<"lighting_exp [ ], ";
1223 if(block->isDummy())
1229 bool full_ignore = true;
1230 bool some_ignore = false;
1231 bool full_air = true;
1232 bool some_air = false;
1233 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1234 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1235 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1238 MapNode n = block->getNode(p);
1239 content_t c = n.getContent();
1240 if(c == CONTENT_IGNORE)
1243 full_ignore = false;
1244 if(c == CONTENT_AIR)
1252 std::ostringstream ss;
1255 ss<<"IGNORE (full), ";
1256 else if(some_ignore)
1264 if(ss.str().size()>=2)
1265 desc<<ss.str().substr(0, ss.str().size()-2);
1270 return desc.str().substr(0, desc.str().size()-2);