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.
26 #include "mapnode_contentfeatures.h"
32 MapBlock::MapBlock(Map *parent, v3s16 pos, bool dummy):
35 m_modified(MOD_STATE_WRITE_NEEDED),
36 is_underground(false),
37 m_lighting_expired(true),
38 m_day_night_differs(false),
40 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
47 //m_spawn_timer = -10000;
50 m_mesh_expired = false;
53 m_temp_mods_mutex.Init();
61 JMutexAutoLock lock(mesh_mutex);
75 bool MapBlock::isValidPositionParent(v3s16 p)
77 if(isValidPosition(p))
82 return m_parent->isValidPosition(getPosRelative() + p);
86 MapNode MapBlock::getNodeParent(v3s16 p)
88 if(isValidPosition(p) == false)
90 return m_parent->getNode(getPosRelative() + p);
95 throw InvalidPositionException();
96 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
100 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
102 if(isValidPosition(p) == false)
104 m_parent->setNode(getPosRelative() + p, n);
109 throw InvalidPositionException();
110 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
114 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
116 if(isValidPosition(p) == false)
119 return m_parent->getNode(getPosRelative() + p);
121 catch(InvalidPositionException &e)
123 return MapNode(CONTENT_IGNORE);
130 return MapNode(CONTENT_IGNORE);
132 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
139 void MapBlock::updateMesh(u32 daynight_ratio)
143 DEBUG: If mesh has been generated, don't generate it again
146 JMutexAutoLock meshlock(mesh_mutex);
153 data.fill(daynight_ratio, this);
155 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
161 replaceMesh(mesh_new);
166 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
170 //scene::SMesh *mesh_old = mesh[daynight_i];
171 //mesh[daynight_i] = mesh_new;
173 scene::SMesh *mesh_old = mesh;
175 setMeshExpired(false);
179 // Remove hardware buffers of meshbuffers of mesh
180 // NOTE: No way, this runs in a different thread and everything
181 /*u32 c = mesh_old->getMeshBufferCount();
182 for(u32 i=0; i<c; i++)
184 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
187 /*dstream<<"mesh_old->getReferenceCount()="
188 <<mesh_old->getReferenceCount()<<std::endl;
189 u32 c = mesh_old->getMeshBufferCount();
190 for(u32 i=0; i<c; i++)
192 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
193 dstream<<"buf->getReferenceCount()="
194 <<buf->getReferenceCount()<<std::endl;
209 Propagates sunlight down through the block.
210 Doesn't modify nodes that are not affected by sunlight.
212 Returns false if sunlight at bottom block is invalid.
213 Returns true if sunlight at bottom block is valid.
214 Returns true if bottom block doesn't exist.
216 If there is a block above, continues from it.
217 If there is no block above, assumes there is sunlight, unless
218 is_underground is set or highest node is water.
220 All sunlighted nodes are added to light_sources.
222 if remove_light==true, sets non-sunlighted nodes black.
224 if black_air_left!=NULL, it is set to true if non-sunlighted
225 air is left in block.
227 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
228 bool remove_light, bool *black_air_left)
230 // Whether the sunlight at the top of the bottom block is valid
231 bool block_below_is_valid = true;
233 v3s16 pos_relative = getPosRelative();
235 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
237 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
240 bool no_sunlight = false;
241 bool no_top_block = false;
242 // Check if node above block has sunlight
244 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
245 if(n.getContent() == CONTENT_IGNORE)
248 no_sunlight = is_underground;
250 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
255 catch(InvalidPositionException &e)
259 // NOTE: This makes over-ground roofed places sunlighted
260 // Assume sunlight, unless is_underground==true
267 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
268 //if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
269 if(content_features(n).sunlight_propagates == false)
274 // NOTE: As of now, this just would make everything dark.
276 //no_sunlight = true;
279 #if 0 // Doesn't work; nothing gets light.
280 bool no_sunlight = true;
281 bool no_top_block = false;
282 // Check if node above block has sunlight
284 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
285 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
290 catch(InvalidPositionException &e)
296 /*std::cout<<"("<<x<<","<<z<<"): "
297 <<"no_top_block="<<no_top_block
298 <<", is_underground="<<is_underground
299 <<", no_sunlight="<<no_sunlight
302 s16 y = MAP_BLOCKSIZE-1;
304 // This makes difference to diminishing in water.
305 bool stopped_to_solid_object = false;
307 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
312 MapNode &n = getNodeRef(pos);
314 if(current_light == 0)
318 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
320 // Do nothing: Sunlight is continued
322 else if(n.light_propagates() == false)
324 /*// DEPRECATED TODO: REMOVE
327 bool upper_is_air = false;
330 if(getNodeParent(pos+v3s16(0,1,0)).getContent() == CONTENT_AIR)
333 catch(InvalidPositionException &e)
336 // Turn mud into grass
337 if(upper_is_air && n.getContent() == CONTENT_MUD
338 && current_light == LIGHT_SUN)
344 // A solid object is on the way.
345 stopped_to_solid_object = true;
353 current_light = diminish_light(current_light);
356 u8 old_light = n.getLight(LIGHTBANK_DAY);
358 if(current_light > old_light || remove_light)
360 n.setLight(LIGHTBANK_DAY, current_light);
363 if(diminish_light(current_light) != 0)
365 light_sources.insert(pos_relative + pos, true);
368 if(current_light == 0 && stopped_to_solid_object)
372 *black_air_left = true;
377 // Whether or not the block below should see LIGHT_SUN
378 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
381 If the block below hasn't already been marked invalid:
383 Check if the node below the block has proper sunlight at top.
384 If not, the block below is invalid.
386 Ignore non-transparent nodes as they always have no light
390 if(block_below_is_valid)
392 MapNode n = getNodeParent(v3s16(x, -1, z));
393 if(n.light_propagates())
395 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
396 && sunlight_should_go_down == false)
397 block_below_is_valid = false;
398 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
399 && sunlight_should_go_down == true)
400 block_below_is_valid = false;
404 catch(InvalidPositionException &e)
406 /*std::cout<<"InvalidBlockException for bottom block node"
408 // Just no block below, no need to panic.
413 return block_below_is_valid;
417 void MapBlock::copyTo(VoxelManipulator &dst)
419 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
420 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
422 // Copy from data to VoxelManipulator
423 dst.copyFrom(data, data_area, v3s16(0,0,0),
424 getPosRelative(), data_size);
427 void MapBlock::copyFrom(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 VoxelManipulator to data
433 dst.copyTo(data, data_area, v3s16(0,0,0),
434 getPosRelative(), data_size);
437 void MapBlock::updateDayNightDiff()
441 m_day_night_differs = false;
445 bool differs = false;
448 Check if any lighting value differs
450 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
452 MapNode &n = data[i];
453 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
461 If some lighting values differ, check if the whole thing is
462 just air. If it is, differ = false
466 bool only_air = true;
467 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
469 MapNode &n = data[i];
470 if(n.getContent() != CONTENT_AIR)
480 // Set member variable
481 m_day_night_differs = differs;
484 s16 MapBlock::getGroundLevel(v2s16 p2d)
490 s16 y = MAP_BLOCKSIZE-1;
493 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
494 if(content_features(n).walkable)
496 if(y == MAP_BLOCKSIZE-1)
504 catch(InvalidPositionException &e)
514 void MapBlock::serialize(std::ostream &os, u8 version)
516 if(!ser_ver_supported(version))
517 throw VersionMismatchException("ERROR: MapBlock format not supported");
521 throw SerializationError("ERROR: Not writing dummy block.");
524 // These have no compression
525 if(version <= 3 || version == 5 || version == 6)
527 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
529 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
530 SharedBuffer<u8> dest(buflen);
532 dest[0] = is_underground;
533 for(u32 i=0; i<nodecount; i++)
535 u32 s = 1 + i * MapNode::serializedLength(version);
536 data[i].serialize(&dest[s], version);
539 os.write((char*)*dest, dest.getSize());
541 else if(version <= 10)
545 Compress the materials and the params separately.
549 os.write((char*)&is_underground, 1);
551 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
553 // Get and compress materials
554 SharedBuffer<u8> materialdata(nodecount);
555 for(u32 i=0; i<nodecount; i++)
557 materialdata[i] = data[i].param0;
559 compress(materialdata, os, version);
561 // Get and compress lights
562 SharedBuffer<u8> lightdata(nodecount);
563 for(u32 i=0; i<nodecount; i++)
565 lightdata[i] = data[i].param1;
567 compress(lightdata, os, version);
571 // Get and compress param2
572 SharedBuffer<u8> param2data(nodecount);
573 for(u32 i=0; i<nodecount; i++)
575 param2data[i] = data[i].param2;
577 compress(param2data, os, version);
580 // All other versions (newest)
587 if(m_day_night_differs)
589 if(m_lighting_expired)
593 if(m_generated == false)
596 os.write((char*)&flags, 1);
598 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
605 SharedBuffer<u8> databuf_nodelist(nodecount*3);
606 for(u32 i=0; i<nodecount; i++)
608 data[i].serialize(&databuf_nodelist[i*3], version);
611 // Create buffer with different parameters sorted
612 SharedBuffer<u8> databuf(nodecount*3);
613 for(u32 i=0; i<nodecount; i++)
615 databuf[i] = databuf_nodelist[i*3];
616 databuf[i+nodecount] = databuf_nodelist[i*3+1];
617 databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
621 Compress data to output stream
624 compress(databuf, os, version);
634 std::ostringstream oss(std::ios_base::binary);
635 m_node_metadata.serialize(oss);
636 os<<serializeString(oss.str());
638 // This will happen if the string is longer than 65535
639 catch(SerializationError &e)
641 // Use an empty string
642 os<<serializeString("");
647 std::ostringstream oss(std::ios_base::binary);
648 m_node_metadata.serialize(oss);
649 compressZlib(oss.str(), os);
650 //os<<serializeLongString(oss.str());
656 void MapBlock::deSerialize(std::istream &is, u8 version)
658 if(!ser_ver_supported(version))
659 throw VersionMismatchException("ERROR: MapBlock format not supported");
661 // These have no lighting info
664 setLightingExpired(true);
667 // These have no "generated" field
673 // These have no compression
674 if(version <= 3 || version == 5 || version == 6)
676 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
680 throw SerializationError
681 ("MapBlock::deSerialize: no enough input data");
682 is_underground = tmp;
683 for(u32 i=0; i<nodecount; i++)
685 s32 len = MapNode::serializedLength(version);
686 SharedBuffer<u8> d(len);
687 is.read((char*)*d, len);
688 if(is.gcount() != len)
689 throw SerializationError
690 ("MapBlock::deSerialize: no enough input data");
691 data[i].deSerialize(*d, version);
694 else if(version <= 10)
696 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
699 is.read((char*)&t8, 1);
703 // Uncompress and set material data
704 std::ostringstream os(std::ios_base::binary);
705 decompress(is, os, version);
706 std::string s = os.str();
707 if(s.size() != nodecount)
708 throw SerializationError
709 ("MapBlock::deSerialize: invalid format");
710 for(u32 i=0; i<s.size(); i++)
712 data[i].param0 = s[i];
716 // Uncompress and set param data
717 std::ostringstream os(std::ios_base::binary);
718 decompress(is, os, version);
719 std::string s = os.str();
720 if(s.size() != nodecount)
721 throw SerializationError
722 ("MapBlock::deSerialize: invalid format");
723 for(u32 i=0; i<s.size(); i++)
725 data[i].param1 = s[i];
731 // Uncompress and set param2 data
732 std::ostringstream os(std::ios_base::binary);
733 decompress(is, os, version);
734 std::string s = os.str();
735 if(s.size() != nodecount)
736 throw SerializationError
737 ("MapBlock::deSerialize: invalid format");
738 for(u32 i=0; i<s.size(); i++)
740 data[i].param2 = s[i];
744 // All other versions (newest)
747 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
750 is.read((char*)&flags, 1);
751 is_underground = (flags & 0x01) ? true : false;
752 m_day_night_differs = (flags & 0x02) ? true : false;
753 m_lighting_expired = (flags & 0x04) ? true : false;
755 m_generated = (flags & 0x08) ? false : true;
758 std::ostringstream os(std::ios_base::binary);
759 decompress(is, os, version);
760 std::string s = os.str();
761 if(s.size() != nodecount*3)
762 throw SerializationError
763 ("MapBlock::deSerialize: decompress resulted in size"
764 " other than nodecount*3");
766 // deserialize nodes from buffer
767 for(u32 i=0; i<nodecount; i++)
771 buf[1] = s[i+nodecount];
772 buf[2] = s[i+nodecount*2];
773 data[i].deSerialize(buf, version);
785 std::string data = deSerializeString(is);
786 std::istringstream iss(data, std::ios_base::binary);
787 m_node_metadata.deSerialize(iss);
791 //std::string data = deSerializeLongString(is);
792 std::ostringstream oss(std::ios_base::binary);
793 decompressZlib(is, oss);
794 std::istringstream iss(oss.str(), std::ios_base::binary);
795 m_node_metadata.deSerialize(iss);
798 catch(SerializationError &e)
800 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
801 <<" while deserializing node metadata"<<std::endl;
807 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
809 // Versions up from 9 have block objects. (DEPRECATED)
816 // Versions up from 15 have static objects.
819 m_static_objects.serialize(os);
825 writeU32(os, getTimestamp());
829 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
832 Versions up from 9 have block objects. (DEPRECATED)
836 u16 count = readU16(is);
837 // Not supported and length not known if count is not 0
839 dstream<<"WARNING: MapBlock::deSerializeDiskExtra(): "
840 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
846 Versions up from 15 have static objects.
850 m_static_objects.deSerialize(is);
856 setTimestamp(readU32(is));
860 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
865 Get a quick string to describe what a block actually contains
867 std::string analyze_block(MapBlock *block)
874 std::ostringstream desc;
876 v3s16 p = block->getPos();
878 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
881 switch(block->getModified())
883 case MOD_STATE_CLEAN:
886 case MOD_STATE_WRITE_AT_UNLOAD:
887 desc<<"WRITE_AT_UNLOAD, ";
889 case MOD_STATE_WRITE_NEEDED:
890 desc<<"WRITE_NEEDED, ";
893 desc<<"unknown getModified()="+itos(block->getModified())+", ";
896 if(block->isGenerated())
897 desc<<"is_gen [X], ";
899 desc<<"is_gen [ ], ";
901 if(block->getIsUnderground())
907 if(block->getMeshExpired())
908 desc<<"mesh_exp [X], ";
910 desc<<"mesh_exp [ ], ";
913 if(block->getLightingExpired())
914 desc<<"lighting_exp [X], ";
916 desc<<"lighting_exp [ ], ";
924 // We'll just define the numbers here, don't want to include
926 const content_t content_water = 2;
927 const content_t content_watersource = 9;
928 const content_t content_tree = 0x801;
929 const content_t content_leaves = 0x802;
930 const content_t content_jungletree = 0x815;
932 bool full_ignore = true;
933 bool some_ignore = false;
934 bool full_air = true;
935 bool some_air = false;
938 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
939 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
940 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
943 MapNode n = block->getNode(p);
944 content_t c = n.getContent();
945 if(c == CONTENT_IGNORE)
953 if(c == content_tree || c == content_jungletree
954 || c == content_leaves)
956 if(c == content_water
957 || c == content_watersource)
963 std::ostringstream ss;
966 ss<<"IGNORE (full), ";
980 if(ss.str().size()>=2)
981 desc<<ss.str().substr(0, ss.str().size()-2);
986 return desc.str().substr(0, desc.str().size()-2);