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(NodeContainer *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),
41 m_usage_timer(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.d == 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.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
264 if(content_features(n.d).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)).d == CONTENT_AIR)
328 catch(InvalidPositionException &e)
331 // Turn mud into grass
332 if(upper_is_air && n.d == 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.d != 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 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
500 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).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].d;
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].param;
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;
610 SharedBuffer<u8> databuf(nodecount*3);
613 for(u32 i=0; i<nodecount; i++)
615 databuf[i] = data[i].d;
619 for(u32 i=0; i<nodecount; i++)
621 databuf[i+nodecount] = data[i].param;
625 for(u32 i=0; i<nodecount; i++)
627 databuf[i+nodecount*2] = data[i].param2;
631 Compress data to output stream
634 compress(databuf, os, version);
644 std::ostringstream oss(std::ios_base::binary);
645 m_node_metadata.serialize(oss);
646 os<<serializeString(oss.str());
648 // This will happen if the string is longer than 65535
649 catch(SerializationError &e)
651 // Use an empty string
652 os<<serializeString("");
657 std::ostringstream oss(std::ios_base::binary);
658 m_node_metadata.serialize(oss);
659 compressZlib(oss.str(), os);
660 //os<<serializeLongString(oss.str());
666 void MapBlock::deSerialize(std::istream &is, u8 version)
668 if(!ser_ver_supported(version))
669 throw VersionMismatchException("ERROR: MapBlock format not supported");
671 // These have no lighting info
674 setLightingExpired(true);
677 // These have no "generated" field
683 // These have no compression
684 if(version <= 3 || version == 5 || version == 6)
686 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
690 throw SerializationError
691 ("MapBlock::deSerialize: no enough input data");
692 is_underground = tmp;
693 for(u32 i=0; i<nodecount; i++)
695 s32 len = MapNode::serializedLength(version);
696 SharedBuffer<u8> d(len);
697 is.read((char*)*d, len);
698 if(is.gcount() != len)
699 throw SerializationError
700 ("MapBlock::deSerialize: no enough input data");
701 data[i].deSerialize(*d, version);
704 else if(version <= 10)
706 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
709 is.read((char*)&t8, 1);
713 // Uncompress and set material data
714 std::ostringstream os(std::ios_base::binary);
715 decompress(is, os, version);
716 std::string s = os.str();
717 if(s.size() != nodecount)
718 throw SerializationError
719 ("MapBlock::deSerialize: invalid format");
720 for(u32 i=0; i<s.size(); i++)
726 // Uncompress and set param data
727 std::ostringstream os(std::ios_base::binary);
728 decompress(is, os, version);
729 std::string s = os.str();
730 if(s.size() != nodecount)
731 throw SerializationError
732 ("MapBlock::deSerialize: invalid format");
733 for(u32 i=0; i<s.size(); i++)
735 data[i].param = s[i];
741 // Uncompress and set param2 data
742 std::ostringstream os(std::ios_base::binary);
743 decompress(is, os, version);
744 std::string s = os.str();
745 if(s.size() != nodecount)
746 throw SerializationError
747 ("MapBlock::deSerialize: invalid format");
748 for(u32 i=0; i<s.size(); i++)
750 data[i].param2 = s[i];
754 // All other versions (newest)
757 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
760 is.read((char*)&flags, 1);
761 is_underground = (flags & 0x01) ? true : false;
762 m_day_night_differs = (flags & 0x02) ? true : false;
763 m_lighting_expired = (flags & 0x04) ? true : false;
765 m_generated = (flags & 0x08) ? false : true;
768 std::ostringstream os(std::ios_base::binary);
769 decompress(is, os, version);
770 std::string s = os.str();
771 if(s.size() != nodecount*3)
772 throw SerializationError
773 ("MapBlock::deSerialize: decompress resulted in size"
774 " other than nodecount*3");
777 for(u32 i=0; i<nodecount; i++)
782 for(u32 i=0; i<nodecount; i++)
784 data[i].param = s[i+nodecount];
787 for(u32 i=0; i<nodecount; i++)
789 data[i].param2 = s[i+nodecount*2];
801 std::string data = deSerializeString(is);
802 std::istringstream iss(data, std::ios_base::binary);
803 m_node_metadata.deSerialize(iss);
807 //std::string data = deSerializeLongString(is);
808 std::ostringstream oss(std::ios_base::binary);
809 decompressZlib(is, oss);
810 std::istringstream iss(oss.str(), std::ios_base::binary);
811 m_node_metadata.deSerialize(iss);
814 catch(SerializationError &e)
816 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
817 <<" while deserializing node metadata"<<std::endl;
823 Translate nodes as specified in the translate_to fields of
826 NOTE: This isn't really used. Should it be removed?
828 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
830 MapNode &n = data[i];
832 MapNode *translate_to = content_features(n.d).translate_to;
835 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
836 <<translate_to->d<<std::endl;
842 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
844 // Versions up from 9 have block objects.
847 //serializeObjects(os, version); // DEPRECATED
852 // Versions up from 15 have static objects.
855 m_static_objects.serialize(os);
861 writeU32(os, getTimestamp());
865 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
868 Versions up from 9 have block objects.
872 updateObjects(is, version, NULL, 0);
876 Versions up from 15 have static objects.
880 m_static_objects.deSerialize(is);
886 setTimestamp(readU32(is));
890 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);