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"
42 MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
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);
86 bool MapBlock::isValidPositionParent(v3s16 p)
88 if(isValidPosition(p))
93 return m_parent->isValidPosition(getPosRelative() + p);
97 MapNode MapBlock::getNodeParent(v3s16 p)
99 if(isValidPosition(p) == false)
101 return m_parent->getNode(getPosRelative() + p);
106 throw InvalidPositionException();
107 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
111 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
113 if(isValidPosition(p) == false)
115 m_parent->setNode(getPosRelative() + p, n);
120 throw InvalidPositionException();
121 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
125 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
127 if(isValidPosition(p) == false)
130 return m_parent->getNode(getPosRelative() + p);
132 catch(InvalidPositionException &e)
134 return MapNode(CONTENT_IGNORE);
141 return MapNode(CONTENT_IGNORE);
143 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
148 Propagates sunlight down through the block.
149 Doesn't modify nodes that are not affected by sunlight.
151 Returns false if sunlight at bottom block is invalid.
152 Returns true if sunlight at bottom block is valid.
153 Returns true if bottom block doesn't exist.
155 If there is a block above, continues from it.
156 If there is no block above, assumes there is sunlight, unless
157 is_underground is set or highest node is water.
159 All sunlighted nodes are added to light_sources.
161 if remove_light==true, sets non-sunlighted nodes black.
163 if black_air_left!=NULL, it is set to true if non-sunlighted
164 air is left in block.
166 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
167 bool remove_light, bool *black_air_left)
169 INodeDefManager *nodemgr = m_gamedef->ndef();
171 // Whether the sunlight at the top of the bottom block is valid
172 bool block_below_is_valid = true;
174 v3s16 pos_relative = getPosRelative();
176 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
178 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
181 bool no_sunlight = false;
182 bool no_top_block = false;
183 // Check if node above block has sunlight
185 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
186 if(n.getContent() == CONTENT_IGNORE)
189 no_sunlight = is_underground;
191 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
196 catch(InvalidPositionException &e)
200 // NOTE: This makes over-ground roofed places sunlighted
201 // Assume sunlight, unless is_underground==true
208 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
209 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
214 // NOTE: As of now, this just would make everything dark.
216 //no_sunlight = true;
219 #if 0 // Doesn't work; nothing gets light.
220 bool no_sunlight = true;
221 bool no_top_block = false;
222 // Check if node above block has sunlight
224 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
225 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
230 catch(InvalidPositionException &e)
236 /*std::cout<<"("<<x<<","<<z<<"): "
237 <<"no_top_block="<<no_top_block
238 <<", is_underground="<<is_underground
239 <<", no_sunlight="<<no_sunlight
242 s16 y = MAP_BLOCKSIZE-1;
244 // This makes difference to diminishing in water.
245 bool stopped_to_solid_object = false;
247 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
252 MapNode &n = getNodeRef(pos);
254 if(current_light == 0)
258 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
260 // Do nothing: Sunlight is continued
262 else if(nodemgr->get(n).light_propagates == false)
264 // A solid object is on the way.
265 stopped_to_solid_object = true;
273 current_light = diminish_light(current_light);
276 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
278 if(current_light > old_light || remove_light)
280 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
283 if(diminish_light(current_light) != 0)
285 light_sources.insert(pos_relative + pos, true);
288 if(current_light == 0 && stopped_to_solid_object)
292 *black_air_left = true;
297 // Whether or not the block below should see LIGHT_SUN
298 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
301 If the block below hasn't already been marked invalid:
303 Check if the node below the block has proper sunlight at top.
304 If not, the block below is invalid.
306 Ignore non-transparent nodes as they always have no light
310 if(block_below_is_valid)
312 MapNode n = getNodeParent(v3s16(x, -1, z));
313 if(nodemgr->get(n).light_propagates)
315 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
316 && sunlight_should_go_down == false)
317 block_below_is_valid = false;
318 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
319 && sunlight_should_go_down == true)
320 block_below_is_valid = false;
324 catch(InvalidPositionException &e)
326 /*std::cout<<"InvalidBlockException for bottom block node"
328 // Just no block below, no need to panic.
333 return block_below_is_valid;
337 void MapBlock::copyTo(VoxelManipulator &dst)
339 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
340 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
342 // Copy from data to VoxelManipulator
343 dst.copyFrom(data, data_area, v3s16(0,0,0),
344 getPosRelative(), data_size);
347 void MapBlock::copyFrom(VoxelManipulator &dst)
349 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
350 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
352 // Copy from VoxelManipulator to data
353 dst.copyTo(data, data_area, v3s16(0,0,0),
354 getPosRelative(), data_size);
357 void MapBlock::actuallyUpdateDayNightDiff()
359 INodeDefManager *nodemgr = m_gamedef->ndef();
360 // Running this function un-expires m_day_night_differs
361 m_day_night_differs_expired = false;
365 m_day_night_differs = false;
369 bool differs = false;
372 Check if any lighting value differs
374 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
376 MapNode &n = data[i];
377 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
385 If some lighting values differ, check if the whole thing is
386 just air. If it is, differ = false
390 bool only_air = true;
391 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
393 MapNode &n = data[i];
394 if(n.getContent() != CONTENT_AIR)
404 // Set member variable
405 m_day_night_differs = differs;
408 void MapBlock::expireDayNightDiff()
410 INodeDefManager *nodemgr = m_gamedef->ndef();
413 m_day_night_differs = false;
414 m_day_night_differs_expired = false;
418 m_day_night_differs_expired = true;
421 s16 MapBlock::getGroundLevel(v2s16 p2d)
427 s16 y = MAP_BLOCKSIZE-1;
430 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
431 if(m_gamedef->ndef()->get(n).walkable)
433 if(y == MAP_BLOCKSIZE-1)
441 catch(InvalidPositionException &e)
450 // List relevant id-name pairs for ids in the block using nodedef
451 // Renumbers the content IDs (starting at 0 and incrementing
452 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
453 INodeDefManager *nodedef)
455 std::map<content_t, content_t> mapping;
456 std::set<content_t> unknown_contents;
457 content_t id_counter = 0;
458 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
460 content_t global_id = nodes[i].getContent();
461 content_t id = CONTENT_IGNORE;
463 // Try to find an existing mapping
464 std::map<content_t, content_t>::iterator j = mapping.find(global_id);
465 if(j != mapping.end())
471 // We have to assign a new mapping
473 mapping.insert(std::make_pair(global_id, id));
475 const ContentFeatures &f = nodedef->get(global_id);
476 const std::string &name = f.name;
478 unknown_contents.insert(global_id);
480 nimap->set(id, name);
483 // Update the MapNode
484 nodes[i].setContent(id);
486 for(std::set<content_t>::const_iterator
487 i = unknown_contents.begin();
488 i != unknown_contents.end(); i++){
489 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
490 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
493 // Correct ids in the block to match nodedef based on names.
494 // Unknown ones are added to nodedef.
495 // Will not update itself to match id-name pairs in nodedef.
496 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
499 INodeDefManager *nodedef = gamedef->ndef();
500 // This means the block contains incorrect ids, and we contain
501 // the information to convert those to names.
502 // nodedef contains information to convert our names to globally
504 std::set<content_t> unnamed_contents;
505 std::set<std::string> unallocatable_contents;
506 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
508 content_t local_id = nodes[i].getContent();
510 bool found = nimap->getName(local_id, name);
512 unnamed_contents.insert(local_id);
516 found = nodedef->getId(name, global_id);
518 global_id = gamedef->allocateUnknownNodeId(name);
519 if(global_id == CONTENT_IGNORE){
520 unallocatable_contents.insert(name);
524 nodes[i].setContent(global_id);
526 for(std::set<content_t>::const_iterator
527 i = unnamed_contents.begin();
528 i != unnamed_contents.end(); i++){
529 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
530 <<"Block contains id "<<(*i)
531 <<" with no name mapping"<<std::endl;
533 for(std::set<std::string>::const_iterator
534 i = unallocatable_contents.begin();
535 i != unallocatable_contents.end(); i++){
536 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
537 <<"Could not allocate global id for node name \""
538 <<(*i)<<"\""<<std::endl;
542 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
544 if(!ser_ver_supported(version))
545 throw VersionMismatchException("ERROR: MapBlock format not supported");
549 throw SerializationError("ERROR: Not writing dummy block.");
554 serialize_pre22(os, version, disk);
562 if(getDayNightDiff())
564 if(m_lighting_expired)
566 if(m_generated == false)
574 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
577 MapNode *tmp_nodes = new MapNode[nodecount];
578 for(u32 i=0; i<nodecount; i++)
579 tmp_nodes[i] = data[i];
580 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
582 u8 content_width = 1;
583 /*u8 content_width = (nimap.size() <= 255) ? 1 : 2;*/
585 writeU8(os, content_width);
586 writeU8(os, params_width);
587 MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
588 content_width, params_width, true);
593 u8 content_width = 1;
594 /*u8 content_width = 2;*/
596 writeU8(os, content_width);
597 writeU8(os, params_width);
598 MapNode::serializeBulk(os, version, data, nodecount,
599 content_width, params_width, true);
605 std::ostringstream oss(std::ios_base::binary);
607 m_node_metadata.serialize(oss);
609 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
610 compressZlib(oss.str(), os);
613 Data that goes to disk, but not the network
619 m_node_timers.serialize(os);
622 m_static_objects.serialize(os);
625 writeU32(os, getTimestamp());
627 // Write block-specific node definition id mapping
633 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
635 if(!ser_ver_supported(version))
636 throw VersionMismatchException("ERROR: MapBlock format not supported");
638 m_day_night_differs_expired = false;
642 deSerialize_pre22(is, version, disk);
646 u8 flags = readU8(is);
647 is_underground = (flags & 0x01) ? true : false;
648 m_day_night_differs = (flags & 0x02) ? true : false;
649 m_lighting_expired = (flags & 0x04) ? true : false;
650 m_generated = (flags & 0x08) ? false : true;
655 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
656 u8 content_width = readU8(is);
657 u8 params_width = readU8(is);
658 if(content_width != 1)
659 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
660 if(params_width != 2)
661 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
662 MapNode::deSerializeBulk(is, version, data, nodecount,
663 content_width, params_width, true);
670 std::ostringstream oss(std::ios_base::binary);
671 decompressZlib(is, oss);
672 std::istringstream iss(oss.str(), std::ios_base::binary);
674 m_node_metadata.deSerialize(iss, m_gamedef);
676 content_nodemeta_deserialize_legacy(iss,
677 &m_node_metadata, &m_node_timers,
680 catch(SerializationError &e)
682 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
683 <<" while deserializing node metadata: "<<e.what()<<std::endl;
687 Data that is only on disk
693 m_node_timers.deSerialize(is);
696 m_static_objects.deSerialize(is);
699 setTimestamp(readU32(is));
700 m_disk_timestamp = m_timestamp;
702 // Dynamically re-set ids based on node names
704 nimap.deSerialize(is);
705 correctBlockNodeIds(&nimap, data, m_gamedef);
713 // List relevant id-name pairs for ids in the block using nodedef
714 // Before serialization version 22
715 static void getBlockNodeIdMapping_pre22(NameIdMapping *nimap, MapNode *nodes,
716 INodeDefManager *nodedef)
718 std::set<content_t> unknown_contents;
719 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
721 content_t id = nodes[i].getContent();
722 const ContentFeatures &f = nodedef->get(id);
723 const std::string &name = f.name;
725 unknown_contents.insert(id);
727 nimap->set(id, name);
729 for(std::set<content_t>::const_iterator
730 i = unknown_contents.begin();
731 i != unknown_contents.end(); i++){
732 errorstream<<"getBlockNodeIdMapping_pre22(): IGNORING ERROR: "
733 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
736 void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
738 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
740 MapNode *tmp_data = new MapNode[nodecount];
742 // Legacy data changes
743 // This code has to change from post-22 to pre-22 format.
744 INodeDefManager *nodedef = m_gamedef->ndef();
745 for(u32 i=0; i<nodecount; i++)
747 const ContentFeatures &f = nodedef->get(tmp_data[i].getContent());
749 if(nodedef->getId("default:stone_with_coal") == tmp_data[i].getContent())
751 tmp_data[i].setContent(nodedef->getId("default:stone"));
752 tmp_data[i].setParam1(1); // MINERAL_COAL
754 else if(nodedef->getId("default:stone_with_iron") == tmp_data[i].getContent())
756 tmp_data[i].setContent(nodedef->getId("default:stone"));
757 tmp_data[i].setParam1(2); // MINERAL_IRON
760 if(f.legacy_facedir_simple)
762 tmp_data[i].setParam1(tmp_data[i].getParam2());
763 tmp_data[i].setParam2(0);
766 if(f.legacy_wallmounted)
768 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
769 u8 dir_new_format = tmp_data[i].getParam2() & 7; // lowest 3 bits
770 u8 dir_old_format = wallmounted_new_to_old[dir_new_format];
771 tmp_data[i].setParam2(dir_old_format);
776 u32 ser_length = MapNode::serializedLength(version);
777 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
778 for(u32 i=0; i<nodecount; i++)
780 tmp_data[i].serialize(&databuf_nodelist[i*ser_length], version);
785 // These have no compression
786 if(version <= 3 || version == 5 || version == 6)
788 writeU8(os, is_underground);
789 os.write((char*)*databuf_nodelist, databuf_nodelist.getSize());
791 else if(version <= 10)
795 Compress the materials and the params separately.
799 writeU8(os, is_underground);
801 // Get and compress materials
802 SharedBuffer<u8> materialdata(nodecount);
803 for(u32 i=0; i<nodecount; i++)
805 materialdata[i] = databuf_nodelist[i*ser_length];
807 compress(materialdata, os, version);
809 // Get and compress lights
810 SharedBuffer<u8> lightdata(nodecount);
811 for(u32 i=0; i<nodecount; i++)
813 lightdata[i] = databuf_nodelist[i*ser_length+1];
815 compress(lightdata, os, version);
819 // Get and compress param2
820 SharedBuffer<u8> param2data(nodecount);
821 for(u32 i=0; i<nodecount; i++)
823 param2data[i] = databuf_nodelist[i*ser_length+2];
825 compress(param2data, os, version);
828 // All other versions (newest)
835 if(getDayNightDiff())
837 if(m_lighting_expired)
841 if(m_generated == false)
850 // Create buffer with different parameters sorted
851 SharedBuffer<u8> databuf(nodecount*3);
852 for(u32 i=0; i<nodecount; i++)
854 databuf[i] = databuf_nodelist[i*ser_length];
855 databuf[i+nodecount] = databuf_nodelist[i*ser_length+1];
856 databuf[i+nodecount*2] = databuf_nodelist[i*ser_length+2];
860 Compress data to output stream
863 compress(databuf, os, version);
873 std::ostringstream oss(std::ios_base::binary);
874 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
875 os<<serializeString(oss.str());
877 // This will happen if the string is longer than 65535
878 catch(SerializationError &e)
880 // Use an empty string
881 os<<serializeString("");
886 std::ostringstream oss(std::ios_base::binary);
887 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
888 compressZlib(oss.str(), os);
889 //os<<serializeLongString(oss.str());
897 // Versions up from 9 have block objects. (DEPRECATED)
904 // Versions up from 15 have static objects.
907 m_static_objects.serialize(os);
913 writeU32(os, getTimestamp());
916 // Scan and write node definition id mapping
920 getBlockNodeIdMapping_pre22(&nimap, data, m_gamedef->ndef());
926 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
928 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
930 // Initialize default flags
931 is_underground = false;
932 m_day_night_differs = false;
933 m_lighting_expired = false;
936 // Make a temporary buffer
937 u32 ser_length = MapNode::serializedLength(version);
938 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
940 // These have no compression
941 if(version <= 3 || version == 5 || version == 6)
946 throw SerializationError
947 ("MapBlock::deSerialize: no enough input data");
948 is_underground = tmp;
949 is.read((char*)*databuf_nodelist, nodecount * ser_length);
950 if(is.gcount() != nodecount * ser_length)
951 throw SerializationError
952 ("MapBlock::deSerialize: no enough input data");
954 else if(version <= 10)
957 is.read((char*)&t8, 1);
961 // Uncompress and set material data
962 std::ostringstream os(std::ios_base::binary);
963 decompress(is, os, version);
964 std::string s = os.str();
965 if(s.size() != nodecount)
966 throw SerializationError
967 ("MapBlock::deSerialize: invalid format");
968 for(u32 i=0; i<s.size(); i++)
970 databuf_nodelist[i*ser_length] = s[i];
974 // Uncompress and set param data
975 std::ostringstream os(std::ios_base::binary);
976 decompress(is, os, version);
977 std::string s = os.str();
978 if(s.size() != nodecount)
979 throw SerializationError
980 ("MapBlock::deSerialize: invalid format");
981 for(u32 i=0; i<s.size(); i++)
983 databuf_nodelist[i*ser_length + 1] = s[i];
989 // Uncompress and set param2 data
990 std::ostringstream os(std::ios_base::binary);
991 decompress(is, os, version);
992 std::string s = os.str();
993 if(s.size() != nodecount)
994 throw SerializationError
995 ("MapBlock::deSerialize: invalid format");
996 for(u32 i=0; i<s.size(); i++)
998 databuf_nodelist[i*ser_length + 2] = s[i];
1002 // All other versions (newest)
1006 is.read((char*)&flags, 1);
1007 is_underground = (flags & 0x01) ? true : false;
1008 m_day_night_differs = (flags & 0x02) ? true : false;
1009 m_lighting_expired = (flags & 0x04) ? true : false;
1011 m_generated = (flags & 0x08) ? false : true;
1014 std::ostringstream os(std::ios_base::binary);
1015 decompress(is, os, version);
1016 std::string s = os.str();
1017 if(s.size() != nodecount*3)
1018 throw SerializationError
1019 ("MapBlock::deSerialize: decompress resulted in size"
1020 " other than nodecount*3");
1022 // deserialize nodes from buffer
1023 for(u32 i=0; i<nodecount; i++)
1025 databuf_nodelist[i*ser_length] = s[i];
1026 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
1027 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
1039 std::string data = deSerializeString(is);
1040 std::istringstream iss(data, std::ios_base::binary);
1041 content_nodemeta_deserialize_legacy(iss,
1042 &m_node_metadata, &m_node_timers,
1047 //std::string data = deSerializeLongString(is);
1048 std::ostringstream oss(std::ios_base::binary);
1049 decompressZlib(is, oss);
1050 std::istringstream iss(oss.str(), std::ios_base::binary);
1051 content_nodemeta_deserialize_legacy(iss,
1052 &m_node_metadata, &m_node_timers,
1056 catch(SerializationError &e)
1058 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
1059 <<" while deserializing node metadata"<<std::endl;
1064 // Deserialize node data
1065 for(u32 i=0; i<nodecount; i++)
1067 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
1073 Versions up from 9 have block objects. (DEPRECATED)
1076 u16 count = readU16(is);
1077 // Not supported and length not known if count is not 0
1079 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
1080 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
1086 Versions up from 15 have static objects.
1089 m_static_objects.deSerialize(is);
1093 setTimestamp(readU32(is));
1094 m_disk_timestamp = m_timestamp;
1096 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
1099 // Dynamically re-set ids based on node names
1100 NameIdMapping nimap;
1101 // If supported, read node definition id mapping
1103 nimap.deSerialize(is);
1104 // Else set the legacy mapping
1106 content_mapnode_get_name_id_mapping(&nimap);
1108 correctBlockNodeIds(&nimap, data, m_gamedef);
1112 // Legacy data changes
1113 // This code has to convert from pre-22 to post-22 format.
1114 INodeDefManager *nodedef = m_gamedef->ndef();
1115 for(u32 i=0; i<nodecount; i++)
1117 const ContentFeatures &f = nodedef->get(data[i].getContent());
1119 if(nodedef->getId("default:stone") == data[i].getContent()
1120 && data[i].getParam1() == 1)
1122 data[i].setContent(nodedef->getId("default:stone_with_coal"));
1123 data[i].setParam1(0);
1125 else if(nodedef->getId("default:stone") == data[i].getContent()
1126 && data[i].getParam1() == 2)
1128 data[i].setContent(nodedef->getId("default:stone_with_iron"));
1129 data[i].setParam1(0);
1132 if(f.legacy_facedir_simple)
1134 data[i].setParam2(data[i].getParam1());
1135 data[i].setParam1(0);
1138 if(f.legacy_wallmounted)
1140 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
1141 u8 dir_old_format = data[i].getParam2();
1142 u8 dir_new_format = 0;
1143 for(u8 j=0; j<8; j++)
1145 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1151 data[i].setParam2(dir_new_format);
1158 Get a quick string to describe what a block actually contains
1160 std::string analyze_block(MapBlock *block)
1165 std::ostringstream desc;
1167 v3s16 p = block->getPos();
1169 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1172 switch(block->getModified())
1174 case MOD_STATE_CLEAN:
1177 case MOD_STATE_WRITE_AT_UNLOAD:
1178 desc<<"WRITE_AT_UNLOAD, ";
1180 case MOD_STATE_WRITE_NEEDED:
1181 desc<<"WRITE_NEEDED, ";
1184 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1187 if(block->isGenerated())
1188 desc<<"is_gen [X], ";
1190 desc<<"is_gen [ ], ";
1192 if(block->getIsUnderground())
1193 desc<<"is_ug [X], ";
1195 desc<<"is_ug [ ], ";
1197 if(block->getLightingExpired())
1198 desc<<"lighting_exp [X], ";
1200 desc<<"lighting_exp [ ], ";
1202 if(block->isDummy())
1208 bool full_ignore = true;
1209 bool some_ignore = false;
1210 bool full_air = true;
1211 bool some_air = false;
1212 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1213 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1214 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1217 MapNode n = block->getNode(p);
1218 content_t c = n.getContent();
1219 if(c == CONTENT_IGNORE)
1222 full_ignore = false;
1223 if(c == CONTENT_AIR)
1231 std::ostringstream ss;
1234 ss<<"IGNORE (full), ";
1235 else if(some_ignore)
1243 if(ss.str().size()>=2)
1244 desc<<ss.str().substr(0, ss.str().size()-2);
1249 return desc.str().substr(0, desc.str().size()-2);