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 m_modified_reason("initial"),
45 m_modified_reason_too_long(false),
46 is_underground(false),
47 m_lighting_expired(true),
48 m_day_night_differs(false),
50 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
51 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
59 m_mesh_expired = false;
62 m_temp_mods_mutex.Init();
70 JMutexAutoLock lock(mesh_mutex);
80 delete m_node_metadata;
86 bool MapBlock::isValidPositionParent(v3s16 p)
88 if(isValidPosition(p))
93 return m_parent->isValidPosition(getPosRelative() + p);
97 MapNode MapBlock::getNodeParent(v3s16 p)
99 if(isValidPosition(p) == false)
101 return m_parent->getNode(getPosRelative() + p);
106 throw InvalidPositionException();
107 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
111 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
113 if(isValidPosition(p) == false)
115 m_parent->setNode(getPosRelative() + p, n);
120 throw InvalidPositionException();
121 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
125 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
127 if(isValidPosition(p) == false)
130 return m_parent->getNode(getPosRelative() + p);
132 catch(InvalidPositionException &e)
134 return MapNode(CONTENT_IGNORE);
141 return MapNode(CONTENT_IGNORE);
143 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
150 void MapBlock::updateMesh(u32 daynight_ratio)
154 DEBUG: If mesh has been generated, don't generate it again
157 JMutexAutoLock meshlock(mesh_mutex);
164 data.fill(daynight_ratio, this);
166 scene::SMesh *mesh_new = makeMapBlockMesh(&data, m_gamedef);
172 replaceMesh(mesh_new);
177 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
181 //scene::SMesh *mesh_old = mesh[daynight_i];
182 //mesh[daynight_i] = mesh_new;
184 scene::SMesh *mesh_old = mesh;
186 setMeshExpired(false);
190 // Remove hardware buffers of meshbuffers of mesh
191 // NOTE: No way, this runs in a different thread and everything
192 /*u32 c = mesh_old->getMeshBufferCount();
193 for(u32 i=0; i<c; i++)
195 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
198 /*infostream<<"mesh_old->getReferenceCount()="
199 <<mesh_old->getReferenceCount()<<std::endl;
200 u32 c = mesh_old->getMeshBufferCount();
201 for(u32 i=0; i<c; i++)
203 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
204 infostream<<"buf->getReferenceCount()="
205 <<buf->getReferenceCount()<<std::endl;
220 Propagates sunlight down through the block.
221 Doesn't modify nodes that are not affected by sunlight.
223 Returns false if sunlight at bottom block is invalid.
224 Returns true if sunlight at bottom block is valid.
225 Returns true if bottom block doesn't exist.
227 If there is a block above, continues from it.
228 If there is no block above, assumes there is sunlight, unless
229 is_underground is set or highest node is water.
231 All sunlighted nodes are added to light_sources.
233 if remove_light==true, sets non-sunlighted nodes black.
235 if black_air_left!=NULL, it is set to true if non-sunlighted
236 air is left in block.
238 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
239 bool remove_light, bool *black_air_left)
241 INodeDefManager *nodemgr = m_gamedef->ndef();
243 // Whether the sunlight at the top of the bottom block is valid
244 bool block_below_is_valid = true;
246 v3s16 pos_relative = getPosRelative();
248 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
250 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
253 bool no_sunlight = false;
254 bool no_top_block = false;
255 // Check if node above block has sunlight
257 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
258 if(n.getContent() == CONTENT_IGNORE)
261 no_sunlight = is_underground;
263 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
268 catch(InvalidPositionException &e)
272 // NOTE: This makes over-ground roofed places sunlighted
273 // Assume sunlight, unless is_underground==true
280 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
281 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
286 // NOTE: As of now, this just would make everything dark.
288 //no_sunlight = true;
291 #if 0 // Doesn't work; nothing gets light.
292 bool no_sunlight = true;
293 bool no_top_block = false;
294 // Check if node above block has sunlight
296 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
297 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
302 catch(InvalidPositionException &e)
308 /*std::cout<<"("<<x<<","<<z<<"): "
309 <<"no_top_block="<<no_top_block
310 <<", is_underground="<<is_underground
311 <<", no_sunlight="<<no_sunlight
314 s16 y = MAP_BLOCKSIZE-1;
316 // This makes difference to diminishing in water.
317 bool stopped_to_solid_object = false;
319 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
324 MapNode &n = getNodeRef(pos);
326 if(current_light == 0)
330 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
332 // Do nothing: Sunlight is continued
334 else if(nodemgr->get(n).light_propagates == false)
336 // A solid object is on the way.
337 stopped_to_solid_object = true;
345 current_light = diminish_light(current_light);
348 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
350 if(current_light > old_light || remove_light)
352 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
355 if(diminish_light(current_light) != 0)
357 light_sources.insert(pos_relative + pos, true);
360 if(current_light == 0 && stopped_to_solid_object)
364 *black_air_left = true;
369 // Whether or not the block below should see LIGHT_SUN
370 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
373 If the block below hasn't already been marked invalid:
375 Check if the node below the block has proper sunlight at top.
376 If not, the block below is invalid.
378 Ignore non-transparent nodes as they always have no light
382 if(block_below_is_valid)
384 MapNode n = getNodeParent(v3s16(x, -1, z));
385 if(nodemgr->get(n).light_propagates)
387 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
388 && sunlight_should_go_down == false)
389 block_below_is_valid = false;
390 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
391 && sunlight_should_go_down == true)
392 block_below_is_valid = false;
396 catch(InvalidPositionException &e)
398 /*std::cout<<"InvalidBlockException for bottom block node"
400 // Just no block below, no need to panic.
405 return block_below_is_valid;
409 void MapBlock::copyTo(VoxelManipulator &dst)
411 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
412 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
414 // Copy from data to VoxelManipulator
415 dst.copyFrom(data, data_area, v3s16(0,0,0),
416 getPosRelative(), data_size);
419 void MapBlock::copyFrom(VoxelManipulator &dst)
421 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
422 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
424 // Copy from VoxelManipulator to data
425 dst.copyTo(data, data_area, v3s16(0,0,0),
426 getPosRelative(), data_size);
429 void MapBlock::updateDayNightDiff()
431 INodeDefManager *nodemgr = m_gamedef->ndef();
435 m_day_night_differs = false;
439 bool differs = false;
442 Check if any lighting value differs
444 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
446 MapNode &n = data[i];
447 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
455 If some lighting values differ, check if the whole thing is
456 just air. If it is, differ = false
460 bool only_air = true;
461 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
463 MapNode &n = data[i];
464 if(n.getContent() != CONTENT_AIR)
474 // Set member variable
475 m_day_night_differs = differs;
478 s16 MapBlock::getGroundLevel(v2s16 p2d)
484 s16 y = MAP_BLOCKSIZE-1;
487 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
488 if(m_gamedef->ndef()->get(n).walkable)
490 if(y == MAP_BLOCKSIZE-1)
498 catch(InvalidPositionException &e)
508 // List relevant id-name pairs for ids in the block using nodedef
509 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapBlock *block,
510 INodeDefManager *nodedef)
512 std::set<content_t> unknown_contents;
513 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
514 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
515 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
518 MapNode n = block->getNode(p);
519 content_t id = n.getContent();
520 const ContentFeatures &f = nodedef->get(id);
521 const std::string &name = f.name;
523 unknown_contents.insert(id);
525 nimap->set(id, name);
527 for(std::set<content_t>::const_iterator
528 i = unknown_contents.begin();
529 i != unknown_contents.end(); i++){
530 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
531 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
534 // Correct ids in the block to match nodedef based on names.
535 // Unknown ones are added to nodedef.
536 // Will not update itself to match id-name pairs in nodedef.
537 void correctBlockNodeIds(const NameIdMapping *nimap, MapBlock *block,
540 INodeDefManager *nodedef = gamedef->ndef();
541 // This means the block contains incorrect ids, and we contain
542 // the information to convert those to names.
543 // nodedef contains information to convert our names to globally
545 std::set<content_t> unnamed_contents;
546 std::set<std::string> unallocatable_contents;
547 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
548 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
549 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
552 MapNode n = block->getNode(p);
553 content_t local_id = n.getContent();
555 bool found = nimap->getName(local_id, name);
557 unnamed_contents.insert(local_id);
561 found = nodedef->getId(name, global_id);
563 global_id = gamedef->allocateUnknownNodeId(name);
564 if(global_id == CONTENT_IGNORE){
565 unallocatable_contents.insert(name);
569 n.setContent(global_id);
570 block->setNode(p, n);
572 for(std::set<content_t>::const_iterator
573 i = unnamed_contents.begin();
574 i != unnamed_contents.end(); i++){
575 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
576 <<"Block contains id "<<(*i)
577 <<" with no name mapping"<<std::endl;
579 for(std::set<std::string>::const_iterator
580 i = unallocatable_contents.begin();
581 i != unallocatable_contents.end(); i++){
582 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
583 <<"Could not allocate global id for node name \""
584 <<(*i)<<"\""<<std::endl;
588 void MapBlock::serialize(std::ostream &os, u8 version)
590 if(!ser_ver_supported(version))
591 throw VersionMismatchException("ERROR: MapBlock format not supported");
595 throw SerializationError("ERROR: Not writing dummy block.");
598 // These have no compression
599 if(version <= 3 || version == 5 || version == 6)
601 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
603 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
604 SharedBuffer<u8> dest(buflen);
606 dest[0] = is_underground;
607 for(u32 i=0; i<nodecount; i++)
609 u32 s = 1 + i * MapNode::serializedLength(version);
610 data[i].serialize(&dest[s], version);
613 os.write((char*)*dest, dest.getSize());
615 else if(version <= 10)
619 Compress the materials and the params separately.
623 os.write((char*)&is_underground, 1);
625 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
627 // Get and compress materials
628 SharedBuffer<u8> materialdata(nodecount);
629 for(u32 i=0; i<nodecount; i++)
631 materialdata[i] = data[i].param0;
633 compress(materialdata, os, version);
635 // Get and compress lights
636 SharedBuffer<u8> lightdata(nodecount);
637 for(u32 i=0; i<nodecount; i++)
639 lightdata[i] = data[i].param1;
641 compress(lightdata, os, version);
645 // Get and compress param2
646 SharedBuffer<u8> param2data(nodecount);
647 for(u32 i=0; i<nodecount; i++)
649 param2data[i] = data[i].param2;
651 compress(param2data, os, version);
654 // All other versions (newest)
661 if(m_day_night_differs)
663 if(m_lighting_expired)
667 if(m_generated == false)
670 os.write((char*)&flags, 1);
672 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
679 SharedBuffer<u8> databuf_nodelist(nodecount*3);
680 for(u32 i=0; i<nodecount; i++)
682 data[i].serialize(&databuf_nodelist[i*3], version);
685 // Create buffer with different parameters sorted
686 SharedBuffer<u8> databuf(nodecount*3);
687 for(u32 i=0; i<nodecount; i++)
689 databuf[i] = databuf_nodelist[i*3];
690 databuf[i+nodecount] = databuf_nodelist[i*3+1];
691 databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
695 Compress data to output stream
698 compress(databuf, os, version);
708 std::ostringstream oss(std::ios_base::binary);
709 m_node_metadata->serialize(oss);
710 os<<serializeString(oss.str());
712 // This will happen if the string is longer than 65535
713 catch(SerializationError &e)
715 // Use an empty string
716 os<<serializeString("");
721 std::ostringstream oss(std::ios_base::binary);
722 m_node_metadata->serialize(oss);
723 compressZlib(oss.str(), os);
724 //os<<serializeLongString(oss.str());
730 void MapBlock::deSerialize(std::istream &is, u8 version)
732 if(!ser_ver_supported(version))
733 throw VersionMismatchException("ERROR: MapBlock format not supported");
735 // These have no lighting info
738 setLightingExpired(true);
741 // These have no "generated" field
747 // These have no compression
748 if(version <= 3 || version == 5 || version == 6)
750 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
754 throw SerializationError
755 ("MapBlock::deSerialize: no enough input data");
756 is_underground = tmp;
757 for(u32 i=0; i<nodecount; i++)
759 s32 len = MapNode::serializedLength(version);
760 SharedBuffer<u8> d(len);
761 is.read((char*)*d, len);
762 if(is.gcount() != len)
763 throw SerializationError
764 ("MapBlock::deSerialize: no enough input data");
765 data[i].deSerialize(*d, version);
768 else if(version <= 10)
770 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
773 is.read((char*)&t8, 1);
777 // Uncompress and set material data
778 std::ostringstream os(std::ios_base::binary);
779 decompress(is, os, version);
780 std::string s = os.str();
781 if(s.size() != nodecount)
782 throw SerializationError
783 ("MapBlock::deSerialize: invalid format");
784 for(u32 i=0; i<s.size(); i++)
786 data[i].param0 = s[i];
790 // Uncompress and set param data
791 std::ostringstream os(std::ios_base::binary);
792 decompress(is, os, version);
793 std::string s = os.str();
794 if(s.size() != nodecount)
795 throw SerializationError
796 ("MapBlock::deSerialize: invalid format");
797 for(u32 i=0; i<s.size(); i++)
799 data[i].param1 = s[i];
805 // Uncompress and set param2 data
806 std::ostringstream os(std::ios_base::binary);
807 decompress(is, os, version);
808 std::string s = os.str();
809 if(s.size() != nodecount)
810 throw SerializationError
811 ("MapBlock::deSerialize: invalid format");
812 for(u32 i=0; i<s.size(); i++)
814 data[i].param2 = s[i];
818 // All other versions (newest)
821 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
824 is.read((char*)&flags, 1);
825 is_underground = (flags & 0x01) ? true : false;
826 m_day_night_differs = (flags & 0x02) ? true : false;
827 m_lighting_expired = (flags & 0x04) ? true : false;
829 m_generated = (flags & 0x08) ? false : true;
832 std::ostringstream os(std::ios_base::binary);
833 decompress(is, os, version);
834 std::string s = os.str();
835 if(s.size() != nodecount*3)
836 throw SerializationError
837 ("MapBlock::deSerialize: decompress resulted in size"
838 " other than nodecount*3");
840 // deserialize nodes from buffer
841 for(u32 i=0; i<nodecount; i++)
845 buf[1] = s[i+nodecount];
846 buf[2] = s[i+nodecount*2];
847 data[i].deSerialize(buf, version);
859 std::string data = deSerializeString(is);
860 std::istringstream iss(data, std::ios_base::binary);
861 m_node_metadata->deSerialize(iss, m_gamedef);
865 //std::string data = deSerializeLongString(is);
866 std::ostringstream oss(std::ios_base::binary);
867 decompressZlib(is, oss);
868 std::istringstream iss(oss.str(), std::ios_base::binary);
869 m_node_metadata->deSerialize(iss, m_gamedef);
872 catch(SerializationError &e)
874 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
875 <<" while deserializing node metadata"<<std::endl;
881 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
883 // Versions up from 9 have block objects. (DEPRECATED)
890 // Versions up from 15 have static objects.
893 m_static_objects.serialize(os);
899 writeU32(os, getTimestamp());
902 // Scan and write node definition id mapping
905 getBlockNodeIdMapping(&nimap, this, m_gamedef->ndef());
910 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
913 Versions up from 9 have block objects. (DEPRECATED)
916 u16 count = readU16(is);
917 // Not supported and length not known if count is not 0
919 errorstream<<"WARNING: MapBlock::deSerializeDiskExtra(): "
920 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
926 Versions up from 15 have static objects.
929 m_static_objects.deSerialize(is);
933 setTimestamp(readU32(is));
934 m_disk_timestamp = m_timestamp;
936 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
939 // Dynamically re-set ids based on node names
941 // If supported, read node definition id mapping
943 nimap.deSerialize(is);
944 // Else set the legacy mapping
946 content_mapnode_get_name_id_mapping(&nimap);
948 correctBlockNodeIds(&nimap, this, m_gamedef);
952 Get a quick string to describe what a block actually contains
954 std::string analyze_block(MapBlock *block)
959 std::ostringstream desc;
961 v3s16 p = block->getPos();
963 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
966 switch(block->getModified())
968 case MOD_STATE_CLEAN:
971 case MOD_STATE_WRITE_AT_UNLOAD:
972 desc<<"WRITE_AT_UNLOAD, ";
974 case MOD_STATE_WRITE_NEEDED:
975 desc<<"WRITE_NEEDED, ";
978 desc<<"unknown getModified()="+itos(block->getModified())+", ";
981 if(block->isGenerated())
982 desc<<"is_gen [X], ";
984 desc<<"is_gen [ ], ";
986 if(block->getIsUnderground())
992 if(block->getMeshExpired())
993 desc<<"mesh_exp [X], ";
995 desc<<"mesh_exp [ ], ";
998 if(block->getLightingExpired())
999 desc<<"lighting_exp [X], ";
1001 desc<<"lighting_exp [ ], ";
1003 if(block->isDummy())
1009 bool full_ignore = true;
1010 bool some_ignore = false;
1011 bool full_air = true;
1012 bool some_air = false;
1013 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1014 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1015 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1018 MapNode n = block->getNode(p);
1019 content_t c = n.getContent();
1020 if(c == CONTENT_IGNORE)
1023 full_ignore = false;
1024 if(c == CONTENT_AIR)
1032 std::ostringstream ss;
1035 ss<<"IGNORE (full), ";
1036 else if(some_ignore)
1044 if(ss.str().size()>=2)
1045 desc<<ss.str().substr(0, ss.str().size()-2);
1050 return desc.str().substr(0, desc.str().size()-2);