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),
58 m_mesh_expired = false;
61 m_temp_mods_mutex.Init();
69 JMutexAutoLock lock(mesh_mutex);
79 delete m_node_metadata;
85 bool MapBlock::isValidPositionParent(v3s16 p)
87 if(isValidPosition(p))
92 return m_parent->isValidPosition(getPosRelative() + p);
96 MapNode MapBlock::getNodeParent(v3s16 p)
98 if(isValidPosition(p) == false)
100 return m_parent->getNode(getPosRelative() + p);
105 throw InvalidPositionException();
106 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
110 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
112 if(isValidPosition(p) == false)
114 m_parent->setNode(getPosRelative() + p, n);
119 throw InvalidPositionException();
120 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
124 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
126 if(isValidPosition(p) == false)
129 return m_parent->getNode(getPosRelative() + p);
131 catch(InvalidPositionException &e)
133 return MapNode(CONTENT_IGNORE);
140 return MapNode(CONTENT_IGNORE);
142 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
149 void MapBlock::updateMesh(u32 daynight_ratio)
153 DEBUG: If mesh has been generated, don't generate it again
156 JMutexAutoLock meshlock(mesh_mutex);
163 data.fill(daynight_ratio, this);
165 scene::SMesh *mesh_new = makeMapBlockMesh(&data, m_gamedef);
171 replaceMesh(mesh_new);
176 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
180 //scene::SMesh *mesh_old = mesh[daynight_i];
181 //mesh[daynight_i] = mesh_new;
183 scene::SMesh *mesh_old = mesh;
185 setMeshExpired(false);
189 // Remove hardware buffers of meshbuffers of mesh
190 // NOTE: No way, this runs in a different thread and everything
191 /*u32 c = mesh_old->getMeshBufferCount();
192 for(u32 i=0; i<c; i++)
194 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
197 /*infostream<<"mesh_old->getReferenceCount()="
198 <<mesh_old->getReferenceCount()<<std::endl;
199 u32 c = mesh_old->getMeshBufferCount();
200 for(u32 i=0; i<c; i++)
202 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
203 infostream<<"buf->getReferenceCount()="
204 <<buf->getReferenceCount()<<std::endl;
219 Propagates sunlight down through the block.
220 Doesn't modify nodes that are not affected by sunlight.
222 Returns false if sunlight at bottom block is invalid.
223 Returns true if sunlight at bottom block is valid.
224 Returns true if bottom block doesn't exist.
226 If there is a block above, continues from it.
227 If there is no block above, assumes there is sunlight, unless
228 is_underground is set or highest node is water.
230 All sunlighted nodes are added to light_sources.
232 if remove_light==true, sets non-sunlighted nodes black.
234 if black_air_left!=NULL, it is set to true if non-sunlighted
235 air is left in block.
237 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
238 bool remove_light, bool *black_air_left)
240 INodeDefManager *nodemgr = m_gamedef->ndef();
242 // Whether the sunlight at the top of the bottom block is valid
243 bool block_below_is_valid = true;
245 v3s16 pos_relative = getPosRelative();
247 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
249 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
252 bool no_sunlight = false;
253 bool no_top_block = false;
254 // Check if node above block has sunlight
256 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
257 if(n.getContent() == CONTENT_IGNORE)
260 no_sunlight = is_underground;
262 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
267 catch(InvalidPositionException &e)
271 // NOTE: This makes over-ground roofed places sunlighted
272 // Assume sunlight, unless is_underground==true
279 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
280 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
285 // NOTE: As of now, this just would make everything dark.
287 //no_sunlight = true;
290 #if 0 // Doesn't work; nothing gets light.
291 bool no_sunlight = true;
292 bool no_top_block = false;
293 // Check if node above block has sunlight
295 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
296 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
301 catch(InvalidPositionException &e)
307 /*std::cout<<"("<<x<<","<<z<<"): "
308 <<"no_top_block="<<no_top_block
309 <<", is_underground="<<is_underground
310 <<", no_sunlight="<<no_sunlight
313 s16 y = MAP_BLOCKSIZE-1;
315 // This makes difference to diminishing in water.
316 bool stopped_to_solid_object = false;
318 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
323 MapNode &n = getNodeRef(pos);
325 if(current_light == 0)
329 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
331 // Do nothing: Sunlight is continued
333 else if(nodemgr->get(n).light_propagates == false)
335 // A solid object is on the way.
336 stopped_to_solid_object = true;
344 current_light = diminish_light(current_light);
347 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
349 if(current_light > old_light || remove_light)
351 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
354 if(diminish_light(current_light) != 0)
356 light_sources.insert(pos_relative + pos, true);
359 if(current_light == 0 && stopped_to_solid_object)
363 *black_air_left = true;
368 // Whether or not the block below should see LIGHT_SUN
369 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
372 If the block below hasn't already been marked invalid:
374 Check if the node below the block has proper sunlight at top.
375 If not, the block below is invalid.
377 Ignore non-transparent nodes as they always have no light
381 if(block_below_is_valid)
383 MapNode n = getNodeParent(v3s16(x, -1, z));
384 if(nodemgr->get(n).light_propagates)
386 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
387 && sunlight_should_go_down == false)
388 block_below_is_valid = false;
389 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
390 && sunlight_should_go_down == true)
391 block_below_is_valid = false;
395 catch(InvalidPositionException &e)
397 /*std::cout<<"InvalidBlockException for bottom block node"
399 // Just no block below, no need to panic.
404 return block_below_is_valid;
408 void MapBlock::copyTo(VoxelManipulator &dst)
410 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
411 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
413 // Copy from data to VoxelManipulator
414 dst.copyFrom(data, data_area, v3s16(0,0,0),
415 getPosRelative(), data_size);
418 void MapBlock::copyFrom(VoxelManipulator &dst)
420 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
421 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
423 // Copy from VoxelManipulator to data
424 dst.copyTo(data, data_area, v3s16(0,0,0),
425 getPosRelative(), data_size);
428 void MapBlock::updateDayNightDiff()
430 INodeDefManager *nodemgr = m_gamedef->ndef();
434 m_day_night_differs = false;
438 bool differs = false;
441 Check if any lighting value differs
443 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
445 MapNode &n = data[i];
446 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
454 If some lighting values differ, check if the whole thing is
455 just air. If it is, differ = false
459 bool only_air = true;
460 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
462 MapNode &n = data[i];
463 if(n.getContent() != CONTENT_AIR)
473 // Set member variable
474 m_day_night_differs = differs;
477 s16 MapBlock::getGroundLevel(v2s16 p2d)
483 s16 y = MAP_BLOCKSIZE-1;
486 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
487 if(m_gamedef->ndef()->get(n).walkable)
489 if(y == MAP_BLOCKSIZE-1)
497 catch(InvalidPositionException &e)
507 // List relevant id-name pairs for ids in the block using nodedef
508 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapBlock *block,
509 INodeDefManager *nodedef)
511 std::set<content_t> unknown_contents;
512 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
513 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
514 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
517 MapNode n = block->getNode(p);
518 content_t id = n.getContent();
519 const ContentFeatures &f = nodedef->get(id);
520 const std::string &name = f.name;
522 unknown_contents.insert(id);
524 nimap->set(id, name);
526 for(std::set<content_t>::const_iterator
527 i = unknown_contents.begin();
528 i != unknown_contents.end(); i++){
529 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
530 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
533 // Correct ids in the block to match nodedef based on names.
534 // Unknown ones are added to nodedef.
535 // Will not update itself to match id-name pairs in nodedef.
536 void correctBlockNodeIds(const NameIdMapping *nimap, MapBlock *block,
539 INodeDefManager *nodedef = gamedef->ndef();
540 // This means the block contains incorrect ids, and we contain
541 // the information to convert those to names.
542 // nodedef contains information to convert our names to globally
544 std::set<content_t> unnamed_contents;
545 std::set<std::string> unallocatable_contents;
546 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
547 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
548 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
551 MapNode n = block->getNode(p);
552 content_t local_id = n.getContent();
554 bool found = nimap->getName(local_id, name);
556 unnamed_contents.insert(local_id);
560 found = nodedef->getId(name, global_id);
562 global_id = gamedef->allocateUnknownNodeId(name);
563 if(global_id == CONTENT_IGNORE){
564 unallocatable_contents.insert(name);
568 n.setContent(global_id);
569 block->setNode(p, n);
571 for(std::set<content_t>::const_iterator
572 i = unnamed_contents.begin();
573 i != unnamed_contents.end(); i++){
574 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
575 <<"Block contains id "<<(*i)
576 <<" with no name mapping"<<std::endl;
578 for(std::set<std::string>::const_iterator
579 i = unallocatable_contents.begin();
580 i != unallocatable_contents.end(); i++){
581 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
582 <<"Could not allocate global id for node name \""
583 <<(*i)<<"\""<<std::endl;
587 void MapBlock::serialize(std::ostream &os, u8 version)
589 if(!ser_ver_supported(version))
590 throw VersionMismatchException("ERROR: MapBlock format not supported");
594 throw SerializationError("ERROR: Not writing dummy block.");
597 // These have no compression
598 if(version <= 3 || version == 5 || version == 6)
600 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
602 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
603 SharedBuffer<u8> dest(buflen);
605 dest[0] = is_underground;
606 for(u32 i=0; i<nodecount; i++)
608 u32 s = 1 + i * MapNode::serializedLength(version);
609 data[i].serialize(&dest[s], version);
612 os.write((char*)*dest, dest.getSize());
614 else if(version <= 10)
618 Compress the materials and the params separately.
622 os.write((char*)&is_underground, 1);
624 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
626 // Get and compress materials
627 SharedBuffer<u8> materialdata(nodecount);
628 for(u32 i=0; i<nodecount; i++)
630 materialdata[i] = data[i].param0;
632 compress(materialdata, os, version);
634 // Get and compress lights
635 SharedBuffer<u8> lightdata(nodecount);
636 for(u32 i=0; i<nodecount; i++)
638 lightdata[i] = data[i].param1;
640 compress(lightdata, os, version);
644 // Get and compress param2
645 SharedBuffer<u8> param2data(nodecount);
646 for(u32 i=0; i<nodecount; i++)
648 param2data[i] = data[i].param2;
650 compress(param2data, os, version);
653 // All other versions (newest)
660 if(m_day_night_differs)
662 if(m_lighting_expired)
666 if(m_generated == false)
669 os.write((char*)&flags, 1);
671 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
678 SharedBuffer<u8> databuf_nodelist(nodecount*3);
679 for(u32 i=0; i<nodecount; i++)
681 data[i].serialize(&databuf_nodelist[i*3], version);
684 // Create buffer with different parameters sorted
685 SharedBuffer<u8> databuf(nodecount*3);
686 for(u32 i=0; i<nodecount; i++)
688 databuf[i] = databuf_nodelist[i*3];
689 databuf[i+nodecount] = databuf_nodelist[i*3+1];
690 databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
694 Compress data to output stream
697 compress(databuf, os, version);
707 std::ostringstream oss(std::ios_base::binary);
708 m_node_metadata->serialize(oss);
709 os<<serializeString(oss.str());
711 // This will happen if the string is longer than 65535
712 catch(SerializationError &e)
714 // Use an empty string
715 os<<serializeString("");
720 std::ostringstream oss(std::ios_base::binary);
721 m_node_metadata->serialize(oss);
722 compressZlib(oss.str(), os);
723 //os<<serializeLongString(oss.str());
729 void MapBlock::deSerialize(std::istream &is, u8 version)
731 if(!ser_ver_supported(version))
732 throw VersionMismatchException("ERROR: MapBlock format not supported");
734 // These have no lighting info
737 setLightingExpired(true);
740 // These have no "generated" field
746 // These have no compression
747 if(version <= 3 || version == 5 || version == 6)
749 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
753 throw SerializationError
754 ("MapBlock::deSerialize: no enough input data");
755 is_underground = tmp;
756 for(u32 i=0; i<nodecount; i++)
758 s32 len = MapNode::serializedLength(version);
759 SharedBuffer<u8> d(len);
760 is.read((char*)*d, len);
761 if(is.gcount() != len)
762 throw SerializationError
763 ("MapBlock::deSerialize: no enough input data");
764 data[i].deSerialize(*d, version);
767 else if(version <= 10)
769 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
772 is.read((char*)&t8, 1);
776 // Uncompress and set material data
777 std::ostringstream os(std::ios_base::binary);
778 decompress(is, os, version);
779 std::string s = os.str();
780 if(s.size() != nodecount)
781 throw SerializationError
782 ("MapBlock::deSerialize: invalid format");
783 for(u32 i=0; i<s.size(); i++)
785 data[i].param0 = s[i];
789 // Uncompress and set param data
790 std::ostringstream os(std::ios_base::binary);
791 decompress(is, os, version);
792 std::string s = os.str();
793 if(s.size() != nodecount)
794 throw SerializationError
795 ("MapBlock::deSerialize: invalid format");
796 for(u32 i=0; i<s.size(); i++)
798 data[i].param1 = s[i];
804 // Uncompress and set param2 data
805 std::ostringstream os(std::ios_base::binary);
806 decompress(is, os, version);
807 std::string s = os.str();
808 if(s.size() != nodecount)
809 throw SerializationError
810 ("MapBlock::deSerialize: invalid format");
811 for(u32 i=0; i<s.size(); i++)
813 data[i].param2 = s[i];
817 // All other versions (newest)
820 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
823 is.read((char*)&flags, 1);
824 is_underground = (flags & 0x01) ? true : false;
825 m_day_night_differs = (flags & 0x02) ? true : false;
826 m_lighting_expired = (flags & 0x04) ? true : false;
828 m_generated = (flags & 0x08) ? false : true;
831 std::ostringstream os(std::ios_base::binary);
832 decompress(is, os, version);
833 std::string s = os.str();
834 if(s.size() != nodecount*3)
835 throw SerializationError
836 ("MapBlock::deSerialize: decompress resulted in size"
837 " other than nodecount*3");
839 // deserialize nodes from buffer
840 for(u32 i=0; i<nodecount; i++)
844 buf[1] = s[i+nodecount];
845 buf[2] = s[i+nodecount*2];
846 data[i].deSerialize(buf, version);
858 std::string data = deSerializeString(is);
859 std::istringstream iss(data, std::ios_base::binary);
860 m_node_metadata->deSerialize(iss, m_gamedef);
864 //std::string data = deSerializeLongString(is);
865 std::ostringstream oss(std::ios_base::binary);
866 decompressZlib(is, oss);
867 std::istringstream iss(oss.str(), std::ios_base::binary);
868 m_node_metadata->deSerialize(iss, m_gamedef);
871 catch(SerializationError &e)
873 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
874 <<" while deserializing node metadata"<<std::endl;
880 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
882 // Versions up from 9 have block objects. (DEPRECATED)
889 // Versions up from 15 have static objects.
892 m_static_objects.serialize(os);
898 writeU32(os, getTimestamp());
901 // Scan and write node definition id mapping
904 getBlockNodeIdMapping(&nimap, this, m_gamedef->ndef());
909 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
912 Versions up from 9 have block objects. (DEPRECATED)
915 u16 count = readU16(is);
916 // Not supported and length not known if count is not 0
918 errorstream<<"WARNING: MapBlock::deSerializeDiskExtra(): "
919 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
925 Versions up from 15 have static objects.
928 m_static_objects.deSerialize(is);
932 setTimestamp(readU32(is));
934 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
936 // Dynamically re-set ids based on node names
938 // If supported, read node definition id mapping
940 nimap.deSerialize(is);
941 // Else set the legacy mapping
943 content_mapnode_get_name_id_mapping(&nimap);
945 correctBlockNodeIds(&nimap, this, m_gamedef);
949 Get a quick string to describe what a block actually contains
951 std::string analyze_block(MapBlock *block)
956 std::ostringstream desc;
958 v3s16 p = block->getPos();
960 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
963 switch(block->getModified())
965 case MOD_STATE_CLEAN:
968 case MOD_STATE_WRITE_AT_UNLOAD:
969 desc<<"WRITE_AT_UNLOAD, ";
971 case MOD_STATE_WRITE_NEEDED:
972 desc<<"WRITE_NEEDED, ";
975 desc<<"unknown getModified()="+itos(block->getModified())+", ";
978 if(block->isGenerated())
979 desc<<"is_gen [X], ";
981 desc<<"is_gen [ ], ";
983 if(block->getIsUnderground())
989 if(block->getMeshExpired())
990 desc<<"mesh_exp [X], ";
992 desc<<"mesh_exp [ ], ";
995 if(block->getLightingExpired())
996 desc<<"lighting_exp [X], ";
998 desc<<"lighting_exp [ ], ";
1000 if(block->isDummy())
1006 bool full_ignore = true;
1007 bool some_ignore = false;
1008 bool full_air = true;
1009 bool some_air = false;
1010 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1011 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1012 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1015 MapNode n = block->getNode(p);
1016 content_t c = n.getContent();
1017 if(c == CONTENT_IGNORE)
1020 full_ignore = false;
1021 if(c == CONTENT_AIR)
1029 std::ostringstream ss;
1032 ss<<"IGNORE (full), ";
1033 else if(some_ignore)
1041 if(ss.str().size()>=2)
1042 desc<<ss.str().substr(0, ss.str().size()-2);
1047 return desc.str().substr(0, desc.str().size()-2);