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.
31 MapBlock::MapBlock(Map *parent, v3s16 pos, bool dummy):
34 m_modified(MOD_STATE_WRITE_NEEDED),
35 is_underground(false),
36 m_lighting_expired(true),
37 m_day_night_differs(false),
39 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
46 //m_spawn_timer = -10000;
49 m_mesh_expired = false;
52 m_temp_mods_mutex.Init();
60 JMutexAutoLock lock(mesh_mutex);
74 bool MapBlock::isValidPositionParent(v3s16 p)
76 if(isValidPosition(p))
81 return m_parent->isValidPosition(getPosRelative() + p);
85 MapNode MapBlock::getNodeParent(v3s16 p)
87 if(isValidPosition(p) == false)
89 return m_parent->getNode(getPosRelative() + p);
94 throw InvalidPositionException();
95 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
99 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
101 if(isValidPosition(p) == false)
103 m_parent->setNode(getPosRelative() + p, n);
108 throw InvalidPositionException();
109 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
113 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
115 if(isValidPosition(p) == false)
118 return m_parent->getNode(getPosRelative() + p);
120 catch(InvalidPositionException &e)
122 return MapNode(CONTENT_IGNORE);
129 return MapNode(CONTENT_IGNORE);
131 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
138 void MapBlock::updateMesh(u32 daynight_ratio)
142 DEBUG: If mesh has been generated, don't generate it again
145 JMutexAutoLock meshlock(mesh_mutex);
152 data.fill(daynight_ratio, this);
154 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
160 replaceMesh(mesh_new);
165 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
169 //scene::SMesh *mesh_old = mesh[daynight_i];
170 //mesh[daynight_i] = mesh_new;
172 scene::SMesh *mesh_old = mesh;
174 setMeshExpired(false);
178 // Remove hardware buffers of meshbuffers of mesh
179 // NOTE: No way, this runs in a different thread and everything
180 /*u32 c = mesh_old->getMeshBufferCount();
181 for(u32 i=0; i<c; i++)
183 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
186 /*dstream<<"mesh_old->getReferenceCount()="
187 <<mesh_old->getReferenceCount()<<std::endl;
188 u32 c = mesh_old->getMeshBufferCount();
189 for(u32 i=0; i<c; i++)
191 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
192 dstream<<"buf->getReferenceCount()="
193 <<buf->getReferenceCount()<<std::endl;
208 Propagates sunlight down through the block.
209 Doesn't modify nodes that are not affected by sunlight.
211 Returns false if sunlight at bottom block is invalid.
212 Returns true if sunlight at bottom block is valid.
213 Returns true if bottom block doesn't exist.
215 If there is a block above, continues from it.
216 If there is no block above, assumes there is sunlight, unless
217 is_underground is set or highest node is water.
219 All sunlighted nodes are added to light_sources.
221 if remove_light==true, sets non-sunlighted nodes black.
223 if black_air_left!=NULL, it is set to true if non-sunlighted
224 air is left in block.
226 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
227 bool remove_light, bool *black_air_left)
229 // Whether the sunlight at the top of the bottom block is valid
230 bool block_below_is_valid = true;
232 v3s16 pos_relative = getPosRelative();
234 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
236 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
239 bool no_sunlight = false;
240 bool no_top_block = false;
241 // Check if node above block has sunlight
243 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
244 if(n.getContent() == CONTENT_IGNORE)
247 no_sunlight = is_underground;
249 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
254 catch(InvalidPositionException &e)
258 // NOTE: This makes over-ground roofed places sunlighted
259 // Assume sunlight, unless is_underground==true
266 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
267 //if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
268 if(content_features(n).sunlight_propagates == false)
273 // NOTE: As of now, this just would make everything dark.
275 //no_sunlight = true;
278 #if 0 // Doesn't work; nothing gets light.
279 bool no_sunlight = true;
280 bool no_top_block = false;
281 // Check if node above block has sunlight
283 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
284 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
289 catch(InvalidPositionException &e)
295 /*std::cout<<"("<<x<<","<<z<<"): "
296 <<"no_top_block="<<no_top_block
297 <<", is_underground="<<is_underground
298 <<", no_sunlight="<<no_sunlight
301 s16 y = MAP_BLOCKSIZE-1;
303 // This makes difference to diminishing in water.
304 bool stopped_to_solid_object = false;
306 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
311 MapNode &n = getNodeRef(pos);
313 if(current_light == 0)
317 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
319 // Do nothing: Sunlight is continued
321 else if(n.light_propagates() == false)
323 /*// DEPRECATED TODO: REMOVE
326 bool upper_is_air = false;
329 if(getNodeParent(pos+v3s16(0,1,0)).getContent() == CONTENT_AIR)
332 catch(InvalidPositionException &e)
335 // Turn mud into grass
336 if(upper_is_air && n.getContent() == CONTENT_MUD
337 && current_light == LIGHT_SUN)
343 // A solid object is on the way.
344 stopped_to_solid_object = true;
352 current_light = diminish_light(current_light);
355 u8 old_light = n.getLight(LIGHTBANK_DAY);
357 if(current_light > old_light || remove_light)
359 n.setLight(LIGHTBANK_DAY, current_light);
362 if(diminish_light(current_light) != 0)
364 light_sources.insert(pos_relative + pos, true);
367 if(current_light == 0 && stopped_to_solid_object)
371 *black_air_left = true;
376 // Whether or not the block below should see LIGHT_SUN
377 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
380 If the block below hasn't already been marked invalid:
382 Check if the node below the block has proper sunlight at top.
383 If not, the block below is invalid.
385 Ignore non-transparent nodes as they always have no light
389 if(block_below_is_valid)
391 MapNode n = getNodeParent(v3s16(x, -1, z));
392 if(n.light_propagates())
394 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
395 && sunlight_should_go_down == false)
396 block_below_is_valid = false;
397 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
398 && sunlight_should_go_down == true)
399 block_below_is_valid = false;
403 catch(InvalidPositionException &e)
405 /*std::cout<<"InvalidBlockException for bottom block node"
407 // Just no block below, no need to panic.
412 return block_below_is_valid;
416 void MapBlock::copyTo(VoxelManipulator &dst)
418 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
419 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
421 // Copy from data to VoxelManipulator
422 dst.copyFrom(data, data_area, v3s16(0,0,0),
423 getPosRelative(), data_size);
426 void MapBlock::copyFrom(VoxelManipulator &dst)
428 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
429 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
431 // Copy from VoxelManipulator to data
432 dst.copyTo(data, data_area, v3s16(0,0,0),
433 getPosRelative(), data_size);
436 void MapBlock::updateDayNightDiff()
440 m_day_night_differs = false;
444 bool differs = false;
447 Check if any lighting value differs
449 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
451 MapNode &n = data[i];
452 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
460 If some lighting values differ, check if the whole thing is
461 just air. If it is, differ = false
465 bool only_air = true;
466 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
468 MapNode &n = data[i];
469 if(n.getContent() != CONTENT_AIR)
479 // Set member variable
480 m_day_night_differs = differs;
483 s16 MapBlock::getGroundLevel(v2s16 p2d)
489 s16 y = MAP_BLOCKSIZE-1;
492 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
493 if(content_features(n).walkable)
495 if(y == MAP_BLOCKSIZE-1)
503 catch(InvalidPositionException &e)
513 void MapBlock::serialize(std::ostream &os, u8 version)
515 if(!ser_ver_supported(version))
516 throw VersionMismatchException("ERROR: MapBlock format not supported");
520 throw SerializationError("ERROR: Not writing dummy block.");
523 // These have no compression
524 if(version <= 3 || version == 5 || version == 6)
526 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
528 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
529 SharedBuffer<u8> dest(buflen);
531 dest[0] = is_underground;
532 for(u32 i=0; i<nodecount; i++)
534 u32 s = 1 + i * MapNode::serializedLength(version);
535 data[i].serialize(&dest[s], version);
538 os.write((char*)*dest, dest.getSize());
540 else if(version <= 10)
544 Compress the materials and the params separately.
548 os.write((char*)&is_underground, 1);
550 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
552 // Get and compress materials
553 SharedBuffer<u8> materialdata(nodecount);
554 for(u32 i=0; i<nodecount; i++)
556 materialdata[i] = data[i].param0;
558 compress(materialdata, os, version);
560 // Get and compress lights
561 SharedBuffer<u8> lightdata(nodecount);
562 for(u32 i=0; i<nodecount; i++)
564 lightdata[i] = data[i].param1;
566 compress(lightdata, os, version);
570 // Get and compress param2
571 SharedBuffer<u8> param2data(nodecount);
572 for(u32 i=0; i<nodecount; i++)
574 param2data[i] = data[i].param2;
576 compress(param2data, os, version);
579 // All other versions (newest)
586 if(m_day_night_differs)
588 if(m_lighting_expired)
592 if(m_generated == false)
595 os.write((char*)&flags, 1);
597 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
604 SharedBuffer<u8> databuf_nodelist(nodecount*3);
605 for(u32 i=0; i<nodecount; i++)
607 data[i].serialize(&databuf_nodelist[i*3], version);
610 // Create buffer with different parameters sorted
611 SharedBuffer<u8> databuf(nodecount*3);
612 for(u32 i=0; i<nodecount; i++)
614 databuf[i] = databuf_nodelist[i*3];
615 databuf[i+nodecount] = databuf_nodelist[i*3+1];
616 databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
620 Compress data to output stream
623 compress(databuf, os, version);
633 std::ostringstream oss(std::ios_base::binary);
634 m_node_metadata.serialize(oss);
635 os<<serializeString(oss.str());
637 // This will happen if the string is longer than 65535
638 catch(SerializationError &e)
640 // Use an empty string
641 os<<serializeString("");
646 std::ostringstream oss(std::ios_base::binary);
647 m_node_metadata.serialize(oss);
648 compressZlib(oss.str(), os);
649 //os<<serializeLongString(oss.str());
655 void MapBlock::deSerialize(std::istream &is, u8 version)
657 if(!ser_ver_supported(version))
658 throw VersionMismatchException("ERROR: MapBlock format not supported");
660 // These have no lighting info
663 setLightingExpired(true);
666 // These have no "generated" field
672 // These have no compression
673 if(version <= 3 || version == 5 || version == 6)
675 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
679 throw SerializationError
680 ("MapBlock::deSerialize: no enough input data");
681 is_underground = tmp;
682 for(u32 i=0; i<nodecount; i++)
684 s32 len = MapNode::serializedLength(version);
685 SharedBuffer<u8> d(len);
686 is.read((char*)*d, len);
687 if(is.gcount() != len)
688 throw SerializationError
689 ("MapBlock::deSerialize: no enough input data");
690 data[i].deSerialize(*d, version);
693 else if(version <= 10)
695 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
698 is.read((char*)&t8, 1);
702 // Uncompress and set material data
703 std::ostringstream os(std::ios_base::binary);
704 decompress(is, os, version);
705 std::string s = os.str();
706 if(s.size() != nodecount)
707 throw SerializationError
708 ("MapBlock::deSerialize: invalid format");
709 for(u32 i=0; i<s.size(); i++)
711 data[i].param0 = s[i];
715 // Uncompress and set param data
716 std::ostringstream os(std::ios_base::binary);
717 decompress(is, os, version);
718 std::string s = os.str();
719 if(s.size() != nodecount)
720 throw SerializationError
721 ("MapBlock::deSerialize: invalid format");
722 for(u32 i=0; i<s.size(); i++)
724 data[i].param1 = s[i];
730 // Uncompress and set param2 data
731 std::ostringstream os(std::ios_base::binary);
732 decompress(is, os, version);
733 std::string s = os.str();
734 if(s.size() != nodecount)
735 throw SerializationError
736 ("MapBlock::deSerialize: invalid format");
737 for(u32 i=0; i<s.size(); i++)
739 data[i].param2 = s[i];
743 // All other versions (newest)
746 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
749 is.read((char*)&flags, 1);
750 is_underground = (flags & 0x01) ? true : false;
751 m_day_night_differs = (flags & 0x02) ? true : false;
752 m_lighting_expired = (flags & 0x04) ? true : false;
754 m_generated = (flags & 0x08) ? false : true;
757 std::ostringstream os(std::ios_base::binary);
758 decompress(is, os, version);
759 std::string s = os.str();
760 if(s.size() != nodecount*3)
761 throw SerializationError
762 ("MapBlock::deSerialize: decompress resulted in size"
763 " other than nodecount*3");
765 // deserialize nodes from buffer
766 for(u32 i=0; i<nodecount; i++)
770 buf[1] = s[i+nodecount];
771 buf[2] = s[i+nodecount*2];
772 data[i].deSerialize(buf, version);
784 std::string data = deSerializeString(is);
785 std::istringstream iss(data, std::ios_base::binary);
786 m_node_metadata.deSerialize(iss);
790 //std::string data = deSerializeLongString(is);
791 std::ostringstream oss(std::ios_base::binary);
792 decompressZlib(is, oss);
793 std::istringstream iss(oss.str(), std::ios_base::binary);
794 m_node_metadata.deSerialize(iss);
797 catch(SerializationError &e)
799 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
800 <<" while deserializing node metadata"<<std::endl;
806 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
808 // Versions up from 9 have block objects. (DEPRECATED)
815 // Versions up from 15 have static objects.
818 m_static_objects.serialize(os);
824 writeU32(os, getTimestamp());
828 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
831 Versions up from 9 have block objects. (DEPRECATED)
835 u16 count = readU16(is);
836 // Not supported and length not known if count is not 0
838 dstream<<"WARNING: MapBlock::deSerializeDiskExtra(): "
839 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
845 Versions up from 15 have static objects.
849 m_static_objects.deSerialize(is);
855 setTimestamp(readU32(is));
859 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
864 Get a quick string to describe what a block actually contains
866 std::string analyze_block(MapBlock *block)
873 std::ostringstream desc;
875 v3s16 p = block->getPos();
877 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
880 switch(block->getModified())
882 case MOD_STATE_CLEAN:
885 case MOD_STATE_WRITE_AT_UNLOAD:
886 desc<<"WRITE_AT_UNLOAD, ";
888 case MOD_STATE_WRITE_NEEDED:
889 desc<<"WRITE_NEEDED, ";
892 desc<<"unknown getModified()="+itos(block->getModified())+", ";
895 if(block->isGenerated())
896 desc<<"is_gen [X], ";
898 desc<<"is_gen [ ], ";
900 if(block->getIsUnderground())
906 if(block->getMeshExpired())
907 desc<<"mesh_exp [X], ";
909 desc<<"mesh_exp [ ], ";
912 if(block->getLightingExpired())
913 desc<<"lighting_exp [X], ";
915 desc<<"lighting_exp [ ], ";
923 // We'll just define the numbers here, don't want to include
925 const content_t content_water = 2;
926 const content_t content_watersource = 9;
927 const content_t content_tree = 0x801;
928 const content_t content_leaves = 0x802;
929 const content_t content_jungletree = 0x815;
931 bool full_ignore = true;
932 bool some_ignore = false;
933 bool full_air = true;
934 bool some_air = false;
937 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
938 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
939 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
942 MapNode n = block->getNode(p);
943 content_t c = n.getContent();
944 if(c == CONTENT_IGNORE)
952 if(c == content_tree || c == content_jungletree
953 || c == content_leaves)
955 if(c == content_water
956 || c == content_watersource)
962 std::ostringstream ss;
965 ss<<"IGNORE (full), ";
979 if(ss.str().size()>=2)
980 desc<<ss.str().substr(0, ss.str().size()-2);
985 return desc.str().substr(0, desc.str().size()-2);