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
38 MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
39 m_node_metadata(new NodeMetadataList),
43 m_modified(MOD_STATE_WRITE_NEEDED),
44 is_underground(false),
45 m_lighting_expired(true),
46 m_day_night_differs(false),
48 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
56 m_mesh_expired = false;
59 m_temp_mods_mutex.Init();
67 JMutexAutoLock lock(mesh_mutex);
77 delete m_node_metadata;
83 bool MapBlock::isValidPositionParent(v3s16 p)
85 if(isValidPosition(p))
90 return m_parent->isValidPosition(getPosRelative() + p);
94 MapNode MapBlock::getNodeParent(v3s16 p)
96 if(isValidPosition(p) == false)
98 return m_parent->getNode(getPosRelative() + p);
103 throw InvalidPositionException();
104 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
108 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
110 if(isValidPosition(p) == false)
112 m_parent->setNode(getPosRelative() + p, n);
117 throw InvalidPositionException();
118 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
122 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
124 if(isValidPosition(p) == false)
127 return m_parent->getNode(getPosRelative() + p);
129 catch(InvalidPositionException &e)
131 return MapNode(CONTENT_IGNORE);
138 return MapNode(CONTENT_IGNORE);
140 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
147 void MapBlock::updateMesh(u32 daynight_ratio)
151 DEBUG: If mesh has been generated, don't generate it again
154 JMutexAutoLock meshlock(mesh_mutex);
161 data.fill(daynight_ratio, this);
163 scene::SMesh *mesh_new = makeMapBlockMesh(&data, m_gamedef);
169 replaceMesh(mesh_new);
174 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
178 //scene::SMesh *mesh_old = mesh[daynight_i];
179 //mesh[daynight_i] = mesh_new;
181 scene::SMesh *mesh_old = mesh;
183 setMeshExpired(false);
187 // Remove hardware buffers of meshbuffers of mesh
188 // NOTE: No way, this runs in a different thread and everything
189 /*u32 c = mesh_old->getMeshBufferCount();
190 for(u32 i=0; i<c; i++)
192 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
195 /*infostream<<"mesh_old->getReferenceCount()="
196 <<mesh_old->getReferenceCount()<<std::endl;
197 u32 c = mesh_old->getMeshBufferCount();
198 for(u32 i=0; i<c; i++)
200 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
201 infostream<<"buf->getReferenceCount()="
202 <<buf->getReferenceCount()<<std::endl;
217 Propagates sunlight down through the block.
218 Doesn't modify nodes that are not affected by sunlight.
220 Returns false if sunlight at bottom block is invalid.
221 Returns true if sunlight at bottom block is valid.
222 Returns true if bottom block doesn't exist.
224 If there is a block above, continues from it.
225 If there is no block above, assumes there is sunlight, unless
226 is_underground is set or highest node is water.
228 All sunlighted nodes are added to light_sources.
230 if remove_light==true, sets non-sunlighted nodes black.
232 if black_air_left!=NULL, it is set to true if non-sunlighted
233 air is left in block.
235 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
236 bool remove_light, bool *black_air_left)
238 INodeDefManager *nodemgr = m_gamedef->ndef();
240 // Whether the sunlight at the top of the bottom block is valid
241 bool block_below_is_valid = true;
243 v3s16 pos_relative = getPosRelative();
245 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
247 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
250 bool no_sunlight = false;
251 bool no_top_block = false;
252 // Check if node above block has sunlight
254 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
255 if(n.getContent() == CONTENT_IGNORE)
258 no_sunlight = is_underground;
260 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
265 catch(InvalidPositionException &e)
269 // NOTE: This makes over-ground roofed places sunlighted
270 // Assume sunlight, unless is_underground==true
277 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
278 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
283 // NOTE: As of now, this just would make everything dark.
285 //no_sunlight = true;
288 #if 0 // Doesn't work; nothing gets light.
289 bool no_sunlight = true;
290 bool no_top_block = false;
291 // Check if node above block has sunlight
293 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
294 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
299 catch(InvalidPositionException &e)
305 /*std::cout<<"("<<x<<","<<z<<"): "
306 <<"no_top_block="<<no_top_block
307 <<", is_underground="<<is_underground
308 <<", no_sunlight="<<no_sunlight
311 s16 y = MAP_BLOCKSIZE-1;
313 // This makes difference to diminishing in water.
314 bool stopped_to_solid_object = false;
316 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
321 MapNode &n = getNodeRef(pos);
323 if(current_light == 0)
327 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
329 // Do nothing: Sunlight is continued
331 else if(nodemgr->get(n).light_propagates == false)
333 // A solid object is on the way.
334 stopped_to_solid_object = true;
342 current_light = diminish_light(current_light);
345 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
347 if(current_light > old_light || remove_light)
349 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
352 if(diminish_light(current_light) != 0)
354 light_sources.insert(pos_relative + pos, true);
357 if(current_light == 0 && stopped_to_solid_object)
361 *black_air_left = true;
366 // Whether or not the block below should see LIGHT_SUN
367 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
370 If the block below hasn't already been marked invalid:
372 Check if the node below the block has proper sunlight at top.
373 If not, the block below is invalid.
375 Ignore non-transparent nodes as they always have no light
379 if(block_below_is_valid)
381 MapNode n = getNodeParent(v3s16(x, -1, z));
382 if(nodemgr->get(n).light_propagates)
384 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
385 && sunlight_should_go_down == false)
386 block_below_is_valid = false;
387 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
388 && sunlight_should_go_down == true)
389 block_below_is_valid = false;
393 catch(InvalidPositionException &e)
395 /*std::cout<<"InvalidBlockException for bottom block node"
397 // Just no block below, no need to panic.
402 return block_below_is_valid;
406 void MapBlock::copyTo(VoxelManipulator &dst)
408 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
409 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
411 // Copy from data to VoxelManipulator
412 dst.copyFrom(data, data_area, v3s16(0,0,0),
413 getPosRelative(), data_size);
416 void MapBlock::copyFrom(VoxelManipulator &dst)
418 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
419 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
421 // Copy from VoxelManipulator to data
422 dst.copyTo(data, data_area, v3s16(0,0,0),
423 getPosRelative(), data_size);
426 void MapBlock::updateDayNightDiff()
428 INodeDefManager *nodemgr = m_gamedef->ndef();
432 m_day_night_differs = false;
436 bool differs = false;
439 Check if any lighting value differs
441 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
443 MapNode &n = data[i];
444 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
452 If some lighting values differ, check if the whole thing is
453 just air. If it is, differ = false
457 bool only_air = true;
458 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
460 MapNode &n = data[i];
461 if(n.getContent() != CONTENT_AIR)
471 // Set member variable
472 m_day_night_differs = differs;
475 s16 MapBlock::getGroundLevel(v2s16 p2d)
481 s16 y = MAP_BLOCKSIZE-1;
484 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
485 if(m_gamedef->ndef()->get(n).walkable)
487 if(y == MAP_BLOCKSIZE-1)
495 catch(InvalidPositionException &e)
505 // List relevant id-name pairs for ids in the block using nodedef
506 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapBlock *block,
507 INodeDefManager *nodedef)
509 std::set<content_t> unknown_contents;
510 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
511 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
512 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
515 MapNode n = block->getNode(p);
516 content_t id = n.getContent();
517 const ContentFeatures &f = nodedef->get(id);
518 const std::string &name = f.name;
520 unknown_contents.insert(id);
522 nimap->set(id, name);
524 for(std::set<content_t>::const_iterator
525 i = unknown_contents.begin();
526 i != unknown_contents.end(); i++){
527 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
528 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
531 // Correct ids in the block to match nodedef based on names.
532 // Unknown ones are added to nodedef.
533 // Will not update itself to match id-name pairs in nodedef.
534 void correctBlockNodeIds(const NameIdMapping *nimap, MapBlock *block,
537 INodeDefManager *nodedef = gamedef->ndef();
538 // This means the block contains incorrect ids, and we contain
539 // the information to convert those to names.
540 // nodedef contains information to convert our names to globally
542 std::set<content_t> unnamed_contents;
543 std::set<std::string> unallocatable_contents;
544 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
545 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
546 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
549 MapNode n = block->getNode(p);
550 content_t local_id = n.getContent();
552 bool found = nimap->getName(local_id, name);
554 unnamed_contents.insert(local_id);
558 found = nodedef->getId(name, global_id);
560 global_id = gamedef->allocateUnknownNodeId(name);
561 if(global_id == CONTENT_IGNORE){
562 unallocatable_contents.insert(name);
566 n.setContent(global_id);
567 block->setNode(p, n);
569 for(std::set<content_t>::const_iterator
570 i = unnamed_contents.begin();
571 i != unnamed_contents.end(); i++){
572 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
573 <<"Block contains id "<<(*i)
574 <<" with no name mapping"<<std::endl;
576 for(std::set<std::string>::const_iterator
577 i = unallocatable_contents.begin();
578 i != unallocatable_contents.end(); i++){
579 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
580 <<"Could not allocate global id for node name \""
581 <<(*i)<<"\""<<std::endl;
585 void MapBlock::serialize(std::ostream &os, u8 version)
587 if(!ser_ver_supported(version))
588 throw VersionMismatchException("ERROR: MapBlock format not supported");
592 throw SerializationError("ERROR: Not writing dummy block.");
595 // These have no compression
596 if(version <= 3 || version == 5 || version == 6)
598 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
600 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
601 SharedBuffer<u8> dest(buflen);
603 dest[0] = is_underground;
604 for(u32 i=0; i<nodecount; i++)
606 u32 s = 1 + i * MapNode::serializedLength(version);
607 data[i].serialize(&dest[s], version);
610 os.write((char*)*dest, dest.getSize());
612 else if(version <= 10)
616 Compress the materials and the params separately.
620 os.write((char*)&is_underground, 1);
622 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
624 // Get and compress materials
625 SharedBuffer<u8> materialdata(nodecount);
626 for(u32 i=0; i<nodecount; i++)
628 materialdata[i] = data[i].param0;
630 compress(materialdata, os, version);
632 // Get and compress lights
633 SharedBuffer<u8> lightdata(nodecount);
634 for(u32 i=0; i<nodecount; i++)
636 lightdata[i] = data[i].param1;
638 compress(lightdata, os, version);
642 // Get and compress param2
643 SharedBuffer<u8> param2data(nodecount);
644 for(u32 i=0; i<nodecount; i++)
646 param2data[i] = data[i].param2;
648 compress(param2data, os, version);
651 // All other versions (newest)
658 if(m_day_night_differs)
660 if(m_lighting_expired)
664 if(m_generated == false)
667 os.write((char*)&flags, 1);
669 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
676 SharedBuffer<u8> databuf_nodelist(nodecount*3);
677 for(u32 i=0; i<nodecount; i++)
679 data[i].serialize(&databuf_nodelist[i*3], version);
682 // Create buffer with different parameters sorted
683 SharedBuffer<u8> databuf(nodecount*3);
684 for(u32 i=0; i<nodecount; i++)
686 databuf[i] = databuf_nodelist[i*3];
687 databuf[i+nodecount] = databuf_nodelist[i*3+1];
688 databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
692 Compress data to output stream
695 compress(databuf, os, version);
705 std::ostringstream oss(std::ios_base::binary);
706 m_node_metadata->serialize(oss);
707 os<<serializeString(oss.str());
709 // This will happen if the string is longer than 65535
710 catch(SerializationError &e)
712 // Use an empty string
713 os<<serializeString("");
718 std::ostringstream oss(std::ios_base::binary);
719 m_node_metadata->serialize(oss);
720 compressZlib(oss.str(), os);
721 //os<<serializeLongString(oss.str());
727 void MapBlock::deSerialize(std::istream &is, u8 version)
729 if(!ser_ver_supported(version))
730 throw VersionMismatchException("ERROR: MapBlock format not supported");
732 // These have no lighting info
735 setLightingExpired(true);
738 // These have no "generated" field
744 // These have no compression
745 if(version <= 3 || version == 5 || version == 6)
747 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
751 throw SerializationError
752 ("MapBlock::deSerialize: no enough input data");
753 is_underground = tmp;
754 for(u32 i=0; i<nodecount; i++)
756 s32 len = MapNode::serializedLength(version);
757 SharedBuffer<u8> d(len);
758 is.read((char*)*d, len);
759 if(is.gcount() != len)
760 throw SerializationError
761 ("MapBlock::deSerialize: no enough input data");
762 data[i].deSerialize(*d, version);
765 else if(version <= 10)
767 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
770 is.read((char*)&t8, 1);
774 // Uncompress and set material data
775 std::ostringstream os(std::ios_base::binary);
776 decompress(is, os, version);
777 std::string s = os.str();
778 if(s.size() != nodecount)
779 throw SerializationError
780 ("MapBlock::deSerialize: invalid format");
781 for(u32 i=0; i<s.size(); i++)
783 data[i].param0 = s[i];
787 // Uncompress and set param data
788 std::ostringstream os(std::ios_base::binary);
789 decompress(is, os, version);
790 std::string s = os.str();
791 if(s.size() != nodecount)
792 throw SerializationError
793 ("MapBlock::deSerialize: invalid format");
794 for(u32 i=0; i<s.size(); i++)
796 data[i].param1 = s[i];
802 // Uncompress and set param2 data
803 std::ostringstream os(std::ios_base::binary);
804 decompress(is, os, version);
805 std::string s = os.str();
806 if(s.size() != nodecount)
807 throw SerializationError
808 ("MapBlock::deSerialize: invalid format");
809 for(u32 i=0; i<s.size(); i++)
811 data[i].param2 = s[i];
815 // All other versions (newest)
818 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
821 is.read((char*)&flags, 1);
822 is_underground = (flags & 0x01) ? true : false;
823 m_day_night_differs = (flags & 0x02) ? true : false;
824 m_lighting_expired = (flags & 0x04) ? true : false;
826 m_generated = (flags & 0x08) ? false : true;
829 std::ostringstream os(std::ios_base::binary);
830 decompress(is, os, version);
831 std::string s = os.str();
832 if(s.size() != nodecount*3)
833 throw SerializationError
834 ("MapBlock::deSerialize: decompress resulted in size"
835 " other than nodecount*3");
837 // deserialize nodes from buffer
838 for(u32 i=0; i<nodecount; i++)
842 buf[1] = s[i+nodecount];
843 buf[2] = s[i+nodecount*2];
844 data[i].deSerialize(buf, version);
856 std::string data = deSerializeString(is);
857 std::istringstream iss(data, std::ios_base::binary);
858 m_node_metadata->deSerialize(iss, m_gamedef);
862 //std::string data = deSerializeLongString(is);
863 std::ostringstream oss(std::ios_base::binary);
864 decompressZlib(is, oss);
865 std::istringstream iss(oss.str(), std::ios_base::binary);
866 m_node_metadata->deSerialize(iss, m_gamedef);
869 catch(SerializationError &e)
871 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
872 <<" while deserializing node metadata"<<std::endl;
878 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
880 // Versions up from 9 have block objects. (DEPRECATED)
887 // Versions up from 15 have static objects.
890 m_static_objects.serialize(os);
896 writeU32(os, getTimestamp());
899 // Scan and write node definition id mapping
902 getBlockNodeIdMapping(&nimap, this, m_gamedef->ndef());
907 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
910 Versions up from 9 have block objects. (DEPRECATED)
913 u16 count = readU16(is);
914 // Not supported and length not known if count is not 0
916 errorstream<<"WARNING: MapBlock::deSerializeDiskExtra(): "
917 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
923 Versions up from 15 have static objects.
926 m_static_objects.deSerialize(is);
930 setTimestamp(readU32(is));
932 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
934 // Dynamically re-set ids based on node names
936 // If supported, read node definition id mapping
938 nimap.deSerialize(is);
939 // Else set the legacy mapping
941 content_mapnode_get_name_id_mapping(&nimap);
943 correctBlockNodeIds(&nimap, this, m_gamedef);
947 Get a quick string to describe what a block actually contains
949 std::string analyze_block(MapBlock *block)
954 std::ostringstream desc;
956 v3s16 p = block->getPos();
958 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
961 switch(block->getModified())
963 case MOD_STATE_CLEAN:
966 case MOD_STATE_WRITE_AT_UNLOAD:
967 desc<<"WRITE_AT_UNLOAD, ";
969 case MOD_STATE_WRITE_NEEDED:
970 desc<<"WRITE_NEEDED, ";
973 desc<<"unknown getModified()="+itos(block->getModified())+", ";
976 if(block->isGenerated())
977 desc<<"is_gen [X], ";
979 desc<<"is_gen [ ], ";
981 if(block->getIsUnderground())
987 if(block->getMeshExpired())
988 desc<<"mesh_exp [X], ";
990 desc<<"mesh_exp [ ], ";
993 if(block->getLightingExpired())
994 desc<<"lighting_exp [X], ";
996 desc<<"lighting_exp [ ], ";
1004 bool full_ignore = true;
1005 bool some_ignore = false;
1006 bool full_air = true;
1007 bool some_air = false;
1008 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1009 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1010 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1013 MapNode n = block->getNode(p);
1014 content_t c = n.getContent();
1015 if(c == CONTENT_IGNORE)
1018 full_ignore = false;
1019 if(c == CONTENT_AIR)
1027 std::ostringstream ss;
1030 ss<<"IGNORE (full), ";
1031 else if(some_ignore)
1039 if(ss.str().size()>=2)
1040 desc<<ss.str().substr(0, ss.str().size()-2);
1045 return desc.str().substr(0, desc.str().size()-2);