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)
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::stepObjects(float dtime, bool server, u32 daynight_ratio)
442 m_objects.step(dtime, server, daynight_ratio);
445 raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
449 void MapBlock::updateDayNightDiff()
453 m_day_night_differs = false;
457 bool differs = false;
460 Check if any lighting value differs
462 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
464 MapNode &n = data[i];
465 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
473 If some lighting values differ, check if the whole thing is
474 just air. If it is, differ = false
478 bool only_air = true;
479 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
481 MapNode &n = data[i];
482 if(n.getContent() != CONTENT_AIR)
492 // Set member variable
493 m_day_night_differs = differs;
496 s16 MapBlock::getGroundLevel(v2s16 p2d)
502 s16 y = MAP_BLOCKSIZE-1;
505 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
506 if(content_features(n).walkable)
508 if(y == MAP_BLOCKSIZE-1)
516 catch(InvalidPositionException &e)
526 void MapBlock::serialize(std::ostream &os, u8 version)
528 if(!ser_ver_supported(version))
529 throw VersionMismatchException("ERROR: MapBlock format not supported");
533 throw SerializationError("ERROR: Not writing dummy block.");
536 // These have no compression
537 if(version <= 3 || version == 5 || version == 6)
539 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
541 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
542 SharedBuffer<u8> dest(buflen);
544 dest[0] = is_underground;
545 for(u32 i=0; i<nodecount; i++)
547 u32 s = 1 + i * MapNode::serializedLength(version);
548 data[i].serialize(&dest[s], version);
551 os.write((char*)*dest, dest.getSize());
553 else if(version <= 10)
557 Compress the materials and the params separately.
561 os.write((char*)&is_underground, 1);
563 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
565 // Get and compress materials
566 SharedBuffer<u8> materialdata(nodecount);
567 for(u32 i=0; i<nodecount; i++)
569 materialdata[i] = data[i].param0;
571 compress(materialdata, os, version);
573 // Get and compress lights
574 SharedBuffer<u8> lightdata(nodecount);
575 for(u32 i=0; i<nodecount; i++)
577 lightdata[i] = data[i].param1;
579 compress(lightdata, os, version);
583 // Get and compress param2
584 SharedBuffer<u8> param2data(nodecount);
585 for(u32 i=0; i<nodecount; i++)
587 param2data[i] = data[i].param2;
589 compress(param2data, os, version);
592 // All other versions (newest)
599 if(m_day_night_differs)
601 if(m_lighting_expired)
605 if(m_generated == false)
608 os.write((char*)&flags, 1);
610 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
617 SharedBuffer<u8> databuf_nodelist(nodecount*3);
618 for(u32 i=0; i<nodecount; i++)
620 data[i].serialize(&databuf_nodelist[i*3], version);
623 // Create buffer with different parameters sorted
624 SharedBuffer<u8> databuf(nodecount*3);
625 for(u32 i=0; i<nodecount; i++)
627 databuf[i] = databuf_nodelist[i*3];
628 databuf[i+nodecount] = databuf_nodelist[i*3+1];
629 databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
633 Compress data to output stream
636 compress(databuf, os, version);
646 std::ostringstream oss(std::ios_base::binary);
647 m_node_metadata.serialize(oss);
648 os<<serializeString(oss.str());
650 // This will happen if the string is longer than 65535
651 catch(SerializationError &e)
653 // Use an empty string
654 os<<serializeString("");
659 std::ostringstream oss(std::ios_base::binary);
660 m_node_metadata.serialize(oss);
661 compressZlib(oss.str(), os);
662 //os<<serializeLongString(oss.str());
668 void MapBlock::deSerialize(std::istream &is, u8 version)
670 if(!ser_ver_supported(version))
671 throw VersionMismatchException("ERROR: MapBlock format not supported");
673 // These have no lighting info
676 setLightingExpired(true);
679 // These have no "generated" field
685 // These have no compression
686 if(version <= 3 || version == 5 || version == 6)
688 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
692 throw SerializationError
693 ("MapBlock::deSerialize: no enough input data");
694 is_underground = tmp;
695 for(u32 i=0; i<nodecount; i++)
697 s32 len = MapNode::serializedLength(version);
698 SharedBuffer<u8> d(len);
699 is.read((char*)*d, len);
700 if(is.gcount() != len)
701 throw SerializationError
702 ("MapBlock::deSerialize: no enough input data");
703 data[i].deSerialize(*d, version);
706 else if(version <= 10)
708 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
711 is.read((char*)&t8, 1);
715 // Uncompress and set material 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].param0 = s[i];
728 // Uncompress and set param data
729 std::ostringstream os(std::ios_base::binary);
730 decompress(is, os, version);
731 std::string s = os.str();
732 if(s.size() != nodecount)
733 throw SerializationError
734 ("MapBlock::deSerialize: invalid format");
735 for(u32 i=0; i<s.size(); i++)
737 data[i].param1 = s[i];
743 // Uncompress and set param2 data
744 std::ostringstream os(std::ios_base::binary);
745 decompress(is, os, version);
746 std::string s = os.str();
747 if(s.size() != nodecount)
748 throw SerializationError
749 ("MapBlock::deSerialize: invalid format");
750 for(u32 i=0; i<s.size(); i++)
752 data[i].param2 = s[i];
756 // All other versions (newest)
759 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
762 is.read((char*)&flags, 1);
763 is_underground = (flags & 0x01) ? true : false;
764 m_day_night_differs = (flags & 0x02) ? true : false;
765 m_lighting_expired = (flags & 0x04) ? true : false;
767 m_generated = (flags & 0x08) ? false : true;
770 std::ostringstream os(std::ios_base::binary);
771 decompress(is, os, version);
772 std::string s = os.str();
773 if(s.size() != nodecount*3)
774 throw SerializationError
775 ("MapBlock::deSerialize: decompress resulted in size"
776 " other than nodecount*3");
778 // deserialize nodes from buffer
779 for(u32 i=0; i<nodecount; i++)
783 buf[1] = s[i+nodecount];
784 buf[2] = s[i+nodecount*2];
785 data[i].deSerialize(buf, version);
797 std::string data = deSerializeString(is);
798 std::istringstream iss(data, std::ios_base::binary);
799 m_node_metadata.deSerialize(iss);
803 //std::string data = deSerializeLongString(is);
804 std::ostringstream oss(std::ios_base::binary);
805 decompressZlib(is, oss);
806 std::istringstream iss(oss.str(), std::ios_base::binary);
807 m_node_metadata.deSerialize(iss);
810 catch(SerializationError &e)
812 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
813 <<" while deserializing node metadata"<<std::endl;
819 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
821 // Versions up from 9 have block objects.
824 //serializeObjects(os, version); // DEPRECATED
829 // Versions up from 15 have static objects.
832 m_static_objects.serialize(os);
838 writeU32(os, getTimestamp());
842 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
845 Versions up from 9 have block objects.
849 updateObjects(is, version, NULL, 0);
853 Versions up from 15 have static objects.
857 m_static_objects.deSerialize(is);
863 setTimestamp(readU32(is));
867 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
872 Get a quick string to describe what a block actually contains
874 std::string analyze_block(MapBlock *block)
881 std::ostringstream desc;
883 v3s16 p = block->getPos();
885 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
888 switch(block->getModified())
890 case MOD_STATE_CLEAN:
893 case MOD_STATE_WRITE_AT_UNLOAD:
894 desc<<"WRITE_AT_UNLOAD, ";
896 case MOD_STATE_WRITE_NEEDED:
897 desc<<"WRITE_NEEDED, ";
900 desc<<"unknown getModified()="+itos(block->getModified())+", ";
903 if(block->isGenerated())
904 desc<<"is_gen [X], ";
906 desc<<"is_gen [ ], ";
908 if(block->getIsUnderground())
914 if(block->getMeshExpired())
915 desc<<"mesh_exp [X], ";
917 desc<<"mesh_exp [ ], ";
920 if(block->getLightingExpired())
921 desc<<"lighting_exp [X], ";
923 desc<<"lighting_exp [ ], ";
931 // We'll just define the numbers here, don't want to include
933 const content_t content_water = 2;
934 const content_t content_watersource = 9;
935 const content_t content_tree = 0x801;
936 const content_t content_leaves = 0x802;
937 const content_t content_jungletree = 0x815;
939 bool full_ignore = true;
940 bool some_ignore = false;
941 bool full_air = true;
942 bool some_air = false;
945 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
946 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
947 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
950 MapNode n = block->getNode(p);
951 content_t c = n.getContent();
952 if(c == CONTENT_IGNORE)
960 if(c == content_tree || c == content_jungletree
961 || c == content_leaves)
963 if(c == content_water
964 || c == content_watersource)
970 std::ostringstream ss;
973 ss<<"IGNORE (full), ";
987 if(ss.str().size()>=2)
988 desc<<ss.str().substr(0, ss.str().size()-2);
993 return desc.str().substr(0, desc.str().size()-2);