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),
62 m_mesh_expired = false;
65 m_temp_mods_mutex.Init();
73 JMutexAutoLock lock(mesh_mutex);
83 delete m_node_metadata;
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];
153 void MapBlock::updateMesh(u32 daynight_ratio)
157 DEBUG: If mesh has been generated, don't generate it again
160 JMutexAutoLock meshlock(mesh_mutex);
167 data.fill(daynight_ratio, this);
169 scene::SMesh *mesh_new = makeMapBlockMesh(&data, m_gamedef);
175 replaceMesh(mesh_new);
180 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
184 //scene::SMesh *mesh_old = mesh[daynight_i];
185 //mesh[daynight_i] = mesh_new;
187 scene::SMesh *mesh_old = mesh;
189 setMeshExpired(false);
193 // Remove hardware buffers of meshbuffers of mesh
194 // NOTE: No way, this runs in a different thread and everything
195 /*u32 c = mesh_old->getMeshBufferCount();
196 for(u32 i=0; i<c; i++)
198 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
201 /*infostream<<"mesh_old->getReferenceCount()="
202 <<mesh_old->getReferenceCount()<<std::endl;
203 u32 c = mesh_old->getMeshBufferCount();
204 for(u32 i=0; i<c; i++)
206 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
207 infostream<<"buf->getReferenceCount()="
208 <<buf->getReferenceCount()<<std::endl;
223 Propagates sunlight down through the block.
224 Doesn't modify nodes that are not affected by sunlight.
226 Returns false if sunlight at bottom block is invalid.
227 Returns true if sunlight at bottom block is valid.
228 Returns true if bottom block doesn't exist.
230 If there is a block above, continues from it.
231 If there is no block above, assumes there is sunlight, unless
232 is_underground is set or highest node is water.
234 All sunlighted nodes are added to light_sources.
236 if remove_light==true, sets non-sunlighted nodes black.
238 if black_air_left!=NULL, it is set to true if non-sunlighted
239 air is left in block.
241 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
242 bool remove_light, bool *black_air_left)
244 INodeDefManager *nodemgr = m_gamedef->ndef();
246 // Whether the sunlight at the top of the bottom block is valid
247 bool block_below_is_valid = true;
249 v3s16 pos_relative = getPosRelative();
251 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
253 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
256 bool no_sunlight = false;
257 bool no_top_block = false;
258 // Check if node above block has sunlight
260 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
261 if(n.getContent() == CONTENT_IGNORE)
264 no_sunlight = is_underground;
266 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
271 catch(InvalidPositionException &e)
275 // NOTE: This makes over-ground roofed places sunlighted
276 // Assume sunlight, unless is_underground==true
283 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
284 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
289 // NOTE: As of now, this just would make everything dark.
291 //no_sunlight = true;
294 #if 0 // Doesn't work; nothing gets light.
295 bool no_sunlight = true;
296 bool no_top_block = false;
297 // Check if node above block has sunlight
299 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
300 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
305 catch(InvalidPositionException &e)
311 /*std::cout<<"("<<x<<","<<z<<"): "
312 <<"no_top_block="<<no_top_block
313 <<", is_underground="<<is_underground
314 <<", no_sunlight="<<no_sunlight
317 s16 y = MAP_BLOCKSIZE-1;
319 // This makes difference to diminishing in water.
320 bool stopped_to_solid_object = false;
322 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
327 MapNode &n = getNodeRef(pos);
329 if(current_light == 0)
333 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
335 // Do nothing: Sunlight is continued
337 else if(nodemgr->get(n).light_propagates == false)
339 // A solid object is on the way.
340 stopped_to_solid_object = true;
348 current_light = diminish_light(current_light);
351 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
353 if(current_light > old_light || remove_light)
355 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
358 if(diminish_light(current_light) != 0)
360 light_sources.insert(pos_relative + pos, true);
363 if(current_light == 0 && stopped_to_solid_object)
367 *black_air_left = true;
372 // Whether or not the block below should see LIGHT_SUN
373 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
376 If the block below hasn't already been marked invalid:
378 Check if the node below the block has proper sunlight at top.
379 If not, the block below is invalid.
381 Ignore non-transparent nodes as they always have no light
385 if(block_below_is_valid)
387 MapNode n = getNodeParent(v3s16(x, -1, z));
388 if(nodemgr->get(n).light_propagates)
390 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
391 && sunlight_should_go_down == false)
392 block_below_is_valid = false;
393 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
394 && sunlight_should_go_down == true)
395 block_below_is_valid = false;
399 catch(InvalidPositionException &e)
401 /*std::cout<<"InvalidBlockException for bottom block node"
403 // Just no block below, no need to panic.
408 return block_below_is_valid;
412 void MapBlock::copyTo(VoxelManipulator &dst)
414 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
415 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
417 // Copy from data to VoxelManipulator
418 dst.copyFrom(data, data_area, v3s16(0,0,0),
419 getPosRelative(), data_size);
422 void MapBlock::copyFrom(VoxelManipulator &dst)
424 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
425 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
427 // Copy from VoxelManipulator to data
428 dst.copyTo(data, data_area, v3s16(0,0,0),
429 getPosRelative(), data_size);
432 void MapBlock::updateDayNightDiff()
434 INodeDefManager *nodemgr = m_gamedef->ndef();
438 m_day_night_differs = false;
442 bool differs = false;
445 Check if any lighting value differs
447 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
449 MapNode &n = data[i];
450 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
458 If some lighting values differ, check if the whole thing is
459 just air. If it is, differ = false
463 bool only_air = true;
464 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
466 MapNode &n = data[i];
467 if(n.getContent() != CONTENT_AIR)
477 // Set member variable
478 m_day_night_differs = differs;
481 s16 MapBlock::getGroundLevel(v2s16 p2d)
487 s16 y = MAP_BLOCKSIZE-1;
490 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
491 if(m_gamedef->ndef()->get(n).walkable)
493 if(y == MAP_BLOCKSIZE-1)
501 catch(InvalidPositionException &e)
510 // List relevant id-name pairs for ids in the block using nodedef
511 // Renumbers the content IDs (starting at 0 and incrementing
512 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
513 INodeDefManager *nodedef)
515 std::map<content_t, content_t> mapping;
516 std::set<content_t> unknown_contents;
517 content_t id_counter = 0;
518 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
520 content_t global_id = nodes[i].getContent();
521 content_t id = CONTENT_IGNORE;
523 // Try to find an existing mapping
524 std::map<content_t, content_t>::iterator j = mapping.find(global_id);
525 if(j != mapping.end())
531 // We have to assign a new mapping
533 mapping.insert(std::make_pair(global_id, id));
535 const ContentFeatures &f = nodedef->get(global_id);
536 const std::string &name = f.name;
538 unknown_contents.insert(global_id);
540 nimap->set(id, name);
543 // Update the MapNode
544 nodes[i].setContent(id);
546 for(std::set<content_t>::const_iterator
547 i = unknown_contents.begin();
548 i != unknown_contents.end(); i++){
549 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
550 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
553 // Correct ids in the block to match nodedef based on names.
554 // Unknown ones are added to nodedef.
555 // Will not update itself to match id-name pairs in nodedef.
556 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
559 INodeDefManager *nodedef = gamedef->ndef();
560 // This means the block contains incorrect ids, and we contain
561 // the information to convert those to names.
562 // nodedef contains information to convert our names to globally
564 std::set<content_t> unnamed_contents;
565 std::set<std::string> unallocatable_contents;
566 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
568 content_t local_id = nodes[i].getContent();
570 bool found = nimap->getName(local_id, name);
572 unnamed_contents.insert(local_id);
576 found = nodedef->getId(name, global_id);
578 global_id = gamedef->allocateUnknownNodeId(name);
579 if(global_id == CONTENT_IGNORE){
580 unallocatable_contents.insert(name);
584 nodes[i].setContent(global_id);
586 for(std::set<content_t>::const_iterator
587 i = unnamed_contents.begin();
588 i != unnamed_contents.end(); i++){
589 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
590 <<"Block contains id "<<(*i)
591 <<" with no name mapping"<<std::endl;
593 for(std::set<std::string>::const_iterator
594 i = unallocatable_contents.begin();
595 i != unallocatable_contents.end(); i++){
596 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
597 <<"Could not allocate global id for node name \""
598 <<(*i)<<"\""<<std::endl;
602 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
604 if(!ser_ver_supported(version))
605 throw VersionMismatchException("ERROR: MapBlock format not supported");
609 throw SerializationError("ERROR: Not writing dummy block.");
614 serialize_pre22(os, version, disk);
622 if(m_day_night_differs)
624 if(m_lighting_expired)
626 if(m_generated == false)
634 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
637 MapNode *tmp_nodes = new MapNode[nodecount];
638 for(u32 i=0; i<nodecount; i++)
639 tmp_nodes[i] = data[i];
640 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
642 u8 content_width = 1;
643 /*u8 content_width = (nimap.size() <= 255) ? 1 : 2;*/
645 writeU8(os, content_width);
646 writeU8(os, params_width);
647 MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
648 content_width, params_width, true);
653 u8 content_width = 1;
654 /*u8 content_width = 2;*/
656 writeU8(os, content_width);
657 writeU8(os, params_width);
658 MapNode::serializeBulk(os, version, data, nodecount,
659 content_width, params_width, true);
665 std::ostringstream oss(std::ios_base::binary);
666 m_node_metadata->serialize(oss);
667 compressZlib(oss.str(), os);
670 Data that goes to disk, but not the network
675 m_static_objects.serialize(os);
678 writeU32(os, getTimestamp());
680 // Write block-specific node definition id mapping
686 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
688 if(!ser_ver_supported(version))
689 throw VersionMismatchException("ERROR: MapBlock format not supported");
693 deSerialize_pre22(is, version, disk);
697 u8 flags = readU8(is);
698 is_underground = (flags & 0x01) ? true : false;
699 m_day_night_differs = (flags & 0x02) ? true : false;
700 m_lighting_expired = (flags & 0x04) ? true : false;
701 m_generated = (flags & 0x08) ? false : true;
706 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
707 u8 content_width = readU8(is);
708 u8 params_width = readU8(is);
709 if(content_width != 1)
710 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
711 if(params_width != 2)
712 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
713 MapNode::deSerializeBulk(is, version, data, nodecount,
714 content_width, params_width, true);
721 std::ostringstream oss(std::ios_base::binary);
722 decompressZlib(is, oss);
723 std::istringstream iss(oss.str(), std::ios_base::binary);
724 m_node_metadata->deSerialize(iss, m_gamedef);
726 catch(SerializationError &e)
728 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
729 <<" while deserializing node metadata"<<std::endl;
733 Data that is only on disk
738 m_static_objects.deSerialize(is);
741 setTimestamp(readU32(is));
742 m_disk_timestamp = m_timestamp;
744 // Dynamically re-set ids based on node names
746 nimap.deSerialize(is);
747 correctBlockNodeIds(&nimap, data, m_gamedef);
755 // List relevant id-name pairs for ids in the block using nodedef
756 // Before serialization version 22
757 static void getBlockNodeIdMapping_pre22(NameIdMapping *nimap, MapNode *nodes,
758 INodeDefManager *nodedef)
760 std::set<content_t> unknown_contents;
761 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
763 content_t id = nodes[i].getContent();
764 const ContentFeatures &f = nodedef->get(id);
765 const std::string &name = f.name;
767 unknown_contents.insert(id);
769 nimap->set(id, name);
771 for(std::set<content_t>::const_iterator
772 i = unknown_contents.begin();
773 i != unknown_contents.end(); i++){
774 errorstream<<"getBlockNodeIdMapping_pre22(): IGNORING ERROR: "
775 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
778 void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
780 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
782 MapNode *tmp_data = new MapNode[nodecount];
784 // Legacy data changes
785 // This code has to change from post-22 to pre-22 format.
786 INodeDefManager *nodedef = m_gamedef->ndef();
787 for(u32 i=0; i<nodecount; i++)
789 const ContentFeatures &f = nodedef->get(tmp_data[i].getContent());
791 if(nodedef->getId("default:stone_with_coal") == tmp_data[i].getContent())
793 tmp_data[i].setContent(nodedef->getId("default:stone"));
794 tmp_data[i].setParam1(1); // MINERAL_COAL
796 else if(nodedef->getId("default:stone_with_iron") == tmp_data[i].getContent())
798 tmp_data[i].setContent(nodedef->getId("default:stone"));
799 tmp_data[i].setParam1(2); // MINERAL_IRON
802 if(f.legacy_facedir_simple)
804 tmp_data[i].setParam1(tmp_data[i].getParam2());
805 tmp_data[i].setParam2(0);
808 if(f.legacy_wallmounted)
810 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
811 u8 dir_new_format = tmp_data[i].getParam2() & 7; // lowest 3 bits
812 u8 dir_old_format = wallmounted_new_to_old[dir_new_format];
813 tmp_data[i].setParam2(dir_old_format);
818 u32 ser_length = MapNode::serializedLength(version);
819 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
820 for(u32 i=0; i<nodecount; i++)
822 tmp_data[i].serialize(&databuf_nodelist[i*ser_length], version);
827 // These have no compression
828 if(version <= 3 || version == 5 || version == 6)
830 writeU8(os, is_underground);
831 os.write((char*)*databuf_nodelist, databuf_nodelist.getSize());
833 else if(version <= 10)
837 Compress the materials and the params separately.
841 writeU8(os, is_underground);
843 // Get and compress materials
844 SharedBuffer<u8> materialdata(nodecount);
845 for(u32 i=0; i<nodecount; i++)
847 materialdata[i] = databuf_nodelist[i*ser_length];
849 compress(materialdata, os, version);
851 // Get and compress lights
852 SharedBuffer<u8> lightdata(nodecount);
853 for(u32 i=0; i<nodecount; i++)
855 lightdata[i] = databuf_nodelist[i*ser_length+1];
857 compress(lightdata, os, version);
861 // Get and compress param2
862 SharedBuffer<u8> param2data(nodecount);
863 for(u32 i=0; i<nodecount; i++)
865 param2data[i] = databuf_nodelist[i*ser_length+2];
867 compress(param2data, os, version);
870 // All other versions (newest)
877 if(m_day_night_differs)
879 if(m_lighting_expired)
883 if(m_generated == false)
892 // Create buffer with different parameters sorted
893 SharedBuffer<u8> databuf(nodecount*3);
894 for(u32 i=0; i<nodecount; i++)
896 databuf[i] = databuf_nodelist[i*ser_length];
897 databuf[i+nodecount] = databuf_nodelist[i*ser_length+1];
898 databuf[i+nodecount*2] = databuf_nodelist[i*ser_length+2];
902 Compress data to output stream
905 compress(databuf, os, version);
915 std::ostringstream oss(std::ios_base::binary);
916 m_node_metadata->serialize(oss);
917 os<<serializeString(oss.str());
919 // This will happen if the string is longer than 65535
920 catch(SerializationError &e)
922 // Use an empty string
923 os<<serializeString("");
928 std::ostringstream oss(std::ios_base::binary);
929 m_node_metadata->serialize(oss);
930 compressZlib(oss.str(), os);
931 //os<<serializeLongString(oss.str());
939 // Versions up from 9 have block objects. (DEPRECATED)
946 // Versions up from 15 have static objects.
949 m_static_objects.serialize(os);
955 writeU32(os, getTimestamp());
958 // Scan and write node definition id mapping
962 getBlockNodeIdMapping_pre22(&nimap, data, m_gamedef->ndef());
968 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
970 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
972 // Initialize default flags
973 is_underground = false;
974 m_day_night_differs = false;
975 m_lighting_expired = false;
978 // Make a temporary buffer
979 u32 ser_length = MapNode::serializedLength(version);
980 SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
982 // These have no compression
983 if(version <= 3 || version == 5 || version == 6)
988 throw SerializationError
989 ("MapBlock::deSerialize: no enough input data");
990 is_underground = tmp;
991 is.read((char*)*databuf_nodelist, nodecount * ser_length);
992 if(is.gcount() != nodecount * ser_length)
993 throw SerializationError
994 ("MapBlock::deSerialize: no enough input data");
996 else if(version <= 10)
999 is.read((char*)&t8, 1);
1000 is_underground = t8;
1003 // Uncompress and set material 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] = s[i];
1016 // Uncompress and set param data
1017 std::ostringstream os(std::ios_base::binary);
1018 decompress(is, os, version);
1019 std::string s = os.str();
1020 if(s.size() != nodecount)
1021 throw SerializationError
1022 ("MapBlock::deSerialize: invalid format");
1023 for(u32 i=0; i<s.size(); i++)
1025 databuf_nodelist[i*ser_length + 1] = s[i];
1031 // Uncompress and set param2 data
1032 std::ostringstream os(std::ios_base::binary);
1033 decompress(is, os, version);
1034 std::string s = os.str();
1035 if(s.size() != nodecount)
1036 throw SerializationError
1037 ("MapBlock::deSerialize: invalid format");
1038 for(u32 i=0; i<s.size(); i++)
1040 databuf_nodelist[i*ser_length + 2] = s[i];
1044 // All other versions (newest)
1048 is.read((char*)&flags, 1);
1049 is_underground = (flags & 0x01) ? true : false;
1050 m_day_night_differs = (flags & 0x02) ? true : false;
1051 m_lighting_expired = (flags & 0x04) ? true : false;
1053 m_generated = (flags & 0x08) ? false : true;
1056 std::ostringstream os(std::ios_base::binary);
1057 decompress(is, os, version);
1058 std::string s = os.str();
1059 if(s.size() != nodecount*3)
1060 throw SerializationError
1061 ("MapBlock::deSerialize: decompress resulted in size"
1062 " other than nodecount*3");
1064 // deserialize nodes from buffer
1065 for(u32 i=0; i<nodecount; i++)
1067 databuf_nodelist[i*ser_length] = s[i];
1068 databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
1069 databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
1081 std::string data = deSerializeString(is);
1082 std::istringstream iss(data, std::ios_base::binary);
1083 m_node_metadata->deSerialize(iss, m_gamedef);
1087 //std::string data = deSerializeLongString(is);
1088 std::ostringstream oss(std::ios_base::binary);
1089 decompressZlib(is, oss);
1090 std::istringstream iss(oss.str(), std::ios_base::binary);
1091 m_node_metadata->deSerialize(iss, m_gamedef);
1094 catch(SerializationError &e)
1096 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
1097 <<" while deserializing node metadata"<<std::endl;
1102 // Deserialize node data
1103 for(u32 i=0; i<nodecount; i++)
1105 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
1111 Versions up from 9 have block objects. (DEPRECATED)
1114 u16 count = readU16(is);
1115 // Not supported and length not known if count is not 0
1117 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
1118 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
1124 Versions up from 15 have static objects.
1127 m_static_objects.deSerialize(is);
1131 setTimestamp(readU32(is));
1132 m_disk_timestamp = m_timestamp;
1134 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
1137 // Dynamically re-set ids based on node names
1138 NameIdMapping nimap;
1139 // If supported, read node definition id mapping
1141 nimap.deSerialize(is);
1142 // Else set the legacy mapping
1144 content_mapnode_get_name_id_mapping(&nimap);
1146 correctBlockNodeIds(&nimap, data, m_gamedef);
1150 // Legacy data changes
1151 // This code has to convert from pre-22 to post-22 format.
1152 INodeDefManager *nodedef = m_gamedef->ndef();
1153 for(u32 i=0; i<nodecount; i++)
1155 const ContentFeatures &f = nodedef->get(data[i].getContent());
1157 if(nodedef->getId("default:stone") == data[i].getContent()
1158 && data[i].getParam1() == 1)
1160 data[i].setContent(nodedef->getId("default:stone_with_coal"));
1161 data[i].setParam1(0);
1163 else if(nodedef->getId("default:stone") == data[i].getContent()
1164 && data[i].getParam1() == 2)
1166 data[i].setContent(nodedef->getId("default:stone_with_iron"));
1167 data[i].setParam1(0);
1170 if(f.legacy_facedir_simple)
1172 data[i].setParam2(data[i].getParam1());
1173 data[i].setParam1(0);
1176 if(f.legacy_wallmounted)
1178 u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
1179 u8 dir_old_format = data[i].getParam2();
1180 u8 dir_new_format = 0;
1181 for(u8 j=0; j<8; j++)
1183 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1189 data[i].setParam2(dir_new_format);
1196 Get a quick string to describe what a block actually contains
1198 std::string analyze_block(MapBlock *block)
1203 std::ostringstream desc;
1205 v3s16 p = block->getPos();
1207 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1210 switch(block->getModified())
1212 case MOD_STATE_CLEAN:
1215 case MOD_STATE_WRITE_AT_UNLOAD:
1216 desc<<"WRITE_AT_UNLOAD, ";
1218 case MOD_STATE_WRITE_NEEDED:
1219 desc<<"WRITE_NEEDED, ";
1222 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1225 if(block->isGenerated())
1226 desc<<"is_gen [X], ";
1228 desc<<"is_gen [ ], ";
1230 if(block->getIsUnderground())
1231 desc<<"is_ug [X], ";
1233 desc<<"is_ug [ ], ";
1236 if(block->getMeshExpired())
1237 desc<<"mesh_exp [X], ";
1239 desc<<"mesh_exp [ ], ";
1242 if(block->getLightingExpired())
1243 desc<<"lighting_exp [X], ";
1245 desc<<"lighting_exp [ ], ";
1247 if(block->isDummy())
1253 bool full_ignore = true;
1254 bool some_ignore = false;
1255 bool full_air = true;
1256 bool some_air = false;
1257 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1258 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1259 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1262 MapNode n = block->getNode(p);
1263 content_t c = n.getContent();
1264 if(c == CONTENT_IGNORE)
1267 full_ignore = false;
1268 if(c == CONTENT_AIR)
1276 std::ostringstream ss;
1279 ss<<"IGNORE (full), ";
1280 else if(some_ignore)
1288 if(ss.str().size()>=2)
1289 desc<<ss.str().substr(0, ss.str().size()-2);
1294 return desc.str().substr(0, desc.str().size()-2);