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):
35 is_underground(false),
36 m_lighting_expired(true),
37 m_day_night_differs(false),
38 //m_not_fully_generated(false),
40 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.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
249 catch(InvalidPositionException &e)
253 // NOTE: This makes over-ground roofed places sunlighted
254 // Assume sunlight, unless is_underground==true
261 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
262 //if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
263 if(content_features(n.d).sunlight_propagates == false)
268 // NOTE: As of now, this just would make everything dark.
270 //no_sunlight = true;
273 #if 0 // Doesn't work; nothing gets light.
274 bool no_sunlight = true;
275 bool no_top_block = false;
276 // Check if node above block has sunlight
278 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
279 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
284 catch(InvalidPositionException &e)
290 /*std::cout<<"("<<x<<","<<z<<"): "
291 <<"no_top_block="<<no_top_block
292 <<", is_underground="<<is_underground
293 <<", no_sunlight="<<no_sunlight
296 s16 y = MAP_BLOCKSIZE-1;
298 // This makes difference to diminishing in water.
299 bool stopped_to_solid_object = false;
301 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
306 MapNode &n = getNodeRef(pos);
308 if(current_light == 0)
312 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
314 // Do nothing: Sunlight is continued
316 else if(n.light_propagates() == false)
318 /*// DEPRECATED TODO: REMOVE
321 bool upper_is_air = false;
324 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
327 catch(InvalidPositionException &e)
330 // Turn mud into grass
331 if(upper_is_air && n.d == CONTENT_MUD
332 && current_light == LIGHT_SUN)
338 // A solid object is on the way.
339 stopped_to_solid_object = true;
347 current_light = diminish_light(current_light);
350 u8 old_light = n.getLight(LIGHTBANK_DAY);
352 if(current_light > old_light || remove_light)
354 n.setLight(LIGHTBANK_DAY, current_light);
357 if(diminish_light(current_light) != 0)
359 light_sources.insert(pos_relative + pos, true);
362 if(current_light == 0 && stopped_to_solid_object)
366 *black_air_left = true;
371 // Whether or not the block below should see LIGHT_SUN
372 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
375 If the block below hasn't already been marked invalid:
377 Check if the node below the block has proper sunlight at top.
378 If not, the block below is invalid.
380 Ignore non-transparent nodes as they always have no light
384 if(block_below_is_valid)
386 MapNode n = getNodeParent(v3s16(x, -1, z));
387 if(n.light_propagates())
389 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
390 && sunlight_should_go_down == false)
391 block_below_is_valid = false;
392 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
393 && sunlight_should_go_down == true)
394 block_below_is_valid = false;
398 catch(InvalidPositionException &e)
400 /*std::cout<<"InvalidBlockException for bottom block node"
402 // Just no block below, no need to panic.
407 return block_below_is_valid;
411 void MapBlock::copyTo(VoxelManipulator &dst)
413 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
414 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
416 // Copy from data to VoxelManipulator
417 dst.copyFrom(data, data_area, v3s16(0,0,0),
418 getPosRelative(), data_size);
421 void MapBlock::copyFrom(VoxelManipulator &dst)
423 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
424 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
426 // Copy from VoxelManipulator to data
427 dst.copyTo(data, data_area, v3s16(0,0,0),
428 getPosRelative(), data_size);
431 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
436 m_objects.step(dtime, server, daynight_ratio);
442 void MapBlock::updateDayNightDiff()
446 m_day_night_differs = false;
450 bool differs = false;
453 Check if any lighting value differs
455 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
457 MapNode &n = data[i];
458 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
466 If some lighting values differ, check if the whole thing is
467 just air. If it is, differ = false
471 bool only_air = true;
472 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
474 MapNode &n = data[i];
475 if(n.d != CONTENT_AIR)
485 // Set member variable
486 m_day_night_differs = differs;
489 s16 MapBlock::getGroundLevel(v2s16 p2d)
495 s16 y = MAP_BLOCKSIZE-1;
498 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
499 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
501 if(y == MAP_BLOCKSIZE-1)
509 catch(InvalidPositionException &e)
519 void MapBlock::serialize(std::ostream &os, u8 version)
521 if(!ser_ver_supported(version))
522 throw VersionMismatchException("ERROR: MapBlock format not supported");
526 throw SerializationError("ERROR: Not writing dummy block.");
529 // These have no compression
530 if(version <= 3 || version == 5 || version == 6)
532 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
534 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
535 SharedBuffer<u8> dest(buflen);
537 dest[0] = is_underground;
538 for(u32 i=0; i<nodecount; i++)
540 u32 s = 1 + i * MapNode::serializedLength(version);
541 data[i].serialize(&dest[s], version);
544 os.write((char*)*dest, dest.getSize());
546 else if(version <= 10)
550 Compress the materials and the params separately.
554 os.write((char*)&is_underground, 1);
556 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
558 // Get and compress materials
559 SharedBuffer<u8> materialdata(nodecount);
560 for(u32 i=0; i<nodecount; i++)
562 materialdata[i] = data[i].d;
564 compress(materialdata, os, version);
566 // Get and compress lights
567 SharedBuffer<u8> lightdata(nodecount);
568 for(u32 i=0; i<nodecount; i++)
570 lightdata[i] = data[i].param;
572 compress(lightdata, os, version);
576 // Get and compress param2
577 SharedBuffer<u8> param2data(nodecount);
578 for(u32 i=0; i<nodecount; i++)
580 param2data[i] = data[i].param2;
582 compress(param2data, os, version);
585 // All other versions (newest)
592 if(m_day_night_differs)
594 if(m_lighting_expired)
596 os.write((char*)&flags, 1);
598 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
604 SharedBuffer<u8> databuf(nodecount*3);
607 for(u32 i=0; i<nodecount; i++)
609 databuf[i] = data[i].d;
613 for(u32 i=0; i<nodecount; i++)
615 databuf[i+nodecount] = data[i].param;
619 for(u32 i=0; i<nodecount; i++)
621 databuf[i+nodecount*2] = data[i].param2;
625 Compress data to output stream
628 compress(databuf, os, version);
638 std::ostringstream oss(std::ios_base::binary);
639 m_node_metadata.serialize(oss);
640 os<<serializeString(oss.str());
642 // This will happen if the string is longer than 65535
643 catch(SerializationError &e)
645 // Use an empty string
646 os<<serializeString("");
651 std::ostringstream oss(std::ios_base::binary);
652 m_node_metadata.serialize(oss);
653 compressZlib(oss.str(), os);
654 //os<<serializeLongString(oss.str());
660 void MapBlock::deSerialize(std::istream &is, u8 version)
662 if(!ser_ver_supported(version))
663 throw VersionMismatchException("ERROR: MapBlock format not supported");
665 // These have no lighting info
668 setLightingExpired(true);
671 // These have no compression
672 if(version <= 3 || version == 5 || version == 6)
674 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
678 throw SerializationError
679 ("MapBlock::deSerialize: no enough input data");
680 is_underground = tmp;
681 for(u32 i=0; i<nodecount; i++)
683 s32 len = MapNode::serializedLength(version);
684 SharedBuffer<u8> d(len);
685 is.read((char*)*d, len);
686 if(is.gcount() != len)
687 throw SerializationError
688 ("MapBlock::deSerialize: no enough input data");
689 data[i].deSerialize(*d, version);
692 else if(version <= 10)
694 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
697 is.read((char*)&t8, 1);
701 // Uncompress and set material data
702 std::ostringstream os(std::ios_base::binary);
703 decompress(is, os, version);
704 std::string s = os.str();
705 if(s.size() != nodecount)
706 throw SerializationError
707 ("MapBlock::deSerialize: invalid format");
708 for(u32 i=0; i<s.size(); i++)
714 // Uncompress and set param data
715 std::ostringstream os(std::ios_base::binary);
716 decompress(is, os, version);
717 std::string s = os.str();
718 if(s.size() != nodecount)
719 throw SerializationError
720 ("MapBlock::deSerialize: invalid format");
721 for(u32 i=0; i<s.size(); i++)
723 data[i].param = s[i];
729 // Uncompress and set param2 data
730 std::ostringstream os(std::ios_base::binary);
731 decompress(is, os, version);
732 std::string s = os.str();
733 if(s.size() != nodecount)
734 throw SerializationError
735 ("MapBlock::deSerialize: invalid format");
736 for(u32 i=0; i<s.size(); i++)
738 data[i].param2 = s[i];
742 // All other versions (newest)
745 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
748 is.read((char*)&flags, 1);
749 is_underground = (flags & 0x01) ? true : false;
750 m_day_night_differs = (flags & 0x02) ? true : false;
751 m_lighting_expired = (flags & 0x04) ? true : false;
754 std::ostringstream os(std::ios_base::binary);
755 decompress(is, os, version);
756 std::string s = os.str();
757 if(s.size() != nodecount*3)
758 throw SerializationError
759 ("MapBlock::deSerialize: decompress resulted in size"
760 " other than nodecount*3");
763 for(u32 i=0; i<nodecount; i++)
768 for(u32 i=0; i<nodecount; i++)
770 data[i].param = s[i+nodecount];
773 for(u32 i=0; i<nodecount; i++)
775 data[i].param2 = s[i+nodecount*2];
787 std::string data = deSerializeString(is);
788 std::istringstream iss(data, std::ios_base::binary);
789 m_node_metadata.deSerialize(iss);
793 //std::string data = deSerializeLongString(is);
794 std::ostringstream oss(std::ios_base::binary);
795 decompressZlib(is, oss);
796 std::istringstream iss(oss.str(), std::ios_base::binary);
797 m_node_metadata.deSerialize(iss);
800 catch(SerializationError &e)
802 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
803 <<" while deserializing node metadata"<<std::endl;
809 Translate nodes as specified in the translate_to fields of
812 NOTE: This isn't really used. Should it be removed?
814 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
816 MapNode &n = data[i];
818 MapNode *translate_to = content_features(n.d).translate_to;
821 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
822 <<translate_to->d<<std::endl;
828 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
830 // Versions up from 9 have block objects.
833 serializeObjects(os, version);
836 // Versions up from 15 have static objects.
839 m_static_objects.serialize(os);
845 writeU32(os, getTimestamp());
849 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
852 Versions up from 9 have block objects.
856 updateObjects(is, version, NULL, 0);
860 Versions up from 15 have static objects.
864 m_static_objects.deSerialize(is);
870 setTimestamp(readU32(is));
874 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);