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"
27 #include "nodemetadata.h"
33 MapBlock::MapBlock(Map *parent, v3s16 pos, bool dummy):
34 m_node_metadata(new NodeMetadataList),
37 m_modified(MOD_STATE_WRITE_NEEDED),
38 is_underground(false),
39 m_lighting_expired(true),
40 m_day_night_differs(false),
42 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
50 m_mesh_expired = false;
53 m_temp_mods_mutex.Init();
61 JMutexAutoLock lock(mesh_mutex);
71 delete m_node_metadata;
77 bool MapBlock::isValidPositionParent(v3s16 p)
79 if(isValidPosition(p))
84 return m_parent->isValidPosition(getPosRelative() + p);
88 MapNode MapBlock::getNodeParent(v3s16 p)
90 if(isValidPosition(p) == false)
92 return m_parent->getNode(getPosRelative() + p);
97 throw InvalidPositionException();
98 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
102 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
104 if(isValidPosition(p) == false)
106 m_parent->setNode(getPosRelative() + p, n);
111 throw InvalidPositionException();
112 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
116 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
118 if(isValidPosition(p) == false)
121 return m_parent->getNode(getPosRelative() + p);
123 catch(InvalidPositionException &e)
125 return MapNode(CONTENT_IGNORE);
132 return MapNode(CONTENT_IGNORE);
134 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
141 void MapBlock::updateMesh(u32 daynight_ratio)
145 DEBUG: If mesh has been generated, don't generate it again
148 JMutexAutoLock meshlock(mesh_mutex);
155 data.fill(daynight_ratio, this);
157 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
163 replaceMesh(mesh_new);
168 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
172 //scene::SMesh *mesh_old = mesh[daynight_i];
173 //mesh[daynight_i] = mesh_new;
175 scene::SMesh *mesh_old = mesh;
177 setMeshExpired(false);
181 // Remove hardware buffers of meshbuffers of mesh
182 // NOTE: No way, this runs in a different thread and everything
183 /*u32 c = mesh_old->getMeshBufferCount();
184 for(u32 i=0; i<c; i++)
186 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
189 /*dstream<<"mesh_old->getReferenceCount()="
190 <<mesh_old->getReferenceCount()<<std::endl;
191 u32 c = mesh_old->getMeshBufferCount();
192 for(u32 i=0; i<c; i++)
194 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
195 dstream<<"buf->getReferenceCount()="
196 <<buf->getReferenceCount()<<std::endl;
211 Propagates sunlight down through the block.
212 Doesn't modify nodes that are not affected by sunlight.
214 Returns false if sunlight at bottom block is invalid.
215 Returns true if sunlight at bottom block is valid.
216 Returns true if bottom block doesn't exist.
218 If there is a block above, continues from it.
219 If there is no block above, assumes there is sunlight, unless
220 is_underground is set or highest node is water.
222 All sunlighted nodes are added to light_sources.
224 if remove_light==true, sets non-sunlighted nodes black.
226 if black_air_left!=NULL, it is set to true if non-sunlighted
227 air is left in block.
229 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
230 bool remove_light, bool *black_air_left)
232 // Whether the sunlight at the top of the bottom block is valid
233 bool block_below_is_valid = true;
235 v3s16 pos_relative = getPosRelative();
237 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
239 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
242 bool no_sunlight = false;
243 bool no_top_block = false;
244 // Check if node above block has sunlight
246 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
247 if(n.getContent() == CONTENT_IGNORE)
250 no_sunlight = is_underground;
252 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
257 catch(InvalidPositionException &e)
261 // NOTE: This makes over-ground roofed places sunlighted
262 // Assume sunlight, unless is_underground==true
269 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
270 //if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
271 if(content_features(n).sunlight_propagates == false)
276 // NOTE: As of now, this just would make everything dark.
278 //no_sunlight = true;
281 #if 0 // Doesn't work; nothing gets light.
282 bool no_sunlight = true;
283 bool no_top_block = false;
284 // Check if node above block has sunlight
286 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
287 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
292 catch(InvalidPositionException &e)
298 /*std::cout<<"("<<x<<","<<z<<"): "
299 <<"no_top_block="<<no_top_block
300 <<", is_underground="<<is_underground
301 <<", no_sunlight="<<no_sunlight
304 s16 y = MAP_BLOCKSIZE-1;
306 // This makes difference to diminishing in water.
307 bool stopped_to_solid_object = false;
309 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
314 MapNode &n = getNodeRef(pos);
316 if(current_light == 0)
320 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
322 // Do nothing: Sunlight is continued
324 else if(n.light_propagates() == false)
326 /*// DEPRECATED TODO: REMOVE
329 bool upper_is_air = false;
332 if(getNodeParent(pos+v3s16(0,1,0)).getContent() == CONTENT_AIR)
335 catch(InvalidPositionException &e)
338 // Turn mud into grass
339 if(upper_is_air && n.getContent() == CONTENT_MUD
340 && current_light == LIGHT_SUN)
346 // A solid object is on the way.
347 stopped_to_solid_object = true;
355 current_light = diminish_light(current_light);
358 u8 old_light = n.getLight(LIGHTBANK_DAY);
360 if(current_light > old_light || remove_light)
362 n.setLight(LIGHTBANK_DAY, current_light);
365 if(diminish_light(current_light) != 0)
367 light_sources.insert(pos_relative + pos, true);
370 if(current_light == 0 && stopped_to_solid_object)
374 *black_air_left = true;
379 // Whether or not the block below should see LIGHT_SUN
380 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
383 If the block below hasn't already been marked invalid:
385 Check if the node below the block has proper sunlight at top.
386 If not, the block below is invalid.
388 Ignore non-transparent nodes as they always have no light
392 if(block_below_is_valid)
394 MapNode n = getNodeParent(v3s16(x, -1, z));
395 if(n.light_propagates())
397 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
398 && sunlight_should_go_down == false)
399 block_below_is_valid = false;
400 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
401 && sunlight_should_go_down == true)
402 block_below_is_valid = false;
406 catch(InvalidPositionException &e)
408 /*std::cout<<"InvalidBlockException for bottom block node"
410 // Just no block below, no need to panic.
415 return block_below_is_valid;
419 void MapBlock::copyTo(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 data to VoxelManipulator
425 dst.copyFrom(data, data_area, v3s16(0,0,0),
426 getPosRelative(), data_size);
429 void MapBlock::copyFrom(VoxelManipulator &dst)
431 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
432 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
434 // Copy from VoxelManipulator to data
435 dst.copyTo(data, data_area, v3s16(0,0,0),
436 getPosRelative(), data_size);
439 void MapBlock::updateDayNightDiff()
443 m_day_night_differs = false;
447 bool differs = false;
450 Check if any lighting value differs
452 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
454 MapNode &n = data[i];
455 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
463 If some lighting values differ, check if the whole thing is
464 just air. If it is, differ = false
468 bool only_air = true;
469 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
471 MapNode &n = data[i];
472 if(n.getContent() != CONTENT_AIR)
482 // Set member variable
483 m_day_night_differs = differs;
486 s16 MapBlock::getGroundLevel(v2s16 p2d)
492 s16 y = MAP_BLOCKSIZE-1;
495 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
496 if(content_features(n).walkable)
498 if(y == MAP_BLOCKSIZE-1)
506 catch(InvalidPositionException &e)
516 void MapBlock::serialize(std::ostream &os, u8 version)
518 if(!ser_ver_supported(version))
519 throw VersionMismatchException("ERROR: MapBlock format not supported");
523 throw SerializationError("ERROR: Not writing dummy block.");
526 // These have no compression
527 if(version <= 3 || version == 5 || version == 6)
529 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
531 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
532 SharedBuffer<u8> dest(buflen);
534 dest[0] = is_underground;
535 for(u32 i=0; i<nodecount; i++)
537 u32 s = 1 + i * MapNode::serializedLength(version);
538 data[i].serialize(&dest[s], version);
541 os.write((char*)*dest, dest.getSize());
543 else if(version <= 10)
547 Compress the materials and the params separately.
551 os.write((char*)&is_underground, 1);
553 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
555 // Get and compress materials
556 SharedBuffer<u8> materialdata(nodecount);
557 for(u32 i=0; i<nodecount; i++)
559 materialdata[i] = data[i].param0;
561 compress(materialdata, os, version);
563 // Get and compress lights
564 SharedBuffer<u8> lightdata(nodecount);
565 for(u32 i=0; i<nodecount; i++)
567 lightdata[i] = data[i].param1;
569 compress(lightdata, os, version);
573 // Get and compress param2
574 SharedBuffer<u8> param2data(nodecount);
575 for(u32 i=0; i<nodecount; i++)
577 param2data[i] = data[i].param2;
579 compress(param2data, os, version);
582 // All other versions (newest)
589 if(m_day_night_differs)
591 if(m_lighting_expired)
595 if(m_generated == false)
598 os.write((char*)&flags, 1);
600 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
607 SharedBuffer<u8> databuf_nodelist(nodecount*3);
608 for(u32 i=0; i<nodecount; i++)
610 data[i].serialize(&databuf_nodelist[i*3], version);
613 // Create buffer with different parameters sorted
614 SharedBuffer<u8> databuf(nodecount*3);
615 for(u32 i=0; i<nodecount; i++)
617 databuf[i] = databuf_nodelist[i*3];
618 databuf[i+nodecount] = databuf_nodelist[i*3+1];
619 databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
623 Compress data to output stream
626 compress(databuf, os, version);
636 std::ostringstream oss(std::ios_base::binary);
637 m_node_metadata->serialize(oss);
638 os<<serializeString(oss.str());
640 // This will happen if the string is longer than 65535
641 catch(SerializationError &e)
643 // Use an empty string
644 os<<serializeString("");
649 std::ostringstream oss(std::ios_base::binary);
650 m_node_metadata->serialize(oss);
651 compressZlib(oss.str(), os);
652 //os<<serializeLongString(oss.str());
658 void MapBlock::deSerialize(std::istream &is, u8 version)
660 if(!ser_ver_supported(version))
661 throw VersionMismatchException("ERROR: MapBlock format not supported");
663 // These have no lighting info
666 setLightingExpired(true);
669 // These have no "generated" field
675 // These have no compression
676 if(version <= 3 || version == 5 || version == 6)
678 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
682 throw SerializationError
683 ("MapBlock::deSerialize: no enough input data");
684 is_underground = tmp;
685 for(u32 i=0; i<nodecount; i++)
687 s32 len = MapNode::serializedLength(version);
688 SharedBuffer<u8> d(len);
689 is.read((char*)*d, len);
690 if(is.gcount() != len)
691 throw SerializationError
692 ("MapBlock::deSerialize: no enough input data");
693 data[i].deSerialize(*d, version);
696 else if(version <= 10)
698 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
701 is.read((char*)&t8, 1);
705 // Uncompress and set material data
706 std::ostringstream os(std::ios_base::binary);
707 decompress(is, os, version);
708 std::string s = os.str();
709 if(s.size() != nodecount)
710 throw SerializationError
711 ("MapBlock::deSerialize: invalid format");
712 for(u32 i=0; i<s.size(); i++)
714 data[i].param0 = s[i];
718 // Uncompress and set param data
719 std::ostringstream os(std::ios_base::binary);
720 decompress(is, os, version);
721 std::string s = os.str();
722 if(s.size() != nodecount)
723 throw SerializationError
724 ("MapBlock::deSerialize: invalid format");
725 for(u32 i=0; i<s.size(); i++)
727 data[i].param1 = s[i];
733 // Uncompress and set param2 data
734 std::ostringstream os(std::ios_base::binary);
735 decompress(is, os, version);
736 std::string s = os.str();
737 if(s.size() != nodecount)
738 throw SerializationError
739 ("MapBlock::deSerialize: invalid format");
740 for(u32 i=0; i<s.size(); i++)
742 data[i].param2 = s[i];
746 // All other versions (newest)
749 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
752 is.read((char*)&flags, 1);
753 is_underground = (flags & 0x01) ? true : false;
754 m_day_night_differs = (flags & 0x02) ? true : false;
755 m_lighting_expired = (flags & 0x04) ? true : false;
757 m_generated = (flags & 0x08) ? false : true;
760 std::ostringstream os(std::ios_base::binary);
761 decompress(is, os, version);
762 std::string s = os.str();
763 if(s.size() != nodecount*3)
764 throw SerializationError
765 ("MapBlock::deSerialize: decompress resulted in size"
766 " other than nodecount*3");
768 // deserialize nodes from buffer
769 for(u32 i=0; i<nodecount; i++)
773 buf[1] = s[i+nodecount];
774 buf[2] = s[i+nodecount*2];
775 data[i].deSerialize(buf, version);
787 std::string data = deSerializeString(is);
788 std::istringstream iss(data, std::ios_base::binary);
789 m_node_metadata->deSerialize(iss);
793 //std::string data = deSerializeLongString(is);
794 std::ostringstream oss(std::ios_base::binary);
795 decompressZlib(is, oss);
796 std::istringstream iss(oss.str(), std::ios_base::binary);
797 m_node_metadata->deSerialize(iss);
800 catch(SerializationError &e)
802 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
803 <<" while deserializing node metadata"<<std::endl;
809 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
811 // Versions up from 9 have block objects. (DEPRECATED)
818 // Versions up from 15 have static objects.
821 m_static_objects.serialize(os);
827 writeU32(os, getTimestamp());
831 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
834 Versions up from 9 have block objects. (DEPRECATED)
838 u16 count = readU16(is);
839 // Not supported and length not known if count is not 0
841 dstream<<"WARNING: MapBlock::deSerializeDiskExtra(): "
842 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
848 Versions up from 15 have static objects.
852 m_static_objects.deSerialize(is);
858 setTimestamp(readU32(is));
862 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
867 Get a quick string to describe what a block actually contains
869 std::string analyze_block(MapBlock *block)
876 std::ostringstream desc;
878 v3s16 p = block->getPos();
880 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
883 switch(block->getModified())
885 case MOD_STATE_CLEAN:
888 case MOD_STATE_WRITE_AT_UNLOAD:
889 desc<<"WRITE_AT_UNLOAD, ";
891 case MOD_STATE_WRITE_NEEDED:
892 desc<<"WRITE_NEEDED, ";
895 desc<<"unknown getModified()="+itos(block->getModified())+", ";
898 if(block->isGenerated())
899 desc<<"is_gen [X], ";
901 desc<<"is_gen [ ], ";
903 if(block->getIsUnderground())
909 if(block->getMeshExpired())
910 desc<<"mesh_exp [X], ";
912 desc<<"mesh_exp [ ], ";
915 if(block->getLightingExpired())
916 desc<<"lighting_exp [X], ";
918 desc<<"lighting_exp [ ], ";
926 // We'll just define the numbers here, don't want to include
928 const content_t content_water = 2;
929 const content_t content_watersource = 9;
930 const content_t content_tree = 0x801;
931 const content_t content_leaves = 0x802;
932 const content_t content_jungletree = 0x815;
934 bool full_ignore = true;
935 bool some_ignore = false;
936 bool full_air = true;
937 bool some_air = false;
940 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
941 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
942 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
945 MapNode n = block->getNode(p);
946 content_t c = n.getContent();
947 if(c == CONTENT_IGNORE)
955 if(c == content_tree || c == content_jungletree
956 || c == content_leaves)
958 if(c == content_water
959 || c == content_watersource)
965 std::ostringstream ss;
968 ss<<"IGNORE (full), ";
982 if(ss.str().size()>=2)
983 desc<<ss.str().substr(0, ss.str().size()-2);
988 return desc.str().substr(0, desc.str().size()-2);