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"
37 #include "util/string.h"
39 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
45 MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
49 m_modified(MOD_STATE_WRITE_NEEDED),
50 m_modified_reason("initial"),
51 m_modified_reason_too_long(false),
52 is_underground(false),
53 m_lighting_expired(true),
54 m_day_night_differs(false),
55 m_day_night_differs_expired(true),
57 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
58 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
75 //JMutexAutoLock lock(mesh_mutex);
89 bool MapBlock::isValidPositionParent(v3s16 p)
91 if(isValidPosition(p))
96 return m_parent->isValidPosition(getPosRelative() + p);
100 MapNode MapBlock::getNodeParent(v3s16 p)
102 if(isValidPosition(p) == false)
104 return m_parent->getNode(getPosRelative() + p);
109 throw InvalidPositionException();
110 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
114 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
116 if(isValidPosition(p) == false)
118 m_parent->setNode(getPosRelative() + p, n);
123 throw InvalidPositionException();
124 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
128 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
130 if(isValidPosition(p) == false)
133 return m_parent->getNode(getPosRelative() + p);
135 catch(InvalidPositionException &e)
137 return MapNode(CONTENT_IGNORE);
144 return MapNode(CONTENT_IGNORE);
146 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
151 Propagates sunlight down through the block.
152 Doesn't modify nodes that are not affected by sunlight.
154 Returns false if sunlight at bottom block is invalid.
155 Returns true if sunlight at bottom block is valid.
156 Returns true if bottom block doesn't exist.
158 If there is a block above, continues from it.
159 If there is no block above, assumes there is sunlight, unless
160 is_underground is set or highest node is water.
162 All sunlighted nodes are added to light_sources.
164 if remove_light==true, sets non-sunlighted nodes black.
166 if black_air_left!=NULL, it is set to true if non-sunlighted
167 air is left in block.
169 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
170 bool remove_light, bool *black_air_left)
172 INodeDefManager *nodemgr = m_gamedef->ndef();
174 // Whether the sunlight at the top of the bottom block is valid
175 bool block_below_is_valid = true;
177 v3s16 pos_relative = getPosRelative();
179 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
181 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
184 bool no_sunlight = false;
185 bool no_top_block = false;
186 // Check if node above block has sunlight
188 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
189 if(n.getContent() == CONTENT_IGNORE)
192 no_sunlight = is_underground;
194 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
199 catch(InvalidPositionException &e)
203 // NOTE: This makes over-ground roofed places sunlighted
204 // Assume sunlight, unless is_underground==true
211 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
212 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
217 // NOTE: As of now, this just would make everything dark.
219 //no_sunlight = true;
222 #if 0 // Doesn't work; nothing gets light.
223 bool no_sunlight = true;
224 bool no_top_block = false;
225 // Check if node above block has sunlight
227 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
228 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
233 catch(InvalidPositionException &e)
239 /*std::cout<<"("<<x<<","<<z<<"): "
240 <<"no_top_block="<<no_top_block
241 <<", is_underground="<<is_underground
242 <<", no_sunlight="<<no_sunlight
245 s16 y = MAP_BLOCKSIZE-1;
247 // This makes difference to diminishing in water.
248 bool stopped_to_solid_object = false;
250 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
255 MapNode &n = getNodeRef(pos);
257 if(current_light == 0)
261 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
263 // Do nothing: Sunlight is continued
265 else if(nodemgr->get(n).light_propagates == false)
267 // A solid object is on the way.
268 stopped_to_solid_object = true;
276 current_light = diminish_light(current_light);
279 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
281 if(current_light > old_light || remove_light)
283 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
286 if(diminish_light(current_light) != 0)
288 light_sources.insert(pos_relative + pos, true);
291 if(current_light == 0 && stopped_to_solid_object)
295 *black_air_left = true;
300 // Whether or not the block below should see LIGHT_SUN
301 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
304 If the block below hasn't already been marked invalid:
306 Check if the node below the block has proper sunlight at top.
307 If not, the block below is invalid.
309 Ignore non-transparent nodes as they always have no light
313 if(block_below_is_valid)
315 MapNode n = getNodeParent(v3s16(x, -1, z));
316 if(nodemgr->get(n).light_propagates)
318 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
319 && sunlight_should_go_down == false)
320 block_below_is_valid = false;
321 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
322 && sunlight_should_go_down == true)
323 block_below_is_valid = false;
327 catch(InvalidPositionException &e)
329 /*std::cout<<"InvalidBlockException for bottom block node"
331 // Just no block below, no need to panic.
336 return block_below_is_valid;
340 void MapBlock::copyTo(VoxelManipulator &dst)
342 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
343 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
345 // Copy from data to VoxelManipulator
346 dst.copyFrom(data, data_area, v3s16(0,0,0),
347 getPosRelative(), data_size);
350 void MapBlock::copyFrom(VoxelManipulator &dst)
352 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
353 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
355 // Copy from VoxelManipulator to data
356 dst.copyTo(data, data_area, v3s16(0,0,0),
357 getPosRelative(), data_size);
360 void MapBlock::actuallyUpdateDayNightDiff()
362 INodeDefManager *nodemgr = m_gamedef->ndef();
363 // Running this function un-expires m_day_night_differs
364 m_day_night_differs_expired = false;
368 m_day_night_differs = false;
372 bool differs = false;
375 Check if any lighting value differs
377 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
379 MapNode &n = data[i];
380 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
388 If some lighting values differ, check if the whole thing is
389 just air. If it is, differ = false
393 bool only_air = true;
394 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
396 MapNode &n = data[i];
397 if(n.getContent() != CONTENT_AIR)
407 // Set member variable
408 m_day_night_differs = differs;
411 void MapBlock::expireDayNightDiff()
413 //INodeDefManager *nodemgr = m_gamedef->ndef();
416 m_day_night_differs = false;
417 m_day_night_differs_expired = false;
421 m_day_night_differs_expired = true;
424 s16 MapBlock::getGroundLevel(v2s16 p2d)
430 s16 y = MAP_BLOCKSIZE-1;
433 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
434 if(m_gamedef->ndef()->get(n).walkable)
436 if(y == MAP_BLOCKSIZE-1)
444 catch(InvalidPositionException &e)
453 // List relevant id-name pairs for ids in the block using nodedef
454 // Renumbers the content IDs (starting at 0 and incrementing
455 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
456 INodeDefManager *nodedef)
458 std::map<content_t, content_t> mapping;
459 std::set<content_t> unknown_contents;
460 content_t id_counter = 0;
461 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
463 content_t global_id = nodes[i].getContent();
464 content_t id = CONTENT_IGNORE;
466 // Try to find an existing mapping
467 std::map<content_t, content_t>::iterator j = mapping.find(global_id);
468 if(j != mapping.end())
474 // We have to assign a new mapping
476 mapping.insert(std::make_pair(global_id, id));
478 const ContentFeatures &f = nodedef->get(global_id);
479 const std::string &name = f.name;
481 unknown_contents.insert(global_id);
483 nimap->set(id, name);
486 // Update the MapNode
487 nodes[i].setContent(id);
489 for(std::set<content_t>::const_iterator
490 i = unknown_contents.begin();
491 i != unknown_contents.end(); i++){
492 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
493 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
496 // Correct ids in the block to match nodedef based on names.
497 // Unknown ones are added to nodedef.
498 // Will not update itself to match id-name pairs in nodedef.
499 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
502 INodeDefManager *nodedef = gamedef->ndef();
503 // This means the block contains incorrect ids, and we contain
504 // the information to convert those to names.
505 // nodedef contains information to convert our names to globally
507 std::set<content_t> unnamed_contents;
508 std::set<std::string> unallocatable_contents;
509 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
511 content_t local_id = nodes[i].getContent();
513 bool found = nimap->getName(local_id, name);
515 unnamed_contents.insert(local_id);
519 found = nodedef->getId(name, global_id);
521 global_id = gamedef->allocateUnknownNodeId(name);
522 if(global_id == CONTENT_IGNORE){
523 unallocatable_contents.insert(name);
527 nodes[i].setContent(global_id);
529 for(std::set<content_t>::const_iterator
530 i = unnamed_contents.begin();
531 i != unnamed_contents.end(); i++){
532 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
533 <<"Block contains id "<<(*i)
534 <<" with no name mapping"<<std::endl;
536 for(std::set<std::string>::const_iterator
537 i = unallocatable_contents.begin();
538 i != unallocatable_contents.end(); i++){
539 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
540 <<"Could not allocate global id for node name \""
541 <<(*i)<<"\""<<std::endl;
545 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
547 if(!ser_ver_supported(version))
548 throw VersionMismatchException("ERROR: MapBlock format not supported");
552 throw SerializationError("ERROR: Not writing dummy block.");
557 serialize_pre22(os, version, disk);
565 if(getDayNightDiff())
567 if(m_lighting_expired)
569 if(m_generated == false)
577 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
580 MapNode *tmp_nodes = new MapNode[nodecount];
581 for(u32 i=0; i<nodecount; i++)
582 tmp_nodes[i] = data[i];
583 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
585 u8 content_width = 1;
586 /*u8 content_width = (nimap.size() <= 255) ? 1 : 2;*/
588 writeU8(os, content_width);
589 writeU8(os, params_width);
590 MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
591 content_width, params_width, true);
596 u8 content_width = 1;
597 /*u8 content_width = 2;*/
599 writeU8(os, content_width);
600 writeU8(os, params_width);
601 MapNode::serializeBulk(os, version, data, nodecount,
602 content_width, params_width, true);
608 std::ostringstream oss(std::ios_base::binary);
610 m_node_metadata.serialize(oss);
612 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
613 compressZlib(oss.str(), os);
616 Data that goes to disk, but not the network
620 // Version 23 doesn't actually contain node timers
621 // (this field should have not been added)
624 // Node timers (uncomment when node timers are taken into use)
626 m_node_timers.serialize(os);*/
629 m_static_objects.serialize(os);
632 writeU32(os, getTimestamp());
634 // Write block-specific node definition id mapping
640 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
642 if(!ser_ver_supported(version))
643 throw VersionMismatchException("ERROR: MapBlock format not supported");
645 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
647 m_day_night_differs_expired = false;
651 deSerialize_pre22(is, version, disk);
655 u8 flags = readU8(is);
656 is_underground = (flags & 0x01) ? true : false;
657 m_day_night_differs = (flags & 0x02) ? true : false;
658 m_lighting_expired = (flags & 0x04) ? true : false;
659 m_generated = (flags & 0x08) ? false : true;
664 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
665 <<": Bulk node data"<<std::endl);
666 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
667 u8 content_width = readU8(is);
668 u8 params_width = readU8(is);
669 if(content_width != 1)
670 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
671 if(params_width != 2)
672 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
673 MapNode::deSerializeBulk(is, version, data, nodecount,
674 content_width, params_width, true);
679 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
680 <<": Node metadata"<<std::endl);
683 std::ostringstream oss(std::ios_base::binary);
684 decompressZlib(is, oss);
685 std::istringstream iss(oss.str(), std::ios_base::binary);
687 m_node_metadata.deSerialize(iss, m_gamedef);
689 content_nodemeta_deserialize_legacy(iss,
690 &m_node_metadata, &m_node_timers,
693 catch(SerializationError &e)
695 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
696 <<" while deserializing node metadata at ("
697 <<PP(getPos())<<": "<<e.what()<<std::endl;
701 Data that is only on disk
709 // Uncomment when node timers are taken into use
710 /*else if(version >= 24){
711 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
712 <<": Node timers"<<std::endl);
713 m_node_timers.deSerialize(is);
717 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
718 <<": Static objects"<<std::endl);
719 m_static_objects.deSerialize(is);
722 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
723 <<": Timestamp"<<std::endl);
724 setTimestamp(readU32(is));
725 m_disk_timestamp = m_timestamp;
727 // Dynamically re-set ids based on node names
728 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
729 <<": NameIdMapping"<<std::endl);
731 nimap.deSerialize(is);
732 correctBlockNodeIds(&nimap, data, m_gamedef);
735 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
736 <<": Done."<<std::endl);
743 // List relevant id-name pairs for ids in the block using nodedef
744 // Before serialization version 22
745 static void getBlockNodeIdMapping_pre22(NameIdMapping *nimap, MapNode *nodes,
746 INodeDefManager *nodedef)
748 std::set<content_t> unknown_contents;
749 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
751 content_t id = nodes[i].getContent();
752 const ContentFeatures &f = nodedef->get(id);
753 const std::string &name = f.name;
755 unknown_contents.insert(id);
757 nimap->set(id, name);
759 for(std::set<content_t>::const_iterator
760 i = unknown_contents.begin();
761 i != unknown_contents.end(); i++){
762 errorstream<<"getBlockNodeIdMapping_pre22(): IGNORING ERROR: "
763 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
766 void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
768 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
770 MapNode *tmp_data = new MapNode[nodecount];
772 // Legacy data changes
773 // This code has to change from post-22 to pre-22 format.
774 INodeDefManager *nodedef = m_gamedef->ndef();
775 for(u32 i=0; i<nodecount; i++)
777 const ContentFeatures &f = nodedef->get(tmp_data[i].getContent());
779 if(nodedef->getId("default:stone_with_coal") == tmp_data[i].getContent())
781 tmp_data[i].setContent(nodedef->getId("default:stone"));
782 tmp_data[i].setParam1(1); // MINERAL_COAL
784 else if(nodedef->getId("default:stone_with_iron") == tmp_data[i].getContent())
786 tmp_data[i].setContent(nodedef->getId("default:stone"));
787 tmp_data[i].setParam1(2); // MINERAL_IRON
790 if(f.legacy_facedir_simple)
792 tmp_data[i].setParam1(tmp_data[i].getParam2());
793 tmp_data[i].setParam2(0);
796 if(f.legacy_wallmounted)
798 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
799 u8 dir_new_format = tmp_data[i].getParam2() & 7; // lowest 3 bits
800 u8 dir_old_format = wallmounted_new_to_old[dir_new_format];
801 tmp_data[i].setParam2(dir_old_format);
806 u32 ser_length = MapNode::serializedLength(version);
807 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
808 for(u32 i=0; i<nodecount; i++)
810 tmp_data[i].serialize(&databuf_nodelist[i*ser_length], version);
815 // These have no compression
816 if(version <= 3 || version == 5 || version == 6)
818 writeU8(os, is_underground);
819 os.write((char*)*databuf_nodelist, databuf_nodelist.getSize());
821 else if(version <= 10)
825 Compress the materials and the params separately.
829 writeU8(os, is_underground);
831 // Get and compress materials
832 SharedBuffer<u8> materialdata(nodecount);
833 for(u32 i=0; i<nodecount; i++)
835 materialdata[i] = databuf_nodelist[i*ser_length];
837 compress(materialdata, os, version);
839 // Get and compress lights
840 SharedBuffer<u8> lightdata(nodecount);
841 for(u32 i=0; i<nodecount; i++)
843 lightdata[i] = databuf_nodelist[i*ser_length+1];
845 compress(lightdata, os, version);
849 // Get and compress param2
850 SharedBuffer<u8> param2data(nodecount);
851 for(u32 i=0; i<nodecount; i++)
853 param2data[i] = databuf_nodelist[i*ser_length+2];
855 compress(param2data, os, version);
858 // All other versions (newest)
865 if(getDayNightDiff())
867 if(m_lighting_expired)
871 if(m_generated == false)
880 // Create buffer with different parameters sorted
881 SharedBuffer<u8> databuf(nodecount*3);
882 for(u32 i=0; i<nodecount; i++)
884 databuf[i] = databuf_nodelist[i*ser_length];
885 databuf[i+nodecount] = databuf_nodelist[i*ser_length+1];
886 databuf[i+nodecount*2] = databuf_nodelist[i*ser_length+2];
890 Compress data to output stream
893 compress(databuf, os, version);
903 std::ostringstream oss(std::ios_base::binary);
904 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
905 os<<serializeString(oss.str());
907 // This will happen if the string is longer than 65535
908 catch(SerializationError &e)
910 // Use an empty string
911 os<<serializeString("");
916 std::ostringstream oss(std::ios_base::binary);
917 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
918 compressZlib(oss.str(), os);
919 //os<<serializeLongString(oss.str());
927 // Versions up from 9 have block objects. (DEPRECATED)
934 // Versions up from 15 have static objects.
937 m_static_objects.serialize(os);
943 writeU32(os, getTimestamp());
946 // Scan and write node definition id mapping
950 getBlockNodeIdMapping_pre22(&nimap, data, m_gamedef->ndef());
956 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
958 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
960 // Initialize default flags
961 is_underground = false;
962 m_day_night_differs = false;
963 m_lighting_expired = false;
966 // Make a temporary buffer
967 u32 ser_length = MapNode::serializedLength(version);
968 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
970 // These have no compression
971 if(version <= 3 || version == 5 || version == 6)
976 throw SerializationError
977 ("MapBlock::deSerialize: no enough input data");
978 is_underground = tmp;
979 is.read((char*)*databuf_nodelist, nodecount * ser_length);
980 if((u32)is.gcount() != nodecount * ser_length)
981 throw SerializationError
982 ("MapBlock::deSerialize: no enough input data");
984 else if(version <= 10)
987 is.read((char*)&t8, 1);
991 // Uncompress and set material data
992 std::ostringstream os(std::ios_base::binary);
993 decompress(is, os, version);
994 std::string s = os.str();
995 if(s.size() != nodecount)
996 throw SerializationError
997 ("MapBlock::deSerialize: invalid format");
998 for(u32 i=0; i<s.size(); i++)
1000 databuf_nodelist[i*ser_length] = s[i];
1004 // Uncompress and set param data
1005 std::ostringstream os(std::ios_base::binary);
1006 decompress(is, os, version);
1007 std::string s = os.str();
1008 if(s.size() != nodecount)
1009 throw SerializationError
1010 ("MapBlock::deSerialize: invalid format");
1011 for(u32 i=0; i<s.size(); i++)
1013 databuf_nodelist[i*ser_length + 1] = s[i];
1019 // Uncompress and set param2 data
1020 std::ostringstream os(std::ios_base::binary);
1021 decompress(is, os, version);
1022 std::string s = os.str();
1023 if(s.size() != nodecount)
1024 throw SerializationError
1025 ("MapBlock::deSerialize: invalid format");
1026 for(u32 i=0; i<s.size(); i++)
1028 databuf_nodelist[i*ser_length + 2] = s[i];
1032 // All other versions (newest)
1036 is.read((char*)&flags, 1);
1037 is_underground = (flags & 0x01) ? true : false;
1038 m_day_night_differs = (flags & 0x02) ? true : false;
1039 m_lighting_expired = (flags & 0x04) ? true : false;
1041 m_generated = (flags & 0x08) ? false : true;
1044 std::ostringstream os(std::ios_base::binary);
1045 decompress(is, os, version);
1046 std::string s = os.str();
1047 if(s.size() != nodecount*3)
1048 throw SerializationError
1049 ("MapBlock::deSerialize: decompress resulted in size"
1050 " other than nodecount*3");
1052 // deserialize nodes from buffer
1053 for(u32 i=0; i<nodecount; i++)
1055 databuf_nodelist[i*ser_length] = s[i];
1056 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
1057 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
1069 std::string data = deSerializeString(is);
1070 std::istringstream iss(data, std::ios_base::binary);
1071 content_nodemeta_deserialize_legacy(iss,
1072 &m_node_metadata, &m_node_timers,
1077 //std::string data = deSerializeLongString(is);
1078 std::ostringstream oss(std::ios_base::binary);
1079 decompressZlib(is, oss);
1080 std::istringstream iss(oss.str(), std::ios_base::binary);
1081 content_nodemeta_deserialize_legacy(iss,
1082 &m_node_metadata, &m_node_timers,
1086 catch(SerializationError &e)
1088 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
1089 <<" while deserializing node metadata"<<std::endl;
1094 // Deserialize node data
1095 for(u32 i=0; i<nodecount; i++)
1097 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
1103 Versions up from 9 have block objects. (DEPRECATED)
1106 u16 count = readU16(is);
1107 // Not supported and length not known if count is not 0
1109 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
1110 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
1116 Versions up from 15 have static objects.
1119 m_static_objects.deSerialize(is);
1123 setTimestamp(readU32(is));
1124 m_disk_timestamp = m_timestamp;
1126 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
1129 // Dynamically re-set ids based on node names
1130 NameIdMapping nimap;
1131 // If supported, read node definition id mapping
1133 nimap.deSerialize(is);
1134 // Else set the legacy mapping
1136 content_mapnode_get_name_id_mapping(&nimap);
1138 correctBlockNodeIds(&nimap, data, m_gamedef);
1142 // Legacy data changes
1143 // This code has to convert from pre-22 to post-22 format.
1144 INodeDefManager *nodedef = m_gamedef->ndef();
1145 for(u32 i=0; i<nodecount; i++)
1147 const ContentFeatures &f = nodedef->get(data[i].getContent());
1149 if(nodedef->getId("default:stone") == data[i].getContent()
1150 && data[i].getParam1() == 1)
1152 data[i].setContent(nodedef->getId("default:stone_with_coal"));
1153 data[i].setParam1(0);
1155 else if(nodedef->getId("default:stone") == data[i].getContent()
1156 && data[i].getParam1() == 2)
1158 data[i].setContent(nodedef->getId("default:stone_with_iron"));
1159 data[i].setParam1(0);
1162 if(f.legacy_facedir_simple)
1164 data[i].setParam2(data[i].getParam1());
1165 data[i].setParam1(0);
1168 if(f.legacy_wallmounted)
1170 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
1171 u8 dir_old_format = data[i].getParam2();
1172 u8 dir_new_format = 0;
1173 for(u8 j=0; j<8; j++)
1175 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1181 data[i].setParam2(dir_new_format);
1188 Get a quick string to describe what a block actually contains
1190 std::string analyze_block(MapBlock *block)
1195 std::ostringstream desc;
1197 v3s16 p = block->getPos();
1199 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1202 switch(block->getModified())
1204 case MOD_STATE_CLEAN:
1207 case MOD_STATE_WRITE_AT_UNLOAD:
1208 desc<<"WRITE_AT_UNLOAD, ";
1210 case MOD_STATE_WRITE_NEEDED:
1211 desc<<"WRITE_NEEDED, ";
1214 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1217 if(block->isGenerated())
1218 desc<<"is_gen [X], ";
1220 desc<<"is_gen [ ], ";
1222 if(block->getIsUnderground())
1223 desc<<"is_ug [X], ";
1225 desc<<"is_ug [ ], ";
1227 if(block->getLightingExpired())
1228 desc<<"lighting_exp [X], ";
1230 desc<<"lighting_exp [ ], ";
1232 if(block->isDummy())
1238 bool full_ignore = true;
1239 bool some_ignore = false;
1240 bool full_air = true;
1241 bool some_air = false;
1242 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1243 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1244 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1247 MapNode n = block->getNode(p);
1248 content_t c = n.getContent();
1249 if(c == CONTENT_IGNORE)
1252 full_ignore = false;
1253 if(c == CONTENT_AIR)
1261 std::ostringstream ss;
1264 ss<<"IGNORE (full), ";
1265 else if(some_ignore)
1273 if(ss.str().size()>=2)
1274 desc<<ss.str().substr(0, ss.str().size()-2);
1279 return desc.str().substr(0, desc.str().size()-2);