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),
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 || n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
250 catch(InvalidPositionException &e)
254 // NOTE: This makes over-ground roofed places sunlighted
255 // Assume sunlight, unless is_underground==true
262 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
263 //if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
264 if(content_features(n).sunlight_propagates == false)
269 // NOTE: As of now, this just would make everything dark.
271 //no_sunlight = true;
274 #if 0 // Doesn't work; nothing gets light.
275 bool no_sunlight = true;
276 bool no_top_block = false;
277 // Check if node above block has sunlight
279 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
280 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
285 catch(InvalidPositionException &e)
291 /*std::cout<<"("<<x<<","<<z<<"): "
292 <<"no_top_block="<<no_top_block
293 <<", is_underground="<<is_underground
294 <<", no_sunlight="<<no_sunlight
297 s16 y = MAP_BLOCKSIZE-1;
299 // This makes difference to diminishing in water.
300 bool stopped_to_solid_object = false;
302 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
307 MapNode &n = getNodeRef(pos);
309 if(current_light == 0)
313 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
315 // Do nothing: Sunlight is continued
317 else if(n.light_propagates() == false)
319 /*// DEPRECATED TODO: REMOVE
322 bool upper_is_air = false;
325 if(getNodeParent(pos+v3s16(0,1,0)).getContent() == CONTENT_AIR)
328 catch(InvalidPositionException &e)
331 // Turn mud into grass
332 if(upper_is_air && n.getContent() == CONTENT_MUD
333 && current_light == LIGHT_SUN)
339 // A solid object is on the way.
340 stopped_to_solid_object = true;
348 current_light = diminish_light(current_light);
351 u8 old_light = n.getLight(LIGHTBANK_DAY);
353 if(current_light > old_light || remove_light)
355 n.setLight(LIGHTBANK_DAY, current_light);
358 if(diminish_light(current_light) != 0)
360 light_sources.insert(pos_relative + pos, true);
363 if(current_light == 0 && stopped_to_solid_object)
367 *black_air_left = true;
372 // Whether or not the block below should see LIGHT_SUN
373 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
376 If the block below hasn't already been marked invalid:
378 Check if the node below the block has proper sunlight at top.
379 If not, the block below is invalid.
381 Ignore non-transparent nodes as they always have no light
385 if(block_below_is_valid)
387 MapNode n = getNodeParent(v3s16(x, -1, z));
388 if(n.light_propagates())
390 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
391 && sunlight_should_go_down == false)
392 block_below_is_valid = false;
393 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
394 && sunlight_should_go_down == true)
395 block_below_is_valid = false;
399 catch(InvalidPositionException &e)
401 /*std::cout<<"InvalidBlockException for bottom block node"
403 // Just no block below, no need to panic.
408 return block_below_is_valid;
412 void MapBlock::copyTo(VoxelManipulator &dst)
414 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
415 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
417 // Copy from data to VoxelManipulator
418 dst.copyFrom(data, data_area, v3s16(0,0,0),
419 getPosRelative(), data_size);
422 void MapBlock::copyFrom(VoxelManipulator &dst)
424 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
425 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
427 // Copy from VoxelManipulator to data
428 dst.copyTo(data, data_area, v3s16(0,0,0),
429 getPosRelative(), data_size);
432 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
437 m_objects.step(dtime, server, daynight_ratio);
443 void MapBlock::updateDayNightDiff()
447 m_day_night_differs = false;
451 bool differs = false;
454 Check if any lighting value differs
456 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
458 MapNode &n = data[i];
459 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
467 If some lighting values differ, check if the whole thing is
468 just air. If it is, differ = false
472 bool only_air = true;
473 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
475 MapNode &n = data[i];
476 if(n.getContent() != CONTENT_AIR)
486 // Set member variable
487 m_day_night_differs = differs;
490 s16 MapBlock::getGroundLevel(v2s16 p2d)
496 s16 y = MAP_BLOCKSIZE-1;
499 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
500 if(content_features(n).walkable)
502 if(y == MAP_BLOCKSIZE-1)
510 catch(InvalidPositionException &e)
520 void MapBlock::serialize(std::ostream &os, u8 version)
522 if(!ser_ver_supported(version))
523 throw VersionMismatchException("ERROR: MapBlock format not supported");
527 throw SerializationError("ERROR: Not writing dummy block.");
530 // These have no compression
531 if(version <= 3 || version == 5 || version == 6)
533 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
535 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
536 SharedBuffer<u8> dest(buflen);
538 dest[0] = is_underground;
539 for(u32 i=0; i<nodecount; i++)
541 u32 s = 1 + i * MapNode::serializedLength(version);
542 data[i].serialize(&dest[s], version);
545 os.write((char*)*dest, dest.getSize());
547 else if(version <= 10)
551 Compress the materials and the params separately.
555 os.write((char*)&is_underground, 1);
557 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
559 // Get and compress materials
560 SharedBuffer<u8> materialdata(nodecount);
561 for(u32 i=0; i<nodecount; i++)
563 materialdata[i] = data[i].param0;
565 compress(materialdata, os, version);
567 // Get and compress lights
568 SharedBuffer<u8> lightdata(nodecount);
569 for(u32 i=0; i<nodecount; i++)
571 lightdata[i] = data[i].param1;
573 compress(lightdata, os, version);
577 // Get and compress param2
578 SharedBuffer<u8> param2data(nodecount);
579 for(u32 i=0; i<nodecount; i++)
581 param2data[i] = data[i].param2;
583 compress(param2data, os, version);
586 // All other versions (newest)
593 if(m_day_night_differs)
595 if(m_lighting_expired)
599 if(m_generated == false)
602 os.write((char*)&flags, 1);
604 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
611 SharedBuffer<u8> databuf_nodelist(nodecount*3);
612 for(u32 i=0; i<nodecount; i++)
614 data[i].serialize(&databuf_nodelist[i*3], version);
617 // Create buffer with different parameters sorted
618 SharedBuffer<u8> databuf(nodecount*3);
619 for(u32 i=0; i<nodecount; i++)
621 databuf[i] = databuf_nodelist[i*3];
622 databuf[i+nodecount] = databuf_nodelist[i*3+1];
623 databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
627 Compress data to output stream
630 compress(databuf, os, version);
640 std::ostringstream oss(std::ios_base::binary);
641 m_node_metadata.serialize(oss);
642 os<<serializeString(oss.str());
644 // This will happen if the string is longer than 65535
645 catch(SerializationError &e)
647 // Use an empty string
648 os<<serializeString("");
653 std::ostringstream oss(std::ios_base::binary);
654 m_node_metadata.serialize(oss);
655 compressZlib(oss.str(), os);
656 //os<<serializeLongString(oss.str());
662 void MapBlock::deSerialize(std::istream &is, u8 version)
664 if(!ser_ver_supported(version))
665 throw VersionMismatchException("ERROR: MapBlock format not supported");
667 // These have no lighting info
670 setLightingExpired(true);
673 // These have no "generated" field
679 // These have no compression
680 if(version <= 3 || version == 5 || version == 6)
682 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
686 throw SerializationError
687 ("MapBlock::deSerialize: no enough input data");
688 is_underground = tmp;
689 for(u32 i=0; i<nodecount; i++)
691 s32 len = MapNode::serializedLength(version);
692 SharedBuffer<u8> d(len);
693 is.read((char*)*d, len);
694 if(is.gcount() != len)
695 throw SerializationError
696 ("MapBlock::deSerialize: no enough input data");
697 data[i].deSerialize(*d, version);
700 else if(version <= 10)
702 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
705 is.read((char*)&t8, 1);
709 // Uncompress and set material data
710 std::ostringstream os(std::ios_base::binary);
711 decompress(is, os, version);
712 std::string s = os.str();
713 if(s.size() != nodecount)
714 throw SerializationError
715 ("MapBlock::deSerialize: invalid format");
716 for(u32 i=0; i<s.size(); i++)
718 data[i].param0 = s[i];
722 // Uncompress and set param data
723 std::ostringstream os(std::ios_base::binary);
724 decompress(is, os, version);
725 std::string s = os.str();
726 if(s.size() != nodecount)
727 throw SerializationError
728 ("MapBlock::deSerialize: invalid format");
729 for(u32 i=0; i<s.size(); i++)
731 data[i].param1 = s[i];
737 // Uncompress and set param2 data
738 std::ostringstream os(std::ios_base::binary);
739 decompress(is, os, version);
740 std::string s = os.str();
741 if(s.size() != nodecount)
742 throw SerializationError
743 ("MapBlock::deSerialize: invalid format");
744 for(u32 i=0; i<s.size(); i++)
746 data[i].param2 = s[i];
750 // All other versions (newest)
753 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
756 is.read((char*)&flags, 1);
757 is_underground = (flags & 0x01) ? true : false;
758 m_day_night_differs = (flags & 0x02) ? true : false;
759 m_lighting_expired = (flags & 0x04) ? true : false;
761 m_generated = (flags & 0x08) ? false : true;
764 std::ostringstream os(std::ios_base::binary);
765 decompress(is, os, version);
766 std::string s = os.str();
767 if(s.size() != nodecount*3)
768 throw SerializationError
769 ("MapBlock::deSerialize: decompress resulted in size"
770 " other than nodecount*3");
772 // deserialize nodes from buffer
773 for(u32 i=0; i<nodecount; i++)
777 buf[1] = s[i+nodecount];
778 buf[2] = s[i+nodecount*2];
779 data[i].deSerialize(buf, version);
791 std::string data = deSerializeString(is);
792 std::istringstream iss(data, std::ios_base::binary);
793 m_node_metadata.deSerialize(iss);
797 //std::string data = deSerializeLongString(is);
798 std::ostringstream oss(std::ios_base::binary);
799 decompressZlib(is, oss);
800 std::istringstream iss(oss.str(), std::ios_base::binary);
801 m_node_metadata.deSerialize(iss);
804 catch(SerializationError &e)
806 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
807 <<" while deserializing node metadata"<<std::endl;
813 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
815 // Versions up from 9 have block objects.
818 //serializeObjects(os, version); // DEPRECATED
823 // Versions up from 15 have static objects.
826 m_static_objects.serialize(os);
832 writeU32(os, getTimestamp());
836 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
839 Versions up from 9 have block objects.
843 updateObjects(is, version, NULL, 0);
847 Versions up from 15 have static objects.
851 m_static_objects.deSerialize(is);
857 setTimestamp(readU32(is));
861 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);