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)
511 // List relevant id-name pairs for ids in the block using nodedef
512 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapBlock *block,
513 INodeDefManager *nodedef)
515 std::set<content_t> unknown_contents;
516 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
517 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
518 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
521 MapNode n = block->getNode(p);
522 content_t id = n.getContent();
523 const ContentFeatures &f = nodedef->get(id);
524 const std::string &name = f.name;
526 unknown_contents.insert(id);
528 nimap->set(id, name);
530 for(std::set<content_t>::const_iterator
531 i = unknown_contents.begin();
532 i != unknown_contents.end(); i++){
533 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
534 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
537 // Correct ids in the block to match nodedef based on names.
538 // Unknown ones are added to nodedef.
539 // Will not update itself to match id-name pairs in nodedef.
540 void correctBlockNodeIds(const NameIdMapping *nimap, MapBlock *block,
543 INodeDefManager *nodedef = gamedef->ndef();
544 // This means the block contains incorrect ids, and we contain
545 // the information to convert those to names.
546 // nodedef contains information to convert our names to globally
548 std::set<content_t> unnamed_contents;
549 std::set<std::string> unallocatable_contents;
550 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
551 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
552 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
555 MapNode n = block->getNode(p);
556 content_t local_id = n.getContent();
558 bool found = nimap->getName(local_id, name);
560 unnamed_contents.insert(local_id);
564 found = nodedef->getId(name, global_id);
566 global_id = gamedef->allocateUnknownNodeId(name);
567 if(global_id == CONTENT_IGNORE){
568 unallocatable_contents.insert(name);
572 n.setContent(global_id);
573 block->setNode(p, n);
575 for(std::set<content_t>::const_iterator
576 i = unnamed_contents.begin();
577 i != unnamed_contents.end(); i++){
578 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
579 <<"Block contains id "<<(*i)
580 <<" with no name mapping"<<std::endl;
582 for(std::set<std::string>::const_iterator
583 i = unallocatable_contents.begin();
584 i != unallocatable_contents.end(); i++){
585 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
586 <<"Could not allocate global id for node name \""
587 <<(*i)<<"\""<<std::endl;
591 void MapBlock::serialize(std::ostream &os, u8 version)
593 if(!ser_ver_supported(version))
594 throw VersionMismatchException("ERROR: MapBlock format not supported");
598 throw SerializationError("ERROR: Not writing dummy block.");
601 // These have no compression
602 if(version <= 3 || version == 5 || version == 6)
604 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
606 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
607 SharedBuffer<u8> dest(buflen);
609 dest[0] = is_underground;
610 for(u32 i=0; i<nodecount; i++)
612 u32 s = 1 + i * MapNode::serializedLength(version);
613 data[i].serialize(&dest[s], version);
616 os.write((char*)*dest, dest.getSize());
618 else if(version <= 10)
622 Compress the materials and the params separately.
626 os.write((char*)&is_underground, 1);
628 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
630 // Get and compress materials
631 SharedBuffer<u8> materialdata(nodecount);
632 for(u32 i=0; i<nodecount; i++)
634 materialdata[i] = data[i].param0;
636 compress(materialdata, os, version);
638 // Get and compress lights
639 SharedBuffer<u8> lightdata(nodecount);
640 for(u32 i=0; i<nodecount; i++)
642 lightdata[i] = data[i].param1;
644 compress(lightdata, os, version);
648 // Get and compress param2
649 SharedBuffer<u8> param2data(nodecount);
650 for(u32 i=0; i<nodecount; i++)
652 param2data[i] = data[i].param2;
654 compress(param2data, os, version);
657 // All other versions (newest)
664 if(m_day_night_differs)
666 if(m_lighting_expired)
670 if(m_generated == false)
673 os.write((char*)&flags, 1);
675 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
682 SharedBuffer<u8> databuf_nodelist(nodecount*3);
683 for(u32 i=0; i<nodecount; i++)
685 data[i].serialize(&databuf_nodelist[i*3], version);
688 // Create buffer with different parameters sorted
689 SharedBuffer<u8> databuf(nodecount*3);
690 for(u32 i=0; i<nodecount; i++)
692 databuf[i] = databuf_nodelist[i*3];
693 databuf[i+nodecount] = databuf_nodelist[i*3+1];
694 databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
698 Compress data to output stream
701 compress(databuf, os, version);
711 std::ostringstream oss(std::ios_base::binary);
712 m_node_metadata->serialize(oss);
713 os<<serializeString(oss.str());
715 // This will happen if the string is longer than 65535
716 catch(SerializationError &e)
718 // Use an empty string
719 os<<serializeString("");
724 std::ostringstream oss(std::ios_base::binary);
725 m_node_metadata->serialize(oss);
726 compressZlib(oss.str(), os);
727 //os<<serializeLongString(oss.str());
733 void MapBlock::deSerialize(std::istream &is, u8 version)
735 if(!ser_ver_supported(version))
736 throw VersionMismatchException("ERROR: MapBlock format not supported");
738 // These have no lighting info
741 setLightingExpired(true);
744 // These have no "generated" field
750 // These have no compression
751 if(version <= 3 || version == 5 || version == 6)
753 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
757 throw SerializationError
758 ("MapBlock::deSerialize: no enough input data");
759 is_underground = tmp;
760 for(u32 i=0; i<nodecount; i++)
762 s32 len = MapNode::serializedLength(version);
763 SharedBuffer<u8> d(len);
764 is.read((char*)*d, len);
765 if(is.gcount() != len)
766 throw SerializationError
767 ("MapBlock::deSerialize: no enough input data");
768 data[i].deSerialize(*d, version);
771 else if(version <= 10)
773 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
776 is.read((char*)&t8, 1);
780 // Uncompress and set material data
781 std::ostringstream os(std::ios_base::binary);
782 decompress(is, os, version);
783 std::string s = os.str();
784 if(s.size() != nodecount)
785 throw SerializationError
786 ("MapBlock::deSerialize: invalid format");
787 for(u32 i=0; i<s.size(); i++)
789 data[i].param0 = s[i];
793 // Uncompress and set param data
794 std::ostringstream os(std::ios_base::binary);
795 decompress(is, os, version);
796 std::string s = os.str();
797 if(s.size() != nodecount)
798 throw SerializationError
799 ("MapBlock::deSerialize: invalid format");
800 for(u32 i=0; i<s.size(); i++)
802 data[i].param1 = s[i];
808 // Uncompress and set param2 data
809 std::ostringstream os(std::ios_base::binary);
810 decompress(is, os, version);
811 std::string s = os.str();
812 if(s.size() != nodecount)
813 throw SerializationError
814 ("MapBlock::deSerialize: invalid format");
815 for(u32 i=0; i<s.size(); i++)
817 data[i].param2 = s[i];
821 // All other versions (newest)
824 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
827 is.read((char*)&flags, 1);
828 is_underground = (flags & 0x01) ? true : false;
829 m_day_night_differs = (flags & 0x02) ? true : false;
830 m_lighting_expired = (flags & 0x04) ? true : false;
832 m_generated = (flags & 0x08) ? false : true;
835 std::ostringstream os(std::ios_base::binary);
836 decompress(is, os, version);
837 std::string s = os.str();
838 if(s.size() != nodecount*3)
839 throw SerializationError
840 ("MapBlock::deSerialize: decompress resulted in size"
841 " other than nodecount*3");
843 // deserialize nodes from buffer
844 for(u32 i=0; i<nodecount; i++)
848 buf[1] = s[i+nodecount];
849 buf[2] = s[i+nodecount*2];
850 data[i].deSerialize(buf, version);
862 std::string data = deSerializeString(is);
863 std::istringstream iss(data, std::ios_base::binary);
864 m_node_metadata->deSerialize(iss, m_gamedef);
868 //std::string data = deSerializeLongString(is);
869 std::ostringstream oss(std::ios_base::binary);
870 decompressZlib(is, oss);
871 std::istringstream iss(oss.str(), std::ios_base::binary);
872 m_node_metadata->deSerialize(iss, m_gamedef);
875 catch(SerializationError &e)
877 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
878 <<" while deserializing node metadata"<<std::endl;
884 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
886 // Versions up from 9 have block objects. (DEPRECATED)
893 // Versions up from 15 have static objects.
896 m_static_objects.serialize(os);
902 writeU32(os, getTimestamp());
905 // Scan and write node definition id mapping
908 getBlockNodeIdMapping(&nimap, this, m_gamedef->ndef());
913 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
916 Versions up from 9 have block objects. (DEPRECATED)
919 u16 count = readU16(is);
920 // Not supported and length not known if count is not 0
922 errorstream<<"WARNING: MapBlock::deSerializeDiskExtra(): "
923 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
929 Versions up from 15 have static objects.
932 m_static_objects.deSerialize(is);
936 setTimestamp(readU32(is));
937 m_disk_timestamp = m_timestamp;
939 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
942 // Dynamically re-set ids based on node names
944 // If supported, read node definition id mapping
946 nimap.deSerialize(is);
947 // Else set the legacy mapping
949 content_mapnode_get_name_id_mapping(&nimap);
951 correctBlockNodeIds(&nimap, this, m_gamedef);
955 Get a quick string to describe what a block actually contains
957 std::string analyze_block(MapBlock *block)
962 std::ostringstream desc;
964 v3s16 p = block->getPos();
966 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
969 switch(block->getModified())
971 case MOD_STATE_CLEAN:
974 case MOD_STATE_WRITE_AT_UNLOAD:
975 desc<<"WRITE_AT_UNLOAD, ";
977 case MOD_STATE_WRITE_NEEDED:
978 desc<<"WRITE_NEEDED, ";
981 desc<<"unknown getModified()="+itos(block->getModified())+", ";
984 if(block->isGenerated())
985 desc<<"is_gen [X], ";
987 desc<<"is_gen [ ], ";
989 if(block->getIsUnderground())
995 if(block->getMeshExpired())
996 desc<<"mesh_exp [X], ";
998 desc<<"mesh_exp [ ], ";
1001 if(block->getLightingExpired())
1002 desc<<"lighting_exp [X], ";
1004 desc<<"lighting_exp [ ], ";
1006 if(block->isDummy())
1012 bool full_ignore = true;
1013 bool some_ignore = false;
1014 bool full_air = true;
1015 bool some_air = false;
1016 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1017 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1018 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1021 MapNode n = block->getNode(p);
1022 content_t c = n.getContent();
1023 if(c == CONTENT_IGNORE)
1026 full_ignore = false;
1027 if(c == CONTENT_AIR)
1035 std::ostringstream ss;
1038 ss<<"IGNORE (full), ";
1039 else if(some_ignore)
1047 if(ss.str().size()>=2)
1048 desc<<ss.str().substr(0, ss.str().size()-2);
1053 return desc.str().substr(0, desc.str().size()-2);