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 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"
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
619 // Version 23 doesn't actually contain node timers
620 // (this field should have not been added)
623 // Node timers (uncomment when node timers are taken into use)
625 m_node_timers.serialize(os);*/
628 m_static_objects.serialize(os);
631 writeU32(os, getTimestamp());
633 // Write block-specific node definition id mapping
639 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
641 if(!ser_ver_supported(version))
642 throw VersionMismatchException("ERROR: MapBlock format not supported");
644 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
646 m_day_night_differs_expired = false;
650 deSerialize_pre22(is, version, disk);
654 u8 flags = readU8(is);
655 is_underground = (flags & 0x01) ? true : false;
656 m_day_night_differs = (flags & 0x02) ? true : false;
657 m_lighting_expired = (flags & 0x04) ? true : false;
658 m_generated = (flags & 0x08) ? false : true;
663 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
664 <<": Bulk node data"<<std::endl);
665 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
666 u8 content_width = readU8(is);
667 u8 params_width = readU8(is);
668 if(content_width != 1)
669 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
670 if(params_width != 2)
671 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
672 MapNode::deSerializeBulk(is, version, data, nodecount,
673 content_width, params_width, true);
678 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
679 <<": Node metadata"<<std::endl);
682 std::ostringstream oss(std::ios_base::binary);
683 decompressZlib(is, oss);
684 std::istringstream iss(oss.str(), std::ios_base::binary);
686 m_node_metadata.deSerialize(iss, m_gamedef);
688 content_nodemeta_deserialize_legacy(iss,
689 &m_node_metadata, &m_node_timers,
692 catch(SerializationError &e)
694 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
695 <<" while deserializing node metadata at ("
696 <<PP(getPos())<<": "<<e.what()<<std::endl;
700 Data that is only on disk
708 // Uncomment when node timers are taken into use
709 /*else if(version >= 24){
710 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
711 <<": Node timers"<<std::endl);
712 m_node_timers.deSerialize(is);
716 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
717 <<": Static objects"<<std::endl);
718 m_static_objects.deSerialize(is);
721 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
722 <<": Timestamp"<<std::endl);
723 setTimestamp(readU32(is));
724 m_disk_timestamp = m_timestamp;
726 // Dynamically re-set ids based on node names
727 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
728 <<": NameIdMapping"<<std::endl);
730 nimap.deSerialize(is);
731 correctBlockNodeIds(&nimap, data, m_gamedef);
734 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
735 <<": Done."<<std::endl);
742 // List relevant id-name pairs for ids in the block using nodedef
743 // Before serialization version 22
744 static void getBlockNodeIdMapping_pre22(NameIdMapping *nimap, MapNode *nodes,
745 INodeDefManager *nodedef)
747 std::set<content_t> unknown_contents;
748 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
750 content_t id = nodes[i].getContent();
751 const ContentFeatures &f = nodedef->get(id);
752 const std::string &name = f.name;
754 unknown_contents.insert(id);
756 nimap->set(id, name);
758 for(std::set<content_t>::const_iterator
759 i = unknown_contents.begin();
760 i != unknown_contents.end(); i++){
761 errorstream<<"getBlockNodeIdMapping_pre22(): IGNORING ERROR: "
762 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
765 void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
767 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
769 MapNode *tmp_data = new MapNode[nodecount];
771 // Legacy data changes
772 // This code has to change from post-22 to pre-22 format.
773 INodeDefManager *nodedef = m_gamedef->ndef();
774 for(u32 i=0; i<nodecount; i++)
776 const ContentFeatures &f = nodedef->get(tmp_data[i].getContent());
778 if(nodedef->getId("default:stone_with_coal") == tmp_data[i].getContent())
780 tmp_data[i].setContent(nodedef->getId("default:stone"));
781 tmp_data[i].setParam1(1); // MINERAL_COAL
783 else if(nodedef->getId("default:stone_with_iron") == tmp_data[i].getContent())
785 tmp_data[i].setContent(nodedef->getId("default:stone"));
786 tmp_data[i].setParam1(2); // MINERAL_IRON
789 if(f.legacy_facedir_simple)
791 tmp_data[i].setParam1(tmp_data[i].getParam2());
792 tmp_data[i].setParam2(0);
795 if(f.legacy_wallmounted)
797 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
798 u8 dir_new_format = tmp_data[i].getParam2() & 7; // lowest 3 bits
799 u8 dir_old_format = wallmounted_new_to_old[dir_new_format];
800 tmp_data[i].setParam2(dir_old_format);
805 u32 ser_length = MapNode::serializedLength(version);
806 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
807 for(u32 i=0; i<nodecount; i++)
809 tmp_data[i].serialize(&databuf_nodelist[i*ser_length], version);
814 // These have no compression
815 if(version <= 3 || version == 5 || version == 6)
817 writeU8(os, is_underground);
818 os.write((char*)*databuf_nodelist, databuf_nodelist.getSize());
820 else if(version <= 10)
824 Compress the materials and the params separately.
828 writeU8(os, is_underground);
830 // Get and compress materials
831 SharedBuffer<u8> materialdata(nodecount);
832 for(u32 i=0; i<nodecount; i++)
834 materialdata[i] = databuf_nodelist[i*ser_length];
836 compress(materialdata, os, version);
838 // Get and compress lights
839 SharedBuffer<u8> lightdata(nodecount);
840 for(u32 i=0; i<nodecount; i++)
842 lightdata[i] = databuf_nodelist[i*ser_length+1];
844 compress(lightdata, os, version);
848 // Get and compress param2
849 SharedBuffer<u8> param2data(nodecount);
850 for(u32 i=0; i<nodecount; i++)
852 param2data[i] = databuf_nodelist[i*ser_length+2];
854 compress(param2data, os, version);
857 // All other versions (newest)
864 if(getDayNightDiff())
866 if(m_lighting_expired)
870 if(m_generated == false)
879 // Create buffer with different parameters sorted
880 SharedBuffer<u8> databuf(nodecount*3);
881 for(u32 i=0; i<nodecount; i++)
883 databuf[i] = databuf_nodelist[i*ser_length];
884 databuf[i+nodecount] = databuf_nodelist[i*ser_length+1];
885 databuf[i+nodecount*2] = databuf_nodelist[i*ser_length+2];
889 Compress data to output stream
892 compress(databuf, os, version);
902 std::ostringstream oss(std::ios_base::binary);
903 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
904 os<<serializeString(oss.str());
906 // This will happen if the string is longer than 65535
907 catch(SerializationError &e)
909 // Use an empty string
910 os<<serializeString("");
915 std::ostringstream oss(std::ios_base::binary);
916 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
917 compressZlib(oss.str(), os);
918 //os<<serializeLongString(oss.str());
926 // Versions up from 9 have block objects. (DEPRECATED)
933 // Versions up from 15 have static objects.
936 m_static_objects.serialize(os);
942 writeU32(os, getTimestamp());
945 // Scan and write node definition id mapping
949 getBlockNodeIdMapping_pre22(&nimap, data, m_gamedef->ndef());
955 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
957 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
959 // Initialize default flags
960 is_underground = false;
961 m_day_night_differs = false;
962 m_lighting_expired = false;
965 // Make a temporary buffer
966 u32 ser_length = MapNode::serializedLength(version);
967 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
969 // These have no compression
970 if(version <= 3 || version == 5 || version == 6)
975 throw SerializationError
976 ("MapBlock::deSerialize: no enough input data");
977 is_underground = tmp;
978 is.read((char*)*databuf_nodelist, nodecount * ser_length);
979 if(is.gcount() != nodecount * ser_length)
980 throw SerializationError
981 ("MapBlock::deSerialize: no enough input data");
983 else if(version <= 10)
986 is.read((char*)&t8, 1);
990 // Uncompress and set material data
991 std::ostringstream os(std::ios_base::binary);
992 decompress(is, os, version);
993 std::string s = os.str();
994 if(s.size() != nodecount)
995 throw SerializationError
996 ("MapBlock::deSerialize: invalid format");
997 for(u32 i=0; i<s.size(); i++)
999 databuf_nodelist[i*ser_length] = s[i];
1003 // Uncompress and set param data
1004 std::ostringstream os(std::ios_base::binary);
1005 decompress(is, os, version);
1006 std::string s = os.str();
1007 if(s.size() != nodecount)
1008 throw SerializationError
1009 ("MapBlock::deSerialize: invalid format");
1010 for(u32 i=0; i<s.size(); i++)
1012 databuf_nodelist[i*ser_length + 1] = s[i];
1018 // Uncompress and set param2 data
1019 std::ostringstream os(std::ios_base::binary);
1020 decompress(is, os, version);
1021 std::string s = os.str();
1022 if(s.size() != nodecount)
1023 throw SerializationError
1024 ("MapBlock::deSerialize: invalid format");
1025 for(u32 i=0; i<s.size(); i++)
1027 databuf_nodelist[i*ser_length + 2] = s[i];
1031 // All other versions (newest)
1035 is.read((char*)&flags, 1);
1036 is_underground = (flags & 0x01) ? true : false;
1037 m_day_night_differs = (flags & 0x02) ? true : false;
1038 m_lighting_expired = (flags & 0x04) ? true : false;
1040 m_generated = (flags & 0x08) ? false : true;
1043 std::ostringstream os(std::ios_base::binary);
1044 decompress(is, os, version);
1045 std::string s = os.str();
1046 if(s.size() != nodecount*3)
1047 throw SerializationError
1048 ("MapBlock::deSerialize: decompress resulted in size"
1049 " other than nodecount*3");
1051 // deserialize nodes from buffer
1052 for(u32 i=0; i<nodecount; i++)
1054 databuf_nodelist[i*ser_length] = s[i];
1055 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
1056 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
1068 std::string data = deSerializeString(is);
1069 std::istringstream iss(data, std::ios_base::binary);
1070 content_nodemeta_deserialize_legacy(iss,
1071 &m_node_metadata, &m_node_timers,
1076 //std::string data = deSerializeLongString(is);
1077 std::ostringstream oss(std::ios_base::binary);
1078 decompressZlib(is, oss);
1079 std::istringstream iss(oss.str(), std::ios_base::binary);
1080 content_nodemeta_deserialize_legacy(iss,
1081 &m_node_metadata, &m_node_timers,
1085 catch(SerializationError &e)
1087 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
1088 <<" while deserializing node metadata"<<std::endl;
1093 // Deserialize node data
1094 for(u32 i=0; i<nodecount; i++)
1096 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
1102 Versions up from 9 have block objects. (DEPRECATED)
1105 u16 count = readU16(is);
1106 // Not supported and length not known if count is not 0
1108 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
1109 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
1115 Versions up from 15 have static objects.
1118 m_static_objects.deSerialize(is);
1122 setTimestamp(readU32(is));
1123 m_disk_timestamp = m_timestamp;
1125 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
1128 // Dynamically re-set ids based on node names
1129 NameIdMapping nimap;
1130 // If supported, read node definition id mapping
1132 nimap.deSerialize(is);
1133 // Else set the legacy mapping
1135 content_mapnode_get_name_id_mapping(&nimap);
1137 correctBlockNodeIds(&nimap, data, m_gamedef);
1141 // Legacy data changes
1142 // This code has to convert from pre-22 to post-22 format.
1143 INodeDefManager *nodedef = m_gamedef->ndef();
1144 for(u32 i=0; i<nodecount; i++)
1146 const ContentFeatures &f = nodedef->get(data[i].getContent());
1148 if(nodedef->getId("default:stone") == data[i].getContent()
1149 && data[i].getParam1() == 1)
1151 data[i].setContent(nodedef->getId("default:stone_with_coal"));
1152 data[i].setParam1(0);
1154 else if(nodedef->getId("default:stone") == data[i].getContent()
1155 && data[i].getParam1() == 2)
1157 data[i].setContent(nodedef->getId("default:stone_with_iron"));
1158 data[i].setParam1(0);
1161 if(f.legacy_facedir_simple)
1163 data[i].setParam2(data[i].getParam1());
1164 data[i].setParam1(0);
1167 if(f.legacy_wallmounted)
1169 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
1170 u8 dir_old_format = data[i].getParam2();
1171 u8 dir_new_format = 0;
1172 for(u8 j=0; j<8; j++)
1174 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1180 data[i].setParam2(dir_new_format);
1187 Get a quick string to describe what a block actually contains
1189 std::string analyze_block(MapBlock *block)
1194 std::ostringstream desc;
1196 v3s16 p = block->getPos();
1198 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1201 switch(block->getModified())
1203 case MOD_STATE_CLEAN:
1206 case MOD_STATE_WRITE_AT_UNLOAD:
1207 desc<<"WRITE_AT_UNLOAD, ";
1209 case MOD_STATE_WRITE_NEEDED:
1210 desc<<"WRITE_NEEDED, ";
1213 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1216 if(block->isGenerated())
1217 desc<<"is_gen [X], ";
1219 desc<<"is_gen [ ], ";
1221 if(block->getIsUnderground())
1222 desc<<"is_ug [X], ";
1224 desc<<"is_ug [ ], ";
1226 if(block->getLightingExpired())
1227 desc<<"lighting_exp [X], ";
1229 desc<<"lighting_exp [ ], ";
1231 if(block->isDummy())
1237 bool full_ignore = true;
1238 bool some_ignore = false;
1239 bool full_air = true;
1240 bool some_air = false;
1241 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1242 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1243 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1246 MapNode n = block->getNode(p);
1247 content_t c = n.getContent();
1248 if(c == CONTENT_IGNORE)
1251 full_ignore = false;
1252 if(c == CONTENT_AIR)
1260 std::ostringstream ss;
1263 ss<<"IGNORE (full), ";
1264 else if(some_ignore)
1272 if(ss.str().size()>=2)
1273 desc<<ss.str().substr(0, ss.str().size()-2);
1278 return desc.str().substr(0, desc.str().size()-2);