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(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
279 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
284 // NOTE: As of now, this just would make everything dark.
286 //no_sunlight = true;
289 #if 0 // Doesn't work; nothing gets light.
290 bool no_sunlight = true;
291 bool no_top_block = false;
292 // Check if node above block has sunlight
294 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
295 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
300 catch(InvalidPositionException &e)
306 /*std::cout<<"("<<x<<","<<z<<"): "
307 <<"no_top_block="<<no_top_block
308 <<", is_underground="<<is_underground
309 <<", no_sunlight="<<no_sunlight
312 s16 y = MAP_BLOCKSIZE-1;
314 // This makes difference to diminishing in water.
315 bool stopped_to_solid_object = false;
317 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
322 MapNode &n = getNodeRef(pos);
324 if(current_light == 0)
328 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
330 // Do nothing: Sunlight is continued
332 else if(nodemgr->get(n).light_propagates == false)
334 /*// DEPRECATED TODO: REMOVE
337 bool upper_is_air = false;
340 if(getNodeParent(pos+v3s16(0,1,0)).getContent() == CONTENT_AIR)
343 catch(InvalidPositionException &e)
346 // Turn mud into grass
347 if(upper_is_air && n.getContent() == CONTENT_MUD
348 && current_light == LIGHT_SUN)
354 // A solid object is on the way.
355 stopped_to_solid_object = true;
363 current_light = diminish_light(current_light);
366 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
368 if(current_light > old_light || remove_light)
370 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
373 if(diminish_light(current_light) != 0)
375 light_sources.insert(pos_relative + pos, true);
378 if(current_light == 0 && stopped_to_solid_object)
382 *black_air_left = true;
387 // Whether or not the block below should see LIGHT_SUN
388 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
391 If the block below hasn't already been marked invalid:
393 Check if the node below the block has proper sunlight at top.
394 If not, the block below is invalid.
396 Ignore non-transparent nodes as they always have no light
400 if(block_below_is_valid)
402 MapNode n = getNodeParent(v3s16(x, -1, z));
403 if(nodemgr->get(n).light_propagates)
405 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
406 && sunlight_should_go_down == false)
407 block_below_is_valid = false;
408 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
409 && sunlight_should_go_down == true)
410 block_below_is_valid = false;
414 catch(InvalidPositionException &e)
416 /*std::cout<<"InvalidBlockException for bottom block node"
418 // Just no block below, no need to panic.
423 return block_below_is_valid;
427 void MapBlock::copyTo(VoxelManipulator &dst)
429 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
430 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
432 // Copy from data to VoxelManipulator
433 dst.copyFrom(data, data_area, v3s16(0,0,0),
434 getPosRelative(), data_size);
437 void MapBlock::copyFrom(VoxelManipulator &dst)
439 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
440 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
442 // Copy from VoxelManipulator to data
443 dst.copyTo(data, data_area, v3s16(0,0,0),
444 getPosRelative(), data_size);
447 void MapBlock::updateDayNightDiff()
449 INodeDefManager *nodemgr = m_gamedef->ndef();
453 m_day_night_differs = false;
457 bool differs = false;
460 Check if any lighting value differs
462 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
464 MapNode &n = data[i];
465 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
473 If some lighting values differ, check if the whole thing is
474 just air. If it is, differ = false
478 bool only_air = true;
479 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
481 MapNode &n = data[i];
482 if(n.getContent() != CONTENT_AIR)
492 // Set member variable
493 m_day_night_differs = differs;
496 s16 MapBlock::getGroundLevel(v2s16 p2d)
502 s16 y = MAP_BLOCKSIZE-1;
505 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
506 if(m_gamedef->ndef()->get(n).walkable)
508 if(y == MAP_BLOCKSIZE-1)
516 catch(InvalidPositionException &e)
526 // List relevant id-name pairs for ids in the block using nodedef
527 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapBlock *block,
528 INodeDefManager *nodedef)
530 std::set<content_t> unknown_contents;
531 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
532 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
533 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
536 MapNode n = block->getNode(p);
537 content_t id = n.getContent();
538 const ContentFeatures &f = nodedef->get(id);
539 const std::string &name = f.name;
541 unknown_contents.insert(id);
543 nimap->set(id, name);
545 for(std::set<content_t>::const_iterator
546 i = unknown_contents.begin();
547 i != unknown_contents.end(); i++){
548 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
549 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
552 // Correct ids in the block to match nodedef based on names.
553 // Unknown ones are added to nodedef.
554 // Will not update itself to match id-name pairs in nodedef.
555 void correctBlockNodeIds(const NameIdMapping *nimap, MapBlock *block,
558 INodeDefManager *nodedef = gamedef->ndef();
559 // This means the block contains incorrect ids, and we contain
560 // the information to convert those to names.
561 // nodedef contains information to convert our names to globally
563 std::set<content_t> unnamed_contents;
564 std::set<std::string> unallocatable_contents;
565 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
566 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
567 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
570 MapNode n = block->getNode(p);
571 content_t local_id = n.getContent();
573 bool found = nimap->getName(local_id, name);
575 unnamed_contents.insert(local_id);
579 found = nodedef->getId(name, global_id);
581 global_id = gamedef->allocateUnknownNodeId(name);
582 if(global_id == CONTENT_IGNORE){
583 unallocatable_contents.insert(name);
587 n.setContent(global_id);
588 block->setNode(p, n);
590 for(std::set<content_t>::const_iterator
591 i = unnamed_contents.begin();
592 i != unnamed_contents.end(); i++){
593 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
594 <<"Block contains id "<<(*i)
595 <<" with no name mapping"<<std::endl;
597 for(std::set<std::string>::const_iterator
598 i = unallocatable_contents.begin();
599 i != unallocatable_contents.end(); i++){
600 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
601 <<"Could not allocate global id for node name \""
602 <<(*i)<<"\""<<std::endl;
606 void MapBlock::serialize(std::ostream &os, u8 version)
608 if(!ser_ver_supported(version))
609 throw VersionMismatchException("ERROR: MapBlock format not supported");
613 throw SerializationError("ERROR: Not writing dummy block.");
616 // These have no compression
617 if(version <= 3 || version == 5 || version == 6)
619 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
621 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
622 SharedBuffer<u8> dest(buflen);
624 dest[0] = is_underground;
625 for(u32 i=0; i<nodecount; i++)
627 u32 s = 1 + i * MapNode::serializedLength(version);
628 data[i].serialize(&dest[s], version);
631 os.write((char*)*dest, dest.getSize());
633 else if(version <= 10)
637 Compress the materials and the params separately.
641 os.write((char*)&is_underground, 1);
643 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
645 // Get and compress materials
646 SharedBuffer<u8> materialdata(nodecount);
647 for(u32 i=0; i<nodecount; i++)
649 materialdata[i] = data[i].param0;
651 compress(materialdata, os, version);
653 // Get and compress lights
654 SharedBuffer<u8> lightdata(nodecount);
655 for(u32 i=0; i<nodecount; i++)
657 lightdata[i] = data[i].param1;
659 compress(lightdata, os, version);
663 // Get and compress param2
664 SharedBuffer<u8> param2data(nodecount);
665 for(u32 i=0; i<nodecount; i++)
667 param2data[i] = data[i].param2;
669 compress(param2data, os, version);
672 // All other versions (newest)
679 if(m_day_night_differs)
681 if(m_lighting_expired)
685 if(m_generated == false)
688 os.write((char*)&flags, 1);
690 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
697 SharedBuffer<u8> databuf_nodelist(nodecount*3);
698 for(u32 i=0; i<nodecount; i++)
700 data[i].serialize(&databuf_nodelist[i*3], version);
703 // Create buffer with different parameters sorted
704 SharedBuffer<u8> databuf(nodecount*3);
705 for(u32 i=0; i<nodecount; i++)
707 databuf[i] = databuf_nodelist[i*3];
708 databuf[i+nodecount] = databuf_nodelist[i*3+1];
709 databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
713 Compress data to output stream
716 compress(databuf, os, version);
726 std::ostringstream oss(std::ios_base::binary);
727 m_node_metadata->serialize(oss);
728 os<<serializeString(oss.str());
730 // This will happen if the string is longer than 65535
731 catch(SerializationError &e)
733 // Use an empty string
734 os<<serializeString("");
739 std::ostringstream oss(std::ios_base::binary);
740 m_node_metadata->serialize(oss);
741 compressZlib(oss.str(), os);
742 //os<<serializeLongString(oss.str());
748 void MapBlock::deSerialize(std::istream &is, u8 version)
750 if(!ser_ver_supported(version))
751 throw VersionMismatchException("ERROR: MapBlock format not supported");
753 // These have no lighting info
756 setLightingExpired(true);
759 // These have no "generated" field
765 // These have no compression
766 if(version <= 3 || version == 5 || version == 6)
768 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
772 throw SerializationError
773 ("MapBlock::deSerialize: no enough input data");
774 is_underground = tmp;
775 for(u32 i=0; i<nodecount; i++)
777 s32 len = MapNode::serializedLength(version);
778 SharedBuffer<u8> d(len);
779 is.read((char*)*d, len);
780 if(is.gcount() != len)
781 throw SerializationError
782 ("MapBlock::deSerialize: no enough input data");
783 data[i].deSerialize(*d, version);
786 else if(version <= 10)
788 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
791 is.read((char*)&t8, 1);
795 // Uncompress and set material data
796 std::ostringstream os(std::ios_base::binary);
797 decompress(is, os, version);
798 std::string s = os.str();
799 if(s.size() != nodecount)
800 throw SerializationError
801 ("MapBlock::deSerialize: invalid format");
802 for(u32 i=0; i<s.size(); i++)
804 data[i].param0 = s[i];
808 // Uncompress and set param 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].param1 = s[i];
823 // Uncompress and set param2 data
824 std::ostringstream os(std::ios_base::binary);
825 decompress(is, os, version);
826 std::string s = os.str();
827 if(s.size() != nodecount)
828 throw SerializationError
829 ("MapBlock::deSerialize: invalid format");
830 for(u32 i=0; i<s.size(); i++)
832 data[i].param2 = s[i];
836 // All other versions (newest)
839 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
842 is.read((char*)&flags, 1);
843 is_underground = (flags & 0x01) ? true : false;
844 m_day_night_differs = (flags & 0x02) ? true : false;
845 m_lighting_expired = (flags & 0x04) ? true : false;
847 m_generated = (flags & 0x08) ? false : true;
850 std::ostringstream os(std::ios_base::binary);
851 decompress(is, os, version);
852 std::string s = os.str();
853 if(s.size() != nodecount*3)
854 throw SerializationError
855 ("MapBlock::deSerialize: decompress resulted in size"
856 " other than nodecount*3");
858 // deserialize nodes from buffer
859 for(u32 i=0; i<nodecount; i++)
863 buf[1] = s[i+nodecount];
864 buf[2] = s[i+nodecount*2];
865 data[i].deSerialize(buf, version);
877 std::string data = deSerializeString(is);
878 std::istringstream iss(data, std::ios_base::binary);
879 m_node_metadata->deSerialize(iss, m_gamedef);
883 //std::string data = deSerializeLongString(is);
884 std::ostringstream oss(std::ios_base::binary);
885 decompressZlib(is, oss);
886 std::istringstream iss(oss.str(), std::ios_base::binary);
887 m_node_metadata->deSerialize(iss, m_gamedef);
890 catch(SerializationError &e)
892 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
893 <<" while deserializing node metadata"<<std::endl;
899 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
901 // Versions up from 9 have block objects. (DEPRECATED)
908 // Versions up from 15 have static objects.
911 m_static_objects.serialize(os);
917 writeU32(os, getTimestamp());
920 // Scan and write node definition id mapping
923 getBlockNodeIdMapping(&nimap, this, m_gamedef->ndef());
928 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
931 Versions up from 9 have block objects. (DEPRECATED)
934 u16 count = readU16(is);
935 // Not supported and length not known if count is not 0
937 errorstream<<"WARNING: MapBlock::deSerializeDiskExtra(): "
938 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
944 Versions up from 15 have static objects.
947 m_static_objects.deSerialize(is);
951 setTimestamp(readU32(is));
953 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
955 // Dynamically re-set ids based on node names
957 // If supported, read node definition id mapping
959 nimap.deSerialize(is);
960 // Else set the legacy mapping
962 content_mapnode_get_name_id_mapping(&nimap);
964 correctBlockNodeIds(&nimap, this, m_gamedef);
968 Get a quick string to describe what a block actually contains
970 std::string analyze_block(MapBlock *block)
975 std::ostringstream desc;
977 v3s16 p = block->getPos();
979 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
982 switch(block->getModified())
984 case MOD_STATE_CLEAN:
987 case MOD_STATE_WRITE_AT_UNLOAD:
988 desc<<"WRITE_AT_UNLOAD, ";
990 case MOD_STATE_WRITE_NEEDED:
991 desc<<"WRITE_NEEDED, ";
994 desc<<"unknown getModified()="+itos(block->getModified())+", ";
997 if(block->isGenerated())
998 desc<<"is_gen [X], ";
1000 desc<<"is_gen [ ], ";
1002 if(block->getIsUnderground())
1003 desc<<"is_ug [X], ";
1005 desc<<"is_ug [ ], ";
1008 if(block->getMeshExpired())
1009 desc<<"mesh_exp [X], ";
1011 desc<<"mesh_exp [ ], ";
1014 if(block->getLightingExpired())
1015 desc<<"lighting_exp [X], ";
1017 desc<<"lighting_exp [ ], ";
1019 if(block->isDummy())
1025 bool full_ignore = true;
1026 bool some_ignore = false;
1027 bool full_air = true;
1028 bool some_air = false;
1029 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1030 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1031 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1034 MapNode n = block->getNode(p);
1035 content_t c = n.getContent();
1036 if(c == CONTENT_IGNORE)
1039 full_ignore = false;
1040 if(c == CONTENT_AIR)
1048 std::ostringstream ss;
1051 ss<<"IGNORE (full), ";
1052 else if(some_ignore)
1060 if(ss.str().size()>=2)
1061 desc<<ss.str().substr(0, ss.str().size()-2);
1066 return desc.str().substr(0, desc.str().size()-2);