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.
27 #include "nodemetadata.h"
34 MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
35 m_node_metadata(new NodeMetadataList),
39 m_modified(MOD_STATE_WRITE_NEEDED),
40 is_underground(false),
41 m_lighting_expired(true),
42 m_day_night_differs(false),
44 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
52 m_mesh_expired = false;
55 m_temp_mods_mutex.Init();
63 JMutexAutoLock lock(mesh_mutex);
73 delete m_node_metadata;
79 bool MapBlock::isValidPositionParent(v3s16 p)
81 if(isValidPosition(p))
86 return m_parent->isValidPosition(getPosRelative() + p);
90 MapNode MapBlock::getNodeParent(v3s16 p)
92 if(isValidPosition(p) == false)
94 return m_parent->getNode(getPosRelative() + p);
99 throw InvalidPositionException();
100 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
104 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
106 if(isValidPosition(p) == false)
108 m_parent->setNode(getPosRelative() + p, n);
113 throw InvalidPositionException();
114 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
118 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
120 if(isValidPosition(p) == false)
123 return m_parent->getNode(getPosRelative() + p);
125 catch(InvalidPositionException &e)
127 return MapNode(CONTENT_IGNORE);
134 return MapNode(CONTENT_IGNORE);
136 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
143 void MapBlock::updateMesh(u32 daynight_ratio)
147 DEBUG: If mesh has been generated, don't generate it again
150 JMutexAutoLock meshlock(mesh_mutex);
157 data.fill(daynight_ratio, this);
159 scene::SMesh *mesh_new = makeMapBlockMesh(&data, m_gamedef);
165 replaceMesh(mesh_new);
170 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
174 //scene::SMesh *mesh_old = mesh[daynight_i];
175 //mesh[daynight_i] = mesh_new;
177 scene::SMesh *mesh_old = mesh;
179 setMeshExpired(false);
183 // Remove hardware buffers of meshbuffers of mesh
184 // NOTE: No way, this runs in a different thread and everything
185 /*u32 c = mesh_old->getMeshBufferCount();
186 for(u32 i=0; i<c; i++)
188 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
191 /*dstream<<"mesh_old->getReferenceCount()="
192 <<mesh_old->getReferenceCount()<<std::endl;
193 u32 c = mesh_old->getMeshBufferCount();
194 for(u32 i=0; i<c; i++)
196 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
197 dstream<<"buf->getReferenceCount()="
198 <<buf->getReferenceCount()<<std::endl;
213 Propagates sunlight down through the block.
214 Doesn't modify nodes that are not affected by sunlight.
216 Returns false if sunlight at bottom block is invalid.
217 Returns true if sunlight at bottom block is valid.
218 Returns true if bottom block doesn't exist.
220 If there is a block above, continues from it.
221 If there is no block above, assumes there is sunlight, unless
222 is_underground is set or highest node is water.
224 All sunlighted nodes are added to light_sources.
226 if remove_light==true, sets non-sunlighted nodes black.
228 if black_air_left!=NULL, it is set to true if non-sunlighted
229 air is left in block.
231 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
232 bool remove_light, bool *black_air_left)
234 INodeDefManager *nodemgr = m_gamedef->ndef();
236 // Whether the sunlight at the top of the bottom block is valid
237 bool block_below_is_valid = true;
239 v3s16 pos_relative = getPosRelative();
241 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
243 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
246 bool no_sunlight = false;
247 bool no_top_block = false;
248 // Check if node above block has sunlight
250 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
251 if(n.getContent() == CONTENT_IGNORE)
254 no_sunlight = is_underground;
256 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
261 catch(InvalidPositionException &e)
265 // NOTE: This makes over-ground roofed places sunlighted
266 // Assume sunlight, unless is_underground==true
273 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
274 //if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
275 if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
280 // NOTE: As of now, this just would make everything dark.
282 //no_sunlight = true;
285 #if 0 // Doesn't work; nothing gets light.
286 bool no_sunlight = true;
287 bool no_top_block = false;
288 // Check if node above block has sunlight
290 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
291 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
296 catch(InvalidPositionException &e)
302 /*std::cout<<"("<<x<<","<<z<<"): "
303 <<"no_top_block="<<no_top_block
304 <<", is_underground="<<is_underground
305 <<", no_sunlight="<<no_sunlight
308 s16 y = MAP_BLOCKSIZE-1;
310 // This makes difference to diminishing in water.
311 bool stopped_to_solid_object = false;
313 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
318 MapNode &n = getNodeRef(pos);
320 if(current_light == 0)
324 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
326 // Do nothing: Sunlight is continued
328 else if(nodemgr->get(n).light_propagates == false)
330 /*// DEPRECATED TODO: REMOVE
333 bool upper_is_air = false;
336 if(getNodeParent(pos+v3s16(0,1,0)).getContent() == CONTENT_AIR)
339 catch(InvalidPositionException &e)
342 // Turn mud into grass
343 if(upper_is_air && n.getContent() == CONTENT_MUD
344 && current_light == LIGHT_SUN)
350 // A solid object is on the way.
351 stopped_to_solid_object = true;
359 current_light = diminish_light(current_light);
362 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
364 if(current_light > old_light || remove_light)
366 n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
369 if(diminish_light(current_light) != 0)
371 light_sources.insert(pos_relative + pos, true);
374 if(current_light == 0 && stopped_to_solid_object)
378 *black_air_left = true;
383 // Whether or not the block below should see LIGHT_SUN
384 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
387 If the block below hasn't already been marked invalid:
389 Check if the node below the block has proper sunlight at top.
390 If not, the block below is invalid.
392 Ignore non-transparent nodes as they always have no light
396 if(block_below_is_valid)
398 MapNode n = getNodeParent(v3s16(x, -1, z));
399 if(nodemgr->get(n).light_propagates)
401 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
402 && sunlight_should_go_down == false)
403 block_below_is_valid = false;
404 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
405 && sunlight_should_go_down == true)
406 block_below_is_valid = false;
410 catch(InvalidPositionException &e)
412 /*std::cout<<"InvalidBlockException for bottom block node"
414 // Just no block below, no need to panic.
419 return block_below_is_valid;
423 void MapBlock::copyTo(VoxelManipulator &dst)
425 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
426 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
428 // Copy from data to VoxelManipulator
429 dst.copyFrom(data, data_area, v3s16(0,0,0),
430 getPosRelative(), data_size);
433 void MapBlock::copyFrom(VoxelManipulator &dst)
435 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
436 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
438 // Copy from VoxelManipulator to data
439 dst.copyTo(data, data_area, v3s16(0,0,0),
440 getPosRelative(), data_size);
443 void MapBlock::updateDayNightDiff()
445 INodeDefManager *nodemgr = m_gamedef->ndef();
449 m_day_night_differs = false;
453 bool differs = false;
456 Check if any lighting value differs
458 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
460 MapNode &n = data[i];
461 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
469 If some lighting values differ, check if the whole thing is
470 just air. If it is, differ = false
474 bool only_air = true;
475 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
477 MapNode &n = data[i];
478 if(n.getContent() != CONTENT_AIR)
488 // Set member variable
489 m_day_night_differs = differs;
492 s16 MapBlock::getGroundLevel(v2s16 p2d)
498 s16 y = MAP_BLOCKSIZE-1;
501 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
502 if(m_gamedef->ndef()->get(n).walkable)
504 if(y == MAP_BLOCKSIZE-1)
512 catch(InvalidPositionException &e)
522 void MapBlock::serialize(std::ostream &os, u8 version)
524 if(!ser_ver_supported(version))
525 throw VersionMismatchException("ERROR: MapBlock format not supported");
529 throw SerializationError("ERROR: Not writing dummy block.");
532 // These have no compression
533 if(version <= 3 || version == 5 || version == 6)
535 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
537 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
538 SharedBuffer<u8> dest(buflen);
540 dest[0] = is_underground;
541 for(u32 i=0; i<nodecount; i++)
543 u32 s = 1 + i * MapNode::serializedLength(version);
544 data[i].serialize(&dest[s], version);
547 os.write((char*)*dest, dest.getSize());
549 else if(version <= 10)
553 Compress the materials and the params separately.
557 os.write((char*)&is_underground, 1);
559 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
561 // Get and compress materials
562 SharedBuffer<u8> materialdata(nodecount);
563 for(u32 i=0; i<nodecount; i++)
565 materialdata[i] = data[i].param0;
567 compress(materialdata, os, version);
569 // Get and compress lights
570 SharedBuffer<u8> lightdata(nodecount);
571 for(u32 i=0; i<nodecount; i++)
573 lightdata[i] = data[i].param1;
575 compress(lightdata, os, version);
579 // Get and compress param2
580 SharedBuffer<u8> param2data(nodecount);
581 for(u32 i=0; i<nodecount; i++)
583 param2data[i] = data[i].param2;
585 compress(param2data, os, version);
588 // All other versions (newest)
595 if(m_day_night_differs)
597 if(m_lighting_expired)
601 if(m_generated == false)
604 os.write((char*)&flags, 1);
606 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
613 SharedBuffer<u8> databuf_nodelist(nodecount*3);
614 for(u32 i=0; i<nodecount; i++)
616 data[i].serialize(&databuf_nodelist[i*3], version);
619 // Create buffer with different parameters sorted
620 SharedBuffer<u8> databuf(nodecount*3);
621 for(u32 i=0; i<nodecount; i++)
623 databuf[i] = databuf_nodelist[i*3];
624 databuf[i+nodecount] = databuf_nodelist[i*3+1];
625 databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
629 Compress data to output stream
632 compress(databuf, os, version);
642 std::ostringstream oss(std::ios_base::binary);
643 m_node_metadata->serialize(oss);
644 os<<serializeString(oss.str());
646 // This will happen if the string is longer than 65535
647 catch(SerializationError &e)
649 // Use an empty string
650 os<<serializeString("");
655 std::ostringstream oss(std::ios_base::binary);
656 m_node_metadata->serialize(oss);
657 compressZlib(oss.str(), os);
658 //os<<serializeLongString(oss.str());
664 void MapBlock::deSerialize(std::istream &is, u8 version)
666 INodeDefManager *nodemgr = m_gamedef->ndef();
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, nodemgr);
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++)
722 data[i].param0 = s[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].param1 = 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");
776 // deserialize nodes from buffer
777 for(u32 i=0; i<nodecount; i++)
781 buf[1] = s[i+nodecount];
782 buf[2] = s[i+nodecount*2];
783 data[i].deSerialize(buf, version, m_gamedef->getNodeDefManager());
795 std::string data = deSerializeString(is);
796 std::istringstream iss(data, std::ios_base::binary);
797 m_node_metadata->deSerialize(iss, m_gamedef);
801 //std::string data = deSerializeLongString(is);
802 std::ostringstream oss(std::ios_base::binary);
803 decompressZlib(is, oss);
804 std::istringstream iss(oss.str(), std::ios_base::binary);
805 m_node_metadata->deSerialize(iss, m_gamedef);
808 catch(SerializationError &e)
810 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
811 <<" while deserializing node metadata"<<std::endl;
817 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
819 // Versions up from 9 have block objects. (DEPRECATED)
826 // Versions up from 15 have static objects.
829 m_static_objects.serialize(os);
835 writeU32(os, getTimestamp());
839 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
842 Versions up from 9 have block objects. (DEPRECATED)
846 u16 count = readU16(is);
847 // Not supported and length not known if count is not 0
849 dstream<<"WARNING: MapBlock::deSerializeDiskExtra(): "
850 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
856 Versions up from 15 have static objects.
860 m_static_objects.deSerialize(is);
866 setTimestamp(readU32(is));
870 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
875 Get a quick string to describe what a block actually contains
877 std::string analyze_block(MapBlock *block)
884 std::ostringstream desc;
886 v3s16 p = block->getPos();
888 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
891 switch(block->getModified())
893 case MOD_STATE_CLEAN:
896 case MOD_STATE_WRITE_AT_UNLOAD:
897 desc<<"WRITE_AT_UNLOAD, ";
899 case MOD_STATE_WRITE_NEEDED:
900 desc<<"WRITE_NEEDED, ";
903 desc<<"unknown getModified()="+itos(block->getModified())+", ";
906 if(block->isGenerated())
907 desc<<"is_gen [X], ";
909 desc<<"is_gen [ ], ";
911 if(block->getIsUnderground())
917 if(block->getMeshExpired())
918 desc<<"mesh_exp [X], ";
920 desc<<"mesh_exp [ ], ";
923 if(block->getLightingExpired())
924 desc<<"lighting_exp [X], ";
926 desc<<"lighting_exp [ ], ";
934 // We'll just define the numbers here, don't want to include
936 const content_t content_water = 2;
937 const content_t content_watersource = 9;
938 const content_t content_tree = 0x801;
939 const content_t content_leaves = 0x802;
940 const content_t content_jungletree = 0x815;
942 bool full_ignore = true;
943 bool some_ignore = false;
944 bool full_air = true;
945 bool some_air = false;
948 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
949 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
950 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
953 MapNode n = block->getNode(p);
954 content_t c = n.getContent();
955 if(c == CONTENT_IGNORE)
963 if(c == content_tree || c == content_jungletree
964 || c == content_leaves)
966 if(c == content_water
967 || c == content_watersource)
973 std::ostringstream ss;
976 ss<<"IGNORE (full), ";
990 if(ss.str().size()>=2)
991 desc<<ss.str().substr(0, ss.str().size()-2);
996 return desc.str().substr(0, desc.str().size()-2);