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),
53 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
54 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
71 //JMutexAutoLock lock(mesh_mutex);
81 delete m_node_metadata;
87 bool MapBlock::isValidPositionParent(v3s16 p)
89 if(isValidPosition(p))
94 return m_parent->isValidPosition(getPosRelative() + p);
98 MapNode MapBlock::getNodeParent(v3s16 p)
100 if(isValidPosition(p) == false)
102 return m_parent->getNode(getPosRelative() + p);
107 throw InvalidPositionException();
108 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
112 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
114 if(isValidPosition(p) == false)
116 m_parent->setNode(getPosRelative() + p, n);
121 throw InvalidPositionException();
122 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
126 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
128 if(isValidPosition(p) == false)
131 return m_parent->getNode(getPosRelative() + p);
133 catch(InvalidPositionException &e)
135 return MapNode(CONTENT_IGNORE);
142 return MapNode(CONTENT_IGNORE);
144 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
149 Propagates sunlight down through the block.
150 Doesn't modify nodes that are not affected by sunlight.
152 Returns false if sunlight at bottom block is invalid.
153 Returns true if sunlight at bottom block is valid.
154 Returns true if bottom block doesn't exist.
156 If there is a block above, continues from it.
157 If there is no block above, assumes there is sunlight, unless
158 is_underground is set or highest node is water.
160 All sunlighted nodes are added to light_sources.
162 if remove_light==true, sets non-sunlighted nodes black.
164 if black_air_left!=NULL, it is set to true if non-sunlighted
165 air is left in block.
167 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
168 bool remove_light, bool *black_air_left)
170 INodeDefManager *nodemgr = m_gamedef->ndef();
172 // Whether the sunlight at the top of the bottom block is valid
173 bool block_below_is_valid = true;
175 v3s16 pos_relative = getPosRelative();
177 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
179 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
182 bool no_sunlight = false;
183 bool no_top_block = false;
184 // Check if node above block has sunlight
186 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
187 if(n.getContent() == CONTENT_IGNORE)
190 no_sunlight = is_underground;
192 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
197 catch(InvalidPositionException &e)
201 // NOTE: This makes over-ground roofed places sunlighted
202 // Assume sunlight, unless is_underground==true
209 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
210 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
215 // NOTE: As of now, this just would make everything dark.
217 //no_sunlight = true;
220 #if 0 // Doesn't work; nothing gets light.
221 bool no_sunlight = true;
222 bool no_top_block = false;
223 // Check if node above block has sunlight
225 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
226 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
231 catch(InvalidPositionException &e)
237 /*std::cout<<"("<<x<<","<<z<<"): "
238 <<"no_top_block="<<no_top_block
239 <<", is_underground="<<is_underground
240 <<", no_sunlight="<<no_sunlight
243 s16 y = MAP_BLOCKSIZE-1;
245 // This makes difference to diminishing in water.
246 bool stopped_to_solid_object = false;
248 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
253 MapNode &n = getNodeRef(pos);
255 if(current_light == 0)
259 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
261 // Do nothing: Sunlight is continued
263 else if(nodemgr->get(n).light_propagates == false)
265 // A solid object is on the way.
266 stopped_to_solid_object = true;
274 current_light = diminish_light(current_light);
277 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
279 if(current_light > old_light || remove_light)
281 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
284 if(diminish_light(current_light) != 0)
286 light_sources.insert(pos_relative + pos, true);
289 if(current_light == 0 && stopped_to_solid_object)
293 *black_air_left = true;
298 // Whether or not the block below should see LIGHT_SUN
299 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
302 If the block below hasn't already been marked invalid:
304 Check if the node below the block has proper sunlight at top.
305 If not, the block below is invalid.
307 Ignore non-transparent nodes as they always have no light
311 if(block_below_is_valid)
313 MapNode n = getNodeParent(v3s16(x, -1, z));
314 if(nodemgr->get(n).light_propagates)
316 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
317 && sunlight_should_go_down == false)
318 block_below_is_valid = false;
319 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
320 && sunlight_should_go_down == true)
321 block_below_is_valid = false;
325 catch(InvalidPositionException &e)
327 /*std::cout<<"InvalidBlockException for bottom block node"
329 // Just no block below, no need to panic.
334 return block_below_is_valid;
338 void MapBlock::copyTo(VoxelManipulator &dst)
340 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
341 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
343 // Copy from data to VoxelManipulator
344 dst.copyFrom(data, data_area, v3s16(0,0,0),
345 getPosRelative(), data_size);
348 void MapBlock::copyFrom(VoxelManipulator &dst)
350 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
351 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
353 // Copy from VoxelManipulator to data
354 dst.copyTo(data, data_area, v3s16(0,0,0),
355 getPosRelative(), data_size);
358 void MapBlock::updateDayNightDiff()
360 INodeDefManager *nodemgr = m_gamedef->ndef();
364 m_day_night_differs = false;
368 bool differs = false;
371 Check if any lighting value differs
373 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
375 MapNode &n = data[i];
376 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
384 If some lighting values differ, check if the whole thing is
385 just air. If it is, differ = false
389 bool only_air = true;
390 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
392 MapNode &n = data[i];
393 if(n.getContent() != CONTENT_AIR)
403 // Set member variable
404 m_day_night_differs = differs;
407 s16 MapBlock::getGroundLevel(v2s16 p2d)
413 s16 y = MAP_BLOCKSIZE-1;
416 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
417 if(m_gamedef->ndef()->get(n).walkable)
419 if(y == MAP_BLOCKSIZE-1)
427 catch(InvalidPositionException &e)
436 // List relevant id-name pairs for ids in the block using nodedef
437 // Renumbers the content IDs (starting at 0 and incrementing
438 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
439 INodeDefManager *nodedef)
441 std::map<content_t, content_t> mapping;
442 std::set<content_t> unknown_contents;
443 content_t id_counter = 0;
444 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
446 content_t global_id = nodes[i].getContent();
447 content_t id = CONTENT_IGNORE;
449 // Try to find an existing mapping
450 std::map<content_t, content_t>::iterator j = mapping.find(global_id);
451 if(j != mapping.end())
457 // We have to assign a new mapping
459 mapping.insert(std::make_pair(global_id, id));
461 const ContentFeatures &f = nodedef->get(global_id);
462 const std::string &name = f.name;
464 unknown_contents.insert(global_id);
466 nimap->set(id, name);
469 // Update the MapNode
470 nodes[i].setContent(id);
472 for(std::set<content_t>::const_iterator
473 i = unknown_contents.begin();
474 i != unknown_contents.end(); i++){
475 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
476 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
479 // Correct ids in the block to match nodedef based on names.
480 // Unknown ones are added to nodedef.
481 // Will not update itself to match id-name pairs in nodedef.
482 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
485 INodeDefManager *nodedef = gamedef->ndef();
486 // This means the block contains incorrect ids, and we contain
487 // the information to convert those to names.
488 // nodedef contains information to convert our names to globally
490 std::set<content_t> unnamed_contents;
491 std::set<std::string> unallocatable_contents;
492 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
494 content_t local_id = nodes[i].getContent();
496 bool found = nimap->getName(local_id, name);
498 unnamed_contents.insert(local_id);
502 found = nodedef->getId(name, global_id);
504 global_id = gamedef->allocateUnknownNodeId(name);
505 if(global_id == CONTENT_IGNORE){
506 unallocatable_contents.insert(name);
510 nodes[i].setContent(global_id);
512 for(std::set<content_t>::const_iterator
513 i = unnamed_contents.begin();
514 i != unnamed_contents.end(); i++){
515 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
516 <<"Block contains id "<<(*i)
517 <<" with no name mapping"<<std::endl;
519 for(std::set<std::string>::const_iterator
520 i = unallocatable_contents.begin();
521 i != unallocatable_contents.end(); i++){
522 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
523 <<"Could not allocate global id for node name \""
524 <<(*i)<<"\""<<std::endl;
528 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
530 if(!ser_ver_supported(version))
531 throw VersionMismatchException("ERROR: MapBlock format not supported");
535 throw SerializationError("ERROR: Not writing dummy block.");
540 serialize_pre22(os, version, disk);
548 if(m_day_night_differs)
550 if(m_lighting_expired)
552 if(m_generated == false)
560 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
563 MapNode *tmp_nodes = new MapNode[nodecount];
564 for(u32 i=0; i<nodecount; i++)
565 tmp_nodes[i] = data[i];
566 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
568 u8 content_width = 1;
569 /*u8 content_width = (nimap.size() <= 255) ? 1 : 2;*/
571 writeU8(os, content_width);
572 writeU8(os, params_width);
573 MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
574 content_width, params_width, true);
579 u8 content_width = 1;
580 /*u8 content_width = 2;*/
582 writeU8(os, content_width);
583 writeU8(os, params_width);
584 MapNode::serializeBulk(os, version, data, nodecount,
585 content_width, params_width, true);
591 std::ostringstream oss(std::ios_base::binary);
592 m_node_metadata->serialize(oss);
593 compressZlib(oss.str(), os);
596 Data that goes to disk, but not the network
601 m_static_objects.serialize(os);
604 writeU32(os, getTimestamp());
606 // Write block-specific node definition id mapping
612 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
614 if(!ser_ver_supported(version))
615 throw VersionMismatchException("ERROR: MapBlock format not supported");
619 deSerialize_pre22(is, version, disk);
623 u8 flags = readU8(is);
624 is_underground = (flags & 0x01) ? true : false;
625 m_day_night_differs = (flags & 0x02) ? true : false;
626 m_lighting_expired = (flags & 0x04) ? true : false;
627 m_generated = (flags & 0x08) ? false : true;
632 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
633 u8 content_width = readU8(is);
634 u8 params_width = readU8(is);
635 if(content_width != 1)
636 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
637 if(params_width != 2)
638 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
639 MapNode::deSerializeBulk(is, version, data, nodecount,
640 content_width, params_width, true);
647 std::ostringstream oss(std::ios_base::binary);
648 decompressZlib(is, oss);
649 std::istringstream iss(oss.str(), std::ios_base::binary);
650 m_node_metadata->deSerialize(iss, m_gamedef);
652 catch(SerializationError &e)
654 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
655 <<" while deserializing node metadata"<<std::endl;
659 Data that is only on disk
664 m_static_objects.deSerialize(is);
667 setTimestamp(readU32(is));
668 m_disk_timestamp = m_timestamp;
670 // Dynamically re-set ids based on node names
672 nimap.deSerialize(is);
673 correctBlockNodeIds(&nimap, data, m_gamedef);
681 // List relevant id-name pairs for ids in the block using nodedef
682 // Before serialization version 22
683 static void getBlockNodeIdMapping_pre22(NameIdMapping *nimap, MapNode *nodes,
684 INodeDefManager *nodedef)
686 std::set<content_t> unknown_contents;
687 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
689 content_t id = nodes[i].getContent();
690 const ContentFeatures &f = nodedef->get(id);
691 const std::string &name = f.name;
693 unknown_contents.insert(id);
695 nimap->set(id, name);
697 for(std::set<content_t>::const_iterator
698 i = unknown_contents.begin();
699 i != unknown_contents.end(); i++){
700 errorstream<<"getBlockNodeIdMapping_pre22(): IGNORING ERROR: "
701 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
704 void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
706 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
708 MapNode *tmp_data = new MapNode[nodecount];
710 // Legacy data changes
711 // This code has to change from post-22 to pre-22 format.
712 INodeDefManager *nodedef = m_gamedef->ndef();
713 for(u32 i=0; i<nodecount; i++)
715 const ContentFeatures &f = nodedef->get(tmp_data[i].getContent());
717 if(nodedef->getId("default:stone_with_coal") == tmp_data[i].getContent())
719 tmp_data[i].setContent(nodedef->getId("default:stone"));
720 tmp_data[i].setParam1(1); // MINERAL_COAL
722 else if(nodedef->getId("default:stone_with_iron") == tmp_data[i].getContent())
724 tmp_data[i].setContent(nodedef->getId("default:stone"));
725 tmp_data[i].setParam1(2); // MINERAL_IRON
728 if(f.legacy_facedir_simple)
730 tmp_data[i].setParam1(tmp_data[i].getParam2());
731 tmp_data[i].setParam2(0);
734 if(f.legacy_wallmounted)
736 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
737 u8 dir_new_format = tmp_data[i].getParam2() & 7; // lowest 3 bits
738 u8 dir_old_format = wallmounted_new_to_old[dir_new_format];
739 tmp_data[i].setParam2(dir_old_format);
744 u32 ser_length = MapNode::serializedLength(version);
745 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
746 for(u32 i=0; i<nodecount; i++)
748 tmp_data[i].serialize(&databuf_nodelist[i*ser_length], version);
753 // These have no compression
754 if(version <= 3 || version == 5 || version == 6)
756 writeU8(os, is_underground);
757 os.write((char*)*databuf_nodelist, databuf_nodelist.getSize());
759 else if(version <= 10)
763 Compress the materials and the params separately.
767 writeU8(os, is_underground);
769 // Get and compress materials
770 SharedBuffer<u8> materialdata(nodecount);
771 for(u32 i=0; i<nodecount; i++)
773 materialdata[i] = databuf_nodelist[i*ser_length];
775 compress(materialdata, os, version);
777 // Get and compress lights
778 SharedBuffer<u8> lightdata(nodecount);
779 for(u32 i=0; i<nodecount; i++)
781 lightdata[i] = databuf_nodelist[i*ser_length+1];
783 compress(lightdata, os, version);
787 // Get and compress param2
788 SharedBuffer<u8> param2data(nodecount);
789 for(u32 i=0; i<nodecount; i++)
791 param2data[i] = databuf_nodelist[i*ser_length+2];
793 compress(param2data, os, version);
796 // All other versions (newest)
803 if(m_day_night_differs)
805 if(m_lighting_expired)
809 if(m_generated == false)
818 // Create buffer with different parameters sorted
819 SharedBuffer<u8> databuf(nodecount*3);
820 for(u32 i=0; i<nodecount; i++)
822 databuf[i] = databuf_nodelist[i*ser_length];
823 databuf[i+nodecount] = databuf_nodelist[i*ser_length+1];
824 databuf[i+nodecount*2] = databuf_nodelist[i*ser_length+2];
828 Compress data to output stream
831 compress(databuf, os, version);
841 std::ostringstream oss(std::ios_base::binary);
842 m_node_metadata->serialize(oss);
843 os<<serializeString(oss.str());
845 // This will happen if the string is longer than 65535
846 catch(SerializationError &e)
848 // Use an empty string
849 os<<serializeString("");
854 std::ostringstream oss(std::ios_base::binary);
855 m_node_metadata->serialize(oss);
856 compressZlib(oss.str(), os);
857 //os<<serializeLongString(oss.str());
865 // Versions up from 9 have block objects. (DEPRECATED)
872 // Versions up from 15 have static objects.
875 m_static_objects.serialize(os);
881 writeU32(os, getTimestamp());
884 // Scan and write node definition id mapping
888 getBlockNodeIdMapping_pre22(&nimap, data, m_gamedef->ndef());
894 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
896 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
898 // Initialize default flags
899 is_underground = false;
900 m_day_night_differs = false;
901 m_lighting_expired = false;
904 // Make a temporary buffer
905 u32 ser_length = MapNode::serializedLength(version);
906 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
908 // These have no compression
909 if(version <= 3 || version == 5 || version == 6)
914 throw SerializationError
915 ("MapBlock::deSerialize: no enough input data");
916 is_underground = tmp;
917 is.read((char*)*databuf_nodelist, nodecount * ser_length);
918 if(is.gcount() != nodecount * ser_length)
919 throw SerializationError
920 ("MapBlock::deSerialize: no enough input data");
922 else if(version <= 10)
925 is.read((char*)&t8, 1);
929 // Uncompress and set material data
930 std::ostringstream os(std::ios_base::binary);
931 decompress(is, os, version);
932 std::string s = os.str();
933 if(s.size() != nodecount)
934 throw SerializationError
935 ("MapBlock::deSerialize: invalid format");
936 for(u32 i=0; i<s.size(); i++)
938 databuf_nodelist[i*ser_length] = s[i];
942 // Uncompress and set param data
943 std::ostringstream os(std::ios_base::binary);
944 decompress(is, os, version);
945 std::string s = os.str();
946 if(s.size() != nodecount)
947 throw SerializationError
948 ("MapBlock::deSerialize: invalid format");
949 for(u32 i=0; i<s.size(); i++)
951 databuf_nodelist[i*ser_length + 1] = s[i];
957 // Uncompress and set param2 data
958 std::ostringstream os(std::ios_base::binary);
959 decompress(is, os, version);
960 std::string s = os.str();
961 if(s.size() != nodecount)
962 throw SerializationError
963 ("MapBlock::deSerialize: invalid format");
964 for(u32 i=0; i<s.size(); i++)
966 databuf_nodelist[i*ser_length + 2] = s[i];
970 // All other versions (newest)
974 is.read((char*)&flags, 1);
975 is_underground = (flags & 0x01) ? true : false;
976 m_day_night_differs = (flags & 0x02) ? true : false;
977 m_lighting_expired = (flags & 0x04) ? true : false;
979 m_generated = (flags & 0x08) ? false : true;
982 std::ostringstream os(std::ios_base::binary);
983 decompress(is, os, version);
984 std::string s = os.str();
985 if(s.size() != nodecount*3)
986 throw SerializationError
987 ("MapBlock::deSerialize: decompress resulted in size"
988 " other than nodecount*3");
990 // deserialize nodes from buffer
991 for(u32 i=0; i<nodecount; i++)
993 databuf_nodelist[i*ser_length] = s[i];
994 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
995 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
1007 std::string data = deSerializeString(is);
1008 std::istringstream iss(data, std::ios_base::binary);
1009 m_node_metadata->deSerialize(iss, m_gamedef);
1013 //std::string data = deSerializeLongString(is);
1014 std::ostringstream oss(std::ios_base::binary);
1015 decompressZlib(is, oss);
1016 std::istringstream iss(oss.str(), std::ios_base::binary);
1017 m_node_metadata->deSerialize(iss, m_gamedef);
1020 catch(SerializationError &e)
1022 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
1023 <<" while deserializing node metadata"<<std::endl;
1028 // Deserialize node data
1029 for(u32 i=0; i<nodecount; i++)
1031 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
1037 Versions up from 9 have block objects. (DEPRECATED)
1040 u16 count = readU16(is);
1041 // Not supported and length not known if count is not 0
1043 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
1044 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
1050 Versions up from 15 have static objects.
1053 m_static_objects.deSerialize(is);
1057 setTimestamp(readU32(is));
1058 m_disk_timestamp = m_timestamp;
1060 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
1063 // Dynamically re-set ids based on node names
1064 NameIdMapping nimap;
1065 // If supported, read node definition id mapping
1067 nimap.deSerialize(is);
1068 // Else set the legacy mapping
1070 content_mapnode_get_name_id_mapping(&nimap);
1072 correctBlockNodeIds(&nimap, data, m_gamedef);
1076 // Legacy data changes
1077 // This code has to convert from pre-22 to post-22 format.
1078 INodeDefManager *nodedef = m_gamedef->ndef();
1079 for(u32 i=0; i<nodecount; i++)
1081 const ContentFeatures &f = nodedef->get(data[i].getContent());
1083 if(nodedef->getId("default:stone") == data[i].getContent()
1084 && data[i].getParam1() == 1)
1086 data[i].setContent(nodedef->getId("default:stone_with_coal"));
1087 data[i].setParam1(0);
1089 else if(nodedef->getId("default:stone") == data[i].getContent()
1090 && data[i].getParam1() == 2)
1092 data[i].setContent(nodedef->getId("default:stone_with_iron"));
1093 data[i].setParam1(0);
1096 if(f.legacy_facedir_simple)
1098 data[i].setParam2(data[i].getParam1());
1099 data[i].setParam1(0);
1102 if(f.legacy_wallmounted)
1104 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
1105 u8 dir_old_format = data[i].getParam2();
1106 u8 dir_new_format = 0;
1107 for(u8 j=0; j<8; j++)
1109 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1115 data[i].setParam2(dir_new_format);
1122 Get a quick string to describe what a block actually contains
1124 std::string analyze_block(MapBlock *block)
1129 std::ostringstream desc;
1131 v3s16 p = block->getPos();
1133 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1136 switch(block->getModified())
1138 case MOD_STATE_CLEAN:
1141 case MOD_STATE_WRITE_AT_UNLOAD:
1142 desc<<"WRITE_AT_UNLOAD, ";
1144 case MOD_STATE_WRITE_NEEDED:
1145 desc<<"WRITE_NEEDED, ";
1148 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1151 if(block->isGenerated())
1152 desc<<"is_gen [X], ";
1154 desc<<"is_gen [ ], ";
1156 if(block->getIsUnderground())
1157 desc<<"is_ug [X], ";
1159 desc<<"is_ug [ ], ";
1161 if(block->getLightingExpired())
1162 desc<<"lighting_exp [X], ";
1164 desc<<"lighting_exp [ ], ";
1166 if(block->isDummy())
1172 bool full_ignore = true;
1173 bool some_ignore = false;
1174 bool full_air = true;
1175 bool some_air = false;
1176 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1177 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1178 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1181 MapNode n = block->getNode(p);
1182 content_t c = n.getContent();
1183 if(c == CONTENT_IGNORE)
1186 full_ignore = false;
1187 if(c == CONTENT_AIR)
1195 std::ostringstream ss;
1198 ss<<"IGNORE (full), ";
1199 else if(some_ignore)
1207 if(ss.str().size()>=2)
1208 desc<<ss.str().substr(0, ss.str().size()-2);
1213 return desc.str().substr(0, desc.str().size()-2);